Проведя небольшое исследование, особенно прочитав исходный код Xeyes (мне всегда казалось, что демо - глупо, но здесь очень помогает!), Я обнаружил:
Calling XSelectInput()
на всех windows и под windows - тщетная попытка, вам нужно установить маску для каждого отдельного окна и дочернего окна, когда-либо созданного, это не надежное решение и не рекомендуется.
Вместо этого лучше просто постоянно перетаскивать указатель мыши с X-сервера явно через XQueryPointer()
, а не просить X-сервер сделать sh MotionEvent нам.
Одно наивное решение - просто установить таймер с помощью XtAppAddTimeOut()
и периодически вызывать XQueryPointer()
, это работает, и действительно, это было то, что Xeyes делал в прошлом ! Но это неоправданно тратит время процессора. В настоящее время лучше всего использовать XInputExtention 2.0. Рабочий процесс:
Инициализация XInput v2.0
Включение различных масок через XISetMask()
и XIEventMask()
для получения XI_RawMotion
событий (или XI_Motion
, см. примечания ниже) от XIAllMasterDevices
(или XIAllDevices
).
При получении события XI_RawMotion
(или XI_Motion
) позвоните XQueryPointer()
.
XQueryPointer()
возвращает:
- Координаты мыши относительно окна root.
- Активное окно под курсором мыши, если он есть.
Выполните XTranslateCoordinates()
, если нам нужны относительные координаты относительно активного окна под курсором мыши.
Демо
Вот демо (сохранить как mouse.c
, скомпилировать с gcc mouse.c -o mouse -lX11 -lXi
). Однако он не может обнаружить XWarpPointer()
, см. Примечания ниже.
#include <stdio.h>
#include <assert.h>
#include <X11/Xlib.h>
#include <X11/extensions/XInput2.h>
int main(int argc, char **argv)
{
Display *display;
Window root_window;
/* Initialize (FIXME: no error checking). */
display = XOpenDisplay(0);
root_window = XRootWindow(display, 0);
/* check XInput */
int xi_opcode, event, error;
if (!XQueryExtension(display, "XInputExtension", &xi_opcode, &event, &error)) {
fprintf(stderr, "Error: XInput extension is not supported!\n");
return 1;
}
/* Check XInput 2.0 */
int major = 2;
int minor = 0;
int retval = XIQueryVersion(display, &major, &minor);
if (retval != Success) {
fprintf(stderr, "Error: XInput 2.0 is not supported (ancient X11?)\n");
return 1;
}
/*
* Set mask to receive XI_RawMotion events. Because it's raw,
* XWarpPointer() events are not included, you can use XI_Motion
* instead.
*/
unsigned char mask_bytes[(XI_LASTEVENT + 7) / 8] = {0}; /* must be zeroed! */
XISetMask(mask_bytes, XI_RawMotion);
/* Set mask to receive events from all master devices */
XIEventMask evmasks[1];
/* You can use XIAllDevices for XWarpPointer() */
evmasks[0].deviceid = XIAllMasterDevices;
evmasks[0].mask_len = sizeof(mask_bytes);
evmasks[0].mask = mask_bytes;
XISelectEvents(display, root_window, evmasks, 1);
XEvent xevent;
while (1) {
XNextEvent(display, &xevent);
if (xevent.xcookie.type != GenericEvent || xevent.xcookie.extension != xi_opcode) {
/* not an XInput event */
continue;
}
XGetEventData(display, &xevent.xcookie);
if (xevent.xcookie.evtype != XI_RawMotion) {
/*
* Not an XI_RawMotion event (you may want to detect
* XI_Motion as well, see comments above).
*/
XFreeEventData(display, &xevent.xcookie);
continue;
}
XFreeEventData(display, &xevent.xcookie);
Window root_return, child_return;
int root_x_return, root_y_return;
int win_x_return, win_y_return;
unsigned int mask_return;
/*
* We need:
* child_return - the active window under the cursor
* win_{x,y}_return - pointer coordinate with respect to root window
*/
int retval = XQueryPointer(display, root_window, &root_return, &child_return,
&root_x_return, &root_y_return,
&win_x_return, &win_y_return,
&mask_return);
if (!retval) {
/* pointer is not in the same screen, ignore */
continue;
}
/* We used root window as its reference, so both should be the same */
assert(root_x_return == win_x_return);
assert(root_y_return == win_y_return);
printf("root: x %d y %d\n", root_x_return, root_y_return);
if (child_return) {
int local_x, local_y;
XTranslateCoordinates(display, root_window, child_return,
root_x_return, root_y_return,
&local_x, &local_y, &child_return);
printf("local: x %d y %d\n\n", local_x, local_y);
}
}
XCloseDisplay(display);
return 0;
}
Пример вывода
root: x 631 y 334
local: x 140 y 251
root: x 628 y 338
local: x 137 y 255
root: x 619 y 343
local: x 128 y 260
XWarpPointer()
Проблемы
Демонстрация выше не работает если указатель перемещается через XWarpPointer()
приложением roboti c в более новых системах после X.Org 1.10.4. Это сделано намеренно, см. Ошибка 30068 на FreeDesktop.
Чтобы получать события мыши, запускаемые всеми движениями мыши, включая XWarpPointer()
, измените XI_RawMotion
на XI_Motion
и измените От XIAllMasterDevices
до XIAllDevices
.
Ссылки
В этой демонстрации отсутствует проверка ошибок, и она может содержать ошибки. В случае сомнений, пожалуйста, проверьте следующие авторитетные источники.