Системные функции мыши для текстового режима


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

typedef struct _MOUSE_EVENT_RECORD {

COORD dwMousePosition;

DWORD dwButtonState;

DWORD dwControlKeyState;

DWORD dwEventFlags;

} MOUSE_EVENT_RECORD, *PMOUSE_EVENT_RECORD;

Компонент dwMousePosition этой структуры дает текущую позицию мыши (точнее позицию на момент формирования операционной системой сообщения от мыши) и состоит из X-координаты и Y-координаты курсора мыши. Поле dwButtonState выдает код нажатия клавиш мыши и может быть произвольной комбинацией следующих битовых значений:

#define FROM_LEFT_1ST_BUTTON_PRESSED 0x0001

#define RIGHTMOST_BUTTON_PRESSED 0x0002

#define FROM_LEFT_2ND_BUTTON_PRESSED 0x0004

#define FROM_LEFT_3RD_BUTTON_PRESSED 0x0008

#define FROM_LEFT_4TH_BUTTON_PRESSED 0x0010

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

#define MOUSE_MOVED 0x0001

#define DOUBLE_CLICK 0x0002

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

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

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

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

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

Для регистрации у сервера программа процесса должна заполнить структуру Gpm_Connect и выполнить вызов функции Gpm_Open с этой структурой в качестве аргумента. Функция Gpm_Open имеет прототип

int Gpm_Open (Gpm_Connect *CONN, int flag),

где флаг flag в простейших случаях должен быть задан равным 0. Структура Gpm_Connect описана в заголовочном файле как

typedef struct Gpm_Connect {

unsigned short eventMask, defaultMask;

unsigned short minMod, maxMod;

int pid;

int vc;

} Gpm_Connect;

Перед регистрацией необходимо заполнить поля eventMask, defaultMask, minMod, maxMod. Остальные поля заполняются автоматически. В простейшем случае для полей minMod, maxMod берется значение 0. Ненулевые значения этих полей используются только, если клиентом предполагается отдельно обрабатывать комбинации нажатия клавиш мыши в сочетании с нажатыми клавишами Shift, Ctrl и Alt. Особенно важно значение поля eventMask, именно оно определяет, какие сообщения будет получать программа клиента. Значение этого поля задается как логическая (побитовая) комбинация символических констант, которые задают типы событий. Основные типы событий обозначаются следующими константами: GPM_MOVE – событие движения мыши при не нажатых ее клавишах; GPM_DRAG – событие протаскивания, когда нажаты одна или более клавиш и мышь движется; GPM_DOWN – событие нажатия клавиши мыши; GPM_UP – событие отпускания клавиши мыши; GPM_DOUBLE – используется для уточнения события типа нажатия, отпускания и протаскивания и обозначает наличие быстрого двойного нажатия на клавишу мыши.

Для того чтобы поручить серверу передавать клиенту сообщения о движении мыши, следует включить GPM_MOVE в значение поля eventMask при вызове клиентом функции Gpm_Open регистрации у сервера. Если же клиента интересуют только нажатия на клавиши мыши, то следует в eventMask включить только GPM_DOWN. Когда предполагается использовать двойные нажатия клавиш мыши, следует занести в eventMask комбинацию GPM_DOWN | GPM_DOUBLE и т.д.

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

Стандартными рекомендациями по использованию поля defaultMask являются следующие. Вариант ~eventMask поручает выполнять стандартную обработку всех событий, которые не обрабатывает клиент. Вариант 0 обозначает отбрасывание всей стандартной обработки событий.

Функция Gpm_Open при неудаче возвращает значение -1, а при удаче – хэндл для обращения к серверу, который, впрочем, явно почти не используется, так как копия его значения сохраняется в глобальной для клиента переменной gpm_fd.

Следует обратить особое внимание, что программу, использующую сервер мыши gpm, не удается нормально запустить из-под инструментальной оболочки mc (полное имя которой MidnightCommander). При попытке такого запуска возвращается код возврата, свидетельствующий о неудаче. Такие программы следует запускать из командной оболочки, не использующей мышь, в частности, выйдя из инструментальной оболочки mc по нажатию управляющей клавиши F10. Если же присутствует необходимость одновременного использования и инструментальной оболочки mc, и прикладной программы, запрашивающей обслуживание gpm-сервером мыши, то следует вызывать функцию открытия доступа к такому серверу с последним аргментом flag, равным числу ‑1.

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

int Gpm_GetEvent (Gpm_Event *EVENT)

Неявно эта функция использует глобальную переменную gpm_fd со значением, установленном при регистрации клиента. Функция возвращает значение 1 при успешном выполнении, -1 – при неудаче и 0 – после закрытия связи с сервером. Единственным аргументом функции является структура данных сообщения Gpm_Event, описываемая как

typedef struct Gpm_Event {

unsigned char buttons, modifiers;

unsigned short vc;

short dx, dy, x, y;

enum Gpm_Etype type;

int clicks;

enum Gpm_Margin margin;

} Gpm_Event;

Поля x, y этой структуры дают координаты позиции курсора мыши на момент формирования сообщения. Поле buttons выдает код нажатия клавиш и является логической комбинацией следующих констант:

#define GPM_B_LEFT 4 // Левая клавиша мыши

#define GPM_B_MIDDLE 2 // Средняя клавиша мыши

#define GPM_B_RIGHT 1 // Правая клавиша мыши

Такая кодировка заметно отличается от используемой в других из рассматриваемых ОС. Поля dx, dy дают значения приращения координат мыши в сравнении с предыдущим сообщением от нее. Поле type информирует о типе полученного сообщения и может принимать одно из следующих значений: GPM_MOVE, GPM_DRAG, GPM_DOWN, GPM_UP. Значение поля clicks дает число нажатий клавиши, зафиксированных в одном событии, и может быть 0, 1, 2 для одиночного двойного и тройного нажатия соответственно. Это поле имеет действенное значение только для событий нажатия, отпускания и протаскивания. Назначения остальных полей в данном изложении рассматриваться не будут, так как реальные возможности современного системного обеспечения намного больше, чем удается изложить в относительно небольшом по объему учебном пособии.

После завершения использования мыши клиентом следует вызвать функцию Gpm_Close(), которая разрывает связь клиента с сервером. Функция эта не имеет аргументов. Заголовочным файлом для программ с использованием подсистемы gpm служит файл gpm.h.

При разработке программ, использующих подсистему gpm, следует иметь в виду необходимость явного подключения библиотеки поддержки этой подсистемы. Такое подключение может задаваться в командной строке дополнительным параметром полного имени библиотеки. Обычно ее полное имя /usr/lib/libgpm.so, так что вызов компиляции исходного текста программы prim1.c будет иметь вид

gcc -o prim1.exe prim1.c /usr/lib/libgpm.so

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

-lуникальнаячастьименибиблиотеки,

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

gcc -o prim1.exe prim1.c -lgpm

Следующий пример, приведенный в листинге 5.5.1, демонстрирует использование рассмотренных функций подсистемы gpm.

#include <stdio.h>

#include <gpm.h>

 

int main()

{int k, xm, ym, button;

struct Gpm_Event evnt;

struct Gpm_Connect conn;

conn.eventMask = GPM_DOWN; //запрашиваем события нажатия кнопок мыши

conn.defaultMask = GPM_MOVE;

conn.minMod = 0;

conn.maxMod = 0;

if (Gpm_Open(&conn,0) = = -1)

{printf("Error Open Mouse");exit(1);}

do

{Gpm_GetEvent(&evnt);

xm=evnt.x; ym=evnt.y;

button=evnt.buttons;

printf("\033[1;60H\033[1;33m"); // позиция на x=60,y=1; цвет YELLOW

printf("row=%3d col=%3d key=%1d\n", xm, ym, button);

} while (button!=5); // при GPM_B_LEFT и GPM_B_RIGHT одновременно

printf("\033[0m");

Gpm_Close();

return 0;

}

Листинг 5.5.1. Использование мыши в Linux

 

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

Если программист запрашивает получение событий типа GPM_MOVE, то обработчик событий от мыши по умолчанию не занимается прорисовкой изображения курсора мыши. Поэтому для принудительной прорисовки этого курсора предусмотрены специальные средства. Они состоят из функции Gpm_DrawPointer и макроса GPM_DRAWPOINTER. Указанная функция имеет прототип

Gpm_DrawPointer(short x, short y, int gpm_consolefd),

а макрос требует единственного параметра – адреса экземпляра события, полученного от сервера мыши функцией Gpm_GetEvent (или аналогичной ей).