3. Мышь

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

Что это за устройство и почему оно используется так же часто, как и клавиатура персонального компьютера?

Как устроена мышь?

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

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

Если вы откроете корпус мыши, то увидите простой механизм, состоящий из шарика, двух осей с резиновыми валиками, двух дисков с отверстиями и четырех фотодатчиков.

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

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

Можно выделить три наиболее часто используемых способа подключения мыши к компьютеру:

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

Что касается программного интерфейса, то можно выделить два типа:

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

Мы рекомендуем вам приобрести мышь фирмы Microsoft. Эта мышь удобна в работе, может подключаться к порту PS/2 и имеет переходник для подключения к последовательному порту. Новая версия мыши Microsoft IntelliMouse снабжена небольшим колесиком, которое можно вращать или нажимать. Это дополнение очень удобно при работе с приложениями Windows, так как упрощает просмотр документов, которые не помещаются целиком в окне приложения.

Драйверы мыши в MS-DOS

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

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

device=c:\mouse\mouse.sys

Если используется резидентная программа, она обычно вызывается в файле AUTOEXEC.BAT:

c:\mouse\mouse.com

Драйвер мыши выполняет следующие функции:

Прерывание для обслуживания мыши

Драйвер мыши, независимо от того, реализован он через устанавливаемый драйвер или резидентную программу, устанавливает в операционной системе MS-DOS обработчик прерывания INT 33h. Этот обработчик выполняет все операции, связанные с обслуживанием мыши:

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

Инициализация мыши

Эта функция с кодом 00h выполняет аппаратный сброс мыши и программную установку драйвера мыши в начальное состояние. Заметим, что с помощью функции 21h можно выполнить установку драйвера в исходное состояние, не выполняя аппаратного сброса мыши.

Регистры на входе: AX = 0000h
Регистры на выходе: AX = состояние мыши;
BX = количество клавиш у мыши

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

В регистре BX возвращается количество клавиш мыши:

Содержимое регистра BX Количество клавиш
0 Больше или меньше, чем две
2 Две клавиши
3 Мышь системы Mouse Systems, имеет три клавиши

При установке драйвера мыши в исходное состояние для программ, работающих в текстовом режиме, выполняются следующие действия:

Включить курсор мыши

Функция 01h позволяет включать или выключать курсор мыши:

Регистры на входе: AX = 0001h
Регистры на выходе: Регистры не используются

Для управления видимостью курсора драйвер мыши использует внутренний счетчик. Этот счетчик можно увеличивать, вызывая функцию 01h прерывания INT 33h, или уменьшать при помощи функции 02h этого же прерывания.

После инициализации драйвера функцией 00h счетчик устанавливается равным -1. После первого вызова функции 01h счетчик становится равным 0. При этом курсор мыши становится видимым, его можно перемещать по экрану.

Если счетчик равен 0, то следующие вызовы функции 01h игнорируются драйвером. Для того, чтобы погасить курсор, используйте функцию 02h, которая при вызове уменьшает каждый раз содержимое счетчика на единицу.

Функция 01h сбрасывает область, в которой курсор не отображается (если такая область была ранее установлена функцией 10h).

Выключить курсор мыши

Функция 02h уменьшает на единицу счетчик видимости курсора. Если содержимое счетчика становится равным -1, изображение курсора пропадает с экрана.

Регистры на входе: AX = 0002h
Регистры на выходе: Регистры не используются

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

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

Программа MSCURSOR

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

Листинг 3.1. Файл mscursor\mscursor.c

// =====================================================
// Включение и выключение курсора мыши
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW:    http://www.glasnet.ru/~frolov
//            или
//         http://www.dials.ccas.ru/frolov
// =====================================================
#include <dos.h>
#include <stdio.h>
#include <conio.h>

int main(void) 
{
  int nButtons;
  union REGS rg;

  printf("MSCURSOR, (c) A. Frolov, 1997\n");
  
  // Инициализируем мышь
  rg.x.ax = 0;
  int86(0x33, &rg, &rg);
  
  if(rg.x.bx == 0)
  {   
    printf("Mouse not found");
    return -1;
  }
    
  // Сохраняем количество клавиш
  nButtons = rg.x.bx;
  
  printf("Mouse type: ");
	
  switch (nButtons) 
  {
    case 2:
    {
	  printf("2-button mouse\n"); 
	  break;
	}
	case 3:
	{
	  printf("2-button Mouse Systems\n");
	  break;
	}
	case 0:
	{
	  printf("Unknown type %d\n", nButtons);
	  break;
	}
	
	default:
	{
	  printf("Unknown type %d\n", nButtons);
	  break;
	}
  }

  // Включаем курсор и ожидаем, пока пользователь
  // нажмет на клавишу
  rg.x.ax = 1;
  int86(0x33, &rg, &rg);
  
  printf("Mouse cursor on. Press any key\n");
  getch();

  // Выключаем курсор
  rg.x.ax = 2;
  int86(0x33, &rg, &rg);

  printf("Mouse cursor off. Press any key\n");
  getch();
  return 0;
}

Определить положение курсора

Функция 03h возвращает текущие координаты курсора мыши и состояние клавиш.

Регистры на входе: AX = 0003h
Регистры на выходе: BX = состояние клавиш мыши;
CX = координата X курсора;
DX = координата Y курсора

В зависимости от того, была ли нажата какая-либо клавиша мыши в момент вызова функции, в регистре BX могут быть установлены следующие флаги:

Установленный бит регистра BX Клавиша, которая была нажата
0 Левая
1 Правая
2 Средняя

Для графических режимов координаты располагаются в различных диапазонах, в зависимости от текущего режима видеоадаптера:

Размер экрана в пикселах Номер режима Диапазон координат по оси X Диапазон координат по оси Y
320x200 4, 5 0 - 638 0 - 199
640x200 6 0 - 639 0 - 199
320x200 0Dh 0 - 638 0 - 199
640x200 0Eh 0 - 639 0 - 199
640x350 0Fh 0 - 639 0 - 349

Программы, работающие в текстовом режиме, должны разделить полученные координаты на 8 (как координату X, так и координату Y).

Программа MSGCURS

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

Листинг 3.2. Файл msgcurs\msgcurs.c

// =====================================================
// Включение и выключение курсора мыши
// в разных видеорежимах
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW:    http://www.glasnet.ru/~frolov
//            или
//         http://www.dials.ccas.ru/frolov
// =====================================================
#include <dos.h>
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>

union REGS rg;
int main(void) 
{
  int i;
  unsigned old_videomode, new_videomode;
  char buf[20], *bufptr;
  int nButtons;
  
  // Определяем текущий видеорежим
  rg.x.ax = 0x0f00;
  int86(0x10, &rg, &rg);
  old_videomode = rg.h.al;

  // Устанавливаем новый видеорежим
  buf[0] = 10;
  printf("Enter new video mode: ");
  bufptr = cgets(buf);

  // Преобразуем введенное число к формату int
  new_videomode = atoi(bufptr);

  rg.h.ah = 0;
  rg.h.al = new_videomode;
  int86(0x10, &rg, &rg);

  // Инициализируем мышь
  rg.x.ax = 0;
  int86(0x33, &rg, &rg);
  
  if(rg.x.bx == 0)
  {   
    printf("Mouse not found\n");
    return -1;
  }
    
  // Сохраняем количество клавиш
  nButtons = rg.x.bx;
  printf("Mouse type: %d\n", nButtons);

  // Включаем курсор
  rg.x.ax = 1;
  int86(0x33, &rg, &rg);
  
  printf("Mouse cursor on. Press any key\n");
  getch();

  while(!kbhit()) 
  {
    rg.x.ax = 3;
    int86(0x33, &rg, &rg);
    
    printf("%2d x:%5d y:%5d",
	  rg.x.bx, rg.x.cx, rg.x.dx);

	for(i=0; i<18; i++) printf("\b");
  }
  getch();

  // Выключаем курсор
  rg.x.ax = 2;
  int86(0x33, &rg, &rg);

  // Восстанавливаем режим видеоадаптера
  rg.h.ah = 0;
  rg.h.al = old_videomode;
  int86(0x10, &rg, &rg);
  
  return 0;
}

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

Позже мы рассмотрим другие способы определения состояния мыши.

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

Обычно курсор мыши устанавливает не программа, а пользователь. Однако с помощью функции 04h программа тоже может установить курсор в заданную позицию.

Регистры на входе: AX = 0004h
CX = новая координата X курсора;
DX = новая координата Y курсора
Регистры на выходе: Регистры не используются

Для текстового режима устанавливаемые номера строки и столбца должны быть умножены на 8.

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

Если область для перемещения курсора была ограничена при помощи функций 07h или 08h, то курсор будет вести себя следущим образом. При попытке установить курсор за границу области ограничения, он будет установлен в точку, которая находится внутри границы на минимальном расстоянии от точки, заданной при вызове функции.

Определить положение курсора при нажатии клавиши

Регистры на входе: AX = 0005h
BX = клавиша, при нажатии которой запоминается состояние мыши:
0 - левая;
1 - правая;
2 - средняя
Регистры на выходе: AX = состояние клавиш мыши;
BX = количество нажатий на заданную клавишу. Это значение обнуляется после вызова функции;
CX = координата курсора X;
DX = координата курсора Y

В зависимости от того, была ли нажата какая-либо клавиша мыши в момент вызова функции, в регистре AX могут быть установлены следующие флаги:

Установленный бит регистра AX Клавиша, которая была нажата
0 Левая
1 Правая
2 Средняя

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

Определить положение курсора при отпускании клавиши

Функция 06h возвращает программе состояние мыши, запомненное в момент отпускания клавиши, которая была заранее определена при вызове функции. Она также возвращает количество отпусканий заданной клавиши.

Регистры на входе: AX = 0006h
BX = клавиша, при отпускании которой запоминается состояние мыши:
0 - левая;
1 - правая;
2 – средняя
Регистры на выходе: AX = состояние клавиш мыши;
BX = количество нажатий на заданную клавишу. Это значение обнуляется после вызова функции;
CX = координата курсора X;
DX = координата курсора Y

Формат регистра AX, определяющий состояние клавиш мыши, такой же как и для функции 05h.

Задать диапазон движения курсора по горизонтали

Функция 07h позволяет ограничить диапазон перемещений курсора мыши по горизонтали:

Регистры на входе: AX = 0007h
CX = минимальная координата X;
DX = максимальная координата X
Регистры на выходе: Регистры не используются

Задать диапазон движения курсора по вертикали

Функция 08h позволяет ограничить диапазон перемещений курсора мыши по вертикали.

Регистры на входе: AX = 0008h
CX = минимальная координата Y;
DX = максимальная координата Y
Регистры на выходе: Регистры не используются

Задать форму курсора в графическом режиме

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

Регистры на входе: AX = 0009h
BX = номер позиции точки-указателя графического курсора (от -16 до 16);
CX = номер строки точки-указателя (от -16 до 16);
ES:DX = указатель на растровое изображение курсора
Регистры на выходе: Регистры не используются

Регистры ES:DX указывают на область памяти размером 64 байт. Эта область состоит из двух массивов длиной по 32 байт. Первый массив представляет собой логическую маску размером 16х16 бит, которая накладывается на участок видеопамяти с использованием логической операции “И”. Второй массив - тоже маска размером 16х16 бит, но она накладывается с использованием логической операции “Исключающее ИЛИ”, инвертируя отдельные точки изображения.

Номера позиции и строки точки-указателя, устанавливаемые по умолчанию, равны 0 (BX=CX=0). Это соответствует верхней левой точке в изображении курсора. Значения BX=CX=15 соответствуют нижней правой точке.

Программа MSGFORM

Мы подготовили исходный текст программы MSGFORM, изменяющий форму курсора в графическом режиме (листинг 3.3).

Листинг 3.3. Файл msgform\msgform.c

// =====================================================
// Изменение формы курсора в графическом режиме
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW:    http://www.glasnet.ru/~frolov
//            или
//         http://www.dials.ccas.ru/frolov
// =====================================================
#include <dos.h>
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>

union REGS reg;
void ms_gform(int xt, int yt, char _far *form);

unsigned char form[64] = 
{
  // Массив маски по "И"
  255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
  255, 255, 255, 255, 255, 255,
  255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
  255, 255, 255, 255, 255, 255,

  // Массив маски по "Исключающее ИЛИ"
  127, 254, 127, 254, 127, 254, 127, 254, 127, 254,
  127, 254, 127, 254, 0,   0,
  0,   0,   127, 254, 127, 254, 127, 254, 127, 254, 127, 254,
  127, 254, 127, 254
};

int main(void) 
{
  unsigned old_videomode, new_videomode;
  char buf[20], *bufptr;

  // Определяем текущий видеорежим
  reg.x.ax = 0x0f00;
  int86(0x10, &reg, &reg);
  old_videomode = reg.h.al;

  // Устанавливаем новый видеорежим
  buf[0] = 10;
  printf("Enter new video mode: ");
  bufptr = cgets(buf);
  new_videomode = atoi(bufptr);

  reg.h.ah = 0;
  reg.h.al = new_videomode;
  int86(0x10, &reg, &reg);

  // Инициализируем мышь
  reg.x.ax = 0;
  int86(0x33, &reg, &reg);
  
  if(reg.x.bx == 0)
  {   
    printf("Mouse not found\n");
    return -1;
  }

  // Задаем новую форму для курсора мыши
  ms_gform(0,0, &form[0]);

  // Включаем курсор
  reg.x.ax = 1;
  int86(0x33, &reg, &reg);
  getch();

  reg.h.ah = 0;
  reg.h.al = old_videomode;
  int86(0x10, &reg, &reg);

  return 0;
}

void ms_gform(int xt, int yt, char _far *form) 
{
  struct SREGS segregs;
  
  reg.x.ax = 9;
  reg.x.bx = xt;
  reg.x.cx = yt;
  reg.x.dx = FP_OFF(form);
  segregs.es = FP_SEG(form);

  int86x(0x33,&reg,&reg,&segregs);
}

Задать форму курсора в текстовом режиме

С помощью функции 0Ah программа может изменять форму курсора мыши в текстовом режиме.

Регистры на входе: AX = 000Ah
BX = тип курсора:
0 - определяемый программно;
1 - определяемый аппаратно;
CX = маска экрана (для BX=0) или начальная строка курсора (для BX=1);
DX = маска курсора (для BX=0) или конечная строка курсора (для BX=1)
Регистры на выходе: Регистры не используются

В зависимости от содержимого регистра BX драйвер мыши использует курсор, определяемый аппаратными средствами, либо курсор, определяемый программно. По умолчанию используется “программный курсор”, который отображается в виде символа с инвертированным значением атрибута. Курсор, сформированный аппаратными средствами, выглядит аналогично обычному текстовому курсору, его форма - прямоугольник. Размер этого прямоугольника можно задавать при помощи регистров CX и DX.

Для курсора, определяемого программно, вначале выполняется операция логического “И” над содержимым видеопамяти в том месте, куда указывает курсор, и маской экрана. Затем выполняется операция “Исключающее ИЛИ” с маской курсора.

Младший байт масок соответствует коду ASCII символа, старший - это байт атрибута символа.

Значения, используемые по умолчанию - BX=7700h, CX=FFFFh.

Если вам надо изменить цвет курсора, не меняя его форму, задайте CX=00FFh, BX=xx00h, где xx определяет цвет (смотри описание формата байта атрибутов в 21 томе «Библиотеки системного программиста», который называется «Программирование видеоадаптеров»).

Программа MSTFORM

Приведем исходный текст программы MSTFORM (листинг 3.4), создающую курсор в виде вертикальной стрелки, направленной вверх, на синем фоне.

Листинг 3.4. Файл mstform\mstform.c

// =====================================================
// Изменение формы курсора в текстовом режиме
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW:    http://www.glasnet.ru/~frolov
//            или
//         http://www.dials.ccas.ru/frolov
// =====================================================
#include <dos.h>
#include <stdio.h>
#include <conio.h>

union REGS reg;
void ms_tform(int type, int mask1, int mask2);

int main(void) 
{
  // Инициализируем мышь
  reg.x.ax = 0;
  int86(0x33, &reg, &reg);
  
  if(reg.x.bx == 0)
  {   
    printf("Mouse not found\n");
    return -1;
  }

  // Задаем новую форму для курсора мыши
  ms_tform(0, 0, 0x1418);

  // Включаем курсор
  reg.x.ax = 1;
  int86(0x33, &reg, &reg);
  getch();

  return 0;
}

void ms_tform(int type, int mask1, int mask2) 
{
  reg.x.ax = 0xA;
  reg.x.bx = type;
  reg.x.cx = mask1;
  reg.x.dx = mask2;

  int86(0x33,&reg,&reg);
}

Определить содержимое счетчиков перемещения

Функция 0Bh позволяет определить относительное перемещение мыши с момента последнего вызова этой функции. Результат возвращается в указанных выше регистрах. Для измерения перемещения используется единица mickey - “мики”. Один мик соответствует 0.005 дюйма (то есть 1/200 дюйма).

Регистры на входе: AX = 000Bh
Регистры на выходе: CX = перемещение по горизонтали с момента последнего вызова функции;
DX = перемещение по вертикали с момента последнего вызова функции

Отрицательные значения перемещения означают движение влево и вверх, положительные - вправо и вниз.

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

Установить драйвер событий

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

Регистры на входе: AX = 000Ch
CX = маска вызова;
ES:DX = адрес подключаемого драйвера событий
Регистры на выходе: Регистры не используются

Биты маски вызова, передаваемой функции в регистре CX, определяют условие вызова драйвера событий:

Бит маски вызова Когда выполняется вызов
0 Перемещение мыши
1 Нажатие левой клавиши
2 Отпускание левой клавиши
3 Нажатие правой клавиши
4 Отпускание правой клавиши
5 Нажатие средней клавиши
6 Отпускание средней клавиши

Если записать в регистр CX значение 7Fh, драйвер будет вызываться при возникновении любого события, а если 00h – драйвер будет отключен.

Адрес процедуры драйвера передается при вызове функции в регистровой паре ES:DX. Драйвер должен быть оформлен в виде процедуры типа FAR, завершающейся командой дальнего возврата RETF.

Когда драйвер получает управление, в регистрах процессора содержатся следующие значения:

Регистр Описание
AX Маска вызова, такая же, как и при вызове функции 0Ch
BX Состояние клавиш мыши:
бит 0 - левая клавиша;
бит 1 - правая клавиша;
бит 2 - средняя клавиша
CX Координата X курсора мыши
DX Координата Y курсора мыши
SI Относительное перемещение мыши по горизонтали в миках
DI Относительное перемещение мыши по вертикалив миках
DS Сегмент данных драйвера мыши

Так как регистр DS при вызове драйвера событий содержит сегмент данных драйвера мыши, ваш драйвер событий должен позаботиться о правильной установке этого регистра. Однако драйвер событий не обязан сохранять и восстанавливать содержимое регистра DS и других регистров процессора.

Отметим, что если вам необходимо отключить драйвер, выполните повторный вызов функции 0Ch, записав в регистр CX нулевое значение. Если ваша программа, устанавливающая собственный драйвер событий, завершает свою работу и передает управление MS-DOS, предварительно она обязательно должна отключить драйвер событий.

Составление программы драйвера событий имеет некоторые особенности. Драйвер событий вызывается не из программы пользователя, а из драйвера мыши. При этом сегментный регистр DS будет указывать на сегмент данных драйвера мыши, а не на сегмент данных вашей программы.

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

Программа MSDRIVER

Программа MSDRIVER иллюстрирует способ работы с драйвером событий.

Исходный текст драйвера событий, составленный на языке ассемблера, представлен в листинге 3.5.

Листинг 3.5. Файл msdriver\handler.asm

;**
;.Name       ms_handl
;.Title      Драйвер событий
;
;.Descr      Драйвер событий вызывается драйвером мыши,
;            когда происходит какое-нибудь событие из числа
;            заданных при установке драйвера событий.
;            Функция не должна вызываться из программы
;            пользователя, ее вызывает только драйвер мыши.
;
;.Proto      void far ms_handl(void);
;
;.Params     Не используются
;**

	DOSSEG
DGROUP  GROUP   _DATA

_DATA   SEGMENT WORD PUBLIC 'DATA'
_DATA   ENDS

_TEXT   SEGMENT WORD PUBLIC 'CODE'
	ASSUME  cs:_TEXT, ds:DGROUP, ss:DGROUP

; Флаг вызова драйвера событий

extrn   _ms_flag:word

; Внешние переменные для записи содержимого регистров

extrn   _ms_bx:word
extrn   _ms_cx:word
extrn   _ms_dx:word
extrn   _ms_si:word
extrn   _ms_di:word
extrn   _ms_ds:word

public  _ms_handl

_ms_handl   proc far

	mov     _ms_ds, ds

; Так как на входе в драйвер событий регистр DS указывает на
; сегмент данных драйвера мыши, устанавливаем его на сегмент
; данных программы;

	push    ax
	mov     ax, DGROUP
	mov     ds, ax
	pop     ax

	mov     _ms_bx, bx
	mov     _ms_cx, cx
	mov     _ms_dx, dx
	mov     _ms_si, si
	mov     _ms_di, di

; Устанавливаем флаг вызова драйвера в 1, сигнализируя
; программе о том, что произошло событие.

	mov     _ms_flag, 1

	ret

_ms_handl   endp

_TEXT   ENDS

	END

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

Программа, установив драйвер событий и сбросив флаг _ms_flag, может выполнять какие-либо действия (например, вывод на экран движущегося изображения), постоянно проверяя флаг _ms_flag. Как только произойдет какое-либо событие (нажатие или отпускание клавиши мыши, перемещение мыши) драйвер событий установит флаг в единицу. Программа при этом может узнать состояние мыши, прочитав содержимое глобальных переменных _ms_bx, _ms_dx, и т.д.

Исходный текст программы MSDRIVER представлен в листинге 3.6.

Листинг 3.6. Файл msdriver\msdriver.с

// =====================================================
// Работа с драйвером событий
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW:    http://www.glasnet.ru/~frolov
//            или
//         http://www.dials.ccas.ru/frolov
// =====================================================
#include <dos.h>
#include <stdio.h>
#include <conio.h>

union REGS reg;
struct SREGS segregs;

extern void far MS_HANDL(void);
void ms_seth(int mask, void (far *hand)());

// Флаг драйвера событий. При вызове драйвер событий
// запишет в эту переменную значение 1
unsigned MS_FLAG;

// Область для содержимого регистров на входе
// в драйвер событий.
unsigned MS_BX;
unsigned MS_CX;
unsigned MS_DX;
unsigned MS_SI;
unsigned MS_DI;
unsigned MS_DS;

int main (void)
{
  MS_FLAG=0;

  // Инициализируем мышь
  reg.x.ax = 0;
  int86(0x33, &reg, &reg);
  
  if(reg.x.bx == 0)
  {   
    printf("Mouse not found\n");
    return -1;
  }

  // Подключаем драйвер событий, устанавливаем маску таким 
  // образом, чтобы драйвер вызывался при нажатии на левую 
  // или правую клавиши мыши
  ms_seth(2 | 8, MS_HANDL);

  // Включаем курсор
  reg.x.ax = 1;
  int86(0x33, &reg, &reg);

  // Ожидаем вызова драйвера событий.
  for(;;) 
  {
    if(MS_FLAG) 
    {
      printf("\nRegisters on driver entry:"
        "\nms_bx: %0X"
        "\nms_cx: %0X"
        "\nms_dx: %0X"
        "\nms_si: %0X"
        "\nms_di: %0X"
        "\nms_ds: %0X",
        MS_BX, MS_CX, MS_DX, MS_SI, MS_DI, MS_DS);

        printf("\nPress any key...");
        getch();
        return 0;
     }
  }
}

void ms_seth(int mask, void (far *hand)()) 
{
  reg.x.ax = 0x14;
  reg.x.cx = mask;
  reg.x.dx = FP_OFF(hand);
  segregs.es = FP_SEG(hand);

  int86x(0x33,&reg,&reg,&segregs);
}

Включить эмуляцию светового пера

Световое перо теперь можно увидеть разве лишь в музее. Однако вы можете заменить световое перо на мышь, если ваша программа использует световое перо (например, она написана на языке Бейсик и вызывает функцию PEN). Для включения эмуляции светового пера вы можете воспользоваться функцией 0Dh:

Регистры на входе: AX = 000Dh
Регистры на выходе: Регистры не используются

После включения режима эмуляции драйвер запоминает координаты курсора в момент, когда пользователь нажимает на клавиши мыши. Эти координаты могут быть впоследствии считаны функцией PEN или функцией 04h прерывания INT 10h, предназначенной для работы со световым пером.

Выключить эмуляцию светового пера

Функция 0Eh выключает режим эмуляции светового пера.

Регистры на входе: AX = 000Eh
Регистры на выходе: Регистры не используются

Задать скорость перемещения курсора мыши

Функция 0Fh определяет чуствительность мыши к перемещению по поверхности стола, то есть устанавливает соответствие между величиной перемещения мыши по столу и величиной перемещения курсора мыши по экрану.

Регистры на входе: AX = 000Fh
CX = количество миков на 8 точек по горизонтали;
DX = количество миков на 8 точек по вертикали
Регистры на выходе: Регистры не используются

При инициализации драйвера мыши используются следующие значения: CX=8, DX=16.

Установить область исключения для курсора

Функция позволяет задать на экране прямоугольную область, в которой автоматически выключается изображение курсора мыши - область исключения. Эта область отменяется функциями 01h (включить курсор мыши) и 00h (инициализация).

Регистры на входе: AX = 0010h
CX, DX = координаты (X, Y) верхнего левого угла области исключения;
SI, DI = координаты (X, Y) нижнего правого угла области исключения
Регистры на выходе: Регистры не используются

Оператор может поместить курсор мыши в область исключения, при этом изображение курсора пропадет.

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

Задать увеличенный графический курсор

Функция 12h позволяет задать увеличенный по размеру курсор мыши, но она определена только для мыши системы PC MOUSE.

Регистры на входе: AX = 0012h
BH = ширина курсора в словах;
CH = количество строк в изображении курсора;
BL = номер позиции точки-указателя графического курсора (от -16 до 16);
CL = номер строки точки-указателя (от -16 до 16);
ES:DX = указатель на растровое изображение курсора
Регистры на выходе: Регистры не используются

Определить порог удвоения скорости

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

Формат вызова функции 13h приведен ниже:

Регистры на входе: AX = 0013h
Регистры на выходе: DX = значение порога удвоения, мики в секунду

При инициализации устанавливается значение порога, равное 64 микам в секунду (1/3 дюйма в секунду). Если вам надо установить это значение, вы можете при вызове функции 13h задать DX=0.

Заменить драйвер событий

Функция 14h аналогична функции 0Ch, однако ее основное назначение - временная замена драйвера событий. Например, подпрограмма в начале своей работы может установить свой драйвер событий, а перед завершением - активизировать драйвер, использовавшийся ранее.

Регистры на входе: AX = 0014h
CX = маска вызова;
ES:DX = адрес подключаемого драйвера событий
Регистры на выходе: CX = маска предыдущего драйвера событий;
ES:DX = адрес предыдущего (заменяемого) драйвера событий

Определить размер буфера состояния драйвера

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

Регистры на входе: AX = 0015h
Регистры на выходе: BX = размер буфера, требующийся для хранения состояния драйвера мыши

Когда может потребоваться запоминание и восстановление состояния драйвера? Например, при использовании мыши резидентными (TSR) программами желательно сохранить состояние драйвера перед началом работы TSR-программы и восстановить его перед завершением работы TSR-программы.

Сохранить состояние драйвера

Функция 16h позволяет записать состояние драйвера в буфер, размер которого должен быть определен с помощью функции 15h.

Регистры на входе: AX = 0016h
ES:DX = адрес буфера для записи состояния драйвера
Регистры на выходе: Регистры не используются

Восстановить состояние драйвера

Функция 17h позволяет восстановить состояние драйвера из буфера, в который оно было записано при помощи функции 16h.

Регистры на входе: AX = 0017h
ES:DX = адрес буфера, содержащего состояние драйвера
Регистры на выходе: Регистры не используются

Установить альтернативный драйвер событий

Функция 18h предназначена для установки альтернативного драйвера событий:

Регистры на входе: AX = 0018h
CX = маска вызова;
ES:DX = адрес подключаемого драйвера событий
Регистры на выходе: AX = результат установки:
0018h - драйвер успешно установлен;
FFFFh - ошибка при установке драйвера

По сравнению с функцией 0Ch эта функция обеспечивает дополнительные возможности:

При попытке установить два драйвера с одной и той же маской событий функция возвращает в регистре AX код ошибки FFFFh. В этом случае вы можете использовать функцию 19h для получения адреса предыдущего установленного драйвера событий, отключить его и повторить попытку подключения своего драйвера.

Функция 18h может применяться для отключения драйвера событий, если в регистрах ES:DX указать его адрес и при этом задать в регистре CX значение маски, равное 0.

Получить адрес альтернативного драйвера событий

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

Регистры на входе: AX = 0019h
CX = маска событий, для которой требуется получить адрес драйвера
Регистры на выходе: CX = маска событий или 0000h, если заданной маске не соответствует ни один установленный драйвер событий;
ES:DX = адрес драйвера событий, использующий заданную маску событий

Установить чувствительность мыши

Функция 1Ah является комбинацией функций 0Fh и 13h. Она позволяет одновременно устанавливать чувствительность мыши и порог удвоения скорости.

Регистры на входе: AX = 001Ah
BX = горизонтальная чувствительность в миках на пиксел;
CX = вертикальная чувствительность в миках на пиксел;
DX = значение порога удвоения, мики в секунду
Регистры на выходе: Регистры не используются

Определить чувствительность мыши

Функция 1Bh позволяет определить текущие значения для чувствительности мыши и порога удвоения.

Регистры на входе: AX = 001Bh
Регистры на выходе: BX = горизонтальная чувствительность в миках на пиксел;
CX = вертикальная чувствительность в миках на пиксел;
DX = значение порога удвоения, мики в секунду

Установить частоту прерываний для Inport Mouse

Мышь периодически вырабатывает сигнал прерывания, по которому драйвер считывает текущее состояние мыши. С помощью функции 1Ch вы можете изменять частоту появления прерываний, но только для мыши системы Inport Mouse (тип мыши нетрудно определить с помощью функции 24h).

Регистры на входе: AX = 001Сh
BX = код скорости прерываний:
1 - нет прерываний;
2 - 30 прерываний в секунду;
4 - 50 прерываний в секунду;
8 - 100 прерываний в секунду;
16 - 200 прерываний в секунду
Регистры на выходе: Регистры не используются

Чем больше частота прерываний, тем больше точность определения состояния мыши.

Установить номер страницы видеопамяти

Функция 1Dh задает номер страницы видеопамяти, на которой будет отображаться курсор мыши. По умолчанию для отображения используется страница 0.

Регистры на входе: AX = 001Dh
BX = номер страницы видеопамяти
Регистры на выходе: Регистры не используются

Определить номер страницы видеопамяти

Функция 1Eh возвращает номер страницы видеопамяти, на которой в настоящее время отображается курсор мыши.

Регистры на входе: AX = 001Eh
Регистры на выходе: BX = номер страницы видеопамяти

Отключить драйвер мыши

После вызова функции 1Fh драйвер мыши полностью отключается. Вектор прерывания INT 33h остается определенным, однако теперь выполняется только одна функция прерывания INT 33h - функция 21h (программный сброс мыши).

Регистры на входе: AX = 001Fh
Регистры на выходе: AX = результат выполнения:
001Fh - драйвер отключен;
FFFFh - отключение невозможно;
ES:DX = адрес предыдущего драйвера мыши

Функцию 1Fh удобно использовать для временной замены драйвера на собственную систему обслуживания мыши. Сначала вы отключаете драйвер функцией 1Fh, запоминая адрес предыдущего драйвера, возвращаемого в регистрах ES:DX. Затем устанавливаете собственную систему обслуживания мыши, а после ее использования - восстанавливаете значение этого адреса.

Восстановить драйвер мыши

Функция 20h восстанавливает связь между мышью и драйвером, отключенную вызовом функции 1Fh.

Регистры на входе: AX = 0020h
Регистры на выходе: Регистры не используются

Сбросить драйвер мыши

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

Регистры на входе: AX = 0021h
Регистры на выходе: AX = результат:
0021h - драйвер сброшен успешно;
FFFFh - невозможно сбросить драйвер (например, из-за того что драйвер не установлен);
BX = количество клавиш на корпусе мыши

Определить тип мыши

Функция 24h дает информацию о типе используемой мыши, версии драйвера мыши и об используемом номере прерывания.

Регистры на входе: AX = 0024h
Регистры на выходе: BH = верхний (major) номер версии драйвера;
BL = нижний (minor) номер версии драйвера;
CH = тип мыши:
1 - Bus Mouse;
2 - Serial Mouse;
3 - Inport Mouse;
4 - PS/2 Mouse;
5 - HP Mouse;
CL = номер используемого прерывания (IRQ):
0 - IBM PS/2;
2, 3, 4, 5, 7 - IBM PC

Мышь Microsoft IntelliMouse

В 1997 году Microsoft выпустила новую мышь с названием Microsoft IntelliMouse, добавив в нее принципиально новый орган управления – небольшое колесо, расположенное между левой и правой клавишами мыши. Это небольшое, на первый взгляд, усовершенствование намного упрощает работу пользователей с приложениями Windows, в которых нужно просматривать длинные документы или выполнять плавное масштабирование изображений.

Колесо имеет двойное назначение.

Во-первых, оно служит вместо третьей кнопки, которая есть в трехкнопочных мышах типа Mouse System и совместимых с ними. Ранее Microsoft упорно игнорировала третью кнопку в приложениях Windows, однако как вы увидите, теперь положение несколько изменилось.

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

Приложение Windows может использовать колесо, например, вместо вертикальной полосы просмотра Scrollbar. В этом случае для того чтобы сдвинуть содержимое окна просмотра, вам не нужно снимать руку с мыши и переносить ее на клавиатуру или перемещать курсор мыши к полосе просмотра. Достаточно просто немного повернуть колесо в нужном направлении – и содержимое окна будет сдвинуто.

К сожалению, для использования возможности новой мыши вам не достаточно просто подключить ее к компьютеру и установить соответствующий драйвер. Программы MS-DOS и приложения Microsoft Windows версии 3.1 совсем не могут пользоваться колесом, принимая новую мышь как обычную двухкнопочную мышь Microsoft. Что же касается приложений Microsoft Windows 95 и Microsoft Windows NT, то в них необходимо предусмотреть обработку сообщений, создаваемых колесом.

Заметим, что все новые приложения, созданные Microsoft, такие как Microsoft Office 97 и Microsoft Visual C++ версии 5.0, умеют работать с мышью Microsoft IntelliMouse.

Для того чтобы воспользоваться колесом в старых приложениях, таких как Microsoft Word 7.0, вы можете применить условно-бесплатное приложение Flywheel, загрузив его из Internet с сервера Web, расположенного по адресу http://www.plannetarium.com. Это приложение преобразует сообщения от колеса в сообщения от вертикальной полосы просмотра и выполняет ряд других интересных функций.

Мы же в этой книге расскажем вам, как можно оорганизовать обработку сообщений от колеса мыши Microsoft IntelliMouse в своих приложениях.

Проверка наличия мыши Microsoft IntelliMouse

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

Способ проверки зависит от операционной системы.

Проверка в среде Microsoft Windows 95 и Microsoft Windows NT версии 3.51

В текущей на момент написания книги версии Microsoft Windows 95 драйвер мыши Microsoft IntelliMouse отсутствует. Если вы купили такую мышь, необходимо установить драйвер с дискеты, которая прилагается к мыши.

Для проверки наличия мыши с колесом в среде Microsoft Windows 95 вам следует сначала зарегистрировать сообщение с кодом MSH_WHEELSUPPORT, вызвав для этого функцию RegisterWindowMessage:

UINT uMSH_SUPPORT = 0;
uMSH_SUPPORT = RegisterWindowMessage(MSH_WHEELSUPPORT);

Идентификатор MSH_WHEELSUPPORT, а также другие идентификаторы, необходимые для программирования новой мыши, находятся в файле zmouse.h, который можно бесплатно переписать в составе Microsoft IntelliMouse SDK с сервера Microsoft с адресом http://www.microsoft.com.

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

HWND hwndMSHWheel  = NULL;
hwndMSHWheel = FindWindow(MSH_WHEELMODULE_CLASS, 
  MSH_WHEELMODULE_TITLE);

Если сообщение MSH_WHEELSUPPORT успешно зарегистрировано, а указанный выше идентификатор определен, можно послать сообщение MSH_WHEELSUPPORT в окно приложения MSWheel, вызвав для этого функцию SendMessage:

BOOL fWheel = FALSE;
if(uMSH_SUPPORT != 0 && hwndMSHWheel != 0)
{
  fWheel = 
    (BOOL)SendMessage(hwndMSHWheel, uMSH_SUPPORT, 0, 0);
}

Если мышь Microsoft IntelliMouse подключена, функция SendMessage возвратит значение TRUE, если нет – FALSE.

Проверка в среде Microsoft Windows NT версии 4.0

Операционная система Microsoft Windows NT версии 4.0 содержит драйвер мыши Microsoft IntelliMouse, поэтому установка дополнительных драйверов не требуется.

Проверить наличие мыши с колесом можно при помощи обычной функции GetSystemMetrics, передав ей в качестве параметра значение SM_MOUSEWHEELPRESENT:

if(!GetSystemMetrics(SM_MOUSEWHEELPRESENT))
{
  MessageBox(NULL, "Microsoft IntelliMouse not found",
  "Error message", MB_OK);
}

Если функция возвратит нулевое значение, значит мышь Microsoft IntelliMouse не подключена к компьютеру.

Определение величины свертки

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

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

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

Определение величины свертки в среде Microsoft Windows 95 и Microsoft Windows NT версии 3.51

В среде операционных систем Microsoft Windows 95 и Microsoft Windows NT версии 3.51 для определения величины свертки вначале необходимо зарегистрировать сообщение MSH_SCROLL_LINES:

UINT uMSH_SCROLL_LINES = 0;
uMSH_SCROLL_LINES = RegisterWindowMessage(MSH_SCROLL_LINES);

Затем это сообщение посылается приложению MSWheel с помощью функции SendMessage:

UINT uiScrollLines = 3;
uiScrollLines = 
  (BOOL)SendMessage(hwndMSHWheel, uMSH_SCROLL_LINES, 0, 0);

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

Определение величины свертки в среде Microsoft Windows NT версии 4.0

Если приложение работает в среде операционной системы Microsoft Windows NT версии 4.0, то оно должно определять величину свертки с помощью функции SystemParametersInfo:

UINT uiScrollLines = 3;
SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, 
  &uiScrollLines, 0);

Эта функция предназначена для определения различных системных параметров. В качестве первого параметра функции SystemParametersInfo передается значение SPI_GETWHEELSCROLLLINES, а в качестве третьего – адрес переменной типа UINT, в которую будет записана величина свертки.

Сообщение MSH_MOUSEWHEEL

Работа мыши Microsoft IntelliMouse в среде операционных систем Microsoft Windows 95 и Microsoft Windows NT версии 3.51 обеспечивается приложением MSWheel, которое устанавливается с дискеты, входящей в комплект мыши. При повороте колеса приложение MSWheel посылает сообщение MSH_MOUSEWHEEL в функцию главного окна приложения, над которым находится курсор мыши.

Перед тем как использовать это сообщение, его необходимо зарегистрировать функцией RegisterWindowMessage:

UINT uMSH_MOUSEEHEEL = 0;
uMSH_MOUSEEHEEL = RegisterWindowMessage(MSH_MOUSEWHEEL);

Ниже мы привели парамтеры сообщения MSH_MOUSEWHEEL:

Параметр Описание
(short)wParam Угол поворота
LOWORD(lParam) Координата X курсора мыши относительно левого верхнего угла окна
HIWORD(lParam) Координата Y курсора мыши относительно левого верхнего угла окна

Значение угла поворота изменяется дискретно с шагом 120. Если это значение положительно, колесо поворачивается в сторону пользователя, если отрицательно – в сторону монитора компьютера (это правило может быть изменено пользователем соответствующей настройкой параметров драйвера мыши).

Получив сообщение MSH_MOUSEWHEEL, функция главного окна приложения может обработать его самостоятельно или передать дочерним окнам.

Сообщение WM_MOUSEWHEEL

Сообщение WM_MOUSEWHEEL определено в операционной системе Microsoft Windows NT версии 4.0 и будет влкючено в новую версию операционной системы Microsoft Windows 95. По своему назначению оно аналогично сообщению MSH_MOUSEWHEEL, но имеет немного другие параметры:

Параметр Описание
LOWORD(wParam) Код виртуальной клавиши, нажатой в момент прихода сообщения
(short)HIWORD(wParam) Угол поворота
(short)LOWORD(lParam) Координата X курсора мыши относительно левого верхнего угла окна
(short)HIWORD(lParam) Координата Y курсора мыши относительно левого верхнего угла окна

Код виртуальной клавиши может иметь следующие значения:

Код Нажатая клавиша
MK_CONTROL <Control>
MK_LBUTTON Левая клавиша мыши
MK_MBUTTON Средняя клавиша мыши
MK_RBUTTON Правая клавиша мыши
MK_SHIFT <Shift>

Другие сообщения от мыши Microsoft IntelliMouse

Помимо только что описанных сообщений MSH_MOUSEWHEEL и WM_MOUSEWHEEL, мышь Microsoft IntelliMouse может посылать такие же сообщения, как и обычная трехкнопочная мышь. Сообщения, возникающие когда пользователь нажимает или отжимает колесо, эквивалентны сообщениям от средней кнопки трехкнопочной мыши.

Подробное описание параметров всех этих сообщений вы найдете в 11 томе «Библиотеки системного программиста», который называется «Операционная система Microsoft Windows для программиста».

Приложение RTFPAD

В 22 томе «Библиотеки системного программиста», который называется «Операционная система Windows 95 для программиста» мы привели исходные тексты приложения RTFPAD. Это приложение представляет собой текстовый редактор, способный работать с документами в формате RTF. Такой документ может содержать шрифтовое оформление.

Для того чтобы продемонстрировать обработку сообщений от мыши Microsoft IntelliMouse, мы немного изменили приложение RTFPAD. В листинге 3.7 вы найдете исходный текст измененных функций WinMain и WndProc.

Обратите внимание, что при инициализации приложения мы определяем версию операционной системы с тем чтобы приложение могло работать как в среде Microsoft Windows 95, так и в среде Microsoft NT версии 4.0.

Листинг 3.7 (сокращенный). Файл rtfpad\rtfpad.с

// =====================================================
// Редактор текста RTFPAD, способный работать
// с мышью Microsoft IntelliMouse
//
// (C) Фролов А.В, 1996, 1997
//
// E-mail: frolov@glas.apc.org
// WWW:    http://www.glasnet.ru/~frolov
//            или
//         http://www.dials.ccas.ru/frolov
// =====================================================

// Это определение нужно для того, чтобы при компилляции
// файла winuser.h были подключены определения 
// идентификаторов SPI_GETWHEELSCROLLLINES
// и WM_MOUSEWHEEL

#define _WIN32_WINNT 0x0400

// Еще один способ определения этих же идентификаторов

//#ifndef SPI_GETWHEELSCROLLLINES
//#define SPI_GETWHEELSCROLLLINES   104
//#endif
//#ifndef WM_MOUSEWHEEL
//#define WM_MOUSEWHEEL WM_MOUSELAST+1
//#endif

#define STRICT
#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>
#include <richedit.h>

// Необходимо для определения значения константы 
// UINT_MAX, которая используется в файле zmouse.h
#include <limits.h>

#include "resource.h"
#include "afxres.h"

// Файл определений для IntelliMouse 
#include "zmouse.h"

#include "rtfpad.h"

// Код сообщения MSH_MOUSEEHEEL
UINT uMSH_MOUSEEHEEL   = 0;

// Код сообщения MSH_SUPPORT
UINT uMSH_SUPPORT      = 0;

// Код сообщения MSH_SCROLL_LINES
UINT uMSH_SCROLL_LINES = 0;

// Идентификатор окна для посылки сообщений
// приложению MSWheel
HWND hwndMSHWheel  = NULL;

// Флаг наличия мыши Microsoft IntelliMouse
BOOL fWheel        = FALSE;

// Количество строк свертки
UINT uiScrollLines = 3;

// Структура для определения версии
// операционной системы
OSVERSIONINFO osv;


HINSTANCE hInst;
char szAppName[]  = "RtfEditApp";
char szAppTitle[] = "Rich Text Editor RtfPad";
HWND hwndEdit;
HINSTANCE hRTFLib;

// -----------------------------------------------------
// Функция WinMain
// -----------------------------------------------------
int APIENTRY 
WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
        LPSTR lpCmdLine, int nCmdShow)
{
  WNDCLASSEX wc;
  HWND hWnd;
  MSG msg;
  
  hInst = hInstance;
  hWnd = FindWindow(szAppName, NULL);
  if(hWnd)
  {
    if(IsIconic(hWnd))
  ShowWindow(hWnd, SW_RESTORE);
  SetForegroundWindow(hWnd);
    return FALSE;
  }

  // Определяем версию операционной системы
  memset(&osv, 0, sizeof(OSVERSIONINFO));
  osv.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  GetVersionEx(&osv);

  // Для Windows 95 и Windows NT версии 3.51 выполняем
  // регистрацию сообщений
  if( (osv.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) ||
     ((osv.dwPlatformId == VER_PLATFORM_WIN32_NT) && 
      (osv.dwMajorVersion < 4)))
  {
    // Регистрируем сообщение MSH_WHEELSUPPORT
    uMSH_SUPPORT = 
      RegisterWindowMessage(MSH_WHEELSUPPORT);
    
    // Определяем наличие мыши Microsoft IntelliMouse
    hwndMSHWheel = 
      FindWindow(MSH_WHEELMODULE_CLASS, 
      MSH_WHEELMODULE_TITLE);
    
    if(uMSH_SUPPORT != 0 && hwndMSHWheel != 0)
    {
      fWheel = 
        (BOOL)SendMessage(hwndMSHWheel, uMSH_SUPPORT, 0, 0);
    }

    if(!fWheel)
    {
      MessageBox(NULL, "MS Wheel not supported",
        "Error message", MB_OK);
    }
  
    // Регистрируем сообщение MSH_MOUSEWHEEL
    uMSH_MOUSEEHEEL = RegisterWindowMessage(MSH_MOUSEWHEEL);
    if(!uMSH_MOUSEEHEEL)
    {
      MessageBox(NULL, "Error: RegisterWindowMessage",
       "Error message", MB_OK);
      return FALSE;
    }

    // Регистрируем сообщение MSH_SCROLL_LINES
    uMSH_SCROLL_LINES = 
      RegisterWindowMessage(MSH_SCROLL_LINES);
    
    // Определяем количество строк свертки
    if(uMSH_SCROLL_LINES != 0 && hwndMSHWheel != 0)
    {
      uiScrollLines = 
        (BOOL)SendMessage(hwndMSHWheel, 
        uMSH_SCROLL_LINES, 0, 0);
    }
  }

  // Для Windows NT версии 4.0 применяем другую методику
  else
  {
    // Проверяем наличие мыши Microsoft IntelliPoint
    if(!GetSystemMetrics(SM_MOUSEWHEELPRESENT))
    {
      MessageBox(NULL, 
        "Microsoft IntelliMouse not found",
        "Error message", MB_OK);
    }

    // Определяем количество строк свертки
    SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 
      0, &uiScrollLines, 0);
  }

  hRTFLib = LoadLibrary("RICHED32.DLL");
  if(!hRTFLib)
    return FALSE;

  memset(&wc, 0, sizeof(wc));
  wc.cbSize = sizeof(WNDCLASSEX);
  wc.hIconSm = LoadImage(hInst,
    MAKEINTRESOURCE(IDI_APPICONSM), IMAGE_ICON, 16, 16, 0);
  wc.style = 0;
  wc.lpfnWndProc = (WNDPROC)WndProc;
  wc.cbClsExtra  = 0;
  wc.cbWndExtra  = 0;
  wc.hInstance = hInst;
  wc.hIcon = LoadImage(hInst,
    MAKEINTRESOURCE(IDI_APPICON), IMAGE_ICON, 32, 32, 0);
  wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
  wc.lpszMenuName = MAKEINTRESOURCE(IDR_APPMENU);
  wc.lpszClassName = szAppName;
  if(!RegisterClassEx(&wc))
    if(!RegisterClass((LPWNDCLASS)&wc.style))
  return FALSE;
    
  hWnd = CreateWindow(szAppName, szAppTitle, 
    WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, 
    CW_USEDEFAULT, 0, NULL, NULL, hInst, NULL);
  if(!hWnd) return(FALSE);

  ShowWindow(hWnd, nCmdShow);
  UpdateWindow(hWnd);
  while(GetMessage(&msg, NULL, 0, 0))
  {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }
  return msg.wParam;
}

// -----------------------------------------------------
// Функция WndProc
// -----------------------------------------------------
LRESULT WINAPI
WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
  // Изменение положения колеса
  short zDelta;

  switch(msg)
  {
    HANDLE_MSG(hWnd, WM_CREATE,     WndProc_OnCreate);
    HANDLE_MSG(hWnd, WM_DESTROY,    WndProc_OnDestroy);
    HANDLE_MSG(hWnd, WM_COMMAND,    WndProc_OnCommand);
    HANDLE_MSG(hWnd, WM_SIZE,       WndProc_OnSize);
    HANDLE_MSG(hWnd, WM_SETFOCUS,   WndProc_OnSetFocus);

  default:
    {
      if(msg == WM_MOUSEWHEEL)
      {
        zDelta = (short)HIWORD(wParam);

        if(zDelta < 0)
          if(uiScrollLines != WHEEL_PAGESCROLL)
            SendMessage(hwndEdit, EM_LINESCROLL, 0,  
              uiScrollLines);
        
          else
            SendMessage(hwndEdit, EM_SCROLL, 
              (WPARAM)(INT)SB_PAGEDOWN, 0);
        else
          
          if(uiScrollLines != WHEEL_PAGESCROLL)
            SendMessage(hwndEdit, EM_LINESCROLL, 0,
              -(LPARAM)uiScrollLines);
          
          else
            SendMessage(hwndEdit, EM_SCROLL, 
              (WPARAM)(INT)SB_PAGEUP, 0);
        return 0L;
      }

      else if(msg == uMSH_MOUSEEHEEL)
      {
        zDelta = (short)wParam;

        if(zDelta < 0)
          if(uiScrollLines != WHEEL_PAGESCROLL)
            SendMessage(hwndEdit, EM_LINESCROLL, 0, 
              uiScrollLines);
       
          else
            SendMessage(hwndEdit, EM_SCROLL, 
              (WPARAM)(INT)SB_PAGEDOWN, 0);
        else
          
          if(uiScrollLines != WHEEL_PAGESCROLL)
            SendMessage(hwndEdit, EM_LINESCROLL, 0,
              -(LPARAM)uiScrollLines);
          
          else
            SendMessage(hwndEdit, EM_SCROLL, 
              (WPARAM)(INT)SB_PAGEUP, 0);
        
        return 0L;
      }
      return(DefWindowProc(hWnd, msg, wParam, lParam));
    }
  }
}
. . .

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