LINUX.ORG.RU

История изменений

Исправление 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, &currentWindow, &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, &currentWindow, &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 не срабатывает из-за плохой куки.