ЭЛЕКТРОННАЯ БИБЛИОТЕКА КОАПП
Сборники Художественной, Технической, Справочной, Английской, Нормативной, Исторической, и др. литературы.



 

Часть 2

                                                               -- 1 --


               Предисловие
     -----------------------------------------------------------------

          Если вы  хотите   создавать   программы   мирового   уровня,
     написанные на Си, то эта книга - для вас!

          Меня зовут Герб Шилдт.  Я ветеран программирования во многих
     компаниях и программирую на Си более десяти лет.  Перед тем,  как
     начать  писать  эту  книгу,  я изучил большое количество удачного
     программного обеспечения,  пытаясь определить,  какие  черты  они
     имеют  в отличие от менее удачных программ. Я  хотел понять,  что
     делает одни программы более удачными, чем другие, аналогичные им.
     После  этого  прояснились  общие  черты.  Удачные  программы были
     написаны  людьми,  которые  не  только  имели  крепкую  хватку  в
     специальной  области,  но  и  в совершенстве освоили оборудование
     компьютера,   включая   операционную   систему    и    аппаратное
     обеспечение.  Только программист,  осуществляющий полный контроль
     над  ними  может  писать  программы  с  дружелюбным   интерфейсом
     пользователя  которые эффективно выполняются и дают пользователям
     большую гибкость.

          Эта книга открывает многие секреты,  используемые  мастерами
     программирования  для достижения профессиональных результатов.  С
     ее  помощью  вы  расширите  подходы  и  методы,  которые   делают
     программы  интересными.  После прочтения книги вы будете способны
     писать  программы,    которые     заслужат     внимание.    Здесь
     рассматриваются следующие вопросы:

     # Прямой доступ к памяти экрана для быстрого отображения
     # Исчезающие и иерархические (popup и pulldown) меню
     # Процедуры работы с окнами
     # Завершение программ и оставление их в памяти
     # Интерфейс с мышью
     # Графические функции, включая вращение объектов
     # Языковые интерпретаторы
     # Передача файлов через последовательный порт

          Эта книга для  любого  и  каждого  программиста  на  Си,  от
     новичка  до  профессионала.  Даже  если вы начинающий,  вы можете
     использовать функции и программы  из  этой  книги  без  понимания
     отдельных деталей их работы.  Более подготовленные читатели могут
     использовать эти программы как основу для своих приложений.

          Исходные тексты этой  книги  соответствуют  стандарту  ANSI,
     кроме  некоторых функций,  специфичных для ПК.  Таким образом все
     эти программы можно компилировать на любом  компиляторе,  который
     поддерживает стандарт. Я использовал для их разработки Турбо Си и
     Microsoft Си.

          Некоторые из  многих  полезных  и   интересных   функций   и
     программ, содержащихся в книги достаточно длинны.  Если вы, как и
     я,  возможно хотите использовать их,  но вам лень набивать их  на
     компьютере и потом выискивать неизбежные опечатки,  то для такого
     случая я предоставляю исходные коды,  содержащиеся в этой  книге,


                                "C" для профессиональных программистов
Глава I                                                       -- 2 --


     на дискете.  Цена услуги - 24.95$.  Заполните приложенную форму и
     пошлите  ее  по  указанному  адресу  вместе  с   оплатой.   Можно
     использовать карточки MasterCard и Visa.





















































                                "C" для профессиональных программистов
Глава I                                                       -- 3 --


                          ГЛАВА 1.
                          --------
               Исчезающие и иерархические меню.
     -----------------------------------------------------------------

          Одна из наиболее очевидных черт  профессионально  написанных
     программ - это использование исчезающих и иерархических меню. При
     правильном использовании,  эти меню дают  программам  дружелюбие,
     которое  пользователи  от  них  и  ожидают.  Хотя  по  существу и
     простые,  и  исчезающие,  и   иерархические   меню   представляют
     некоторые трудности в программировании.

          Создание исчезающих  и  иерархических  меню  требует прямого
     управления  экраном.  Хотя  основные  программы  меню   полностью
     мобильны,  программы  доступа  к  экрану  зависят от операционной
     системы  и  оборудования  и  не  используют  обычные  функции  Си
     ввода/вывода на консоль.  Программы видео доступа разработаны для
     работы с любым  компьютером,  использующим  ДОС  и  имеющим  BIOS
     операционной  системы  совместимый с IBM.  BIOS-ДОС выбран потому
     что он широко используется,  но вы можете применить основные идеи
     и в других системах.

          Даже  если   вас   сейчас   не   интересуют   исчезающие   и
     иерархические меню,  то вам следует прочитать часть этой главы, в
     которой  обсуждаются  видеоадаптеры,  знание многих основных идей
     необходимо для понимания последующих глав.






























                                "C" для профессиональных программистов
Глава I                                                       -- 4 --


               Что такое исчезающие и иерархические меню?
     -----------------------------------------------------------------

          Важно понимать  что  такое исчезающие и иерархические меню и
     чем  они  отличаются  от  стандартных  меню.  При   использовании
     стандартных  меню  экран  очищается или сдвигается,  и появляется
     меню.  Когда выбор сделан, экран опять очищается или сдвигается и
     программа продолжается. Выбор выполняется по номеру или по первой
     букве каждой альтернативы.

          Когда используется исчезающее или иерархическое меню, то оно
     покрывает прямо  содержимое  экрана.  После выбора режима,  экран
     возвращается в предыдущее состояние. Вы выбираете нужный режим из
     меню  одним  из  двух  способов:  (1)  нажимая  активную клавишу,
     которая является буквой или номером, связанным с выбором, или (2)
     используя    клавиши   управления   курсором   для   передвижения
     подсвеченного поля  и   клавишу   Ввод.   Обычно   текущее   поле
     показывается   в   инверсном   виде.   Основная   разница   между
     стандартными меню и исчезающими и иерархическими меню в  том, что
     стандартное меню прерывает программу.  Исчезающие и иерархические
     меню только приостанавливают текущие действия программы.  С точки
     зрения  пользователя  стандартное меню - прерывание концентрации,
     тогда  как  исчезающее  меню  -   просто   легкая   приостановка,
     концентрация внимания пользователя не нарушена.

          Разница между  исчезающими  и  иерархическими  меню  проста.
     Только одно исчезающее меню может быть на экране в  данный момент
     времени.  Оно используется когда меню имеет только один уровень в
     глубину,  это бывает,  когда выбор из меню не имеет подвыборов. С
     другой  стороны  несколько  иерархических меню могут быть активны
     одновременно.  Они используются когда выбор из одного меню  может
     потребовать  использования другого меню для определения некоторых
     альтернатив. Например, вы можете использовать иерархическое меню,
     если   вы   пишете  программу,  которая  определяет  фрукт.  Если
     пользователь выбрал "яблоко",  следующее меню предлагает  выбрать
     цвет   яблока,   а   третье   меню  высвечивает  яблоки,  которые
     удоволетворяют предыдущим выборам.

          Вы можете   представлять   исчезающее   меню   просто    как
     иерархическое  меню,  которое  не  имеет  подменю,  но разработка
     отдельных процедур для этих типов меню имеет то преимущество, что
     иерархическое  меню  требует значительно более сложной программы,
     чем простое исчезающее меню.

          Хотя имеется много способов  расположения  меню  на  экране,
     функции,  разработанные  в  этой  главе имеют наиболее общий вид.
     Этот метод помещает очередное  поле  меню  на  новую  строку  под
     первым полем.








                                "C" для профессиональных программистов
Глава I                                                       -- 5 --


               Работа видеоадаптеров.
     -----------------------------------------------------------------

          Из-за того,  что создание исчезающих  и  иерархических  меню
     требует  прямого  управления  экраном,  важно понимание адаптеров
     дисплея. Три основных типа адаптеров - это  одноцветный  адаптер,
     цветной/графический    адаптер    (CGA)   и   усовершенствованный
     графический адаптер  (EGA).  CGA  и  EGA  могут  иметь  несколько
     режимов   работы,  включая  40-  или  80-  символьный  текст  или
     графические операции.  Эти  режимы  показаны   в   таблице   1-1.
     Программы  меню,  разработанные  в  этой  главе,  разработаны для
     использования  режима  80-символьного  текста,  который  является
     наиболее общим  режимом  для общецелевых применений.  Это значит,
     что видео режим системы должен быть 2,  3 или  7.  Независимо  от
     используемого режима - координаты левого верхнего угла - 0,0.


     Таблица 1-1.
     ------------

     Режим    Тип                    Размеры          Адаптеры
     ---------------------------------------------------------------
      0       текст,ч/б              40*25            CGA,EGA
      1       текст 16 цветов        40*25            CGA,EGA
      2       текст ч/б              80*25            CGA,EGA
      3       текст 16 цветов        80*25            CGA,EGA
      4       графика 4 цвета        320*200          CGA,EGA
      5       графика 4 серых тона   320*200          CGA,EGA
      6       графика ч/б            640*200          CGA,EGA
      7       текст ч/б              80*25            монохромный
      8       графика 16 цветов      160*200          PCjr
      9       графика 16 цветов      320*200          PCjr
     10       графика 4 или 16 цв.   640*200          PCjr,EGA
     13       графика 16 цветов      320*200          EGA
     14       графика 16 цветов      640*200          EGA
     15       графика 4 цвета        640*350          EGA
     --------------------------------------------------------------


          Символы, выводимые  на   экран,   содержатся   в   некоторой
     зарезервированной  области  памяти  на  адаптере  дисплея.  Адрес
     одноцветной информации В0000000H. И CGA, и EGA хранят информацию,
     начиная  с  B80000000H.  (Они различны для того,  чтобы позволить
     использовать раздельно текстовый и  графический  экран  -  но  на
     практике  это  делается редко.) Хотя функции CGA и EGA различны в
     разных режимах, они одинаковы в режимах 2 и 3.

          Каждый символ,  выводимый на экран,  требует два байта видео
     памяти.  Первый байт содержит собственно символ,  второй содержит
     аттрибуты   экрана.   Для   цветного   экрана   байт   аттрибутов
     интерпретируется так,  как показано в таблице 1-2. Если у вас EGA
     или CGA, то по умолчанию принимается режим 3, и символы выводятся
     с байтом аттрибутов 7.  Это значение включает три основных цвета,
     производя для символа белый цвет.  Для переключения  в  инверсный


                                "C" для профессиональных программистов
Глава I                                                       -- 6 --


     режим  небходимо  выключить  три  основных  бита  и  включить три
     фоновых бита, что дает значение 70H.

          Одноцветный адаптер распознает биты мигания и интенсивности.
     К счастью,  он разработан так, что интерпретирует аттрибут 7, как
     нормальный текст (белое на черном) и  70Н  как  инверсное  видео.
     Кстати, значение 1 дает подчеркнутые символы.



     Таблица 1-2
     -----------
     Байт видеоадаптера

          Бит      Двоичная величина     Значение при установке
     ----------------------------------------------------------------
           0             1               голубой основной
           1             2               зеленый основной
           2             4               красный основной
           3             8               малая интенсивность
           4            16               голубой фоновый
           5            32               зеленый фоновый
           6            64               красный фоновый
           7           128               мигающий символ
     ----------------------------------------------------------------

          Каждый адаптер имеет по крайней мере в 4 раза больше памяти,
     чем необходимо для вывода  текста  в  80-символьном  режиме.  Для
     этого есть  две  причины.  Во-первых,  лишняя  память  нужна  для
     графики, (конечно кроме  одноцветного  адаптера).  Во-вторых  это
     позволяет  держать  в  памяти  несколько  экранов  и потом просто
     переключаться между ними по мере  необходимости.  Каждая  область
     памяти  называется  видеостраницей и эффект от переключения между
     видеостраницами впечатляющ.  По умолчанию при  инициализации  ДОС
     используется  страница 0,  и виртуально все приложения используют
     страницу 0.  По этой причине она используется  и  в  этой  главе.
     Однако,  вы можете использовать и другие страницы, если захотите.

          Имеется три  способа  доступа  к  видеоадаптеру.  Первый это
     через прерывание ДОС, которое достаточно медленно для исчезающего
     меню.  Второй - это через процедуры BIOS,  которые быстрее,  и на
     быстродействующих машинах,  таких,  как AT  или  PS/2  достаточно
     быстры,  если меню невелики.  Третий способ - это чтение и запись
     прямо в видеопамять,  что происходит  очень  быстро,  но  требует
     большей работы от вас. Эта глава рассматривает два разных подхода
     в видео процедурах.  Один использует BIOS, а другой прямой доступ
     к видеопамяти.









                                "C" для профессиональных программистов
Глава I                                                       -- 7 --


               Доступ к экрану через BIOS
     -----------------------------------------------------------------

          Из-за того,  что  исчезающие  и  иерархические  меню  должны
     сохранять   информацию  с  того  места  экрана,  на  котором  они
     расположены, и восстанавливать его после выбора,  вы должны иметь
     процедуры,  которые  сохраняют  и  загружают часть экрана.  Метод
     сохранения и восстановления части экрана,  рассматриваемый в этом
     разделе связан с вызовами двух встроенных в BIOS функций, которые
     читают и записывают символы на экран.

          Как вы знаете,  вызовы BIOS  могут  быть  очень  медленными.
     Однако,  они  (более  или  менее)  гарантируют  работу  на  любом
     компьютере,  который имеет BIOS,  совместимый с  IBM,  даже  если
     аппаратура  экрана  другая.  Позже  в этой главе вы узнаете,  как
     выполнять прямой  доступ  к  видеопамяти  на  IBM   PC   и   100%
     совместимых   машинах   для   того,   чтобы   увеличить  скорость
     выполнения. Однако,  использование прямого доступа к  видеопамяти
     снижает в некоторой степени переносимость, так как требуется 100%
     совместимость компьютера с IBM PC.  Программы меню, основанные на
     BIOS следует использовать в применениях,  которые требуют большей
     мобильности.


































                                "C" для профессиональных программистов
Глава I                                                       -- 8 --


               Использование int86()
     -----------------------------------------------------------------

          Вызовы BIOS  используют  программные прерывания.  BIOS имеет
     несколько различных прерываний для разных целей.  Одно из них  мы
     будем использовать для доступа к экрану. Это прерывание 16 (10Н),
     которое используется для доступа к дисплею. (Если вы не знакомы с
     доступом к BIOS, то вы найдете хорошее описание в моей книге "Си:
     Полный справочник",  Беркли, 1987). Как и многие прерывания BIOS,
     прерывание    16   имеет   несколько   режимов,   выбор   которых
     выполняется по значению  регистра  AH.  Если  функция  возвращает
     значение,  то  оно  заносится  в регистр AL.  Однако,  иногда для
     возвращения нескольких значений используются другие регистры. Для
     доступа  к  прерываниям BIOS вам придется использовать функцию Си
     int86(). (Некоторые компиляторы могут называть эту функцию другим
     именем,   но   MicroSoft  C  и  Турбо  Си  называют  ее  int86().
     Последующие рассуждения ориентированы на эти трансляторы,  но  вы
     можете их обобщить.

         Функция int86() имеет следующую форму:

      int int86(num,inregs,outregs)
      int num; /* номер прерывания */
      union REGS *inregs; /* входные значения регистров */
      union REGS *outregs; /* выходные значения регистров */

         Функция int86() возвращает значение  регистра  АХ.  Тип  REGS
     описывается в заголовке DOS.H. Этот тип показан здесь так, как он
     определен в Турбо Си, однако, он аналогично определен в MisroSoft
     C и в других компиляторах.

     struct WORDREGS {
      unsigned int ax, bx, cx, dx, si, di, cflag, flags;
     };
     struct BYTEREGS {
     unsigned char al, ah, bl, bh, cl, ch, dl, dh;
     };
     union REGS {
     struct WORDREGS x;
     struct BYTEREGS h;
     };

          Как вы можете видеть,  REGS - это объединение двух структур.
     Использование структуры WORDREGS позволяет рассматривать регистры
     ЦП как 16-битные числа.  BYTREGS дает вам доступ к  отдельным  8-
     битным регистрам.  Например, для доступа к прерыванию 16, функции
     5, вы должны использовать следующую последовательность.

     union REGS in,out;

     in.h.ah=5;
     int86(16,&in,&out);




                                "C" для профессиональных программистов
Глава I                                                       -- 9 --



               Сохранение части экрана.
     -----------------------------------------------------------------

          Для сохранения содержимого экрана,  должно быть прочитано  и
     запомнено текущее значение каждой позиции экрана.  Для считывания
     символа с определенной позиции  экрана,  используется  прерывание
     16,  функция  8,  которая  возвращает  символ  и  связанный с ним
     аттрибут текущей  позиции  курсора.  Для  считывания  символа   с
     определенного  места  экрана,  вы  должны  иметь способ установки
     курсора.  Хотя некоторые компиляторы Си поддерживают эту функцию,
     многие ее   не  имеют.  Тем  не  менее  показанная  ниже  функция
     goto_xy() может быть использована.  Она использует прерывание 16,
     функцию  2  с  координатой  столбца в DL и координатой ряда в DH.
     Видеостраница задается  в  ВН   (используется   страница   0   по
     умолчанию).

     /* установка курсора в x,y */
     void goto_xy(x,y)
     int x,y;
     {
      union REGS r;

      r.h.ah=2; /* функция установки курсора */
      r.h.dl=y; /* координата колонки       */
      r.h.dh=x; /* координата строки        */
      r.h.bh=0; /* видео страница           */
      int86(0x10,&r,&r);
     }

          Прерывание 16,  функция  8  возвращает  символ  из   текущей
     позиции курсора  в  AL и его атрибут в AH.  Функция save_video(),
     показанная здесь,  считывает часть экрана, сохраняет информацию в
     буфер, и очищает эту часть экрана.

     /* сохранение части экрана */
     void save_video(startx,endx,starty,endy,buf_ptr)
     int   startx,endx,starty,endy;
     unsigned int *buf_ptr;
     {
      union REGS r;
      register int i,j;

      for(i=starty;i [] = {
     "первая строка",
     "вторая строка",
      .
      .

      .
     "N-ая   строка" };

          Это описание   автоматически   заставляет   компилятор    Си
     поместить строки в таблицу строк.  Переменная указывает на первый
     символ первой строки в таблице.  Например,  это описание  создает
     переменную fruit (фрукт), которая указывает на "Я" в "Яблоко".

     char *fruit[] = {
      "Яблоко",
      "Апельсин",
      "Груша",
      "Грейпфрут",
      "Малина",
      "Клубника"
     };



                                "C" для профессиональных программистов
Глава I                                                      -- 14 --


























































                                "C" для профессиональных программистов
Глава I                                                      -- 15 --


               Высвечивание рамки
     -----------------------------------------------------------------
          Если нужна рамка,  то можно воспользоваться  нижеприведенной
     программой для  вывода рамки вокруг меню с заданными координатами
     левого верхнего и правого нижнего углов.  Она использует символы,
     которые  являются частью стандартного набора символов на машинах,
     совместимых с IBM. Если вы хотите, вы можете выбрать другие.

     void draw_border(startx,starty,endx,endy)
     int   startx,starty,endx,endy;
     {
        register int i;
        for(i=startx+1;i24)||(x<0)||(y>79)||(y<0)) {
       printf(" выход за пределы экрана");
       return -2;
      }
     /* вычисление размеров */
     len=0;
     for(i=0;i len) len=strlen(menu[i]);
     endy=len+2+y;
     endx=count+1+x;
     if((endx+1>24) || (endy+1>79)) {
       printf(" выход за пределы экрана");
       return -2;
      }
     /* размещение памяти для видео буфера */
     p=(unsigned int *)malloc((endx-x+1)*(endy-y+1));
      if(!p) exit(1); /* Вы можете здесь сами обработать ошибку */

     /* сохранение части экрана */
     void save_video(startx,endx,starty,endy,p);

     if(border) draw_border(x,y,endx,endy);

     /* высвечивание меню на своем месте */
     void display_menu(menu,x,y,count);

     /* ввести выбор пользователя */
     choice=get_resp(x,y,count,menu,keys)

     /* восстановление части экрана */
     void restore_video(startx,endx,starty,endy,p);
     free(p);


                                "C" для профессиональных программистов
Глава I                                                      -- 20 --


     return choice;
     }

          Как вы  можете  видеть,  popup()  проверяет выход за пределы
     экрана и слишком большой размер меню.  Она  возвращает  -2,  если
     возникла  одна  из  этих  ситуаций.  Из-за  того,  что get_resp()
     возвращает -1,  при  нажатии  клавиши  ESC,   возвращение   этого
     значения  функцией  popup следует рассматривать как "уничтожение"
     меню.  Если пользователь сделал выбор,  то возвращаемое  значение
     будет в пределах от 0 до count-1 с соответствием первому значению
     меню 0.  Как уже  указывалось,  popup()  использует  динамическое
     размещение памяти для обеспечения временной памяти для информации
     об экране.  Обычно это  лучший  подход,  но  вы  можете  свободно
     изменить его, если это важно для вашего приложения.










































                                "C" для профессиональных программистов
Глава I                                                      -- 21 --


               Общий обзор
     -----------------------------------------------------------------

          Простая программа,   показанная   здесь,   использует    все
     программы, разработанные для использования исчезающих меню. Вы не
     видели только функции cls(),  которая  очищает  экран.  Некоторые
     трансляторы Си не имеют функции для этого,  и если это так, то вы
     не можете использовать следующую программу  (в чистом виде).

     /* процедура исчезающего меню для работы в текстовом режиме */
     #include "stdio.h"
     #include "dos.h"
     #include "stdlib.h"

     #define BORDER 1
     #define ESC 27
     #define REV_VID 0x70
     #define NORM_VID  7

     void save_video(),restore_video();
     void goto_xy(),cls(),write_video();
     void display_menu(),draw_border();

     char *fruit[] = {
      "Яблоко",
      "Апельсин",
      "Груша",
      "Грейпфрут",
      "Малина",
      "Клубника"
     };

     char *color[]={
      "Красный",
      "Желтый",
      "Оранжевый",
      "Зеленый"
     };

     char *apple_type[] = {
      "Красный деликатес",
      "Джонатан",
      "Белый налив",
      "Антоновка"
     };

     main()
     {
      int i;

     cls();
     goto_xy(0,0);
     for(i=0;i<25;i++)
      printf("Это тест исчезающего меню\n");


                                "C" для профессиональных программистов
Глава I                                                      -- 22 --


     popup(fruit,"яагрмк",6,1,3,BORDER);
     popup(color,"кжоз",4,5,10,BORDER);
     popup(apple_type,"кдба",4,10,18,BORDER);
     }

     /* вывести исчезающее меню и вернуть выбор
        возвращает -2, если меню не может быть создано
        возвращает -1, если пользователь нажал клавишу ESC
        в остальных случаях она возвращает номер выбранной
        альтернативы, начиная с 0 */
     int popup(menu,keys,count,x,y,border)
     char *menu[];  /* текст меню */
     char *keys;    /* горячие клавиши */
     int count;     /* число альтернатив */
     int x,y;       /* координаты левого верхнего угла */
     int border;    /* если 0 то без рамки */
     {
      register int i,len;
      int endx endy choice;
      unsigned char *p;

      if((x>24)||(x<0)||(y>79)||(y<0)) {
       printf(" выход за пределы экрана");
       return -2;
      }
     /* вычисление размеров */
     len=0;
     for(i=0;i len) len=strlen(menu[i]);
     endy=len+2+y;
     endx=count+1+x;
     if((endx+1>24) || (endy+1>79)) {
       printf(" выход за пределы экрана");
       return -2;
      }
     /* размещение памяти для видео буфера */
     p=(unsigned int *)malloc((endx-x+1)*(endy-y+1));
      if(!p) exit(1); /* Вы можете здесь сами обработать ошибку */

     /* сохранение части экрана */
     void save_video(x,endx+1,y,endy+1,p);

     if(border) draw_border(x,y,endx,endy);

     /* высвечивание меню на своем месте */
     void display_menu(menu,x,y,count);

     /* ввести выбор пользователя */
     choice=get_resp(x,y,count,menu,keys)

     /* восстановление части экрана */
     void restore_video(x,endx+1,y,endy+1,p);
     free(p);
     return choice;


                                "C" для профессиональных программистов
Глава I                                                      -- 23 --


     }

     /* высвечивание меню на своем месте */
     void display_menu(menu,x,y,count)
     char *menu[];
     int x,y,count;
     {
      register int i;

     for(i=0;i24)||(x<0)||(y>79)||(y<0)) {
       printf(" выход за пределы экрана");
       return -2;
      }
     /* вычисление размеров */
     len=0;
     for(i=0;i len) len=strlen(menu[i]);
     endy=len+2+y;
     endx=count+1+x;


                                "C" для профессиональных программистов
Глава I                                                      -- 32 --


     if((endx+1>24) || (endy+1>79)) {
       printf(" выход за пределы экрана");
       return -2;
      }
     vmode = video_mode();
      if((vmode!=2) && (vmode!=3) && (vmode!=7)) {
        printf(" должен быть 80 символьный текстовый режим");
        exit(1);
     }

      /* присвоить соответствующий адрес видео памяти */
     if(vmode==7) vid_mem=(char far *)0xB0000000;
     else   vid_mem=(char far *)0xB8000000;
     /* размещение памяти для видео буфера */
     p=(unsigned int *)malloc((endx-x+1)*(endy-y+1));
      if(!p) exit(1); /* Вы можете здесь сами обработать ошибку */

     /* сохранение части экрана */
     void save_video(x,endx+1,y,endy+1,p);

     if(border) draw_border(x,y,endx,endy);

     /* высвечивание меню на своем месте */
     void display_menu(menu,x,y,count);

     /* ввести выбор пользователя */
     choice=get_resp(x,y,count,menu,keys)

     /* восстановление части экрана */
     void restore_video(x,endx+1,y,endy+1,p);
     free(p);
     return choice;
     }

     /* высвечивание меню на своем месте */
     void display_menu(menu,x,y,count)
     char *menu[];
     int x,y,count;
     {
      register int i;

     for(i=0;iMAX_FRAME) {
       printf("Слишком много меню");
       return 0;
     }
      if((x>24)||(x<0)||(y>79)||(y<0)) {
       printf(" выход за пределы экрана");
       return 0;
      }

     /* вычисление размеров */
     len=0;
     for(i=0;i len) len=strlen(menu[i]);
     endy=len+2+y;
     endx=count+1+x;
     if((endx+1>24) || (endy+1>79)) {
       printf(" выход за пределы экрана");
       return 0;
      }

     /* размещение памяти для видео буфера */
     p=(unsigned int *)malloc((endx-x+1)*(endy-y+1));
      if(!p) exit(1); /* Вы можете здесь сами обработать ошибку */

     /* создание фрейма */
     frame[num].startx=x;
     frame[num].endx=endx;
     frame[num].starty=y;
     frame[num].endy=endy;
     frame[num].p = p;
     frame[num].menu = (char **) menu;
     frame[num].border = border;
     frame[num].keys = keys;


                                "C" для профессиональных программистов
Глава I                                                      -- 40 --


     frame[num].count = count;
     frame[num].active =0;
     return 1;
     }

          Вы вызываете   make_menu   с   теми  же  аргументами,  какие
     используются в popup() кроме номера  меню,  который  должен  быть
     определен   в  первом  аргументе.  Этот  номер  используется  для
     идентификации меню.















































                                "C" для профессиональных программистов
Глава I                                                      -- 41 --


               Функция pulldown()
     -----------------------------------------------------------------

          Функция pulldown() показана здесь:

     /* Высветить меню и получить выбор
        возвращает -1, если пользователь нажал клавишу ESC
        в остальных случаях номер альтернативы, начиная с 0 */

     int pulldown(num)
     int num; /* номер фрейма */
     {
      int vmode,choice;

      vmode=video_mode();
      if((vmode!=2) && (vmode!=3) && (vmode!=7)) {
        printf(" должен быть 80 символьный текстовый режим");
        exit(1);
     }

      /* присвоить соответствующий адрес видео памяти */
     if(vmode==7) vid_mem=(char far *)0xB0000000;
     else   vid_mem=(char far *)0xB8000000;

     /* узнать активность окна */
     if(!frame[num].active) { /* не активно */
         save_video(num);
         frame[num].active= 1; /* установить признак активности */
     }

     if( frame[num].border) draw_worder(num);

     display_menu(num);  /* высветить меню */
     return get_resp(num);  /* возвратить выбор */
     }

          Для использования pulldown()  просто  передайте  номер  того
     меню,  которое  вы  хотите высветить.  Однако вы должны помнить о
     восстановлении окна,  используя  restore_video()  везде  в  своей
     программе.  Запомните,  что основное отличие иерархических меню в
     том,  что они позволяют двум или более меню оставаться на  экране
     одновременно  и  быть  потенциально  активными,  по мере того как
     пользователь  выбирает  опции.  Тем  не  менее  вам  не   следует
     восстанавливать  экран  до  того,  как  процесс  выбора полностью
     завершен.

          Обратите внимание,  что часть экрана, используемая для меню,
     сохраняется  только  если  признак  активности  равен 0.  Так как
     иерархическое меню может быть вызвано повторно,  экран не  должен
     сохраняться  много раз.  (Другими словами при повторном вхождении
     будет сохранено  само  меню,  записанное  поверх  первоначального
     содержимого экрана, которое уже сохранено.)




                                "C" для профессиональных программистов
Глава I                                                      -- 42 --



               Восстановление экрана
     -----------------------------------------------------------------

          Как и   другие   функции    поддержки    меню,    измененная
     restore_video   показанная  здесь,  преобразована  для  работы  с
     фреймами.  Поэтому  функции  restore_video()  теперь   передается
     только номер меню, что делает интерфейс более очевидным.

     /* восстановление части экрана */
     void restore_video(num)
     int   num;
     {
      register int i,j;
      char far *v, far *t;
      char *buf_ptr;

      buf_ptr=frame[num].p;
      v=vid_mem;
      t=v;
      for(i=frame[num].starty;iMAX_FRAME) {
       printf("Слишком много меню");
       return 0;
     }
      if((x>24)||(x<0)||(y>79)||(y<0)) {
       printf(" выход за пределы экрана");


                                "C" для профессиональных программистов
Глава I                                                      -- 46 --


       return 0;
      }

     /* вычисление размеров */
     len=0;
     for(i=0;i len) len=strlen(menu[i]);
     endy=len+2+y;
     endx=count+1+x;
     if((endx+1>24) || (endy+1>79)) {
       printf(" выход за пределы экрана");
       return 0;
      }

     /* размещение памяти для видео буфера */
     p=(unsigned int *)malloc((endx-x+1)*(endy-y+1));
      if(!p) exit(1); /* Вы можете здесь сами обработать ошибку */

     /* создание фрейма */
     frame[num].startx=x;
     frame[num].endx=endx;
     frame[num].starty=y;
     frame[num].endy=endy;
     frame[num].p = p;
     frame[num].menu = (char **) menu;
     frame[num].border = border;
     frame[num].keys = keys;
     frame[num].count = count;
     frame[num].active =0;
     return 1;
     }

     /* высвечивание меню на своем месте */
     void display_menu(num)
      int num;
     {
      char **m;
      register int i, x;

      x = frame[num].startx+1;
      m = frame[num].menu;

     for(i=0;i




?????? ???????????