diff options
Diffstat (limited to 'src/plugins/platforms/xcb')
-rw-r--r-- | src/plugins/platforms/xcb/qxcbconnection.cpp | 164 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbconnection.h | 26 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbconnection_xi2.cpp | 14 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbcursor.cpp | 115 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbcursor.h | 7 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbdrag.cpp | 24 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbdrag.h | 2 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbintegration.cpp | 32 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbintegration.h | 2 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbkeyboard.cpp | 909 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbkeyboard.h | 89 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbnativeinterface.cpp | 4 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbscreen.cpp | 10 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbscreen.h | 5 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbxsettings.cpp | 280 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbxsettings.h | 71 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/xcb-plugin.pro | 25 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/xcb-static/xcb-static.pro | 3 |
18 files changed, 1330 insertions, 452 deletions
diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp index 10a8f26614..209c7bb187 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection.cpp @@ -253,10 +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,6 +300,9 @@ 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, +#ifndef QT_NO_XKB + &xcb_xkb_id, +#endif #ifdef XCB_USE_RENDER &xcb_render_id, #endif @@ -335,6 +341,7 @@ QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGra initializeXInput2(); #endif initializeXShape(); + initializeXKB(); m_wmSupport.reset(new QXcbWMSupport(this)); m_keyboard = new QXcbKeyboard(this); @@ -478,7 +485,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)); } @@ -744,6 +750,23 @@ void QXcbConnection::handleButtonRelease(xcb_generic_event_t *ev) m_buttons &= ~translateMouseButton(event->detail); } +#ifndef QT_NO_XKB +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; +} +#endif + void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event) { #ifdef Q_XCB_DEBUG @@ -768,12 +791,21 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event) case XCB_EXPOSE: HANDLE_PLATFORM_WINDOW_EVENT(xcb_expose_event_t, window, handleExposeEvent); case XCB_BUTTON_PRESS: +#ifdef QT_NO_XKB + m_keyboard->updateXKBStateFromCore(((xcb_button_press_event_t *)event)->state); +#endif handleButtonPress(event); HANDLE_PLATFORM_WINDOW_EVENT(xcb_button_press_event_t, event, handleButtonPressEvent); case XCB_BUTTON_RELEASE: +#ifdef QT_NO_XKB + m_keyboard->updateXKBStateFromCore(((xcb_button_release_event_t *)event)->state); +#endif handleButtonRelease(event); HANDLE_PLATFORM_WINDOW_EVENT(xcb_button_release_event_t, event, handleButtonReleaseEvent); case XCB_MOTION_NOTIFY: +#ifdef QT_NO_XKB + m_keyboard->updateXKBStateFromCore(((xcb_motion_notify_event_t *)event)->state); +#endif HANDLE_PLATFORM_WINDOW_EVENT(xcb_motion_notify_event_t, event, handleMotionNotifyEvent); case XCB_CONFIGURE_NOTIFY: HANDLE_PLATFORM_WINDOW_EVENT(xcb_configure_notify_event_t, event, handleConfigureNotifyEvent); @@ -787,18 +819,29 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event) case XCB_ENTER_NOTIFY: HANDLE_PLATFORM_WINDOW_EVENT(xcb_enter_notify_event_t, event, handleEnterNotifyEvent); case XCB_LEAVE_NOTIFY: +#ifdef QT_NO_XKB + m_keyboard->updateXKBStateFromCore(((xcb_leave_notify_event_t *)event)->state); +#endif HANDLE_PLATFORM_WINDOW_EVENT(xcb_leave_notify_event_t, event, handleLeaveNotifyEvent); case XCB_FOCUS_IN: HANDLE_PLATFORM_WINDOW_EVENT(xcb_focus_in_event_t, event, handleFocusInEvent); case XCB_FOCUS_OUT: HANDLE_PLATFORM_WINDOW_EVENT(xcb_focus_out_event_t, event, handleFocusOutEvent); case XCB_KEY_PRESS: +#ifdef QT_NO_XKB + m_keyboard->updateXKBStateFromCore(((xcb_key_press_event_t *)event)->state); +#endif HANDLE_KEYBOARD_EVENT(xcb_key_press_event_t, handleKeyPressEvent); case XCB_KEY_RELEASE: +#ifdef QT_NO_XKB + m_keyboard->updateXKBStateFromCore(((xcb_key_release_event_t *)event)->state); +#endif HANDLE_KEYBOARD_EVENT(xcb_key_release_event_t, handleKeyReleaseEvent); +#ifdef QT_NO_XKB case XCB_MAPPING_NOTIFY: m_keyboard->handleMappingNotifyEvent((xcb_mapping_notify_event_t *)event); break; +#endif case XCB_SELECTION_REQUEST: { xcb_selection_request_event_t *sr = (xcb_selection_request_event_t *)event; @@ -861,6 +904,24 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event) } } handled = true; +#ifndef QT_NO_XKB + } 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; + } + } +#endif } } @@ -868,7 +929,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); @@ -1016,32 +1076,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()); @@ -1333,6 +1397,7 @@ static const char * xcb_atomnames = { #if XCB_USE_MAEMO_WINDOW_PROPERTIES "_MEEGOTOUCH_ORIENTATION_ANGLE\0" #endif + "_XSETTINGS_SETTINGS" }; xcb_atom_t QXcbConnection::atom(QXcbAtom::Atom atom) @@ -1538,6 +1603,67 @@ void QXcbConnection::initializeXShape() free(shape_query); } +void QXcbConnection::initializeXKB() +{ +#ifndef QT_NO_XKB + 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; + } +#endif +} + #if defined(XCB_USE_EGL) bool QXcbConnection::hasEgl() const { @@ -1594,4 +1720,24 @@ bool QXcbConnection::xi2PrepareXIGenericDeviceEvent(xcb_ge_event_t *event, int o } #endif // defined(XCB_USE_XINPUT2) || defined(XCB_USE_XINPUT2_MAEMO) +QXcbConnectionGrabber::QXcbConnectionGrabber(QXcbConnection *connection) + :m_connection(connection) +{ + connection->grabServer(); +} + +QXcbConnectionGrabber::~QXcbConnectionGrabber() +{ + if (m_connection) + m_connection->ungrabServer(); +} + +void QXcbConnectionGrabber::release() +{ + if (m_connection) { + m_connection->ungrabServer(); + m_connection = 0; + } +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/xcb/qxcbconnection.h b/src/plugins/platforms/xcb/qxcbconnection.h index 44c0e28dd5..883ee95e22 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.h +++ b/src/plugins/platforms/xcb/qxcbconnection.h @@ -53,6 +53,14 @@ #include <QVarLengthArray> #include <qpa/qwindowsysteminterface.h> +// This is needed to make Qt compile together with XKB. xkb.h is using a variable +// which is called 'explicit', this is a reserved keyword in c++ */ +#ifndef QT_NO_XKB +#define explicit dont_use_cxx_explicit +#include <xcb/xkb.h> +#undef explicit +#endif + #ifndef QT_NO_TABLETEVENT #include <QTabletEvent> #endif @@ -261,6 +269,7 @@ namespace QXcbAtom { #if XCB_USE_MAEMO_WINDOW_PROPERTIES MeegoTouchOrientationAngle, #endif + _XSETTINGS_SETTINGS, NPredefinedAtoms, @@ -355,7 +364,7 @@ public: #endif QXcbWMSupport *wmSupport() const { return m_wmSupport.data(); } - + xcb_window_t rootWindow(); #ifdef XCB_USE_XLIB void *xlib_display() const { return m_xlib_display; } #endif @@ -401,6 +410,8 @@ public: bool hasXShape() const { return has_shape_extension; } bool hasXRandr() const { return has_randr_extension; } bool hasInputShape() const { return has_input_shape; } + bool hasTouchWithoutMouseEmulation() const { return has_touch_without_mouse_emulation; } + bool hasXKB() const { return has_xkb; } bool supportsThreadedRendering() const { return m_reader->isRunning(); } @@ -429,6 +440,7 @@ private: void initializeXRender(); void initializeXRandr(); void initializeXShape(); + void initializeXKB(); #ifdef XCB_USE_XINPUT2_MAEMO void initializeXInput2Maemo(); void finalizeXInput2Maemo(); @@ -538,11 +550,14 @@ private: uint32_t xfixes_first_event; uint32_t xrandr_first_event; + uint32_t xkb_first_event; bool has_glx_extension; bool has_shape_extension; bool has_randr_extension; bool has_input_shape; + bool has_touch_without_mouse_emulation; + bool has_xkb; Qt::MouseButtons m_buttons; @@ -570,6 +585,15 @@ xcb_generic_event_t *QXcbConnection::checkEvent(T &checker) return 0; } +class QXcbConnectionGrabber +{ +public: + QXcbConnectionGrabber(QXcbConnection *connection); + ~QXcbConnectionGrabber(); + void release(); +private: + QXcbConnection *m_connection; +}; #ifdef Q_XCB_DEBUG template <typename cookie_t> diff --git a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp index dfd4feb254..991c82eaaa 100644 --- a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp @@ -92,6 +92,9 @@ void QXcbConnection::initializeXInput2() // Tablet support: Find the stylus-related devices. xi2SetupTabletDevices(); #endif // QT_NO_TABLETEVENT +#ifdef XI2_TOUCH_DEBUG + qDebug("XInput version %d.%d is supported", xiMajor, m_xi2Minor); +#endif } } } @@ -118,7 +121,16 @@ void QXcbConnection::xi2Select(xcb_window_t window) mask.deviceid = XIAllMasterDevices; mask.mask_len = sizeof(bitMask); mask.mask = xiBitMask; - XISelectEvents(xDisplay, window, &mask, 1); + Status result = XISelectEvents(xDisplay, window, &mask, 1); + // If we have XInput 2.2 and successfully enable touch on the master + // devices, then evdev touchscreens will provide touch only. In most other + // cases, there will be emulated mouse events, because true X11 touch + // support is so new that for the older drivers, mouse emulation was the + // only way; and it's still the fallback even with the modern evdev driver. + // But if neither Qt nor X11 does mouse emulation, it will not be possible + // to interact with mouse-oriented QWidgets; so we have to let Qt do it. + if (m_xi2Minor >= 2 && result == Success) + has_touch_without_mouse_emulation = true; #endif #ifndef QT_NO_TABLETEVENT diff --git a/src/plugins/platforms/xcb/qxcbcursor.cpp b/src/plugins/platforms/xcb/qxcbcursor.cpp index 3fd2ca70e3..756c3c22dd 100644 --- a/src/plugins/platforms/xcb/qxcbcursor.cpp +++ b/src/plugins/platforms/xcb/qxcbcursor.cpp @@ -43,6 +43,8 @@ #include "qxcbconnection.h" #include "qxcbwindow.h" #include "qxcbimage.h" +#include "qxcbxsettings.h" + #include <QtCore/QLibrary> #include <QtGui/QWindow> #include <QtGui/QBitmap> @@ -54,9 +56,17 @@ QT_BEGIN_NAMESPACE typedef int (*PtrXcursorLibraryLoadCursor)(void *, const char *); +typedef char *(*PtrXcursorLibraryGetTheme)(void *); +typedef int (*PtrXcursorLibrarySetTheme)(void *, const char *); +typedef int (*PtrXcursorLibraryGetDefaultSize)(void *); + #ifdef XCB_USE_XLIB static PtrXcursorLibraryLoadCursor ptrXcursorLibraryLoadCursor = 0; +static PtrXcursorLibraryGetTheme ptrXcursorLibraryGetTheme = 0; +static PtrXcursorLibrarySetTheme ptrXcursorLibrarySetTheme = 0; +static PtrXcursorLibraryGetDefaultSize ptrXcursorLibraryGetDefaultSize = 0; #endif + static xcb_font_t cursorFont = 0; static int cursorCount = 0; @@ -263,7 +273,7 @@ static const char * const cursorNames[] = { }; QXcbCursor::QXcbCursor(QXcbConnection *conn, QXcbScreen *screen) - : QXcbObject(conn), m_screen(screen) + : QXcbObject(conn), m_screen(screen), m_gtkCursorThemeInitialized(false) { if (cursorCount++) return; @@ -273,21 +283,38 @@ QXcbCursor::QXcbCursor(QXcbConnection *conn, QXcbScreen *screen) xcb_open_font(xcb_connection(), cursorFont, strlen(cursorStr), cursorStr); #ifdef XCB_USE_XLIB - QLibrary xcursorLib(QLatin1String("Xcursor"), 1); - bool xcursorFound = xcursorLib.load(); - if (!xcursorFound) { // try without the version number - xcursorLib.setFileName(QLatin1String("Xcursor")); - xcursorFound = xcursorLib.load(); + static bool function_ptrs_not_initialized = true; + if (function_ptrs_not_initialized) { + QLibrary xcursorLib(QLatin1String("Xcursor"), 1); + bool xcursorFound = xcursorLib.load(); + if (!xcursorFound) { // try without the version number + xcursorLib.setFileName(QLatin1String("Xcursor")); + xcursorFound = xcursorLib.load(); + } + if (xcursorFound) { + ptrXcursorLibraryLoadCursor = + (PtrXcursorLibraryLoadCursor) xcursorLib.resolve("XcursorLibraryLoadCursor"); + ptrXcursorLibraryGetTheme = + (PtrXcursorLibraryGetTheme) xcursorLib.resolve("XcursorGetTheme"); + ptrXcursorLibrarySetTheme = + (PtrXcursorLibrarySetTheme) xcursorLib.resolve("XcursorSetTheme"); + ptrXcursorLibraryGetDefaultSize = + (PtrXcursorLibraryGetDefaultSize) xcursorLib.resolve("XcursorGetDefaultSize"); + } + function_ptrs_not_initialized = false; } - if (xcursorFound) - ptrXcursorLibraryLoadCursor = - (PtrXcursorLibraryLoadCursor) xcursorLib.resolve("XcursorLibraryLoadCursor"); + #endif } QXcbCursor::~QXcbCursor() { xcb_connection_t *conn = xcb_connection(); + + if (m_gtkCursorThemeInitialized) { + m_screen->xSettings()->removeCallbackForHandle(this); + } + if (!--cursorCount) xcb_close_font(conn, cursorFont); @@ -448,6 +475,52 @@ xcb_cursor_t QXcbCursor::createNonStandardCursor(int cshape) return cursor; } +#ifdef XCB_USE_XLIB +bool updateCursorTheme(void *dpy, const QByteArray theme) { + if (!ptrXcursorLibraryGetTheme + || !ptrXcursorLibrarySetTheme) + return false; + QByteArray oldTheme = ptrXcursorLibraryGetTheme(dpy); + if (oldTheme == theme) + return false; + + int setTheme = ptrXcursorLibrarySetTheme(dpy,theme.constData()); + return setTheme; +} + + void QXcbCursor::cursorThemePropertyChanged(QXcbScreen *screen, const QByteArray &name, const QVariant &property, void *handle) +{ + Q_UNUSED(screen); + Q_UNUSED(name); + QXcbCursor *self = static_cast<QXcbCursor *>(handle); + updateCursorTheme(self->connection()->xlib_display(),property.toByteArray()); +} + +static xcb_cursor_t loadCursor(void *dpy, int cshape) +{ + xcb_cursor_t cursor = XCB_NONE; + if (!ptrXcursorLibraryLoadCursor || !dpy) + return cursor; + switch (cshape) { + case Qt::DragCopyCursor: + cursor = ptrXcursorLibraryLoadCursor(dpy, "dnd-copy"); + break; + case Qt::DragMoveCursor: + cursor = ptrXcursorLibraryLoadCursor(dpy, "dnd-move"); + break; + case Qt::DragLinkCursor: + cursor = ptrXcursorLibraryLoadCursor(dpy, "dnd-link"); + break; + default: + break; + } + if (!cursor) { + cursor = ptrXcursorLibraryLoadCursor(dpy, cursorNames[cshape]); + } + return cursor; +} +#endif //XCB_USE_XLIB + xcb_cursor_t QXcbCursor::createFontCursor(int cshape) { xcb_connection_t *conn = xcb_connection(); @@ -456,24 +529,18 @@ xcb_cursor_t QXcbCursor::createFontCursor(int cshape) // Try Xcursor first #ifdef XCB_USE_XLIB - if (ptrXcursorLibraryLoadCursor && cshape >= 0 && cshape < Qt::LastCursor) { + if (cshape >= 0 && cshape < Qt::LastCursor) { void *dpy = connection()->xlib_display(); // special case for non-standard dnd-* cursors - switch (cshape) { - case Qt::DragCopyCursor: - cursor = ptrXcursorLibraryLoadCursor(dpy, "dnd-copy"); - break; - case Qt::DragMoveCursor: - cursor = ptrXcursorLibraryLoadCursor(dpy, "dnd-move"); - break; - case Qt::DragLinkCursor: - cursor = ptrXcursorLibraryLoadCursor(dpy, "dnd-link"); - break; - default: - break; + cursor = loadCursor(dpy, cshape); + if (!cursor && !m_gtkCursorThemeInitialized) { + QByteArray gtkCursorTheme = m_screen->xSettings()->setting("Gtk/CursorThemeName").toByteArray(); + m_screen->xSettings()->registerCallbackForProperty("Gtk/CursorThemeName",cursorThemePropertyChanged,this); + if (updateCursorTheme(dpy,gtkCursorTheme)) { + cursor = loadCursor(dpy, cshape); + } + m_gtkCursorThemeInitialized = true; } - if (!cursor) - cursor = ptrXcursorLibraryLoadCursor(dpy, cursorNames[cshape]); } if (cursor) return cursor; diff --git a/src/plugins/platforms/xcb/qxcbcursor.h b/src/plugins/platforms/xcb/qxcbcursor.h index 4bbb99e802..081300868c 100644 --- a/src/plugins/platforms/xcb/qxcbcursor.h +++ b/src/plugins/platforms/xcb/qxcbcursor.h @@ -72,6 +72,13 @@ private: QMap<int, xcb_cursor_t> m_shapeCursorMap; QMap<qint64, xcb_cursor_t> m_bitmapCursorMap; #endif +#ifdef XCB_USE_XLIB + static void cursorThemePropertyChanged(QXcbScreen *screen, + const QByteArray &name, + const QVariant &property, + void *handle); +#endif + bool m_gtkCursorThemeInitialized; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/xcb/qxcbdrag.cpp b/src/plugins/platforms/xcb/qxcbdrag.cpp index dceac09be5..db736cef4e 100644 --- a/src/plugins/platforms/xcb/qxcbdrag.cpp +++ b/src/plugins/platforms/xcb/qxcbdrag.cpp @@ -140,7 +140,6 @@ QXcbDrag::QXcbDrag(QXcbConnection *c) : QXcbObject(c) dropData = new QXcbDropData(this); init(); - heartbeat = -1; cleanup_timer = -1; } @@ -179,9 +178,6 @@ void QXcbDrag::startDrag() init(); - heartbeat = startTimer(200); - - xcb_set_selection_owner(xcb_connection(), connection()->clipboard()->owner(), atom(QXcbAtom::XdndSelection), connection()->time()); @@ -202,10 +198,6 @@ void QXcbDrag::startDrag() void QXcbDrag::endDrag() { - if (heartbeat != -1) { - killTimer(heartbeat); - heartbeat = -1; - } QBasicDrag::endDrag(); } @@ -485,11 +477,6 @@ void QXcbDrag::drop(const QMouseEvent *event) { QBasicDrag::drop(event); - if (heartbeat != -1) { - killTimer(heartbeat); - heartbeat = -1; - } - if (!current_target) return; @@ -536,7 +523,6 @@ void QXcbDrag::drop(const QMouseEvent *event) current_proxy_target = 0; source_time = 0; // current_embedding_widget = 0; - // #fixme resetDndState(false); } Qt::DropAction QXcbDrag::toDropAction(xcb_atom_t a) const @@ -1041,12 +1027,7 @@ void QXcbDrag::handleFinished(const xcb_client_message_event_t *event) void QXcbDrag::timerEvent(QTimerEvent* e) { - if (e->timerId() == heartbeat && source_sameanswer.isNull()) { - QPointF pos = QCursor::pos(); - QMouseEvent me(QEvent::MouseMove, pos, pos, pos, Qt::LeftButton, - QGuiApplication::mouseButtons(), QGuiApplication::keyboardModifiers()); - move(&me); - } else if (e->timerId() == cleanup_timer) { + if (e->timerId() == cleanup_timer) { bool stopTimer = true; for (int i = 0; i < transactions.count(); ++i) { const Transaction &t = transactions.at(i); @@ -1160,7 +1141,7 @@ bool QXcbDrag::dndEnable(QXcbWindow *w, bool on) if (desktop_proxy) // *WE* already have one. return false; - connection()->grabServer(); + QXcbConnectionGrabber grabber(connection()); // As per Xdnd4, use XdndProxy xcb_window_t proxy_id = xdndProxy(connection(), w->xcb_window()); @@ -1176,7 +1157,6 @@ bool QXcbDrag::dndEnable(QXcbWindow *w, bool on) XCB_ATOM_WINDOW, 32, 1, &proxy_id); } - connection()->ungrabServer(); } else { xdnd_widget = w; } diff --git a/src/plugins/platforms/xcb/qxcbdrag.h b/src/plugins/platforms/xcb/qxcbdrag.h index cc74d85b51..5678c2d303 100644 --- a/src/plugins/platforms/xcb/qxcbdrag.h +++ b/src/plugins/platforms/xcb/qxcbdrag.h @@ -143,8 +143,6 @@ private: xcb_window_t current_proxy_target; QXcbScreen *current_screen; - // timer used when target wants "continuous" move messages (eg. scroll) - int heartbeat; // 10 minute timer used to discard old XdndDrop transactions enum { XdndDropTransactionTimeout = 600000 }; diff --git a/src/plugins/platforms/xcb/qxcbintegration.cpp b/src/plugins/platforms/xcb/qxcbintegration.cpp index dd1466d23c..77c265fd09 100644 --- a/src/plugins/platforms/xcb/qxcbintegration.cpp +++ b/src/plugins/platforms/xcb/qxcbintegration.cpp @@ -327,6 +327,11 @@ Qt::KeyboardModifiers QXcbIntegration::queryKeyboardModifiers() const return conn->keyboard()->translateModifiers(keybMask); } +QList<int> QXcbIntegration::possibleKeys(const QKeyEvent *e) const +{ + return m_connections.at(0)->keyboard()->possibleKeys(e); +} + QStringList QXcbIntegration::themeNames() const { return QGenericUnixTheme::themeNames(); @@ -337,4 +342,31 @@ QPlatformTheme *QXcbIntegration::createPlatformTheme(const QString &name) const return QGenericUnixTheme::createUnixTheme(name); } +QVariant QXcbIntegration::styleHint(QPlatformIntegration::StyleHint hint) const +{ + switch (hint) { + case QPlatformIntegration::CursorFlashTime: + case QPlatformIntegration::KeyboardInputInterval: + case QPlatformIntegration::MouseDoubleClickInterval: + case QPlatformIntegration::StartDragDistance: + case QPlatformIntegration::StartDragTime: + case QPlatformIntegration::KeyboardAutoRepeatRate: + case QPlatformIntegration::PasswordMaskDelay: + case QPlatformIntegration::FontSmoothingGamma: + case QPlatformIntegration::StartDragVelocity: + case QPlatformIntegration::UseRtlExtensions: + case QPlatformIntegration::PasswordMaskCharacter: + // TODO using various xcb, gnome or KDE settings + break; // Not implemented, use defaults + case QPlatformIntegration::ShowIsFullScreen: + // X11 always has support for windows, but the + // window manager could prevent it (e.g. matchbox) + return false; + case QPlatformIntegration::SynthesizeMouseFromTouchEvents: + // We do not want Qt to synthesize mouse events if X11 already does it. + return m_connections.at(0)->hasTouchWithoutMouseEmulation(); + } + return QPlatformIntegration::styleHint(hint); +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/xcb/qxcbintegration.h b/src/plugins/platforms/xcb/qxcbintegration.h index 451dc43475..7042628203 100644 --- a/src/plugins/platforms/xcb/qxcbintegration.h +++ b/src/plugins/platforms/xcb/qxcbintegration.h @@ -91,9 +91,11 @@ public: QPlatformServices *services() const; Qt::KeyboardModifiers queryKeyboardModifiers() const; + QList<int> possibleKeys(const QKeyEvent *e) const; QStringList themeNames() const; QPlatformTheme *createPlatformTheme(const QString &name) const; + QVariant styleHint(StyleHint hint) const; QXcbConnection *defaultConnection() const { return m_connections.first(); } diff --git a/src/plugins/platforms/xcb/qxcbkeyboard.cpp b/src/plugins/platforms/xcb/qxcbkeyboard.cpp index c66ed53152..155b327315 100644 --- a/src/plugins/platforms/xcb/qxcbkeyboard.cpp +++ b/src/plugins/platforms/xcb/qxcbkeyboard.cpp @@ -42,8 +42,6 @@ #include "qxcbkeyboard.h" #include "qxcbwindow.h" #include "qxcbscreen.h" -#include "qxlibconvenience.h" -#include <xcb/xcb_keysyms.h> #include <X11/keysym.h> #include <qpa/qwindowsysteminterface.h> #include <QtCore/QTextCodec> @@ -55,6 +53,10 @@ #include <qpa/qplatformintegration.h> #include <qpa/qplatformcursor.h> +#ifdef XKBCOMMON_0_2_0 +#include <xkbcommon_workaround.h> +#endif + #ifndef XK_ISO_Left_Tab #define XK_ISO_Left_Tab 0xFE20 #endif @@ -561,288 +563,333 @@ static const unsigned int KeyTbl[] = { 0, 0 }; -static const unsigned short katakanaKeysymsToUnicode[] = { - 0x0000, 0x3002, 0x300C, 0x300D, 0x3001, 0x30FB, 0x30F2, 0x30A1, - 0x30A3, 0x30A5, 0x30A7, 0x30A9, 0x30E3, 0x30E5, 0x30E7, 0x30C3, - 0x30FC, 0x30A2, 0x30A4, 0x30A6, 0x30A8, 0x30AA, 0x30AB, 0x30AD, - 0x30AF, 0x30B1, 0x30B3, 0x30B5, 0x30B7, 0x30B9, 0x30BB, 0x30BD, - 0x30BF, 0x30C1, 0x30C4, 0x30C6, 0x30C8, 0x30CA, 0x30CB, 0x30CC, - 0x30CD, 0x30CE, 0x30CF, 0x30D2, 0x30D5, 0x30D8, 0x30DB, 0x30DE, - 0x30DF, 0x30E0, 0x30E1, 0x30E2, 0x30E4, 0x30E6, 0x30E8, 0x30E9, - 0x30EA, 0x30EB, 0x30EC, 0x30ED, 0x30EF, 0x30F3, 0x309B, 0x309C -}; - -static const unsigned short cyrillicKeysymsToUnicode[] = { - 0x0000, 0x0452, 0x0453, 0x0451, 0x0454, 0x0455, 0x0456, 0x0457, - 0x0458, 0x0459, 0x045a, 0x045b, 0x045c, 0x0000, 0x045e, 0x045f, - 0x2116, 0x0402, 0x0403, 0x0401, 0x0404, 0x0405, 0x0406, 0x0407, - 0x0408, 0x0409, 0x040a, 0x040b, 0x040c, 0x0000, 0x040e, 0x040f, - 0x044e, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433, - 0x0445, 0x0438, 0x0439, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e, - 0x043f, 0x044f, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432, - 0x044c, 0x044b, 0x0437, 0x0448, 0x044d, 0x0449, 0x0447, 0x044a, - 0x042e, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413, - 0x0425, 0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, - 0x041f, 0x042f, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412, - 0x042c, 0x042b, 0x0417, 0x0428, 0x042d, 0x0429, 0x0427, 0x042a -}; - -static const unsigned short greekKeysymsToUnicode[] = { - 0x0000, 0x0386, 0x0388, 0x0389, 0x038a, 0x03aa, 0x0000, 0x038c, - 0x038e, 0x03ab, 0x0000, 0x038f, 0x0000, 0x0000, 0x0385, 0x2015, - 0x0000, 0x03ac, 0x03ad, 0x03ae, 0x03af, 0x03ca, 0x0390, 0x03cc, - 0x03cd, 0x03cb, 0x03b0, 0x03ce, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, - 0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d, 0x039e, 0x039f, - 0x03a0, 0x03a1, 0x03a3, 0x0000, 0x03a4, 0x03a5, 0x03a6, 0x03a7, - 0x03a8, 0x03a9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x03b1, 0x03b2, 0x03b3, 0x03b4, 0x03b5, 0x03b6, 0x03b7, - 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03be, 0x03bf, - 0x03c0, 0x03c1, 0x03c3, 0x03c2, 0x03c4, 0x03c5, 0x03c6, 0x03c7, - 0x03c8, 0x03c9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 -}; - -static const unsigned short technicalKeysymsToUnicode[] = { - 0x0000, 0x23B7, 0x250C, 0x2500, 0x2320, 0x2321, 0x2502, 0x23A1, - 0x23A3, 0x23A4, 0x23A6, 0x239B, 0x239D, 0x239E, 0x23A0, 0x23A8, - 0x23AC, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x2264, 0x2260, 0x2265, 0x222B, - 0x2234, 0x221D, 0x221E, 0x0000, 0x0000, 0x2207, 0x0000, 0x0000, - 0x223C, 0x2243, 0x0000, 0x0000, 0x0000, 0x21D4, 0x21D2, 0x2261, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x221A, 0x0000, - 0x0000, 0x0000, 0x2282, 0x2283, 0x2229, 0x222A, 0x2227, 0x2228, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2202, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0192, 0x0000, - 0x0000, 0x0000, 0x0000, 0x2190, 0x2191, 0x2192, 0x2193, 0x0000 -}; - -static const unsigned short specialKeysymsToUnicode[] = { - 0x25C6, 0x2592, 0x2409, 0x240C, 0x240D, 0x240A, 0x0000, 0x0000, - 0x2424, 0x240B, 0x2518, 0x2510, 0x250C, 0x2514, 0x253C, 0x23BA, - 0x23BB, 0x2500, 0x23BC, 0x23BD, 0x251C, 0x2524, 0x2534, 0x252C, - 0x2502, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 +// Possible modifier states. +static const Qt::KeyboardModifiers ModsTbl[] = { + Qt::NoModifier, // 0 + Qt::ShiftModifier, // 1 + Qt::ControlModifier, // 2 + Qt::ControlModifier | Qt::ShiftModifier, // 3 + Qt::AltModifier, // 4 + Qt::AltModifier | Qt::ShiftModifier, // 5 + Qt::AltModifier | Qt::ControlModifier, // 6 + Qt::AltModifier | Qt::ShiftModifier | Qt::ControlModifier, // 7 + Qt::NoModifier // Fall-back to raw Key_* }; -static const unsigned short publishingKeysymsToUnicode[] = { - 0x0000, 0x2003, 0x2002, 0x2004, 0x2005, 0x2007, 0x2008, 0x2009, - 0x200a, 0x2014, 0x2013, 0x0000, 0x0000, 0x0000, 0x2026, 0x2025, - 0x2153, 0x2154, 0x2155, 0x2156, 0x2157, 0x2158, 0x2159, 0x215a, - 0x2105, 0x0000, 0x0000, 0x2012, 0x2329, 0x0000, 0x232a, 0x0000, - 0x0000, 0x0000, 0x0000, 0x215b, 0x215c, 0x215d, 0x215e, 0x0000, - 0x0000, 0x2122, 0x2613, 0x0000, 0x25c1, 0x25b7, 0x25cb, 0x25af, - 0x2018, 0x2019, 0x201c, 0x201d, 0x211e, 0x0000, 0x2032, 0x2033, - 0x0000, 0x271d, 0x0000, 0x25ac, 0x25c0, 0x25b6, 0x25cf, 0x25ae, - 0x25e6, 0x25ab, 0x25ad, 0x25b3, 0x25bd, 0x2606, 0x2022, 0x25aa, - 0x25b2, 0x25bc, 0x261c, 0x261e, 0x2663, 0x2666, 0x2665, 0x0000, - 0x2720, 0x2020, 0x2021, 0x2713, 0x2717, 0x266f, 0x266d, 0x2642, - 0x2640, 0x260e, 0x2315, 0x2117, 0x2038, 0x201a, 0x201e, 0x0000 -}; - -static const unsigned short aplKeysymsToUnicode[] = { - 0x0000, 0x0000, 0x0000, 0x003c, 0x0000, 0x0000, 0x003e, 0x0000, - 0x2228, 0x2227, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x00af, 0x0000, 0x22a5, 0x2229, 0x230a, 0x0000, 0x005f, 0x0000, - 0x0000, 0x0000, 0x2218, 0x0000, 0x2395, 0x0000, 0x22a4, 0x25cb, - 0x0000, 0x0000, 0x0000, 0x2308, 0x0000, 0x0000, 0x222a, 0x0000, - 0x2283, 0x0000, 0x2282, 0x0000, 0x22a2, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x22a3, 0x0000, 0x0000, 0x0000 -}; - -static const unsigned short koreanKeysymsToUnicode[] = { - 0x0000, 0x3131, 0x3132, 0x3133, 0x3134, 0x3135, 0x3136, 0x3137, - 0x3138, 0x3139, 0x313a, 0x313b, 0x313c, 0x313d, 0x313e, 0x313f, - 0x3140, 0x3141, 0x3142, 0x3143, 0x3144, 0x3145, 0x3146, 0x3147, - 0x3148, 0x3149, 0x314a, 0x314b, 0x314c, 0x314d, 0x314e, 0x314f, - 0x3150, 0x3151, 0x3152, 0x3153, 0x3154, 0x3155, 0x3156, 0x3157, - 0x3158, 0x3159, 0x315a, 0x315b, 0x315c, 0x315d, 0x315e, 0x315f, - 0x3160, 0x3161, 0x3162, 0x3163, 0x11a8, 0x11a9, 0x11aa, 0x11ab, - 0x11ac, 0x11ad, 0x11ae, 0x11af, 0x11b0, 0x11b1, 0x11b2, 0x11b3, - 0x11b4, 0x11b5, 0x11b6, 0x11b7, 0x11b8, 0x11b9, 0x11ba, 0x11bb, - 0x11bc, 0x11bd, 0x11be, 0x11bf, 0x11c0, 0x11c1, 0x11c2, 0x316d, - 0x3171, 0x3178, 0x317f, 0x3181, 0x3184, 0x3186, 0x318d, 0x318e, - 0x11eb, 0x11f0, 0x11f9, 0x0000, 0x0000, 0x0000, 0x0000, 0x20a9 -}; - -static QChar keysymToUnicode(unsigned char byte3, unsigned char byte4) -{ - switch (byte3) { - case 0x04: - // katakana - if (byte4 > 0xa0 && byte4 < 0xe0) - return QChar(katakanaKeysymsToUnicode[byte4 - 0xa0]); - else if (byte4 == 0x7e) - return QChar(0x203e); // Overline - break; - case 0x06: - // russian, use lookup table - if (byte4 > 0xa0) - return QChar(cyrillicKeysymsToUnicode[byte4 - 0xa0]); - break; - case 0x07: - // greek - if (byte4 > 0xa0) - return QChar(greekKeysymsToUnicode[byte4 - 0xa0]); - break; - case 0x08: - // technical - if (byte4 > 0xa0) - return QChar(technicalKeysymsToUnicode[byte4 - 0xa0]); - break; - case 0x09: - // special - if (byte4 >= 0xe0) - return QChar(specialKeysymsToUnicode[byte4 - 0xe0]); - break; - case 0x0a: - // publishing - if (byte4 > 0xa0) - return QChar(publishingKeysymsToUnicode[byte4 - 0xa0]); - break; - case 0x0b: - // APL - if (byte4 > 0xa0) - return QChar(aplKeysymsToUnicode[byte4 - 0xa0]); - break; - case 0x0e: - // Korean - if (byte4 > 0xa0) - return QChar(koreanKeysymsToUnicode[byte4 - 0xa0]); - break; - default: - break; - } - return QChar(0x0); -} - -Qt::KeyboardModifiers QXcbKeyboard::translateModifiers(int s) +Qt::KeyboardModifiers QXcbKeyboard::translateModifiers(int s) const { Qt::KeyboardModifiers ret = 0; if (s & XCB_MOD_MASK_SHIFT) ret |= Qt::ShiftModifier; if (s & XCB_MOD_MASK_CONTROL) ret |= Qt::ControlModifier; - if (s & m_alt_mask) + if (s & rmod_masks.alt) ret |= Qt::AltModifier; - if (s & m_meta_mask) + if (s & rmod_masks.meta) ret |= Qt::MetaModifier; + if (s & rmod_masks.altgr) + ret |= Qt::GroupSwitchModifier; return ret; } -int QXcbKeyboard::translateKeySym(uint key) const +void QXcbKeyboard::readXKBConfig(struct xkb_rule_names *xkb_names) { - int code = Qt::Key_unknown; - int i = 0; // any other keys - while (KeyTbl[i]) { - if (key == KeyTbl[i]) { - code = (int)KeyTbl[i+1]; - break; + xcb_generic_error_t *error; + xcb_get_property_cookie_t cookie; + xcb_get_property_reply_t *config_reply; + + xcb_connection_t *c = xcb_connection(); + xcb_window_t rootWindow = connection()->rootWindow(); + + cookie = xcb_get_property(c, 0, rootWindow, + atom(QXcbAtom::_XKB_RULES_NAMES), XCB_ATOM_STRING, 0, 1024); + + config_reply = xcb_get_property_reply(c, cookie, &error); + if (!config_reply) { + qWarning("Qt: Couldn't interpret the _XKB_RULES_NAMES property"); + return; + } + char *xkb_config = (char *)xcb_get_property_value(config_reply); + int length = xcb_get_property_value_length(config_reply); + + char *names[5] = { 0, 0, 0, 0, 0 }; + char *p = xkb_config, *end = p + length; + int i = 0; + // The result from xcb_get_property_value() is not necessarily \0-terminated, + // we need to make sure that too many or missing '\0' symbols are handled safely. + do { + uint len = qstrnlen(p, length); + names[i++] = p; + p += len + 1; + length -= len + 1; + } while (p < end || i < 5); + + xkb_names->rules = qstrdup(names[0]); + xkb_names->model = qstrdup(names[1]); + xkb_names->layout = qstrdup(names[2]); + xkb_names->variant = qstrdup(names[3]); + xkb_names->options = qstrdup(names[4]); + + free(config_reply); +} + +void QXcbKeyboard::updateKeymap() +{ + m_config = true; + if (!xkb_context) { + xkb_context = xkb_context_new((xkb_context_flags)0); + if (!xkb_context) { + qWarning("Qt: Failed to create XKB context"); + m_config = false; + return; } - i += 2; } - if (m_meta_mask) { - // translate Super/Hyper keys to Meta if we're using them as the MetaModifier - if (m_meta_mask == m_super_mask && (code == Qt::Key_Super_L || code == Qt::Key_Super_R)) { - code = Qt::Key_Meta; - } else if (m_meta_mask == m_hyper_mask && (code == Qt::Key_Hyper_L || code == Qt::Key_Hyper_R)) { - code = Qt::Key_Meta; + + struct xkb_rule_names xkb_names = {0, 0, 0, 0, 0}; + + readXKBConfig(&xkb_names); + // Compile a keymap from RMLVO (rules, models, layouts, variants and options) names + if (xkb_keymap) + xkb_keymap_unref(xkb_keymap); + + xkb_keymap = xkb_keymap_new_from_names(xkb_context, &xkb_names, (xkb_keymap_compile_flags)0); + + delete[] xkb_names.rules; + delete[] xkb_names.model; + delete[] xkb_names.layout; + delete[] xkb_names.variant; + delete[] xkb_names.options; + + if (!xkb_keymap) { + qWarning("Qt: Failed to compile a keymap"); + m_config = false; + return; + } + // Create a new keyboard state object for a keymap + struct xkb_state *new_state = xkb_state_new(xkb_keymap); + if (!new_state) { + qWarning("Qt: Failed to create a new keyboard state"); + m_config = false; + return; + } + + if (xkb_state) { + xkb_state_unref(xkb_state); + xkb_state = new_state; + } else { + xkb_state = new_state; +#ifndef QT_NO_XKB + // get initial state from the X server (and keep it up-to-date at all times) + xcb_xkb_get_state_cookie_t state; + xcb_xkb_get_state_reply_t *init_state; + + xcb_connection_t *c = xcb_connection(); + state = xcb_xkb_get_state(c, XCB_XKB_ID_USE_CORE_KBD); + init_state = xcb_xkb_get_state_reply(c, state, 0); + if (!init_state) { + qWarning("Qt: couldn't retrieve an initial keyboard state"); + return; } + /* The xkb keyboard state is comprised of the state of all keyboard modifiers, + the keyboard group, and the state of the pointer buttons */ + xkb_state_update_mask(xkb_state, + init_state->baseMods, + init_state->latchedMods, + init_state->lockedMods, + init_state->baseGroup, + init_state->latchedGroup, + init_state->lockedGroup); + free(init_state); +#else + updateXKBMods(); +#endif } - return code; } -QString QXcbKeyboard::translateKeySym(xcb_keysym_t keysym, uint xmodifiers, - int &code, Qt::KeyboardModifiers &modifiers, - QByteArray &chars, int &count) +#ifndef QT_NO_XKB +void QXcbKeyboard::updateXKBState(xcb_xkb_state_notify_event_t *state) { - // all keysyms smaller than 0xff00 are actally keys that can be mapped to unicode chars - - QTextCodec *mapper = QTextCodec::codecForLocale(); - QChar converted; - - if (/*count == 0 &&*/ keysym < 0xff00) { - unsigned char byte3 = (unsigned char)(keysym >> 8); - int mib = -1; - switch(byte3) { - case 0: // Latin 1 - case 1: // Latin 2 - case 2: //latin 3 - case 3: // latin4 - mib = byte3 + 4; break; - case 5: // arabic - mib = 82; break; - case 12: // Hebrew - mib = 85; break; - case 13: // Thai - mib = 2259; break; - case 4: // kana - case 6: // cyrillic - case 7: // greek - case 8: // technical, no mapping here at the moment - case 9: // Special - case 10: // Publishing - case 11: // APL - case 14: // Korean, no mapping - mib = -1; // manual conversion - mapper= 0; -#if !defined(QT_NO_XIM) - converted = keysymToUnicode(byte3, keysym & 0xff); -#endif - case 0x20: - // currency symbols - if (keysym >= 0x20a0 && keysym <= 0x20ac) { - mib = -1; // manual conversion - mapper = 0; - converted = (uint)keysym; - } - break; - default: - break; + if (!m_config) + return; + + if (connection()->hasXKB()) { + + xkb_state_component newState; + newState = xkb_state_update_mask(xkb_state, + state->baseMods, + state->latchedMods, + state->lockedMods, + state->baseGroup, + state->latchedGroup, + state->lockedGroup); + + if ((newState & XKB_STATE_LAYOUT_EFFECTIVE) == XKB_STATE_LAYOUT_EFFECTIVE) { + //qWarning("TODO: Support KeyboardLayoutChange on QPA (QTBUG-27681)"); } - if (mib != -1) { - mapper = QTextCodec::codecForMib(mib); - if (chars.isEmpty()) - chars.resize(1); - chars[0] = (unsigned char) (keysym & 0xff); // get only the fourth bit for conversion later - count = 1; + } +} + +#else +void QXcbKeyboard::updateXKBStateFromCore(quint16 state) +{ + if (!m_config) + return; + + quint32 modsDepressed, modsLatched, modsLocked; + modsDepressed = xkb_state_serialize_mods(xkb_state, XKB_STATE_MODS_DEPRESSED); + modsLatched = xkb_state_serialize_mods(xkb_state, XKB_STATE_MODS_LATCHED); + modsLocked = xkb_state_serialize_mods(xkb_state, XKB_STATE_MODS_LOCKED); + + quint32 xkbMask = xkbModMask(state); + xkb_state_component newState; + newState = xkb_state_update_mask(xkb_state, + modsDepressed & xkbMask, + modsLatched & xkbMask, + modsLocked & xkbMask, + 0, + 0, + (state >> 13) & 3); // bits 13 and 14 report the state keyboard group + + if ((newState & XKB_STATE_LAYOUT_EFFECTIVE) == XKB_STATE_LAYOUT_EFFECTIVE) { + //qWarning("TODO: Support KeyboardLayoutChange on QPA (QTBUG-27681)"); + } +} + +quint32 QXcbKeyboard::xkbModMask(quint16 state) +{ + quint32 xkb_mask = 0; + + if ((state & XCB_MOD_MASK_SHIFT) && xkb_mods.shift != XKB_MOD_INVALID) + xkb_mask |= (1 << xkb_mods.shift); + if ((state & XCB_MOD_MASK_LOCK) && xkb_mods.lock != XKB_MOD_INVALID) + xkb_mask |= (1 << xkb_mods.lock); + if ((state & XCB_MOD_MASK_CONTROL) && xkb_mods.control != XKB_MOD_INVALID) + xkb_mask |= (1 << xkb_mods.control); + if ((state & XCB_MOD_MASK_1) && xkb_mods.mod1 != XKB_MOD_INVALID) + xkb_mask |= (1 << xkb_mods.mod1); + if ((state & XCB_MOD_MASK_2) && xkb_mods.mod2 != XKB_MOD_INVALID) + xkb_mask |= (1 << xkb_mods.mod2); + if ((state & XCB_MOD_MASK_3) && xkb_mods.mod3 != XKB_MOD_INVALID) + xkb_mask |= (1 << xkb_mods.mod3); + if ((state & XCB_MOD_MASK_4) && xkb_mods.mod4 != XKB_MOD_INVALID) + xkb_mask |= (1 << xkb_mods.mod4); + if ((state & XCB_MOD_MASK_5) && xkb_mods.mod5 != XKB_MOD_INVALID) + xkb_mask |= (1 << xkb_mods.mod5); + + return xkb_mask; +} + +void QXcbKeyboard::updateXKBMods() +{ + xkb_mods.shift = xkb_map_mod_get_index(xkb_keymap, XKB_MOD_NAME_SHIFT); + xkb_mods.lock = xkb_map_mod_get_index(xkb_keymap, XKB_MOD_NAME_CAPS); + xkb_mods.control = xkb_map_mod_get_index(xkb_keymap, XKB_MOD_NAME_CTRL); + xkb_mods.mod1 = xkb_map_mod_get_index(xkb_keymap, "Mod1"); + xkb_mods.mod2 = xkb_map_mod_get_index(xkb_keymap, "Mod2"); + xkb_mods.mod3 = xkb_map_mod_get_index(xkb_keymap, "Mod3"); + xkb_mods.mod4 = xkb_map_mod_get_index(xkb_keymap, "Mod4"); + xkb_mods.mod5 = xkb_map_mod_get_index(xkb_keymap, "Mod5"); +} +#endif + +QList<int> QXcbKeyboard::possibleKeys(const QKeyEvent *event) const +{ + // turn off the modifier bits which doesn't participate in shortcuts + Qt::KeyboardModifiers notNeeded = Qt::MetaModifier | Qt::KeypadModifier | Qt::GroupSwitchModifier; + Qt::KeyboardModifiers modifiers = event->modifiers() &= ~notNeeded; + // create a fresh kb state and test against the relevant modifier combinations + // NOTE: it should be possible to query the keymap directly, once it gets + // supported by libxkbcommon + struct xkb_state * kb_state = xkb_state_new(xkb_keymap); + if (!kb_state) { + qWarning("QXcbKeyboard: failed to compile xkb keymap"); + return QList<int>(); + } + // get kb state from the master xkb_state and update the temporary kb_state + xkb_layout_index_t baseLayout = xkb_state_serialize_layout(xkb_state, XKB_STATE_LAYOUT_DEPRESSED); + xkb_layout_index_t latchedLayout = xkb_state_serialize_layout(xkb_state, XKB_STATE_LAYOUT_LATCHED); + xkb_layout_index_t lockedLayout = xkb_state_serialize_layout(xkb_state, XKB_STATE_LAYOUT_LOCKED); + xkb_mod_index_t latchedMods = xkb_state_serialize_mods(xkb_state, XKB_STATE_MODS_LATCHED); + xkb_mod_index_t lockedMods = xkb_state_serialize_mods(xkb_state, XKB_STATE_MODS_LOCKED); + + xkb_state_update_mask(kb_state, 0, latchedMods, lockedMods, + baseLayout, latchedLayout, lockedLayout); + + xkb_keysym_t sym = xkb_state_key_get_one_sym(kb_state, event->nativeScanCode()); + if (sym == XKB_KEY_NoSymbol) + return QList<int>(); + + QList<int> result; + int baseQtKey = keysymToQtKey(sym, modifiers, keysymToUnicode(sym)); + result += (baseQtKey + modifiers); // The base key is _always_ valid, of course + + xkb_mod_index_t shiftMod = xkb_keymap_mod_get_index(xkb_keymap, "Shift"); + xkb_mod_index_t altMod = xkb_keymap_mod_get_index(xkb_keymap, "Alt"); + xkb_mod_index_t controlMod = xkb_keymap_mod_get_index(xkb_keymap, "Control"); + + xkb_mod_mask_t depressed; + + int qtKey = 0; + //obtain a list of possible shortcuts for the given key event + for (uint i = 1; i < sizeof(ModsTbl) / sizeof(*ModsTbl) ; ++i) { + Qt::KeyboardModifiers neededMods = ModsTbl[i]; + if ((modifiers & neededMods) == neededMods) { + + depressed = 0; + if (neededMods & Qt::AltModifier) + depressed |= (1 << altMod); + if (neededMods & Qt::ShiftModifier) + depressed |= (1 << shiftMod); + if (neededMods & Qt::ControlModifier) + depressed |= (1 << controlMod); + + // update a keyboard state from a set of explicit masks + xkb_state_update_mask(kb_state, depressed, latchedMods, lockedMods, + baseLayout, latchedLayout, lockedLayout); + sym = xkb_state_key_get_one_sym(kb_state, event->nativeScanCode()); + + if (sym == XKB_KEY_NoSymbol) + continue; + + Qt::KeyboardModifiers mods = modifiers & ~neededMods; + qtKey = keysymToQtKey(sym, mods, keysymToUnicode(sym)); + + if (qtKey == baseQtKey) + continue; + + result += (qtKey + mods); } - } else if (keysym >= 0x1000000 && keysym <= 0x100ffff) { - converted = (ushort) (keysym - 0x1000000); - mapper = 0; } - if (count < (int)chars.size()-1) - chars[count] = '\0'; - - QString text; - if (!mapper && converted.unicode() != 0x0) { - text = converted; - } else if (!chars.isEmpty()) { - // convert chars (8bit) to text (unicode). - if (mapper) - text = mapper->toUnicode(chars.data(), count, 0); - if (text.isEmpty()) { - // no mapper, or codec couldn't convert to unicode (this - // can happen when running in the C locale or with no LANG - // set). try converting from latin-1 - text = QString::fromLatin1(chars); + + xkb_state_unref(kb_state); + return result; + } + +int QXcbKeyboard::keysymToQtKey(xcb_keysym_t key) const +{ + int code = 0; + int i = 0; + while (KeyTbl[i]) { + if (key == KeyTbl[i]) { + code = (int)KeyTbl[i+1]; + break; } + i += 2; } - modifiers = translateModifiers(xmodifiers); + return code; +} +int QXcbKeyboard::keysymToQtKey(xcb_keysym_t keysym, Qt::KeyboardModifiers &modifiers, QString text) const +{ + int code = 0; + QTextCodec *systemCodec = QTextCodec::codecForLocale(); // Commentary in X11/keysymdef says that X codes match ASCII, so it // is safe to use the locale functions to process X codes in ISO8859-1. - // // This is mainly for compatibility - applications should not use the - // Qt keycodes between 128 and 255, but should rather use the - // QKeyEvent::text(). - // - if (keysym < 128 || (keysym < 256 && (!mapper || mapper->mibEnum()==4))) { + // Qt keycodes between 128 and 255 (extended ACSII codes), but should + // rather use the QKeyEvent::text(). + if (keysym < 128 || (keysym < 256 && systemCodec->mibEnum() == 4)) { // upper-case key, if known code = isprint((int)keysym) ? toupper((int)keysym) : 0; } else if (keysym >= XK_F1 && keysym <= XK_F35) { @@ -853,48 +900,207 @@ QString QXcbKeyboard::translateKeySym(xcb_keysym_t keysym, uint xmodifiers, // numeric keypad keys code = Qt::Key_0 + ((int)keysym - XK_KP_0); } else { - code = translateKeySym(keysym); + code = keysymToQtKey(keysym); } modifiers |= Qt::KeypadModifier; - } else if (text.length() == 1 && text.unicode()->unicode() > 0x1f && text.unicode()->unicode() != 0x7f && !(keysym >= XK_dead_grave && keysym <= XK_dead_horn)) { + } else if (text.length() == 1 && text.unicode()->unicode() > 0x1f + && text.unicode()->unicode() != 0x7f + && !(keysym >= XK_dead_grave && keysym <= XK_dead_currency)) { code = text.unicode()->toUpper().unicode(); } else { // any other keys - code = translateKeySym(keysym); - - if (code == Qt::Key_Tab && (modifiers & Qt::ShiftModifier)) { - // map shift+tab to shift+backtab, QShortcutMap knows about it - // and will handle it. - code = Qt::Key_Backtab; - text = QString(); - } + code = keysymToQtKey(keysym); } - return text; + return code; } QXcbKeyboard::QXcbKeyboard(QXcbConnection *connection) : QXcbObject(connection) , m_autorepeat_code(0) + , xkb_context(0) + , xkb_keymap(0) + , xkb_state(0) +#ifndef QT_NO_XKB + , core_device_id(0) +#endif { + updateKeymap(); +#ifndef QT_NO_XKB + if (connection->hasXKB()) { + + updateVModMapping(); + updateVModToRModMapping(); + + // get the core keyboard id + xcb_xkb_get_device_info_cookie_t device_id_cookie; + xcb_xkb_get_device_info_reply_t *device_id; + + device_id_cookie = xcb_xkb_get_device_info(xcb_connection(), + XCB_XKB_ID_USE_CORE_KBD, + 0, 0, 0, 0, 0, 0); + + device_id = xcb_xkb_get_device_info_reply(xcb_connection(), device_id_cookie, 0); + if (!device_id) { + qWarning("Qt: couldn't get core keyboard device info"); + return; + } + + core_device_id = device_id->deviceID; + free(device_id); + } +#else m_key_symbols = xcb_key_symbols_alloc(xcb_connection()); - setupModifiers(); + updateModifiers(); +#endif } QXcbKeyboard::~QXcbKeyboard() { + if (xkb_state) + xkb_state_unref(xkb_state); + if (xkb_keymap) + xkb_keymap_unref(xkb_keymap); + if (xkb_context) + xkb_context_unref(xkb_context); +#ifdef QT_NO_XKB xcb_key_symbols_free(m_key_symbols); +#endif +} + +#ifndef QT_NO_XKB +void QXcbKeyboard::updateVModMapping() +{ + xcb_xkb_get_names_cookie_t names_cookie; + xcb_xkb_get_names_reply_t *name_reply; + xcb_xkb_get_names_value_list_t names_list; + + memset(&vmod_masks, 0, sizeof(vmod_masks)); + + names_cookie = xcb_xkb_get_names(xcb_connection(), + XCB_XKB_ID_USE_CORE_KBD, + XCB_XKB_NAME_DETAIL_VIRTUAL_MOD_NAMES); + + name_reply = xcb_xkb_get_names_reply(xcb_connection(), names_cookie, 0); + if (!name_reply) { + qWarning("Qt: failed to retrieve the virtual modifier names from XKB"); + return; + } + + const void *buffer = xcb_xkb_get_names_value_list(name_reply); + xcb_xkb_get_names_value_list_unpack(buffer, + name_reply->nTypes, + name_reply->indicators, + name_reply->virtualMods, + name_reply->groupNames, + name_reply->nKeys, + name_reply->nKeyAliases, + name_reply->nRadioGroups, + name_reply->which, + &names_list); + + int count = 0; + uint vmod_mask, bit; + char *vmod_name; + vmod_mask = name_reply->virtualMods; + // find the virtual modifiers for which names are defined. + for (bit = 1; vmod_mask; bit <<= 1) { + vmod_name = 0; + + if (!(vmod_mask & bit)) + continue; + + vmod_mask &= ~bit; + // virtualModNames - the list of virtual modifier atoms beginning with the lowest-numbered + // virtual modifier for which a name is defined and proceeding to the highest. + QByteArray atomName = connection()->atomName(names_list.virtualModNames[count]); + vmod_name = atomName.data(); + count++; + + if (!vmod_name) + continue; + + // similarly we could retrieve NumLock, Super, Hyper modifiers if needed. + if (qstrcmp(vmod_name, "Alt") == 0) + vmod_masks.alt = bit; + else if (qstrcmp(vmod_name, "Meta") == 0) + vmod_masks.meta = bit; + else if (qstrcmp(vmod_name, "AltGr") == 0) + vmod_masks.altgr = bit; + } + + free(name_reply); } -void QXcbKeyboard::setupModifiers() +void QXcbKeyboard::updateVModToRModMapping() +{ + xcb_xkb_get_map_cookie_t map_cookie; + xcb_xkb_get_map_reply_t *map_reply; + xcb_xkb_get_map_map_t map; + + memset(&rmod_masks, 0, sizeof(rmod_masks)); + + map_cookie = xcb_xkb_get_map(xcb_connection(), + XCB_XKB_ID_USE_CORE_KBD, + XCB_XKB_MAP_PART_VIRTUAL_MODS, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + + map_reply = xcb_xkb_get_map_reply(xcb_connection(), map_cookie, 0); + if (!map_reply) { + qWarning("Qt: failed to retrieve the virtual modifier map from XKB"); + return; + } + + const void *buffer = xcb_xkb_get_map_map(map_reply); + xcb_xkb_get_map_map_unpack(buffer, + map_reply->nTypes, + map_reply->nKeySyms, + map_reply->nKeyActions, + map_reply->totalActions, + map_reply->totalKeyBehaviors, + map_reply->nVModMapKeys, + map_reply->totalKeyExplicit, + map_reply->totalModMapKeys, + map_reply->totalVModMapKeys, + map_reply->present, + &map); + + uint vmod_mask, bit; + // the virtual modifiers mask for which a set of corresponding + // real modifiers is to be returned + vmod_mask = map_reply->virtualMods; + int count = 0; + + for (bit = 1; vmod_mask; bit <<= 1) { + uint modmap; + + if (!(vmod_mask & bit)) + continue; + + vmod_mask &= ~bit; + // real modifier bindings for the specified virtual modifiers + modmap = map.vmods_rtrn[count]; + count++; + + if (vmod_masks.alt == bit) + rmod_masks.alt = modmap; + else if (vmod_masks.meta == bit) + rmod_masks.meta = modmap; + else if (vmod_masks.altgr == bit) + rmod_masks.altgr = modmap; + } + + free(map_reply); +} +#else +void QXcbKeyboard::updateModifiers() { - m_alt_mask = 0; - m_super_mask = 0; - m_hyper_mask = 0; - m_meta_mask = 0; - m_mode_switch_mask = 0; - m_num_lock_mask = 0; - m_caps_lock_mask = 0; + // The core protocol does not provide a convenient way to determine the mapping + // of modifier bits. Clients must retrieve and search the modifier map to determine + // the keycodes bound to each modifier, and then retrieve and search the keyboard + // mapping to determine the keysyms bound to the keycodes. They must repeat this + // process for all modifiers whenever any part of the modifier mapping is changed. + memset(&rmod_masks, 0, sizeof(rmod_masks)); xcb_generic_error_t *error = 0; xcb_connection_t *conn = xcb_connection(); @@ -902,15 +1108,14 @@ void QXcbKeyboard::setupModifiers() xcb_get_modifier_mapping_reply_t *modMapReply = xcb_get_modifier_mapping_reply(conn, modMapCookie, &error); if (error) { - qWarning("QXcbKeyboard: failed to get modifier mapping"); + qWarning("Qt: failed to get modifier mapping"); free(error); return; } // for Alt and Meta L and R are the same static const xcb_keysym_t symbols[] = { - XK_Alt_L, XK_Meta_L, XK_Super_L, XK_Super_R, - XK_Hyper_L, XK_Hyper_R, XK_Num_Lock, XK_Mode_switch, XK_Caps_Lock, + XK_Alt_L, XK_Meta_L, XK_Mode_switch }; static const size_t numSymbols = sizeof symbols / sizeof *symbols; @@ -928,8 +1133,15 @@ void QXcbKeyboard::setupModifiers() xcb_keycode_t keyCode = modMap[x + bit * w]; xcb_keycode_t *itk = modKeyCodes[i]; while (itk && *itk != XCB_NO_SYMBOL) - if (*itk++ == keyCode) - setMask(symbols[i], mask); + if (*itk++ == keyCode) { + uint sym = symbols[i]; + if ((sym == XK_Alt_L || sym == XK_Alt_R)) + rmod_masks.alt = mask; + if ((sym == XK_Meta_L || sym == XK_Meta_R)) + rmod_masks.meta = mask; + if (sym == XK_Mode_switch) + rmod_masks.altgr = mask; + } } } } @@ -938,53 +1150,7 @@ void QXcbKeyboard::setupModifiers() free(modKeyCodes[i]); free(modMapReply); } - -void QXcbKeyboard::setMask(uint sym, uint mask) -{ - if (m_alt_mask == 0 - && m_meta_mask != mask - && m_super_mask != mask - && m_hyper_mask != mask - && (sym == XK_Alt_L || sym == XK_Alt_R)) - m_alt_mask = mask; - - if (m_meta_mask == 0 - && m_alt_mask != mask - && m_super_mask != mask - && m_hyper_mask != mask - && (sym == XK_Meta_L || sym == XK_Meta_R)) - m_meta_mask = mask; - - if (m_super_mask == 0 - && m_alt_mask != mask - && m_meta_mask != mask - && m_hyper_mask != mask - && (sym == XK_Super_L || sym == XK_Super_R)) - m_super_mask = mask; - - if (m_hyper_mask == 0 - && m_alt_mask != mask - && m_meta_mask != mask - && m_super_mask != mask - && (sym == XK_Hyper_L || sym == XK_Hyper_R)) - m_hyper_mask = mask; - - if (m_mode_switch_mask == 0 - && m_alt_mask != mask - && m_meta_mask != mask - && m_super_mask != mask - && m_hyper_mask != mask - && sym == XK_Mode_switch) - m_mode_switch_mask = mask; - - if (m_num_lock_mask == 0 && sym == XK_Num_Lock) - m_num_lock_mask = mask; - - if (m_caps_lock_mask == 0 && sym == XK_Caps_Lock) - m_caps_lock_mask = mask; -} - -// #define XCB_KEYBOARD_DEBUG +#endif class KeyChecker { @@ -1046,16 +1212,21 @@ void QXcbKeyboard::handleKeyEvent(QWindow *window, QEvent::Type type, xcb_keycod quint16 state, xcb_timestamp_t time) { Q_XCB_NOOP(connection()); -#ifdef XCB_KEYBOARD_DEBUG - printf("key code: %d, state: %d, syms: ", code, state); - for (int i = 0; i <= 5; ++i) { - printf("%d ", xcb_key_symbols_get_keysym(m_key_symbols, code, i)); - } - printf("\n"); + + if (!m_config) + return; + // It is crucial the order of xkb_state_key_get_one_sym & + // xkb_state_update_key operations is not reversed! + xcb_keysym_t sym = xkb_state_key_get_one_sym(xkb_state, code); +#ifdef QT_NO_XKB + enum xkb_key_direction direction; + if (type == QEvent::KeyPress) + direction = XKB_KEY_DOWN; + else + direction = XKB_KEY_UP; + xkb_state_update_key(xkb_state, code, direction); #endif - QByteArray chars; - xcb_keysym_t sym = lookupString(window, state, code, type, &chars); QPlatformInputContext *inputContext = QGuiApplicationPrivate::platformIntegration()->inputContext(); QMetaMethod method; @@ -1077,12 +1248,12 @@ void QXcbKeyboard::handleKeyEvent(QWindow *window, QEvent::Type type, xcb_keycod return; } - Qt::KeyboardModifiers modifiers; - int qtcode = 0; - int count = chars.count(); - QString string = translateKeySym(sym, state, qtcode, modifiers, chars, count); + Qt::KeyboardModifiers modifiers = translateModifiers(state); + QString string = keysymToUnicode(sym); + int count = string.size(); string.truncate(count); + int qtcode = keysymToQtKey(sym, modifiers, string); bool isAutoRepeat = false; if (type == QEvent::KeyPress) { @@ -1141,35 +1312,33 @@ void QXcbKeyboard::handleKeyEvent(QWindow *window, QEvent::Type type, xcb_keycod } } -xcb_keysym_t QXcbKeyboard::lookupString(QWindow *window, uint state, xcb_keycode_t code, - QEvent::Type type, QByteArray *chars) +QString QXcbKeyboard::keysymToUnicode(xcb_keysym_t sym) const { -#ifdef XCB_USE_XLIB - xcb_window_t xWindow = static_cast<QXcbWindow *>(window->handle())->xcb_window(); - xcb_window_t root = connection()->screens().at(0)->root(); - void *xDisplay = connection()->xlib_display(); - int xType = (type == QEvent::KeyRelease ? 3 : 2); - return q_XLookupString(xDisplay, xWindow, root, state, code, xType, chars); -#else - - // No XLookupString available. The following is really incomplete... - - int col = state & XCB_MOD_MASK_SHIFT ? 1 : 0; - const int altGrOffset = 4; - if (state & 128) - col += altGrOffset; - xcb_keysym_t sym = xcb_key_symbols_get_keysym(m_key_symbols, code, col); - if (sym == XCB_NO_SYMBOL) - sym = xcb_key_symbols_get_keysym(m_key_symbols, code, col ^ 0x1); - if (state & XCB_MOD_MASK_LOCK && sym <= 0x7f && isprint(sym)) { - if (isupper(sym)) - sym = tolower(sym); + QByteArray chars; + int bytes; + chars.resize(7); + +#ifdef XKBCOMMON_0_2_0 + if (needWorkaround(sym)) { + quint32 codepoint; + if (sym == XKB_KEY_KP_Space) + codepoint = XKB_KEY_space & 0x7f; else - sym = toupper(sym); - } - return sym; + codepoint = sym & 0x7f; + bytes = utf32_to_utf8(codepoint, chars.data()); + } else { + bytes = xkb_keysym_to_utf8(sym, chars.data(), chars.size()); + } +#else + bytes = xkb_keysym_to_utf8(sym, chars.data(), chars.size()); #endif + + if (bytes == -1) + qWarning("QXcbKeyboard::handleKeyEvent - buffer too small"); + chars.resize(bytes-1); + + return QString::fromUtf8(chars); } void QXcbKeyboard::handleKeyPressEvent(QXcbWindowEventListener *eventListener, const xcb_key_press_event_t *event) @@ -1189,10 +1358,20 @@ void QXcbKeyboard::handleKeyReleaseEvent(QXcbWindowEventListener *eventListener, handleKeyEvent(window->window(), QEvent::KeyRelease, event->detail, event->state, event->time); } -void QXcbKeyboard::handleMappingNotifyEvent(const xcb_mapping_notify_event_t *event) +void QXcbKeyboard::handleMappingNotifyEvent(const void *event) { - xcb_refresh_keyboard_mapping(m_key_symbols, const_cast<xcb_mapping_notify_event_t *>(event)); - setupModifiers(); + updateKeymap(); +#ifdef QT_NO_XKB + void *ev = const_cast<void *>(event); + xcb_refresh_keyboard_mapping(m_key_symbols, static_cast<xcb_mapping_notify_event_t *>(ev)); + updateModifiers(); +#else + Q_UNUSED(event) + if (connection()->hasXKB()) { + updateVModMapping(); + updateVModToRModMapping(); + } +#endif } QT_END_NAMESPACE diff --git a/src/plugins/platforms/xcb/qxcbkeyboard.h b/src/plugins/platforms/xcb/qxcbkeyboard.h index 3c71daa57f..af6677c20f 100644 --- a/src/plugins/platforms/xcb/qxcbkeyboard.h +++ b/src/plugins/platforms/xcb/qxcbkeyboard.h @@ -44,7 +44,11 @@ #include "qxcbobject.h" -#include "xcb/xcb_keysyms.h" +#ifdef QT_NO_XKB +#include <xcb/xcb_keysyms.h> +#endif + +#include <xkbcommon/xkbcommon.h> #include <QEvent> @@ -56,37 +60,80 @@ class QXcbKeyboard : public QXcbObject { public: QXcbKeyboard(QXcbConnection *connection); + ~QXcbKeyboard(); void handleKeyPressEvent(QXcbWindowEventListener *eventListener, const xcb_key_press_event_t *event); void handleKeyReleaseEvent(QXcbWindowEventListener *eventListener, const xcb_key_release_event_t *event); - void handleMappingNotifyEvent(const xcb_mapping_notify_event_t *event); + void handleMappingNotifyEvent(const void *event); - Qt::KeyboardModifiers translateModifiers(int s); + Qt::KeyboardModifiers translateModifiers(int s) const; -private: + void updateKeymap(); + QList<int> possibleKeys(const QKeyEvent *e) const; + +#ifdef QT_NO_XKB + void updateXKBStateFromCore(quint16 state); + void updateXKBMods(); + quint32 xkbModMask(quint16 state); +#else + int coreDeviceId() { return core_device_id; } + void updateXKBState(xcb_xkb_state_notify_event_t *state); +#endif + +protected: void handleKeyEvent(QWindow *window, QEvent::Type type, xcb_keycode_t code, quint16 state, xcb_timestamp_t time); - int translateKeySym(uint key) const; - QString translateKeySym(xcb_keysym_t keysym, uint xmodifiers, - int &code, Qt::KeyboardModifiers &modifiers, - QByteArray &chars, int &count); - void setupModifiers(); - void setMask(uint sym, uint mask); - xcb_keysym_t lookupString(QWindow *window, uint state, xcb_keycode_t code, - QEvent::Type type, QByteArray *chars); - - uint m_alt_mask; - uint m_super_mask; - uint m_hyper_mask; - uint m_meta_mask; - uint m_mode_switch_mask; - uint m_num_lock_mask; - uint m_caps_lock_mask; + QString keysymToUnicode(xcb_keysym_t sym) const; - xcb_key_symbols_t *m_key_symbols; + int keysymToQtKey(xcb_keysym_t keysym) const; + int keysymToQtKey(xcb_keysym_t keysym, Qt::KeyboardModifiers &modifiers, QString text) const; + + void readXKBConfig(struct xkb_rule_names *names); + +#ifdef QT_NO_XKB + void updateModifiers(); +#else + void updateVModMapping(); + void updateVModToRModMapping(); +#endif + +private: + bool m_config; xcb_keycode_t m_autorepeat_code; + + struct xkb_context *xkb_context; + struct xkb_keymap *xkb_keymap; + struct xkb_state *xkb_state; + + struct _mod_masks { + uint alt; + uint altgr; + uint meta; + }; + + _mod_masks rmod_masks; + +#ifdef QT_NO_XKB + xcb_key_symbols_t *m_key_symbols; + + struct _xkb_mods { + xkb_mod_index_t shift; + xkb_mod_index_t lock; + xkb_mod_index_t control; + xkb_mod_index_t mod1; + xkb_mod_index_t mod2; + xkb_mod_index_t mod3; + xkb_mod_index_t mod4; + xkb_mod_index_t mod5; + }; + + _xkb_mods xkb_mods; +#else + _mod_masks vmod_masks; + int core_device_id; +#endif }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/xcb/qxcbnativeinterface.cpp b/src/plugins/platforms/xcb/qxcbnativeinterface.cpp index da60cfd2bd..7d832a1c08 100644 --- a/src/plugins/platforms/xcb/qxcbnativeinterface.cpp +++ b/src/plugins/platforms/xcb/qxcbnativeinterface.cpp @@ -58,7 +58,9 @@ #include "qglxintegration.h" #endif -#ifndef XCB_USE_XLIB +#ifdef XCB_USE_XLIB +# include <X11/Xlib.h> +#else # include <stdio.h> #endif diff --git a/src/plugins/platforms/xcb/qxcbscreen.cpp b/src/plugins/platforms/xcb/qxcbscreen.cpp index a6ead49a27..37c6c97bc4 100644 --- a/src/plugins/platforms/xcb/qxcbscreen.cpp +++ b/src/plugins/platforms/xcb/qxcbscreen.cpp @@ -44,6 +44,7 @@ #include "qxcbcursor.h" #include "qxcbimage.h" #include "qnamespace.h" +#include "qxcbxsettings.h" #include <stdio.h> @@ -68,6 +69,7 @@ QXcbScreen::QXcbScreen(QXcbConnection *connection, xcb_screen_t *scr, , m_refreshRate(60) , m_forcedDpi(-1) , m_hintStyle(QFontEngine::HintStyle(-1)) + , m_xSettings(0) { if (connection->hasXRandr()) xcb_randr_select_input(xcb_connection(), screen()->root, true); @@ -580,4 +582,12 @@ void QXcbScreen::readXResources() } } +QXcbXSettings *QXcbScreen::xSettings() const +{ + if (!m_xSettings) { + QXcbScreen *self = const_cast<QXcbScreen *>(this); + self->m_xSettings = new QXcbXSettings(self); + } + return m_xSettings; +} QT_END_NAMESPACE diff --git a/src/plugins/platforms/xcb/qxcbscreen.h b/src/plugins/platforms/xcb/qxcbscreen.h index 0382be8a29..c36492db64 100644 --- a/src/plugins/platforms/xcb/qxcbscreen.h +++ b/src/plugins/platforms/xcb/qxcbscreen.h @@ -56,6 +56,7 @@ QT_BEGIN_NAMESPACE class QXcbConnection; class QXcbCursor; +class QXcbXSettings; class QXcbScreen : public QXcbObject, public QPlatformScreen { @@ -102,6 +103,9 @@ public: void readXResources(); QFontEngine::HintStyle hintStyle() const { return m_hintStyle; } + + QXcbXSettings *xSettings() const; + private: static bool xResource(const QByteArray &identifier, const QByteArray &expectedIdentifier, @@ -127,6 +131,7 @@ private: int m_refreshRate; int m_forcedDpi; QFontEngine::HintStyle m_hintStyle; + QXcbXSettings *m_xSettings; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/xcb/qxcbxsettings.cpp b/src/plugins/platforms/xcb/qxcbxsettings.cpp new file mode 100644 index 0000000000..7ffd3e105f --- /dev/null +++ b/src/plugins/platforms/xcb/qxcbxsettings.cpp @@ -0,0 +1,280 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qxcbxsettings.h" + +#include <QtCore/QByteArray> + +#include <X11/extensions/XIproto.h> + +QT_BEGIN_NAMESPACE +/* Implementation of http://standards.freedesktop.org/xsettings-spec/xsettings-0.5.html */ + +enum XSettingsType { + XSettingsTypeInteger = 0, + XSettingsTypeString = 1, + XSettingsTypeColor = 2 +}; + +class QXcbXSettingsCallback +{ +public: + QXcbXSettings::PropertyChangeFunc func; + void *handle; +}; + +class QXcbXSettingsPropertyValue +{ +public: + QXcbXSettingsPropertyValue() + : last_change_serial(-1) + {} + + void updateValue(QXcbScreen *screen, const QByteArray &name, const QVariant &value, int last_change_serial) + { + if (last_change_serial <= this->last_change_serial) + return; + this->value = value; + this->last_change_serial = last_change_serial; + QLinkedList<QXcbXSettingsCallback>::const_iterator it = callback_links.begin(); + for (;it != callback_links.end();++it) { + it->func(screen,name,value,it->handle); + } + } + + void addCallback(QXcbXSettings::PropertyChangeFunc func, void *handle) + { + QXcbXSettingsCallback callback; + callback.func = func; + callback.handle = handle; + callback_links.append(callback); + } + + QVariant value; + int last_change_serial; + QLinkedList<QXcbXSettingsCallback> callback_links; + +}; + +class QXcbXSettingsPrivate +{ +public: + QXcbXSettingsPrivate(QXcbScreen *screen) + : screen(screen) + { + } + + QByteArray getSettings() + { + QXcbConnectionGrabber connectionGrabber(screen->connection()); + + int offset = 0; + QByteArray settings; + xcb_atom_t _xsettings_atom = screen->connection()->atom(QXcbAtom::_XSETTINGS_SETTINGS); + while (1) { + xcb_get_property_cookie_t get_prop_cookie = + xcb_get_property_unchecked(screen->xcb_connection(), + false, + x_settings_window, + _xsettings_atom, + _xsettings_atom, + offset/4, + 8192); + xcb_get_property_reply_t *reply = xcb_get_property_reply(screen->xcb_connection(), get_prop_cookie, NULL); + bool more = false; + if (!reply) + return settings; + + settings += QByteArray((const char *)xcb_get_property_value(reply), xcb_get_property_value_length(reply)); + offset += xcb_get_property_value_length(reply); + more = reply->bytes_after != 0; + + free(reply); + + if (!more) + break; + } + + return settings; + } + + static int round_to_nearest_multiple_of_4(int value) + { + int remainder = value % 4; + if (!remainder) + return value; + return value + 4 - remainder; + } + + void populateSettings(const QByteArray &xSettings) + { + if (xSettings.length() < 12) + return; + // we ignore byteorder for now + char byteOrder = xSettings.at(1); + Q_UNUSED(byteOrder); + uint serial = *reinterpret_cast<const uint *>(xSettings.mid(4,4).constData()); + serial = serial; + uint number_of_settings = *reinterpret_cast<const uint *>(xSettings.mid(8,4).constData()); + + const char *data = xSettings.constData() + 12; + size_t offset = 0; + for (uint i = 0; i < number_of_settings; i++) { + int local_offset = 0; + XSettingsType type = static_cast<XSettingsType>(*reinterpret_cast<const quint8 *>(data + offset)); + local_offset += 2; + + quint16 name_len = *reinterpret_cast<const quint16 *>(data + offset + local_offset); + local_offset += 2; + + QByteArray name(data + offset + local_offset, name_len); + local_offset += round_to_nearest_multiple_of_4(name_len); + + int last_change_serial = *reinterpret_cast<const int *>(data + offset + local_offset); + Q_UNUSED(last_change_serial); + local_offset += 4; + + QVariant value; + if (type == XSettingsTypeString) { + int value_length = *reinterpret_cast<const int *>(data + offset + local_offset); + local_offset+=4; + QByteArray value_string(data + offset + local_offset, value_length); + value.setValue(value_string); + local_offset += round_to_nearest_multiple_of_4(value_length); + } else if (type == XSettingsTypeInteger) { + int value_length = *reinterpret_cast<const int *>(data + offset + local_offset); + local_offset += 4; + value.setValue(value_length); + } else if (type == XSettingsTypeColor) { + quint16 red = *reinterpret_cast<const quint16 *>(data + offset + local_offset); + local_offset += 2; + quint16 green = *reinterpret_cast<const quint16 *>(data + offset + local_offset); + local_offset += 2; + quint16 blue = *reinterpret_cast<const quint16 *>(data + offset + local_offset); + local_offset += 2; + quint16 alpha= *reinterpret_cast<const quint16 *>(data + offset + local_offset); + local_offset += 2; + QColor color_value(red,green,blue,alpha); + value.setValue(color_value); + } + offset += local_offset; + settings[name].updateValue(screen,name,value,last_change_serial); + } + + } + + QXcbScreen *screen; + xcb_window_t x_settings_window; + int serial; + QMap<QByteArray, QXcbXSettingsPropertyValue> settings; +}; + + +QXcbXSettings::QXcbXSettings(QXcbScreen *screen) + : d_ptr(new QXcbXSettingsPrivate(screen)) +{ + QByteArray settings_atom_for_screen("_XSETTINGS_S"); + settings_atom_for_screen.append(QByteArray::number(screen->screenNumber())); + xcb_intern_atom_cookie_t atom_cookie = xcb_intern_atom(screen->xcb_connection(), + false, + settings_atom_for_screen.length(), + settings_atom_for_screen.constData()); + xcb_intern_atom_reply_t *atom_reply = xcb_intern_atom_reply(screen->xcb_connection(),atom_cookie,NULL); + xcb_atom_t selection_owner_atom = atom_reply->atom; + free(atom_reply); + + xcb_get_selection_owner_cookie_t selection_cookie = + xcb_get_selection_owner(screen->xcb_connection(), selection_owner_atom); + xcb_get_selection_owner_reply_t *selection_result = + xcb_get_selection_owner_reply(screen->xcb_connection(), selection_cookie, NULL); + + d_ptr->x_settings_window = selection_result->owner; + free(selection_result); + + const uint32_t event_mask[] = { XCB_EVENT_MASK_STRUCTURE_NOTIFY|XCB_EVENT_MASK_PROPERTY_CHANGE }; + xcb_change_window_attributes(screen->xcb_connection(),d_ptr->x_settings_window,XCB_CW_EVENT_MASK,event_mask); + + d_ptr->populateSettings(d_ptr->getSettings()); +} + +void QXcbXSettings::handlePropertyNotifyEvent(const xcb_property_notify_event_t *event) +{ + Q_D(QXcbXSettings); + if (event->window != d->x_settings_window) + return; + d->populateSettings(d->getSettings()); +} + +void QXcbXSettings::registerCallbackForProperty(const QByteArray &property, QXcbXSettings::PropertyChangeFunc func, void *handle) +{ + Q_D(QXcbXSettings); + d->settings[property].addCallback(func,handle); +} + +void QXcbXSettings::removeCallbackForHandle(const QByteArray &property, void *handle) +{ + Q_D(QXcbXSettings); + QXcbXSettingsPropertyValue &value = d->settings[property]; + QLinkedList<QXcbXSettingsCallback>::iterator it = value.callback_links.begin(); + while (it != value.callback_links.end()) { + if (it->handle == handle) + it = value.callback_links.erase(it); + else + ++it; + } +} + +void QXcbXSettings::removeCallbackForHandle(void *handle) +{ + Q_D(QXcbXSettings); + for (QMap<QByteArray, QXcbXSettingsPropertyValue>::const_iterator it = d->settings.cbegin(); + it != d->settings.cend(); ++it) { + removeCallbackForHandle(it.key(),handle); + } +} + +QVariant QXcbXSettings::setting(const QByteArray &property) const +{ + Q_D(const QXcbXSettings); + return d->settings.value(property).value; +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/xcb/qxcbxsettings.h b/src/plugins/platforms/xcb/qxcbxsettings.h new file mode 100644 index 0000000000..16fed862bc --- /dev/null +++ b/src/plugins/platforms/xcb/qxcbxsettings.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QXCBXSETTINGS_H +#define QXCBXSETTINGS_H + +#include "qxcbscreen.h" + +QT_BEGIN_NAMESPACE + +class QXcbXSettingsPrivate; + +class QXcbXSettings : public QXcbWindowEventListener +{ + Q_DECLARE_PRIVATE(QXcbXSettings) +public: + QXcbXSettings(QXcbScreen *screen); + + QVariant setting(const QByteArray &property) const; + + typedef void (*PropertyChangeFunc)(QXcbScreen *screen, const QByteArray &name, const QVariant &property, void *handle); + void registerCallbackForProperty(const QByteArray &property, PropertyChangeFunc func, void *handle); + void removeCallbackForHandle(const QByteArray &property, void *handle); + void removeCallbackForHandle(void *handle); + + void handlePropertyNotifyEvent(const xcb_property_notify_event_t *event) Q_DECL_OVERRIDE; +private: + QXcbXSettingsPrivate *d_ptr; +}; + +QT_END_NAMESPACE + +#endif // QXCBXSETTINGS_H diff --git a/src/plugins/platforms/xcb/xcb-plugin.pro b/src/plugins/platforms/xcb/xcb-plugin.pro index 5823e97f36..82995286c4 100644 --- a/src/plugins/platforms/xcb/xcb-plugin.pro +++ b/src/plugins/platforms/xcb/xcb-plugin.pro @@ -6,7 +6,6 @@ load(qt_plugin) QT += core-private gui-private platformsupport-private - SOURCES = \ qxcbclipboard.cpp \ qxcbconnection.cpp \ @@ -22,7 +21,7 @@ SOURCES = \ qxcbnativeinterface.cpp \ qxcbcursor.cpp \ qxcbimage.cpp \ - qxlibconvenience.cpp + qxcbxsettings.cpp HEADERS = \ qxcbclipboard.h \ @@ -39,11 +38,11 @@ HEADERS = \ qxcbnativeinterface.h \ qxcbcursor.h \ qxcbimage.h \ - qxlibconvenience.h + qxcbxsettings.h LIBS += -ldl -# needed by GLX, Xcursor, XLookupString, ... +# needed by GLX, Xcursor ... contains(QT_CONFIG, xcb-xlib) { DEFINES += XCB_USE_XLIB LIBS += -lX11 -lX11-xcb @@ -112,7 +111,23 @@ contains(QT_CONFIG, xcb-qt) { INCLUDEPATH += $$XCB_DIR/include $$XCB_DIR/sysinclude LIBS += -lxcb -L$$OUT_PWD/xcb-static -lxcb-static } else { - LIBS += -lxcb -lxcb-image -lxcb-keysyms -lxcb-icccm -lxcb-sync -lxcb-xfixes -lxcb-shm -lxcb-randr + LIBS += -lxcb -lxcb-image -lxcb-icccm -lxcb-sync -lxcb-xfixes -lxcb-shm -lxcb-randr !contains(DEFINES, QT_NO_SHAPE):LIBS += -lxcb-shape + contains(DEFINES, QT_NO_XKB) { + LIBS += -lxcb-keysyms + } else { + LIBS += -lxcb-xkb + } } +# libxkbcommon +contains(QT_CONFIG, xkbcommon-qt): { + include(../../../3rdparty/xkbcommon.pri) +} else { + LIBS += $$QMAKE_LIBS_XKBCOMMON + QMAKE_CXXFLAGS += $$QMAKE_CFLAGS_XKBCOMMON + equals(QMAKE_VERSION_XKBCOMMON, "0.2.0") { + DEFINES += XKBCOMMON_0_2_0 + INCLUDEPATH += ../../../3rdparty/xkbcommon/xkbcommon/ + } +} diff --git a/src/plugins/platforms/xcb/xcb-static/xcb-static.pro b/src/plugins/platforms/xcb/xcb-static/xcb-static.pro index 01667d41db..2fd5519053 100644 --- a/src/plugins/platforms/xcb/xcb-static/xcb-static.pro +++ b/src/plugins/platforms/xcb/xcb-static/xcb-static.pro @@ -24,7 +24,8 @@ SOURCES += \ $$LIBXCB_DIR/shm.c \ $$LIBXCB_DIR/sync.c \ $$LIBXCB_DIR/render.c \ - $$LIBXCB_DIR/shape.c + $$LIBXCB_DIR/shape.c \ + $$LIBXCB_DIR/xkb.c # # xcb-util |