Общение программы с мышью похоже на работу с клавиатурой, X-сервер получает сигналы от устройства, преобразует их в события и помещает последние в очередь программы. Однако есть существенная разница. Если события от клавиатуры передаются лишь программе, окно которой имеет фокус ввода, то события от мыши могут передаваться, в принципе, любой задаче, окно (окна) которой присутствуют на экране.
Чаще всего приходится обрабатывать события нажатия (отпускания) кнопки мыши. Для регистрации такого типа событий, необходимо добавить одну из следующих масок с помощью функции XSelectInput()
:
ButtonPressMask
- уведомлять о нажатии любой кнопки
в одном из окон программы.ButtonReleaseMask
- уведомлять об отпускании любой кнопки
в одном из окон программы.
В цикле обработки сообщений могут проверяться такие события:ButtonPress
- нажата кнопка в одном из окон программы.ButtonRelease
- отпущена кнопка в одном из окон программы.Структура для этих сообщений получается доступом к полю xbutton
объединения XEvent
и содержит, в частности, такие поля:
Window window
- идентификатор окна, которому было
послано сообщение (в случае, если оно было зарегистрировано для
нескольких окон программы).int x, y
- координаты x и y (в пикселях)
мышиного курсора в момент нажатия.int button
- номер нажатой кнопки
(может принимать значения Button1, Button2, Button3).Time time
- время (в миллисекундах),
которое длилось событие. Может использоваться для определения
"двойного щелчка".В качестве примера фрагмент кода, в котором рисуется черный пиксель в позиции мыши всякий раз, когда получаем событие "нажатие кнопки" от первой кнопки мыши, и стирается пиксель (то есть рисуется белый), когда нажата вторая кнопка мыши. Предполагается существование двух GC: gc_draw с черным цветом переднего плана и gc_erase с белым цветом переднего плана.
. . . . . . case ButtonPress: /* сохраняем координаты кнопки мыши в целых переменных */ /* также сохраняем идентификатор окна, в котором была */ /* нажата кнопка мыши */ x = an_event.xbutton.x; y = an_event.xbutton.y; the_win = an_event.xbutton.window; /* проверяем, какая из кнопок была нажата, */ /* и действуем соответственно */ switch (an_event.xbutton.button) { case Button1: /* рисуем пиксель в позиции мыши */ XDrawPoint(display, window, gc_draw, x, y); break; case Button2: /* стираем пиксель в позиции мыши */ XDrawPoint(display, window, gc_erase, x, y); break; default: /* возможно, третья кнопка - игнорируем */ break; } break; . . . . . .
Пример программы, левой кнопкой мышки рисуем, правой стираем example5.c
Подобно событиям нажатия и отпускания кнопки мыши, нас также могут извещать о различных событиях перемещения мыши. Они делятся на два семейства. Первое - перемещение указателя мыши, пока никакие кнопки не нажимаются, и второе - движение указателя мыши при одной (или более) нажатых кнопках (это иногда называется операцией "перетаскивания" (drag)). Следующие маски событий должны быть добавлены в вызов XSelectInput()
для получения извещений о таких событиях:
PointerMotionMask
- события указателя,
перемещающегося в одном из окон программы,
когда ни одна кнопка мыши не нажата.
ButtonMotionMask
- события перемещения указателя,
пока одна (или более) кнопок мыши удерживается нажатой.
Button1MotionMask
- тоже, что и ButtonMotionMask
,
но только когда первая кнопка мыши удерживается нажатой.
Button2MotionMask, Button3MotionMask, Button4MotionMask,
Button5MotionMask
- аналогично кнопок 2, 3, 4 или 5.
В цикле обработки сообщений проверяется событие MotionNotify
- указатель мыши перемещался в одном из окон, для которых мы запросили уведомление о таких событиях.
Структура для этих сообщений получается доступом к полю xmotion
объединения XEvent
и содержит, в частности, такие поля:
Window window
- идентификатор окна,
которому было послано сообщение движения мыши
(в случае, если оно было зарегистрировано для нескольких окон программы).
int x, y
- координаты x и y (в пикселях)
мышиного курсора в момент генерации сообщения.
unsigned int state
- маска кнопок (или клавиш),
удерживаемых во время этого события (если таковые имеются).
Эта поле -побитовое "ИЛИ" любого из следующих значений:
Button1Mask
, Button2Mask
,
Button3Mask
, Button4Mask
,
Button5Mask
, ShiftMask
,
LockMask
, ControlMask
,
Mod1Mask
, Mod2Mask
,
Mod3Mask
, Mod4Mask
,
Mod5Mask
.
Первые пять значений ссылаются на кнопки мыши, которые нажимаются,
остальные соответствуют различным специальным клавишам
(Mod1
- обычно клавиша Alt
или
Meta
).
Time time
- время (в миллисекундах),
которое длилось событие.
Следующий код определяет режим рисования для графического редактора, в котором, если пользователь перемещает мышь, удерживая первую ее кнопку, рисуем на экране. Поскольку перемещение мыши сгенерирует много событий, не для каждого события движения мыши будет закрашен пиксель, над которым проходит мышь. Решить проблему можно запоминанием последнего пикселя, над которым прошла мышь, и рисованием линии между запомненной и новой позициями указателя мыши.
. . . . . . case MotionNotify: /* сохраняем координаты кнопки мыши в целых переменных */ /* также сохраняем идентификатор окна, в котором была */ /* протащена мышь */ x = an_event.xmotion.x; y = an_event.xmotion.y; window = an_event.xbutton.window; /* если первая кнопка мыши удерживалась во время этого события, */ /* рисуем пиксель в позиции мышиного курсора */ if (an_event.xmotion.state & Button1Mask) { XDrawPoint(display, window, gc_draw, x, y); } break; . . . . . .
Другой тип мышиных событий - вход указателя мыши в окно программы или выход из окна. Некоторые программы используют эти события, чтобы показать пользователю, что приложение получило фокус. Для регистрации событий этого типа необходимо добавить один (или более) из следующих масок в функции XSelectInput()
:
EnterWindowMask
- уведомлять, когда указатель мыши входит в любое из окон программы.LeaveWindowMask
- уведомлять, когда указатель мыши выходит из окна программы.В цикле обработки сообщений проверяется одно из следующих событий:
EnterNotify
- указатель мыши только что вошел в одно из окон программы.LeaveNotify
- указатель мыши только что вышел из окна программы.Структура для этих сообщений получается доступом к полю xcrossing
объединения XEvent
и содержит, в частности, такие поля:
Window window
- идентификатор окна, которому было послано сообщение от мыши (в случае, если оно было зарегистрировано для нескольких окон программы).Window subwindow
- идентификатор дочернего окна,
из которого мышь перешла в текущее (в событии EnterNotify
),
или в которое указатель мыши переместился
(в событии LeaveNotify
), или None
,
если мышь переместилась за пределы окон программы.int x, y
- координаты x и y (в пикселях)
мышиного курсора в момент генерации сообщения.int mode
- номер нажатой кнопки
(может принимать значения Button1, Button2, Button3).Time time
- время (в миллисекундах),
которое длилось событие. Может использоваться для определения
"двойного щелчка".unsigned int state
- маска кнопок (или клавиш),
удерживаемых во время этого события (если таковые имеются).
Эта поле - побитовое "ИЛИ" любого из следующих значений:
Button1Mask, Button2Mask, Button3Mask, Button4Mask,
Button5Mask, ShiftMask, LockMask, ControlMask,
Mod1Mask, Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask
.
Первые пять значений ссылаются на кнопки мыши, которые нажимаются,
остальные соответствуют различным специальным клавишам
(Mod1
- обычно клавиша Alt
или
Meta
).True
,
если окно имеет клавиатурный фокус, и False
в противном случае.Обычно фокус ввода может свободно переходить от окна к окну. Но иногда программе необходимо запретить передачу фокуса. Это называется захватом клавиатуры. Для того, чтобы его реализовать, используется функция XGrabKeyboard()
.
Функция XGrabKey()
запрещает передачу фокуса после нажатия определенной комбинации клавиш. Освободить клавиатуру можно, обратившись к функции XUngrabKeyboard()
(XGrabKey()
).
Рассмотрим поведение системы при обработке событий от мыши. Если ее кнопка нажата в момент, когда ее курсор находится в неактивном окне, то последнее активизируется, и события от мыши передаются ему. Сказанное означает, что в нормальном состоянии окно получает только те события от мыши, которые соответствуют сигналам, пришедшим тогда, когда ее курсор находится в пределах окна. Но если программа вызывает
int XGrabPointer (Display* display, Window grabWindow, Bool ownerEvents, unsigned int eventMask, int pointer, int keyboardMode, Window confine, Cursor cursor, Time time);
то положение меняется. Теперь все события будут направляться окну с дескриптором grabWindow
. Освобождается мышь вызовом XUngrabPointer()
. Функция XGrabButton()
указывает, что курсор должен быть захвачен после нажатия определенной кнопки. Обратной к ней является функция XUngrabButton()
.
Функции, захватывающие устройство, - мышь или клавиатуру - имеют ряд аргументов, влияющих на поведение системы.
Так, параметр confine
есть идентификатор окна, за пределы которого не должен выходить курсор мыши, если он захвачен.
Если аргумент ownerEvents
равен Тrue
, то события мыши будут передаваться окнам программы. Если ownerEvents
- False
, или курсор находится в окне, не принадлежащем программе, то события мыши передаются окну grabWindow
.
Если ownerEvents
равен False
, то параметр eventMask
указывает, какие события следует передавать окну grabWindow
.
Обработка событий от клавиатуры или ныши может быть приостановлена, если pointer
или keyboardMode
равен GrabModeSync
. В этом случае события буферизуются сервером, пока устройство не будет освобождено с помощью XUngrabKeyboard()
, XUngrabKey()
, XUngrabPointer()
или XUngrabButton()
.
Параметр cursor
задает форму курсора во время того, как мышь захвачена. Аргумент time
указывает, когда система должна активизировать режим захвата.