summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/xcb
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/platforms/xcb')
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection.cpp164
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection.h26
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection_xi2.cpp14
-rw-r--r--src/plugins/platforms/xcb/qxcbcursor.cpp115
-rw-r--r--src/plugins/platforms/xcb/qxcbcursor.h7
-rw-r--r--src/plugins/platforms/xcb/qxcbdrag.cpp24
-rw-r--r--src/plugins/platforms/xcb/qxcbdrag.h2
-rw-r--r--src/plugins/platforms/xcb/qxcbintegration.cpp32
-rw-r--r--src/plugins/platforms/xcb/qxcbintegration.h2
-rw-r--r--src/plugins/platforms/xcb/qxcbkeyboard.cpp909
-rw-r--r--src/plugins/platforms/xcb/qxcbkeyboard.h89
-rw-r--r--src/plugins/platforms/xcb/qxcbnativeinterface.cpp4
-rw-r--r--src/plugins/platforms/xcb/qxcbscreen.cpp10
-rw-r--r--src/plugins/platforms/xcb/qxcbscreen.h5
-rw-r--r--src/plugins/platforms/xcb/qxcbxsettings.cpp280
-rw-r--r--src/plugins/platforms/xcb/qxcbxsettings.h71
-rw-r--r--src/plugins/platforms/xcb/xcb-plugin.pro25
-rw-r--r--src/plugins/platforms/xcb/xcb-static/xcb-static.pro3
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