Исправление KivApple, (текущая версия) :
Нашёл вот что: http://stackoverflow.com/questions/32449666/my-xlib-code-can-listen-to-keyboa...
Суть такова: если приложение использует XInput2, то я не могу получить нормально его события с помощью одного лишь XSelectInput.
Теоретически возможное решение:
bool KeyboardWidget::nativeEventFilter(const QByteArray &eventType, void *message, long *) {
xcb_generic_event_t *ev = static_cast<xcb_generic_event_t*>(message);
XGenericEventCookie *cookie;
if (m_displayHasXInput && XGetEventData(QX11Info::display(), cookie)) {
if ((cookie->type == GenericEvent) && (cookie->extension == m_xi_opcode)) {
XIDeviceEvent *devev = static_cast<XIDeviceEvent*>(cookie->data);
switch (devev->evtype) {
case XI_KeyPress:
keyEventReceived(devev->detail, true);
break;
case XI_KeyRelease:
keyEventReceived(devev->detail, false);
break;
case XI_FocusOut:
focusChanged();
break;
}
}
} else {
switch (ev->response_type & ~0x80) {
case XCB_KEY_PRESS: {
xcb_key_press_event_t *key_press_event = reinterpret_cast<xcb_key_press_event_t*>(ev);
keyEventReceived(key_press_event->detail, true);
break;
}
case XCB_KEY_RELEASE: {
xcb_key_release_event_t *key_release_event = reinterpret_cast<xcb_key_release_event_t*>(ev);
keyEventReceived(key_release_event->detail, false);
break;
}
case XCB_FOCUS_OUT:
focusChanged();
break;
}
}
return false;
}
void KeyboardWidget::focusChanged() {
Display *display = QX11Info::display();
Window currentWindow;
int rev;
XGetInputFocus(display, ¤tWindow, &rev);
if ((currentWindow == PointerRoot) || (currentWindow == None)) {
currentWindow = DefaultRootWindow(display);
}
if (m_displayHasXInput) {
XIEventMask evmask;
evmask.deviceid = XIAllMasterDevices;
evmask.mask_len = XIMaskLen(XI_LASTEVENT);
evmask.mask = (unsigned char*)malloc(evmask.mask_len);
memset(evmask.mask, 0, evmask.mask_len);
XISetMask(evmask.mask, XI_KeyPress);
XISetMask(evmask.mask, XI_KeyRelease);
XISetMask(evmask.mask, XI_FocusOut);
int prev_evmask_count;
XIEventMask *prev_evmask = XIGetSelectedEvents(display, currentWindow, &prev_evmask_count);
if (prev_evmask) {
for (int i = 0; i < prev_evmask_count; ++i) {
if (prev_evmask[i].deviceid != XIAllMasterDevices) continue;
for (int j = 0; j < qMin(evmask.mask_len, prev_evmask[i].mask_len); ++j) {
evmask.mask[j] |= prev_evmask[i].mask[j];
}
}
XFree(prev_evmask);
}
XISelectEvents(display, currentWindow, &evmask, 1);
}
XSelectInput(display, currentWindow, KeyPressMask | KeyReleaseMask | FocusChangeMask);
}
...
QApplication::instance()->installNativeEventFilter(this);
{
int ev, err;
m_displayHasXInput = XQueryExtension(QX11Info::display(), "XInputExtension", &m_xi_opcode, &ev, &err);
}
focusChanged();
Однако возникает проблема, что XGenericEventCookie *cookie должен быть инициализирован каким-то значением. Например, в этом примере https://github.com/esjeon/xinput2-touch/blob/master/event.c делают так:
XGenericEventCookie *cookie = &ev.xcookie;
Но ведь Qt-то выдаёт мне xcb-событие, а не напрямую иксовое, так что я так сделать не могу. Пробовал сам выделять память под куку, но в итоге ничего хорошего не вышло.
В Google Chrome теперь вообще события не идут, в Qt Creator не идут только если навести курсор на моё окно. То есть получается, что события таки перехватываются, однако их надо как-то обрабатывать (иначе они могут и не дойти), а XGetEventData не срабатывает из-за плохой куки.
Исходная версия KivApple, :
Нашёл вот что: http://stackoverflow.com/questions/32449666/my-xlib-code-can-listen-to-keyboa...
Суть такова: если приложение использует XInput2, то я не могу получить нормально его события с помощью одного лишь XSelectInput.
Теоретически возможное решение:
bool KeyboardWidget::nativeEventFilter(const QByteArray &eventType, void *message, long *) {
xcb_generic_event_t *ev = static_cast<xcb_generic_event_t*>(message);
XGenericEventCookie *cookie;
if (m_displayHasXInput && XGetEventData(QX11Info::display(), cookie)) {
if ((cookie->type == GenericEvent) && (cookie->extension == m_xi_opcode)) {
XIDeviceEvent *devev = static_cast<XIDeviceEvent*>(cookie->data);
switch (devev->evtype) {
case XI_KeyPress:
keyEventReceived(devev->detail, true);
break;
case XI_KeyRelease:
keyEventReceived(devev->detail, false);
break;
case XI_FocusOut:
focusChanged();
break;
}
}
} else {
switch (ev->response_type & ~0x80) {
case XCB_KEY_PRESS: {
xcb_key_press_event_t *key_press_event = reinterpret_cast<xcb_key_press_event_t*>(ev);
keyEventReceived(key_press_event->detail, true);
break;
}
case XCB_KEY_RELEASE: {
xcb_key_release_event_t *key_release_event = reinterpret_cast<xcb_key_release_event_t*>(ev);
keyEventReceived(key_release_event->detail, false);
break;
}
case XCB_FOCUS_OUT:
focusChanged();
break;
}
}
return false;
}
void KeyboardWidget::focusChanged() {
Display *display = QX11Info::display();
Window currentWindow;
int rev;
XGetInputFocus(display, ¤tWindow, &rev);
if ((currentWindow == PointerRoot) || (currentWindow == None)) {
currentWindow = DefaultRootWindow(display);
}
if (m_displayHasXInput) {
XIEventMask evmask;
evmask.deviceid = XIAllMasterDevices;
evmask.mask_len = XIMaskLen(XI_LASTEVENT);
evmask.mask = (unsigned char*)malloc(evmask.mask_len);
memset(evmask.mask, 0, evmask.mask_len);
XISetMask(evmask.mask, XI_KeyPress);
XISetMask(evmask.mask, XI_KeyRelease);
XISetMask(evmask.mask, XI_FocusOut);
int prev_evmask_count;
XIEventMask *prev_evmask = XIGetSelectedEvents(display, currentWindow, &prev_evmask_count);
if (prev_evmask) {
for (int i = 0; i < prev_evmask_count; ++i) {
if (prev_evmask[i].deviceid != XIAllMasterDevices) continue;
for (int j = 0; j < qMin(evmask.mask_len, prev_evmask[i].mask_len); ++j) {
evmask.mask[j] |= prev_evmask[i].mask[j];
}
}
XFree(prev_evmask);
}
XISelectEvents(display, currentWindow, &evmask, 1);
}
XSelectInput(display, currentWindow, KeyPressMask | KeyReleaseMask | FocusChangeMask);
}
...
QApplication::instance()->installNativeEventFilter(this);
{
int ev, err;
m_displayHasXInput = XQueryExtension(QX11Info::display(), "XInputExtension", &m_xi_opcode, &ev, &err);
}
focusChanged();
Однако возникает проблема, что XGenericEventCookie *cookie должен быть инициализирован каким-то значением. Например, в этом примере https://github.com/esjeon/xinput2-touch/blob/master/event.c делают так:
XGenericEventCookie *cookie = &ev.xcookie;
Но ведь Qt-то выдаёт мне xcb-событие, а не напрямую иксовое, так что я так сделать не могу. Пробовал сам выделять память под куку, но в итоге ничего хорошего не вышло.
В Google Chrome теперь вообще события не идут, в Qt Creator только если навести курсор на моё окно. То есть получается, что события таки перехватываются, однако их надо как-то обрабатывать (иначе они могут и не дойти), а XGetEventData не срабатывает из-за плохой куки.