diff options
Diffstat (limited to 'src/plugins/platforms/xcb/qxcbconnection.cpp')
-rw-r--r-- | src/plugins/platforms/xcb/qxcbconnection.cpp | 118 |
1 files changed, 105 insertions, 13 deletions
diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp index a7c60cac84..34508260d9 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection.cpp @@ -253,11 +253,13 @@ QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGra #endif , xfixes_first_event(0) , xrandr_first_event(0) + , xkb_first_event(0) , has_glx_extension(false) , has_shape_extension(false) , has_randr_extension(false) , has_input_shape(false) , has_touch_without_mouse_emulation(false) + , has_xkb(false) , m_buttons(0) , m_focusWindow(0) { @@ -297,7 +299,7 @@ QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGra } xcb_extension_t *extensions[] = { - &xcb_shm_id, &xcb_xfixes_id, &xcb_randr_id, &xcb_shape_id, &xcb_sync_id, + &xcb_shm_id, &xcb_xfixes_id, &xcb_randr_id, &xcb_shape_id, &xcb_sync_id, &xcb_xkb_id, #ifdef XCB_USE_RENDER &xcb_render_id, #endif @@ -336,6 +338,7 @@ QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGra initializeXInput2(); #endif initializeXShape(); + initializeXKB(); m_wmSupport.reset(new QXcbWMSupport(this)); m_keyboard = new QXcbKeyboard(this); @@ -479,7 +482,6 @@ void printXcbEvent(const char *message, xcb_generic_event_t *event) PRINT_XCB_EVENT(XCB_SELECTION_NOTIFY); PRINT_XCB_EVENT(XCB_COLORMAP_NOTIFY); PRINT_XCB_EVENT(XCB_CLIENT_MESSAGE); - PRINT_XCB_EVENT(XCB_MAPPING_NOTIFY); default: qDebug("QXcbConnection: %s: unknown event - response_type: %d - sequence: %d", message, int(event->response_type & ~0x80), int(event->sequence)); } @@ -745,6 +747,21 @@ void QXcbConnection::handleButtonRelease(xcb_generic_event_t *ev) m_buttons &= ~translateMouseButton(event->detail); } +namespace { + typedef union { + /* All XKB events share these fields. */ + struct { + uint8_t response_type; + uint8_t xkbType; + uint16_t sequence; + xcb_timestamp_t time; + uint8_t deviceID; + } any; + xcb_xkb_map_notify_event_t map_notify; + xcb_xkb_state_notify_event_t state_notify; + } _xkb_event; +} + void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event) { #ifdef Q_XCB_DEBUG @@ -797,9 +814,6 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event) HANDLE_KEYBOARD_EVENT(xcb_key_press_event_t, handleKeyPressEvent); case XCB_KEY_RELEASE: HANDLE_KEYBOARD_EVENT(xcb_key_release_event_t, handleKeyReleaseEvent); - case XCB_MAPPING_NOTIFY: - m_keyboard->handleMappingNotifyEvent((xcb_mapping_notify_event_t *)event); - break; case XCB_SELECTION_REQUEST: { xcb_selection_request_event_t *sr = (xcb_selection_request_event_t *)event; @@ -862,6 +876,22 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event) } } handled = true; + } else if (response_type == xkb_first_event) { // https://bugs.freedesktop.org/show_bug.cgi?id=51295 + _xkb_event *xkb_event = reinterpret_cast<_xkb_event *>(event); + if (xkb_event->any.deviceID == m_keyboard->coreDeviceId()) { + switch (xkb_event->any.xkbType) { + case XCB_XKB_STATE_NOTIFY: + m_keyboard->updateXKBState(&xkb_event->state_notify); + handled = true; + break; + case XCB_XKB_MAP_NOTIFY: + m_keyboard->handleMappingNotifyEvent(&xkb_event->map_notify); + handled = true; + break; + default: + break; + } + } } } @@ -869,7 +899,6 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event) if (!handled) { // Check if a custom XEvent constructor was registered in xlib for this event type, and call it discarding the constructed XEvent if any. // XESetWireToEvent might be used by libraries to intercept messages from the X server e.g. the OpenGL lib waiting for DRI2 events. - Display *xdisplay = (Display *)m_xlib_display; XLockDisplay(xdisplay); Bool (*proc)(Display*, XEvent*, xEvent*) = XESetWireToEvent(xdisplay, response_type, 0); @@ -1017,32 +1046,36 @@ namespace xcb_timestamp_t QXcbConnection::getTimestamp() { // send a dummy event to myself to get the timestamp from X server. - xcb_window_t rootWindow = screens().at(primaryScreen())->root(); - xcb_change_property(xcb_connection(), XCB_PROP_MODE_APPEND, rootWindow, atom(QXcbAtom::CLIP_TEMPORARY), + xcb_window_t root_win = rootWindow(); + xcb_change_property(xcb_connection(), XCB_PROP_MODE_APPEND, root_win, atom(QXcbAtom::CLIP_TEMPORARY), XCB_ATOM_INTEGER, 32, 0, NULL); connection()->flush(); - PropertyNotifyEvent checker(rootWindow, atom(QXcbAtom::CLIP_TEMPORARY)); + PropertyNotifyEvent checker(root_win, atom(QXcbAtom::CLIP_TEMPORARY)); xcb_generic_event_t *event = 0; // lets keep this inside a loop to avoid a possible race condition, where // reader thread has not yet had the time to acquire the mutex in order // to add the new set of events to its event queue - while (true) { + while (!event) { connection()->sync(); - if ((event = checkEvent(checker))) - break; + event = checkEvent(checker); } xcb_property_notify_event_t *pn = (xcb_property_notify_event_t *)event; xcb_timestamp_t timestamp = pn->time; free(event); - xcb_delete_property(xcb_connection(), rootWindow, atom(QXcbAtom::CLIP_TEMPORARY)); + xcb_delete_property(xcb_connection(), root_win, atom(QXcbAtom::CLIP_TEMPORARY)); return timestamp; } +xcb_window_t QXcbConnection::rootWindow() +{ + return screens().at(primaryScreen())->root(); +} + void QXcbConnection::processXcbEvents() { int connection_error = xcb_connection_has_error(xcb_connection()); @@ -1539,6 +1572,65 @@ void QXcbConnection::initializeXShape() free(shape_query); } +void QXcbConnection::initializeXKB() +{ + const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_connection, &xcb_xkb_id); + if (!reply || !reply->present) { + xkb_first_event = 0; + return; + } + xkb_first_event = reply->first_event; + + xcb_connection_t *c = connection()->xcb_connection(); + xcb_xkb_use_extension_cookie_t xkb_query_cookie; + xcb_xkb_use_extension_reply_t *xkb_query; + + xkb_query_cookie = xcb_xkb_use_extension(c, XCB_XKB_MAJOR_VERSION, XCB_XKB_MINOR_VERSION); + xkb_query = xcb_xkb_use_extension_reply(c, xkb_query_cookie, 0); + + if (!xkb_query) { + qWarning("Qt: Failed to initialize XKB extension"); + return; + } else if (!xkb_query->supported) { + qWarning("Qt: Unsupported XKB version (want %d %d, has %d %d)", + XCB_XKB_MAJOR_VERSION, XCB_XKB_MINOR_VERSION, + xkb_query->serverMajor, xkb_query->serverMinor); + free(xkb_query); + return; + } + + has_xkb = true; + free(xkb_query); + + uint affectMap, map; + affectMap = map = XCB_XKB_MAP_PART_KEY_TYPES | + XCB_XKB_MAP_PART_KEY_SYMS | + XCB_XKB_MAP_PART_MODIFIER_MAP | + XCB_XKB_MAP_PART_EXPLICIT_COMPONENTS | + XCB_XKB_MAP_PART_KEY_ACTIONS | + XCB_XKB_MAP_PART_KEY_BEHAVIORS | + XCB_XKB_MAP_PART_VIRTUAL_MODS | + XCB_XKB_MAP_PART_VIRTUAL_MOD_MAP; + + // Xkb events are reported to all interested clients without regard + // to the current keyboard input focus or grab state + xcb_void_cookie_t select = xcb_xkb_select_events_checked(c, + XCB_XKB_ID_USE_CORE_KBD, + XCB_XKB_EVENT_TYPE_STATE_NOTIFY | XCB_XKB_EVENT_TYPE_MAP_NOTIFY, + 0, + XCB_XKB_EVENT_TYPE_STATE_NOTIFY | XCB_XKB_EVENT_TYPE_MAP_NOTIFY, + affectMap, + map, + 0); + + xcb_generic_error_t *error = xcb_request_check(c, select); + if (error) { + free(error); + qWarning() << "Qt: failed to select notify events from xcb-xkb"; + return; + } +} + #if defined(XCB_USE_EGL) bool QXcbConnection::hasEgl() const { |