Наиболее популярным устройством ввода данных после клавиатуры является "мышь" (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.

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

изложения материала подпрограммы,  могут  использоваться  вами  в

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

Некоторые начальные сведения о мыши.

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

device = mouse.sys

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

mouse

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

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

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

Виртуализация и реальный экран

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

Библиотека поддержки "мыши".

Подпрограммы внутри 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 отображает указатель-курсор "мыши". Она не возвращает никакого значения.

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

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

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

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

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

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

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

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

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

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

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

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

Функции поддержки "мыши" верхнего уровня.

Используя функцию 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);

 

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

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(), представленная ниже, возвращает изменение

местоположения    "мыши"    в   горизонтальном   и   вертикальном

направлениях в переменных,  чьи  указатели  являются  аргументами

функции.  Если  оба  значения  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);

 

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

void mouse_position(x, y)

int *x, *y;

 

int fnum, arg2, arg3, arg4;

fnum = 3; /* получить позицию и статус клавиши */

cmouses( &fnum, &arg2, &arg3, &arg4);

*x = arg3;

*y = arg4;

Простейшая демонстрационная программа.

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

/*  Интерфейс с "мышью" 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;

 

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()

 

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;

 

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

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

Ввод информации с помощью "мыши" в программе рисования.

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

Такой путь выгоден прежде всего тем, что функциональны возможности клавиш управления курсором сохраняются на все 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

графическом режиме. Функции передается двумерный массив символов,

который   содержит   элементы   меню   (которые   может   выбрать

пользователь),  значение каждого элемента меню (его код),а  также

координаты  Х  и  У  отображения меню на экране.  Массив символов

определяет  максимальную  длину  каждого  элемента  меню   в   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

printf("%s   ", item[i]);

len[i][0] = t;

/* каждый символ имеет ширину в 16 точек растра */ len[i][1] = t + strlen(item[i])*16;

t = len[i][1] + 32; /* добавляется два пробела между элементами меню */

 

/* ожидание выбора пользователем элемента меню */

do

if(rightb_pressed() || leftb_pressed()) break;

 while(!kbhit());

/* ожидание нажатия клавиши */

while(rightb_pressed() || leftb_pressed());

/* получить текущую позицию курсора "мыши" */

mouse_position(&mousex, &mousey);

/* анализируется, находится ли курсор в пределах меню */

if(mousey>=0 && mousey<8)  /* символ имеет высоту

8 точек растра */ for(i=0; i

if(mousex>len[i][0] && mousex

return i;

 

return i;

 

returtn -1; /* выбор из меню не осуществлялся */