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



 

Часть 10

                                  ГЛАВА 9                     -- 1 --
                                  -------

                            ИНТЕРФЕЙС С "МЫШЬЮ"
     -----------------------------------------------------------------

          Наиболее   популярным   устройством   ввода   данных   после
     клавиатуры является "мышь"  (mouse). Несмотря на то, что "мышь" и
     сходные  технологии,  такие  как "roller ball",  получили широкое
     распространение   лишь  в  последнее  время,  популярность "мыши"
     берет свое начало с момента выхода на  рынок очередной разработки
     фирмы  Apple  компьютера  Apple  Lisa,  в  котором  впервые  была
     применена технология "мышь" для работы с  пиктограммным (иконным)
     интерфейсом операционной  системы этого компьютера.  Модель Apple
     Lisa  произвела форменный переворот  в  фирме  Macintosh, которая
     пошла по пути использования "мыши"  и пиктограммного интерфейса в
     своих  программных продуктах.  Перед выходом на  рынок  серии IBM
     PS/2  "мышь",  по существу, была третьим дополнением к РС. Тем не
     менее уже при анонсировании системы IBM  PS/2 сообщалось, что она
     снабжена  портом  для  подключения  "мыши",   и  "мышь"  занимает
     значительное место среди РС.

          Наилучшее   использование   "мыши"   -   предмет  постоянных
     дискуссий.  Не все программисты  (или  пользователи) воспринимают
     пиктограмный  интерфейс.  В связи с тем,  что "мышь" впервые была
     использована   именно  с   пиктограммным   интерфейсом,   вопросы
     использования "мыши"  чаще всего сводятся именно к нему, а это в,
     свою очередь,  отталкивает от "мыши"  большой круг программистов,
     негативно относящихся к  такому  интерфейсу.  Однако  "мышь" ведь
     может  использоваться   и   без   такого   интерфейса.  Например,
     практически все  согласны  с  тем,  что  "мышь"  эффективно может
     использоваться при работе  с  интерактивной  графикой.

          Некоторые модели манипуляторов  типа  "мышь",  равно  как  и
     выполняемые  ими  функции,  могут  значительно отличаться друг от
     друга.  Поэтому заметим,  что все программы,  приведенные в  этой
     главе,  ориентированы  на  использование  "мыши" фирмы Microsoft,
     которая функционально идентична "мыши", используемой в моделях PS
     /2.  Для  обеспечения  интерфейса  с  "мышью" фирмы Microsoft вам
     необходимо  иметь  по  крайней  мере  саму  "мышь",   руководство
     пользователя  по программному обеспечению "мыши" (Microsoft Mouse
     Programmer's Reference Guide) и поставляемый с  этим руководством
     диск.  На  этом диске расположена специальная библиотека с именем
     MOUSE.LIB, выполняющая поддержку функционирования "мыши" на самом
     нижнем уровне. Мы будем использовать функции из этой библиотеки в
     качестве базовых функций при рассмотрении  программ, предлагаемых
     вам в этой главе.  вы должны помнить, что ваш компилятор С должен
     быть совместим с подключаемыми  на  этапе  редактирования  связей
     подпрограммами  из  библиотеки  подпрограмм поставляемых на диске
     фирмой Microsoft.  вам также надлежит  помнить,  что  обязательно
     необходимо наличие драйвера устройства MOUSE.SYS.

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


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


     изложения материала подпрограммы,  могут  использоваться  вами  в
     дальнейшем при создании различных конкретных приложений).






















































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


                   НЕКОТОРЫЕ НАЧАЛЬНЫЕ СВЕДЕНИЯ О "МЫШИ"
     -----------------------------------------------------------------

          Для того, чтобы использовать "мышь", прежде всего необходимо
     инсталировать соответствующий драйвер. Для "мыши" фирмы Microsoft
     в файл CONFIG.SYS должна быть добавлена следующая строка:

                             device = mouse.sys

          Для инсталяции   драйвера   "мыши"  фирмы  IBM  должна  быть
     запущена программа MOUSE.COM.  С этой целью в  файл  AUTOEXEC.BAT
     может быть добавлена строка вида:

                                   mouse

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

          Точно так же,  как  с  клавиатурой  ассоциирован  курсор,  с
     "мышью"  также  ассоциирован  некий  маркер на экране (называемый
     далее указатель).  Подпрограммы в библиотеке в  поддержке  "мыши"
     фирмы   Microsoft   определяют   по   умолчанию  следующую  форму
     указателя:  в графических режимах - это стрелка,  а  в  текстовых
     режимах  -  прямоугольник  в  рамках габаритных размеров символа.
     Аналогично  курсору  клавиатуры  указатель-курсор  "мыши"   может
     отображаться    лишь    тогда,   когда   "мышь"   непосредственно
     используется.  В  противном  случае  указатель-курсор  "мыши"  не
     отображается на экране так, как будто интерфейса с "мышью" вообще
     не существует.

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










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


                       ВИРТУАЛИЗАЦИЯ И РЕАЛЬНЫЙ ЭКРАН
     -----------------------------------------------------------------

          Библиотека подпрограмм  поддержки  "мыши"  фирмы   Microsoft
     работает  с  виртуальным  экраном  в  виде  массива  точек растра
     (массива из  единиц  минимального  изображения,  цвет  и  яркость
     которых  можно  задать  независимо  от  остального  изображения),
     который может отличаться от  реального  экрана.  При  перемещении
     "мыши"  счетчики  местоположения  курсора изменяют свое значение.
     Перед  отображением  курсора   виртуальные   координаты   курсора
     преобразуются  в  координаты реального экрана.  В видеорежимах 6,
     14,  15 и 16 это преобразование осуществляется один к  одному.  В
     режимах  4 и 5 не каждая точка виртуальной горизонтальной позиции
     преобразуется в координты реального  экрана,  а  через  одну.  вы
     должны   обратить  внимание  на  этот  факт,  так  как  программа
     рисования, к которой будет добавлен интерфейс с "мышью" и которая
     будет  рассматриваться  в  данной  главе,  работает  именно  в  4
     графическом режиме.






































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


                        БИБЛИОТЕКА ПОДДЕРЖКИ "МЫШИ"
     -----------------------------------------------------------------

          Подпрограммы внутри MOUSE.LIB ассоциируются у пользователя с
     одной функцией, использующей в качестве входного аргумента число,
     специфицирующее  номер  функции  поддержки "мыши".  (Этот процесс
     некоторым образом сходен  с  процессом  доступа  к  функциям  DOS
     посредством  прерывания  21Н  с указанием номера нужной функции).
     Имя   этой   функции   определяется   моделью   памяти,   которая
     используется  при  компиляции  вашей  программы.  Используйте имя
     cmouses() для  модели  маленькой  памяти,  cmousec()  для  модели
     компактной памяти,   cmousem()   для   модели  средней  памяти  и
     cmousel() для модели большой и самой большой  (огромной)  памяти.
     (Заметим,  что функция не может работать в модели самой маленькой
     памяти).  Пример,  представленный в этой главе, использует модель
     маленькой памяти,  однако вы можете изменить тип модели памяти по
     своему усмотрению.

          Основным форматом функции cmouses() является:

          void cmouses(fnum, arg2, arg3, arg4);
               int *fnum, *arg2, *arg3, *arg4;
          Как видно,  fnum  является номером функции поддержки "мыши",
     которую необходимо вызвать. Другие параметры содержат информацию,
     необходимую  для  спецификации  функции.  Обратите внимание,  что
     функции передаются не сами аргументы, а указатели на их значения.
     Функция  cmouses() возвращает результаты работы в виде параметров
     и,  следовательно,  нуждается в  их  адресации.  Фирма  Microsoft
     определила тридцать функций поддержки "мыши".  Однако в программе
     рисования будут использованы лишь  пять  из  них.  Ниже  приведен
     краткий  обзор функций поддержки "мыши" фирмы Microsoft,  которые
     будут использованы нами в этой главе.


          Привести в исходное состояние, выдать статус.
          ---------------------------------------------

          Функция 0 приводит "мышь" в начальное  состояние (сбрасывает
     "мышь")  Она  перемещает курсор-указатель "мыши" в центр экрана и
     "выключает  "  его.  Функция  возвращает  номер  нажатой  клавиши
     "мыши"   в качестве значения arg2.  После завершения функции fnum
     принимает значение 0,  если "мышь" и соответствующее  программное
     обеспечение не инсталированы, и -1 в противном случае.


          Отобразить курсор
          -----------------

          Функция 1   отображает   указатель-курсор   "мыши".  Она  не
     возвращает никакого значения.


          Переместить курсор
          ------------------


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



          Функция 2 перемещает курсор по  экрану.  Она  не  возвращает
     никакого значения.


          Выдать статус клавиши и позицию курсора
          ---------------------------------------

          Функция 3  возвращает  статус  клавиши  в arg2,  виртуальную
     горизонтальную позицию курсора в arg3, а виртуальную вертикальную
     позицию курсора в arg4.

          Статус клавиши  кодируется  в  битах 0 и 1 байта arg2.  Если
     значение бита 0 установлено  (равно  1),  то  была  нажата  левая
     клавиша  "мыши",  если значение бита 1 установлено (равно 1),  то
     была  нажата  правая  клавиша.  Если  значения  обоих  битов   не
     установлены (равны 0), то никакая клавиша нажата не была.


          Установить координаты курсора
          ------------------------------

          Функция 4  устанавливает  месторасположение  курсора "мыши".
     Значение arg3  определяет горизонтальную позицию, а значение arg4
     -  вертикальную  позицию курсора.  вы всегда должны  помнить, что
     значения  не  должны  выходить  за  пределы  виртуального экрана,
     который вы используете.


          Индикация движения
          ------------------

          Функция 11  возвращает  число  вертикальных и горизонтальных
     "мышиных" шагов,  которое "мышь"  прошла  со  времени  последнего
     обращения   к   функции  11,  другими  словами  -  это  изменение
     вертикальных и горизонтальных  координат  "мыши".  Функция  также
     сбрасывает внутренний регистр-счетчик в 0. Значение вертикального
     счетчика возвращается в arg3,  а горизонтального -  в  arg4.  Это
     позволяет,  если  "мышь"  после последнего обращения к функции не
     перемещалась на плоскости, получить значения как горизонтального,
     так и вертикального счетчиков равными 0.  Если значение одного из
     счетчиков (или обоих) отлично от 0,  то  "мышь"  перемещалась  на
     плоскости.

          Положительное значение   изменения   вертикальных  "мышиных"
     шагов   позволяет   заключить,   что   "мышь"   двигалась   вниз.
     Отрицательное  же  значение  свидетельствует  о  движении  "мыши"
     вверх.

          Положительное значение изменения числа  горизонтальных шагов
     свидетельствует   о   том,  что  "мышь"  перемещалась  вправо,  а
     отрицательное значение показывает на то,  что "мышь" перемещалась
     влево.



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


























































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


                  ФУНКЦИИ ПОДДЕРЖКИ "МЫШИ" ВЕРХНЕГО УРОВНЯ
     -----------------------------------------------------------------

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


          Установка "мыши" в исходное состояние.
          --------------------------------------

          Функция, представленная ниже, mouse_reset() используется для
     установки "мыши"  в  исходное  состояние.  Заметим,  что  функция
     требует   наличия  соответствующего  программного  обеспечения  и
     аппаратной части компьютера,  а  также  инсталяции  двухклавишной
     "мыши".

     /* Установка "мыши" в исходное состояние */

     void mouse_reset()
     {
          int fnum, arg2, arg3, arg4;

          fnum = 0;  /* Установка "мыши" в исходное состояние  */
          cmouses( &fnum, &arg2, &arg3, &arg4);
          if(fnum!=-1) {
          printf("Аппаратные или программные средства поддержки ");
          printf("'мыши' не инсталированы");
               exit(1);
          }
          if(arg2!=2) {
       printf("Разрешено использование только двухклавишной 'мыши'");
               exit(1);
          }
     }


          Отображение и перемещение курсора "мыши".
          -----------------------------------------

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


     /*  Включение курсора "мыши"  */
     void cursor_on()
     {
          int fnum;

          fnum = 1; /* отобразить курсор */
          cmouses( &fnum,  &fnum,  &fnum,  &fnum);
     }


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



     /*  Выключение курсора "мыши"  */
     void cursor_off()
     {
          int fnum;

          fnum = 2; /* стереть курсор */
          cmouses( &fnum,  &fnum,  &fnum,  &fnum);
     }


          Какая из клавиш "мыши" была нажата?
          -----------------------------------

          Другой парой  взаимодополняющих  друг друга функций являются
     функции rightb_pressed() и leftb_pressed(),  представленные ниже.
     Эти функции возвращают значение "истина",  если нажата правая или
     левая клавиши.


     /* Возвращает значение "истина", если нажата правая клавиша,
          и "ложь" в противном случае                            */

     rightb_pressed()
     {
          int fnum, arg2, arg3, arg4;

          fnum = 3;   /* Чтение позиции и статуса клавиши */
          cmouses( &fnum, &arg2, &arg3, &arg4);
          return arg2 & 2;
     }



     /* Возвращает значение "истина", если нажата левая клавиша,
          и "ложь" в противном случае                            */

     leftb_pressed()
     {
          int fnum, arg2, arg3, arg4;

          fnum = 3;   /* Чтение позиции и статуса клавиши */
          cmouses( &fnum, &arg2, &arg3, &arg4);
          return arg2 & 1;
     }


          Как обнаружить перемещение "мыши"?
          ----------------------------------

          Функция 11,  которая  возвращает изменение значения счетчика
     "мыши" (в "мышиных" шагах)  после  последнего  обращения  к  ней,
     позволяет    определить    факт   перемещения   "мыши".   Функция
     mouse_motion(),   представленная   ниже,   возвращает   изменение


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


     местоположения    "мыши"    в   горизонтальном   и   вертикальном
     направлениях в переменных,  чьи  указатели  являются  аргументами
     функции.  Если  оба  значения  deltax  и deltay равны 0,  то факт
     движения "мыши" не регистрируется.

     /*  Возвращает направление движения  */
     void mouse_motion(deltax, deltay)
     char *deltax, *deltay;
     {
          int fnum, arg2, arg3, arg4;

          fnum = 11; /* получить направление движения */
          cmouses( &fnum, &arg2, &arg3, &arg4);
          if(arg3>0) *deltax = RIGHT;
          else if(arg3<0) *deltax = LEFT;
          else *deltax = NOT_MOVED;

          if(arg4>0) *deltay = DOWN;
          else if(arg4<0) *deltay = UP;
          else *deltay = NOT_MOVED;
     }


          Макросы RIGHT, LEFT, UP, DOWN и NOT_MOVED определены
     следующим образом:

     #define NOT_MOVED 0
     #define RIGHT     1
     #define LEFT      2
     #define UP        3
     #define DOWN      4



          Чтение и установка позиции курсора.
          -----------------------------------

          Функции set_mouse_position()       и       mouse_position(),
     представленные  ниже,  используются  для установки чтения текущей
     позиции курсора "мыши".


     /* Установить координаты курсора "мыши" */
     void set_mouse_position(x, y)
     int x, y;
     {

          int fnum, arg2;

          fnum = 4; /* установка позиции */
          cmouses(&fnum, &arg2, &x, &y);
     }

     /* Возвращает координаты курсора "мыши" */


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


     void mouse_position(x, y)
     int *x, *y;
     {
          int fnum, arg2, arg3, arg4;

          fnum = 3; /* получить позицию и статус клавиши */
          cmouses( &fnum, &arg2, &arg3, &arg4);
          *x = arg3;
          *y = arg4;
     }














































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


                   ПРОСТЕЙШАЯ ДЕМОНСТРАЦИОННАЯ ПРОГРАММА
     -----------------------------------------------------------------

          Программа, представленная   ниже,  демонстрирует  применение
     функций поддержки "мыши" высокого уровня.  вы можете сразу ввести
     ее в ваш компьютер и попробовать ее в деле.

     /*  Интерфейс с "мышью" Microsoft/IBM */

     #include "dos.h"
     #define NOT_MOVED 0
     #define RIGHT     1
     #define LEFT      2
     #define UP        3
     #define DOWN      4
     void mouse_position(), mode(), goto_xy(), mouse_motion();
     void cursor_on(), cursor_off(), mouse_reset();

     main(argc, argv)
     int argc;
     char *argv[];
     {
          char deltax, deltay, x, y;

          if(argc!=2) {
               printf(" Используйте формат: mouser <видеорежим> ");
               exit(1);
          }

          mode(atoi(argv[1]));

          mouse_reset();  /* инициализация "мыши"  */
          cursor_on();    /* "включение" курсора   */

          do {
               goto_xy(0, 0);
               if(leftb_presed()) printf("Левая клавиша");
               if(rightb_pressed()) {
                    printf("Правая клавиша");
                    mouse_position(&x, &y);
                    printf("%d %d - ", x, y);
               }

               /* Отображение местоположения "мыши" */
               mouse_motion(&deltax, &deltay);
               if(deltax || deltay) {
                    printf("Перемещение");
                    switch(deltax) {
                         case NOT_MOVED: break;
                         case RIGHT: printf("Вправо");
                              break;
                         case LEFT: printf("Влево");
                              break;
                    }


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


                    switch(deltay) {
                         case NOT_MOVED: break;
                         case UP: printf("Вверх");
                              break;
                         case DOWN: printf("Вниз");
                              break;
                    }
               }
          /* Цикл выполняется пока обе клавиши нажаты одновременно */
          } while(!(leftb_pressed() && rightb_pressed()));
          mode(3);
     }

     /* Установка видеорежима  */
     void mode(mode_code)
     int mode_code;
     {
          union REGS r;

          r.h.al = mode_code;
          r.h.ah = 0;
          int86(0x10, &r, &r);
     }

     /* Пересылка курсора в позицию, специфицированную
          координатами х и у */
     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);
     }

     /**********************************************************/
     /* Функции, обеспечивающие интерфейс с "мышью"            */
     /**********************************************************/

     /*  Включение курсора "мыши"  */
     void cursor_on()
     {
          int fnum;

          fnum = 1; /* отобразить курсор */
          cmouses( &fnum,  &fnum,  &fnum,  &fnum);
     }


     /*  Выключение курсора "мыши"  */
     void cursor_off()


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


     {
          int fnum;

          fnum = 2; /* стереть курсор */
          cmouses( &fnum,  &fnum,  &fnum,  &fnum);
     }


     /* Возвращает значение "истина", если нажата правая клавиша,
          и "ложь" в противном случае                            */

     rightb_pressed()
     {
          int fnum, arg2, arg3, arg4;

          fnum = 3;   /* Чтение позиции и статуса клавиши   */
          cmouses( &fnum, &arg2, &arg3, &arg4);
          return arg2 & 2;
     }


     /* Возвращает значение "истина", если нажата левая клавиша,
          и "ложь" в противном случае                            */

     leftb_pressed()
     {
          int fnum, arg2, arg3, arg4;

          fnum = 3;   /* Чтение позиции и статуса клавиши   */
          cmouses( &fnum, &arg2, &arg3, &arg4);
          return arg2 & 1;
     }


     /*  Возвращает направление движения  */
     void mouse_motion(deltax, deltay)
     char *deltax, *deltay;
     {
          int fnum, arg2, arg3, arg4;

          fnum = 11; /* получить направление движения */
          cmouses( &fnum, &arg2, &arg3, &arg4);
          if(arg3>0) *deltax = RIGHT;
          else if(arg3<0) *deltax = LEFT;
          else *deltax = NOT_MOVED;

          if(arg4>0) *deltay = DOWN;
          else if(arg4<0) *deltay = UP;
          else *deltay = NOT_MOVED;
     }



     /* Установить координаты курсора "мыши" */


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


     void set_mouse_position(x, y)
     int x, y;
     {
          int fnum, arg2;

          fnum = 4; /* установка позиции */
          cmouses(&fnum, &arg2, &x, &y);
     }

     /* Возвращает координаты курсора "мыши" */
     void mouse_position(x, y)
     int *x, *y;
     {
          int fnum, arg2, arg3, arg4;

          fnum = 3; /* получить позицию и статус клавиши */
          cmouses( &fnum, &arg2, &arg3, &arg4);
          *x = arg3;
          *y = arg4;
     }




     /* Установка "мыши" в исходное состояние   */

     void mouse_reset()
     {
          int fnum, arg2, arg3, arg4;

          fnum = 0;  /* Установка "мыши" в исходное состояние  */
          cmouses( &fnum, &arg2, &arg3, &arg4);
          if(fnum!=-1) {
          printf("Аппаратные или программные средства поддержки ");
          printf("'мыши' не инсталированы");
               exit(1);

          }
          if(arg2!=2) {
          printf("Разрешено использование только двухклавишной ");
          printf("'мыши'");
               exit(1);
          }
     }

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



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


























































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


           ВВОД ИНФОРМАЦИИ С ПОМОЩЬЮ "МЫШИ" В ПРОГРАММЕ РИСОВАНИЯ
     -----------------------------------------------------------------
          Итак, вы   теперь   готовы   к   тому,   чтобы   разработать
     подпрограммы,  позволяющие  с помощью "мыши" управлять программой
     рисования. Интерфейс с "мышью" может быть добавлен в существующие
     подпрограммы    управления,   что,   естественно,   будет   более
     предпочтительно, чем разработка новых подпрограмм или модификация
     существующих.
           Такой путь выгоден  прежде  всего  тем,  что  функциональны
     возможности  клавиш  управления  курсором  сохраняются на все 100
     процентов,  и пользователь в  каждой  конкретной  ситуации  может
     выбрать   наиболее   подходящее   устройство   для  ввода  данных
     (клавиатура или "мышь").
           Прежде, чем "мышь" будет включена  как  устройство  ввода
     программу  рисования,  необходимо  разработать  две подпрограммы,
     учитывающие специфику "мыши".  Первая  подпрограмма  -  wait_on()
     позволяет  реализовать процесс ожидания отпускания (освобождения)
     специфицированной клавиши пользователем.  Анализ  подобного  рода
     имеет весьма большое значение, так как соответствующие прерывания
     генерируются  постоянно,  пока   клавиша   не   нажата.   (Однако
     невозможно  обеспечить  такое  мгновенное  нажатие на клавишу,  в
     результате которого сформировалось бы лишь одно  прерывание).  Во
     многих  подпрограммах  наоборот  важно  избежать такой ситуации и
     поэтому в них каждое нажатие на клавишу генерирует  (точнее будет
     сказать  кажется,  что  генерирует)  только одно прерывание за то
     время, пока клавиша нажата. В соответствии с этим, ваша программа
     должна   обращаться  к  фунции  wait_on(),  представленной  ниже,
     непосредственно перед выполнением  и  после  того,  когда  нажата
     соответствующая клавиша.


     /* Возвращает 1, если специфицированная клавиша не нажата */

     void wait_on(button)
     int button;
     {
          if(button== LEFTB)
               while(leftb_pressed());
          else
               while(rightb_pressed());
     }

          Макросы LEFTB  и RIGHTB,  представленные ниже,  используются
     при обращении к wait_on().


     #define LEFTB     1
     #define RIGHTB    2

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


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


     графическом режиме. Функции передается двумерный массив символов,
     который   содержит   элементы   меню   (которые   может   выбрать
     пользователь),  значение каждого элемента меню (его код),а  также
     координаты  Х  и  У  отображения меню на экране.  Массив символов
     определяет  максимальную  длину  каждого  элемента  меню   в   19
     символов.   Функция   возвращает   в  качестве  результата  номер
     выбранного пользователем элемента меню, начиная с 0, или -1, если
     пользователь  не выбрал ни один из элементов меню.  Когда функция
     начинает свою работу,  то она вначале вычисляет длину в  пикселах
     (элементах растра) каждого элемента меню,  после чего резервирует
     пространство по начальной и конечной  точке  растра  для  каждого
     элемента  меню,  одновременно  запоминая эту информацию в массиве
     len. (В четвертом графическом режиме каждый символ имеет высоту в
     8   точек  растра  и  ширину  в  16  точек  растра.)  После  этих
     вычислений, функция переходит в состояние ожидаения прерывания от
     клавиш  "мыши".  При  этом  осуществляется  анализ нажата или нет
     клавиша "мыши" в момент нахождения ее курсора в области  меню, и,
     если  да,  то в месте расположения какого элемента меню.  Функция
     mouse_menu() приведена ниже.


     /* Отображает однострочное меню для "мыши" и возвращает
        код выбранного пользователем элемента меню          */

     mouse_menu(count, item, x, y)
     int count;           /* количество элементов меню */
     char item[][20];      /* элементы меню */
     int x, y;           /* позиции отображения */
     {
          int i, len[MENU_MAX][2], t;
          int mousex, mousey;

          goto_xy(x, y);
          t = 0;
          for(i=0; i=0 && mousey<8)  /* символ имеет высоту
                                   8 точек растра */
                    for(i=0; ilen[i][0] && mousex  используется  для этих же целей при работе с клавиатурой.
     Установите "мышь" в  первой  конечной  точке  объекта  и  нажмите
     правую клавишу. Затем переместите "мышь" во вторую конечную точку
     и опять нажмите клавишу. Так вы получите изображение интересующей
     вас фигуры.

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

          В конце концов,  если "мышь" изменила свои координаты, будут
     обновлены значения счетчиков X и Y.

          Функция menu(),    представленная   ниже,   использует   для
     установки  и  управления  меню  функцию  mouse_menu(),  описанную
     ранее.

     /*  Отображение меню */
     menu()
     {
          register int i, j;
          char far *ptr = (char far *) 0xB8000000; /* Указатель на
                                                       CGA-память */
          char far *temp;
          unsigned char buf[14][80]; /* для хранения содержимого
                                        экрана */
          int x, y, choice;
          char items[][20] = {


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


               "BOX",
               "CIRCLE",
               "LINE",
               "FILL BOX",
               "FILL CIRCLE"
          };

          temp = ptr;
          /* сохранение верхней части текущего экрана */
          for(i=0; i<14; i++)
               for(j=0; j<80; j+=2) {
                    buf[i][j] = *temp; /* четный байт */
                    buf[i][j+1] = *(temp+8152); /* нечетный байт */
                    *temp = 0; *(temp+8152) = 0; /* очистка верхней
                                                  части экрана */
                    temp++;
               }

          goto_xy(0, 0);
          /* ожидание, которое будет прервано в результате нажатия на
             клавишу */
          while(rightb_pressed() || leftb_pressed());

          cursor_on();

          choice = mouse_menu(5, items, 0, 0);

          cursor_off();
          temp = ptr;
          /* восстановление изображения верхней части экрана */
          for(i=0; i<14; i++)
               for(j=0; j<80; j+=2) {
                    *temp = buf[i][j];
                    *(temp+8152) = buf[i][j+1];
                    temp++;
               }
          return choice;
     }

          В процессе работы функция прежде всего сохраняет изображение
     верхней части  текущего  экрана,  не  ожидая  нажатия  какой-либо
     клавиши.  Затем  следует "включение" курсора "мыши" и обращение к
     функции mouse_menu(). После передачи управления от функции mouse_
     menu()    курсор    включается,    восстанавливается   предыдущее
     изображение  верхней   части   экрана,   и   функция   возвращает
     соответствующий код выбранного элемента меню.










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


                   ОПРЕДЕЛЕНИЕ ОБЪЕКТОВ С ПОМОЩЬЮ "МЫШИ"
     -----------------------------------------------------------------

          Оригинальная версия    программы     рисования     допускает
     использование    объектов,   определяемых   с   помощью   функции
     define_object(),  путем специфицирования конечных  точек  каждого
     сегмента  линии.  Каждый  определенный  ранее  объект  может быть
     подвергнут    вращению.    Версия    функции     define_object(),
     представленная  ниже,  также позволяет с помощью "мыши" указывать
     конечные точки (габариты) объектов.


     /* Определение объекта по его габаритным точкам с помощью
        клавиатуры или "мыши" */
     define_object(ob, x, y)
     double ob[][4];
     int x, y;
     {
          union k{
               char c[2];
               int i;
          } key;
          register int i, j;
          char far *ptr = (char far *) 0xB8000000; /* Указатель на
                                   CGA-память */
          char far *temp;
          unsigned char buf[14][80];
          int sides=0;
          int deltax, deltay, oldx, oldy;

          temp = ptr;
          /* сохранение верхней части текущего экрана */
          for(i=0; i<14; i++)
               for(j=0; j<80; j+=2) {
                    buf[i][j] = *temp; /* четный байт */
                    buf[i][j+1] = *(temp+8152); /* нечетный байт */
                    *temp = 0; *(temp+8152) = 0; /* очистка верхней
                                                    части экрана */
                    temp++;
               }

          i = 0;
          key.i = 0;
          xhairs(x, y);
          do {
               goto_xy(0, 0);
               printf("Определите сторону %d", sides+1);
               if(i==0) printf("Укажите первую габаритную точку");
               else printf("Укажите вторую габаритную точку");

               do {
     /************** Добавочная часть для мыши. *******************/
               /* Просматривается, если "мышь" перемещается */



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


                    mouse_motion(&deltax, &deltay);
               /* Используйте левую клавишу для определения точки*/
                    if(leftb_pressed()) {
                         /* стирание графического курсора */
                         xhairs(x, y);
                         /* запоминание координат точки */
                         ob[sides][i++] = (double) x;
                         ob[sides][i++] = (double) y;
                         if(i==4) {
                              i = 0;
                              sides++;
                         }
                         break;
                    }
               } while(!kbhit() && !deltax && ! deltay);
               if(leftb_pressed()) wait_on(LEFTB);

               if(deltax || deltay) {
               /* если "мышь" переместилась, то пересчет координат*/
                    oldx = x; oldy = y;
                    mouse_position(&y, &x);
                    y = y / 2; /* нормализация коодинат виртуального
                                   экрана*/
                    /* стирание графического курсора */
                    xhairs(oldx, oldy);
               }
     /********** Конец добавочной части для "мыши" *****************/

               else if(kbhit()) {
                    key.i = bioskey(0);
                    /* изображение графического курсора */
                    xhairs(x, y);
                    if(key.c[0]==13) {
                        /* используйте <ВВОД> для определения точки*/
                         ob[sides][i++] = (double) x;
                         ob[sides][i++] = (double) y;
                         if(i==4) {
                              i = 0;
                              sides++;
                         }
                    }

                   /* если клавиши управления курсором, то перемещение
                        графического курсора */
                    if(!key.c[0]) switch(key.c[1]) {
                         case 75: /* влево */
                              y-=1;
                              break;
                         case 77: /* вправо */
                              y+=1;
                              break;
                         case 72: /* вверх */
                              x-=1;
                              break;


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


                         case 80: /* вниз */
                              x+=1;
                              break;
                         case 71: /* влево вверх */
                              x-=1; y-=1;
                              break;
                         case 73: /* вправо вверх */
                              x-=1; y+=1;
                              break;
                         case 79: /* влево вниз */
                              x+=1; y-=1;
                              break;
                         case 81: /* вправо вниз */
                              x+=1; y+=1;
                              break;
                    }
               }
               if(key.c[1] != 59) xhairs(x, y);
          } while(key.c[1] != 59); /* нажмите F1 для остановки */

          temp = ptr;
          /* востановление изображения в верхней части экрана */
          for(i=0; i<14; i++)
               for(j=0; j<80; j+=2) {

                    *temp = buf[i][j];
                    *(temp+8152) = buf[i][j+1];
                    temp++;
               }
          return sides;
     }


          Как вы   можете   видеть,  левая  клавиша  используется  для
     указания габаритных точек.  Движение "мыши" кодируется точно  так
     же,  как  и  в функции read_mouse().  За исключением того,  что в
     функцию добавлен фрагмент исходного текста  для  поддержки  ввода
     информации   с   помощью   "мыши",  оригинальная  версия  функции
     define_object()  больше  никаких  изменений  не  претерпела.  Для
     указания  очередной  габаритной точки переместите "мышь" в нужную
     позицию и нажмите клавишу.















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


                ПОЛНЫЙ ТЕКСТ МОДИФИЦИРОВАННОЙ ПРОГРАММЫ РИСОВАНИЯ
     -----------------------------------------------------------------

          Ниже приведен  исходный  текст  программы рисования с учетом
     всех изменений и дополнений, обсужденных выше.

     /* Эта версия программы рисования для адаптеров  CGA/EGA
        позволяет использовать "мышь" фирмы Microsoft/IBM,
        а также альтернативное устройство ввода (клавиатуру)
     */
     #define NUM_SIDES 20 /* Максимальное число сторон фигуры.
                               По желанию можно увеличить */
     #define NOT_MOVED 0
     #define RIGHT     1
     #define LEFT      2
     #define UP        3
     #define DOWN      4
     #define LEFTB     1
     #define RIGHTB    2

     #define MENU_MAX 20 /* Максимальное число элементов меню */

     #include "dos.h"
     #include "stdio.h"
     #include "math.h"

     void mode(), line(), box(), fill_box();
     void mempoint(), palette(), xhairs();
     void circle(), plot_circle(), fill_circle();
     void rotate_point(), rotate_object(), goto_xy();
     void display_object(), copy(), move();
     void save_pic(), load_pic();
     void set_mouse_position(), mouse_position(), mouse_motion();
     void cursor_on(), cursor_off(), wait_on(), mouse_reset();

     unsigned char read_point();

     /* Это массив предназначен для хранения координат динамически
           определяемой фигуры   */

     double object[NUM_SIDES][4];

     double asp_ratio; /* коэффициент сжатия окружностей */

          int x=10, y=10; /* текущая позиция экрана */
          int cc=2; /* текущий цвет */
          int on_flag=1, mouse_on_flag=0; /* перо опущено или
                                              поднято */
          int pal_num=1;                 /* номер палитры */

          /* Габаритные точки, определяющие линию, окружность
          или прямоугольник */
          int startx=0, starty=0, endx=0, endy=0, first_point=1;
          int inc=1;       /* приращение движения */


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


          int sides=0;     /* номер стороны определяемой фигуры */
          int deltax, deltay;    /* "мышь" изменяет данные в
                                     индицированной позиции */


     main()
     {
          char done=0;

          mode(4); /* переключатель в 4 графический режим для
                         адаптеров CGA/EGA  */
          palette(0);    /* палитра 0  */
          mouse_reset(); /* инсталяция "мыши" */

          xhairs(x, y);  /* установить графический курсор */
          set_mouse_position(y*2, x); /* установить начальную позицию
                                        курсора "мыши" */
          do {
               /* просматривается, перемещалась ли "мышь" */
               mouse_motion(&deltax, &deltay);
               if(deltax || deltay) read_mouse();
               /* проверка нажатия клавиши */
               if(leftb_pressed() || rightb_pressed())
                    read_mouse();
               if(kbhit()) {
                    done = read_kb();
               /* перемещение "мыши" в соответствии с размещением
                    графического курсора */
                    set_mouse_position(y*2, x);
               }
          } while (!done);
          mode(2);
     }


     /* Чтение и обработка команд, вводимых с помощью "мыши" */
     read_mouse()
     {
          int oldx, oldy;
          int choice;

          oldx = x; oldy = y;
          xhairs(x, y); /* стереть с текущей позиции */

          /* нажаты обе клавиши для активизации меню */
          if(rightb_pressed() && leftb_pressed()) {
               choice = menu(); /* получить результат выбора
                                   из меню */
               switch(choice) {
                 case 0: box(startx, starty, endx, endy, cc);
                      break;
                 case 1: circle(startx, starty, endy-starty, cc);
                      break;
                 case 2: line(startx, starty, endx, endy, cc);


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


                      break;
                 case 3: fill_box(startx, starty, endx, endy, cc);
                      break;
                 case 4: fill_circle(startx, starty, endy-starty, cc);
                      break;
               }
          }

          /* правая клавиша определяет конечную точку фигуры */
          else if(rightb_pressed()) {
               if(first_point) {
                    startx = x; starty = y;
               }
               else {
                    endx = x; endy = y;
               }
               first_point = !first_point;
               wait_on(RIGHTB); /* ожидание освобождения клавиши */
          }

          if(deltax || deltay) {
               mouse_position(&y, &x);
               y = y / 2; /* нормализация координат виртуального
                         экрана */
               /* нажмите левую клавишу для рисования */
               if(leftb_pressed()) mouse_on_flag = 1;
               else mouse_on_flag = 0;
               if(mouse_on_flag) line(oldx, oldy, x, y, cc);
          }
          /* восстановить изображение графического курсора */
          xhairs(x, y);
     }

     /* Чтение и обработка команд, вводимых с клавиатуры */
     read_kb()
     {
          union k{
               char c[2];
               int i;
          } key;

          key.i = bioskey(0);
          xhairs(x, y); /* стереть графический курсор */
          if(!key.c[0]) switch(key.c[1]){
               case 75:  /* влево */
                    if(on_flag) line(x, y, x, y-inc, cc);
                    y -= inc;
                    break;
               case 77:  /* вправо */
                    if(on_flag) line(x, y, x, y+inc, cc);
                    y += inc;
                    break;
               case 72:  /* вверх */
                    if(on_flag) line(x, y, x-inc, y, cc);


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


                    x -= inc;
                    break;
               case 80:  /* вниз */
                    if(on_flag) line(x, y, x+inc, y, cc);
                    x += inc;
                    break;
               case 71:  /* влево вверх */
                    if(on_flag) line(x, y, x-inc, y-inc, cc);
                    y -= inc; x -= inc;
                    break;
               case 73:  /* вправо вверх */
                    if(on_flag) line(x, y, x-inc, y+inc, cc);
                    y += inc; x -= inc;
                    break;
               case 79: /* влево вниз */
                    if(on_flag) line(x, y, x+inc, y-inc, cc);
                    y -= inc; x += inc;
                    break;
               case 81:  /* вправо вниз */
                    if(on_flag) line(x, y, x+inc, y+inc, cc);
                    y += inc; x += inc;
                    break;
               case 59: inc = 1; /* F1 - уменьшить скорость */
                    break;
               case 60: inc = 5; /* F2 - увеличить скорость */
                    break;
          }
          else switch(tolower(key.c[0])) {
               case 'o': on_flag = !on_flag; /* переключатель
                                                шаблона цвета */
                    break;
               case '1': cc = 1; /* цвет 1 */
                    break;
               case '2': cc = 2; /* цвет 2 */
                    break;
               case '3': cc = 3; /* цвет 3 */
                    break;
               case '0': cc = 0; /* цвет 0 */
                    break;
               case 'b': box(startx, starty, endx, endy, cc);
                    break;
               case 'f': fill_box(startx, starty, endx, endy, cc);
                    break;
               case 'l': line(startx, starty, endx, endy, cc);
                    break;
               case 'c': circle(startx, starty, endy-starty, cc);
                    break;
               case 'h': fill_circle(startx, starty, endy-starty, cc);
                    break;
               case 's': save_pic();
                    break;
               case 'r': load_pic();
                    break;
               case 'm': /* переместить область */


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


                    move(startx, starty, endx, endy, x, y);
                    break;
               case 'x': /* копировать область */
                    copy(startx, starty, endx, endy, x, y);
                    break;
               case 'd': /* определить объект для вращения */
                    sides = define_object(object, x, y);
                    break;
               case 'a': /* вращать объект */
                    rotate_object(object, 0.05, x, y, sides);
                    break;
               case '\r': /* установить конечную точку для линии,
                         окружности или прямоугольника */
                    if(first_point) {
                         startx = x; starty = y;
                    }
                    else {
                         endx = x; endy = y;
                    }
                    first_point = !first_point;
                    break;
               case 'p': pal_num = pal_num==1 ? 2:1;
                    palette(pal_num);
          }
          /* восстановить изображение графического курсора */
          xhairs(x, y);
          if(tolowel(key.c[0])=='q') return 1;
          return 0;
     }

     /* Установить палитру */
     void palette(pnum)
     int pnum;
     {
          union REGS r;

          r.h.bh = 1; /* кодирование 4 графического режима */
          r.h.bl = pnum;
          r.h.ah = 11; /* функцкия установления палитры */
          int86(0x10, &r, &r);
     }

     /* Установить видеорежим */
     void mode(mode_code)
     int mode_code;
     {
          union REGS r;

          r.h.al = mode_code;
          r.h.ah = 0;
          int86(0x10, &r, &r);
     }

     /* Рисование прямоугольника */


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


     void box(startx, starty, endx, endy, color_code)
     int startx, starty, endx, endy, color_code;
     {
          line(startx, starty, endx, starty, color_code);
          line(startx, starty, startx, endy, color_code);
          line(startx, endy, endx, endy, color_code);
          line(endx, starty, endx, endy, color_code);
     }


     /* Рисование линии в специфицированном цвете с использованием
        основного алгоритма Брезенхама  */
     void line(startx, starty, endx, endy, color)
     int startx, starty, endx, endy, color;
     {
          register int t, distance;
          int x=0, y=0, delta_x, delta_y;
          int incx, incy;

          /* определение расстояния в обоих направлениях */
          delta_x = endx-startx;
          delta_y = endy-starty;

          /* Вычисление направления приращения. Приращение
          вычисляется относительно 0 как для горизонтальной,
          так и для вертикальной линии
          */
          if(delta_x>0) incx = 1;
          else if(delta_x==0) incx = 0;
          else incx=-1;

          if(delta_y>0) incy = 1;
          else if(delta_y==0) incy = 0;
          else incy=-1;

          /* Определяется, какое расстояние больше */
          delta_x = abs(delta_x);
          delta_y = abs(delta_y);
          if(delta_x>delta_y) distance = delta_x;
          else distance = delta_y;

          /* Вычерчиывние линии */
          for(t=0; t<=distance+1; t++) {
               mempoint(startx, starty, color);
               x+=delta_x;
               y+=delta_y;
               if(x>distance) {
                    x-=distance;
                    startx+=incx;
               }
               if(y>distance) {
                    y-=distance;
                    starty+=incy;
               }


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


          }
     }

     /* Закрашивание прямоугольника специфицированным цветом */
     void fill_box(startx, starty, endx, endy, color_code)
     int startx, starty, endx, endy, color_code;
     {
          register int i, begin, end;

          begin = startxendx ? startx : endx;

          for(i=begin; i<=end; i++)
               line(i, starty, i, endy, color_code);
     }

     /* Рисование окружности с использованием целочисленного алгоритма
        Брезенхама */
     void circle(x_center, y_center, radius, color_code)
     int x_center, y_center, radius, color_code;
     {
          register int x, y, delta;

          asp_ratio = 1.0; /* Если Вас не удовлетворяют предлагаемые
                              пропорции фигуры, измените значение этой
                              переменной */
          y = radius;
          delta = 3 - 2 * radius;

          for(x=0; x199 || y<0 || y>319) return;

          xor= color_code & 128; /* просматривается, если режим
                         установлен */
          color_code = color_code & 127; /* маска старшего бита */

          /* установка маски бита и кода цвета для правой ячейки
             памяти */
           bit_position = y%4;
           color_code<<=2*(3-bit_position);
           bit_mask.i>>=2*bit_position;

           /* поиск байта для корректировки в видеопамяти */
           index = x*40 +(y >> 2);
           if(x % 2) index += 8152; /* если дополнительно
                                       использовался 2-й сегмент
                                       памяти */

           /* запись цвета */
           if(!xor) { /* режим перезаписи */
                t = *(ptr+index) & bit_mask.c[0];
                *(ptr+index) = t | color_code;
           }
           else { /* режим записи цвета */
                t = *(ptr+index) | (char)0;
                *(ptr+index) = t ^ color_code;
           }
     }


     /* Непосредственное чтение байта из видеопамяти CGA/EGA
        в режиме 4 */
     unsigned char read_point(x, y)
     int x, y;
     {
          union mask {
               char c[2];
               int i;
          } bit_mask;
          int i, index, bit_position;
          unsigned char t;
          char xor; /* xor - цвет изображения записывается или
                       перезаписывается */
          char far *ptr = (char far *) 0xB8000000; /* Указатель
                                                      CGA памяти */
          bit_mask.i=0xFF3F;  /* 11111111 00111111 в двоичном коде */

          /* проверка значений для 4 режима */
          if(x<0 || x>199 || y<0 || y>319) return;



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


          /* установка маски бита и кода цвета для правой ячейки
             памяти */
           bit_position = y%4;
           bit_mask.i<<=2*(3-bit_position);

           /* поиск нужного байта в видеопамяти */
           index = x*40 +(y >> 2);
           if(x % 2) index += 8152; /* если дополнительно
                                       использовался 2-й блок
                                       памяти */

          /* чтение цвета */
           t = *(ptr+index) & bit_mask.c[0];
          t >>=2*(3-bit_position);
          return t;
     }



     /* сохранение видео изображения на экране */
     void save_pic()
     {
          char fname[80];
          FILE *fp;
          register int i, j;
          char far *ptr = (char far *) 0xB8000000; /* Указатель
                                                      CGA памяти */
          char far *temp;
          unsigned char buf[14][80]; /* буфер содержимого экрана */


          temp = ptr;
          /* сохранение верхней части текущего экрана */
          for(i=0; i<14; i++)
               for(j=0; j<80; j+=2) {
                    buf[i][j] = *temp; /* четный байт */
                    buf[i][j+1] = *(temp+8152); /* нечетный байт */
                    *temp = 0; *(temp+8152) = 0; /* очистка верхней
                                                    части экрана */
                    temp++;
               }

          goto_xy(0, 0);
          printf("Имя файла : ");
          gets(fname);
          if(!(fp=fopen(fname, "wb"))) {
               printf("Файл не может быть открыт\n");
               return;
          }

          temp = ptr;
          /* восстановление изображения верхней части экрана */
          for(i=0; i<14; i++)
               for(j=0; j<80; j+=2) {


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


                    *temp = buf[i][j];
                    *(temp+8152) = buf[i][j+1];
                    temp++;
               }

          /* запись изображения в файл */
          for(i=0; i<8152; i++) {
               putc(*ptr, fp); /* четный байт */
               putc(*(ptr+8152), fp); /* нечетный байт */
               ptr++;
          }
          fclose(fp);
     }


     /* загрузка в память изображения из файла */
     void load_pic()
     {
          char fname[80];
          FILE *fp;
          register int i, j;
          char far *ptr = (char far *) 0xB8000000; /* Указатель
                                   CGA памяти */
          char far *temp;
          unsigned char buf[14][80]; /* буфер содержимого экрана */


          temp = ptr;
          /* сохранение верхней части текущего экрана */
          for(i=0; i<14; i++)
               for(j=0; j<80; j+=2) {
                    buf[i][j] = *temp; /* четный байт */
                    buf[i][j+1] = *(temp+8152); /* нечетный байт */
                    *temp = 0; *(temp+8152) = 0; /* очистка верхней
                                                    части экрана */
                    temp++;
               }

          goto_xy(0, 0);
          printf("Имя файла : ");
          gets(fname);
          if(!(fp=fopen(fname, "rb"))) {
               goto_xy(0, 0);
               printf("Файл не может быть открыт\n");
               temp = ptr;
               /* восстановление изображения верхней части экрана */
               for(i=0; i<14; i++)
                    for(j=0; j<80; j+=2) {
                         *temp = buf[i][j];
                         *(temp+8152) = buf[i][j+1];
                         temp++;
                    }
               return;
          }


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



          /* Загрузка изображения из файла */
          for(i=0; i<8152; i++) {
               *ptr = getc(fp); /* четный байт */
               *(ptr+8152) = getc(fp); /* нечетный байт */
               ptr++;
          }
          fclose(fp);
     }


     /* Перемещение курсора в специфицированную позицию */
     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);
     }



     /* Перемещение области в другое место */
     void move(startx, starty, endx, endy, x, y)
     int startx, starty; /* верхняя левая координата */
     int endx, endy; /* нижняя правая координата места, куда будет
                        перемещена область */
     int x, y; /* верхняя левая координата области, откуда будет
                  перемещено изображение*/
     {
          int i, j;
          unsigned char c;

          for(; startx<=endx; startx++, endx++)
               for(i=starty, j=y; i<=endy; i++, j++) {
                    c = read_point(startx, i); /* чтение точки */
                    mempoint(startx, i, 0); /* стирание предыдущего
                                               изображения */
                    mempoint(x, j, c); /* запись его в новое место */
               }
     }



     /* Копировать область в другое место */
     void copy (startx, starty, endx, endy, x, y)
     int startx, starty; /* верхняя левая координата */
     int endx, endy; /* нижняя правая координата места, куда будет
                        скопирована область */
     int x, y; /* верхняя левая координата области, откуда будет


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


                  перемещено изображение */
     {
          int i, j;
          unsigned char c;

          for(; startx<=endx; startx++, endx++)
               for(i=starty, j=y; i<=endy; i++, j++) {
                    c = read_point(startx, i); /* чтение точки */
                    mempoint(x, j, c); /* запись ее в новое место */
               }
     }



     /* Вращение точки относительно специфицированной пользователем
        начальной точки x_org и y_org на угол theta */

     void rotate_point(theta, x, y, x_org, y_org)
     double theta, *x, *y;
     int x_org, y_org;
     {
          double tx, ty;

          /* нормализация координат х и у исходной точки */
          tx = *x - x_org;
          ty = *y - y_org;

          /* вращение */
          *x = tx * cos(theta) - ty * sin(theta);
          *y = tx * sin(theta) + ty * cos(theta);

          /* возврат значений координат */
          *x += x_org;
          *y += y_org;
     }

     /* Вращение специфицированного объекта */
     void rotate_object(ob, theta, x, y, sides)
     double ob[][4]; /* определение объекта */
     double theta; /* угол поворота в радианах */
     int x, y; /* центр вращения */
     int sides;
     {
          register int i, j;
          double tempx, tempy; /* это вспомогательные переменные,
                    используемые при преобразовании типов */
          char ch;

          for(;;){
               ch = getch(); /* просматривается, если указывается
                                направление врашения */
               switch(tolower(ch)) {
                    case 'l': /* вращение против часовой стрелки */
                         theta = theta < 0 ? -theta : theta;


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


                         break;
                    case 'r': /* вращение по часовой стрелке */
                         theta = theta > 0 ? -theta : theta;
                         break;
                    default: return;
               }

               for(j=0; j для определения точки*/
                         ob[sides][i++] = (double) x;
                         ob[sides][i++] = (double) y;
                         if(i==4) {
                              i = 0;
                              sides++;
                         }
                    }

                    /* если клавиши управления курсором, то
                       перемещение графического курсора */
                    if(!key.c[0]) switch(key.c[1]) {
                         case 75: /* влево */
                              y-=1;
                              break;
                         case 77: /* вправо */
                              y+=1;
                              break;
                         case 72: /* вверх */
                              x-=1;
                              break;
                         case 80: /* вниз */
                              x+=1;
                              break;
                         case 71: /* влево вверх */
                              x-=1; y-=1;
                              break;
                         case 73: /* вправо вверх */
                              x-=1; y+=1;
                              break;
                         case 79: /* влево вниз */
                              x+=1; y-=1;
                              break;
                         case 81: /* вправо вниз */
                              x+=1; y+=1;
                              break;
                    }
               }
               if(key.c[1] != 59) xhairs(x, y);
          } while(key.c[1] != 59); /* нажмите F1 для остановки */

          temp = ptr;
          /* восстановление изображения в верхней части экрана */
          for(i=0; i<14; i++)
               for(j=0; j<80; j+=2) {
                    *temp = buf[i][j];
                    *(temp+8152) = buf[i][j+1];
                    temp++;
               }
          return sides;
     }

     /*  Отображение меню */
     menu()


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


     {
          register int i, j;
          char far *ptr = (char far *) 0xB8000000; /* Указатель на
                                                      CGA-память */
          char far *temp;
          unsigned char buf[14][80]; /* для хранения содержимого
                                        экрана */
          int x, y, choice;
          char items[][20] = {
               "BOX",
               "CIRCLE",
               "LINE",
               "FILL BOX",
               "FILL CIRCLE"
          };

          temp = ptr;
          /* сохранение верхней части текущего экрана */
          for(i=0; i<14; i++)
               for(j=0; j<80; j+=2) {
                    buf[i][j] = *temp; /* четный байт */
                    buf[i][j+1] = *(temp+8152); /* нечетный байт */
                    *temp = 0; *(temp+8152) = 0; /* очистка верхней
                                                    части экрана */
                    temp++;
               }

          goto_xy(0, 0);
          /* ожидание, которое будет прервано в результате нажатия на
             клавишу */
          while(rightb_pressed() || leftb_pressed());

          cursor_on();

          choice = mouse_menu(5, items, 0, 0);

          cursor_off();
          temp = ptr;
          /* восстановление изображения верхней части экрана */
          for(i=0; i<14; i++)
               for(j=0; j<80; j+=2) {
                    *temp = buf[i][j];
                    *(temp+8152) = buf[i][j+1];
                    temp++;
               }
          return choice;
     }


     /**********************************************************/
     /* Функции, обеспечивающие интерфейс с "мышью"            */
     /**********************************************************/

     /*  Включение курсора "мыши"  */


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


     void cursor_on()
     {
          int fnum;

          fnum = 1; /* отобразить курсор */
          cmouses( &fnum,  &fnum,  &fnum,  &fnum);
     }


     /*  Выключение курсора "мыши"  */
     void cursor_off()
     {
          int fnum;

          fnum = 2; /* стереть курсор */
          cmouses( &fnum,  &fnum,  &fnum,  &fnum);
     }



     /* Возвращает значение "истина", если нажата правая клавиша,
          и "ложь" в противном случае                           */

     rightb_pressed()
     {
          int fnum, arg2, arg3, arg4;

          fnum = 3;   /* Чтение позиции и статуса клавиши   */
          cmouses( &fnum, &arg2, &arg3, &arg4);
          return arg2 & 2;
     }



     /* Возвращает значение "истина", если нажата левая клавиша,
          и "ложь" в противном случае */

     leftb_pressed()
     {
          int fnum, arg2, arg3, arg4;

          fnum = 3;   /* Чтение позиции и статуса клавиши   */
          cmouses( &fnum, &arg2, &arg3, &arg4);
          return arg2 & 1;
     }


     /* Установить координаты курсора "мыши" */
     void set_mouse_position(x, y)
     int x, y;
     {
          int fnum, arg2;

          fnum = 4; /* установка позиции */


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


          cmouses(&fnum, &arg2, &x, &y);
     }

     /* Возвращает координаты курсора "мыши" */
     void mouse_position(x, y)
     int *x, *y;
     {
          int fnum, arg2, arg3, arg4;

          fnum = 3; /* получить позицию и статус клавиши */
          cmouses( &fnum, &arg2, &arg3, &arg4);
          *x = arg3;
          *y = arg4;
     }

     /*  Возвращает направление движения  */
     void mouse_motion(deltax, deltay)
     char *deltax, *deltay;
     {
          int fnum, arg2, arg3, arg4;

          fnum = 11; /* получить направление движения */
          cmouses( &fnum, &arg2, &arg3, &arg4);
          if(arg3>0) *deltax = RIGHT;
          else if(arg3<0) *deltax = LEFT;
          else *deltax = NOT_MOVED;

          if(arg4>0) *deltay = DOWN;
          else if(arg4<0) *deltay = UP;
          else *deltay = NOT_MOVED;
     }

     /* Отображает однострочное меню для "мыши" и возвращает
          код выбранного пользователем элемента меню */
     mouse_menu(count, item, x, y)
     int count;           /* количество элементов меню */
     char item[][20];      /* элементы меню */
     int x, y;           /* позиции отображения */
     {
          int i, len[MENU_MAX][2], t;
          int mousex, mousey;

          goto_xy(x, y);
          t = 0;
          for(i=0; i=0 && mousey<8)  /* символ имеет высоту
                                   8 точек растра */
                    for(i=0; ilen[i][0] && mousex




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