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/gl_integrations/gl_integrations.pro2
-rw-r--r--src/plugins/platforms/xcb/gl_integrations/qxcbglintegration.h2
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection.cpp79
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection.h34
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection_xi2.cpp450
-rw-r--r--src/plugins/platforms/xcb/qxcbkeyboard.cpp31
-rw-r--r--src/plugins/platforms/xcb/qxcbkeyboard.h1
-rw-r--r--src/plugins/platforms/xcb/qxcbmain.cpp2
-rw-r--r--src/plugins/platforms/xcb/qxcbwindow.cpp126
-rw-r--r--src/plugins/platforms/xcb/qxcbwindow.h10
-rw-r--r--src/plugins/platforms/xcb/xcb_qpa_lib.pro5
11 files changed, 501 insertions, 241 deletions
diff --git a/src/plugins/platforms/xcb/gl_integrations/gl_integrations.pro b/src/plugins/platforms/xcb/gl_integrations/gl_integrations.pro
index 29b6d1d2f7..9de0476810 100644
--- a/src/plugins/platforms/xcb/gl_integrations/gl_integrations.pro
+++ b/src/plugins/platforms/xcb/gl_integrations/gl_integrations.pro
@@ -1,6 +1,6 @@
TEMPLATE = subdirs
-contains(QT_CONFIG, egl):contains(QT_CONFIG, egl_x11) {
+contains(QT_CONFIG, egl): contains(QT_CONFIG, egl_x11): contains(QT_CONFIG, opengl) {
SUBDIRS += xcb_egl
}
diff --git a/src/plugins/platforms/xcb/gl_integrations/qxcbglintegration.h b/src/plugins/platforms/xcb/gl_integrations/qxcbglintegration.h
index c71c668f31..74c117582a 100644
--- a/src/plugins/platforms/xcb/gl_integrations/qxcbglintegration.h
+++ b/src/plugins/platforms/xcb/gl_integrations/qxcbglintegration.h
@@ -57,7 +57,9 @@ public:
virtual bool handleXcbEvent(xcb_generic_event_t *event, uint responseType);
virtual QXcbWindow *createWindow(QWindow *window) const = 0;
+#ifndef QT_NO_OPENGL
virtual QPlatformOpenGLContext *createPlatformOpenGLContext(QOpenGLContext *context) const = 0;
+#endif
virtual QPlatformOffscreenSurface *createPlatformOffscreenSurface(QOffscreenSurface *surface) const = 0;
virtual QXcbNativeInterfaceHandler *nativeInterfaceHandler() const { return Q_NULLPTR; }
diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp
index 5d32eb7614..78282d1415 100644
--- a/src/plugins/platforms/xcb/qxcbconnection.cpp
+++ b/src/plugins/platforms/xcb/qxcbconnection.cpp
@@ -70,7 +70,6 @@
#endif
#if defined(XCB_USE_XINPUT2)
-#include <X11/extensions/XInput2.h>
#include <X11/extensions/XI2proto.h>
#endif
@@ -457,6 +456,7 @@ QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGra
, m_focusWindow(0)
, m_systemTrayTracker(0)
, m_glIntegration(Q_NULLPTR)
+ , m_xiGrab(false)
{
#ifdef XCB_USE_XLIB
Display *dpy = XOpenDisplay(m_displayName.constData());
@@ -909,7 +909,7 @@ static Qt::MouseButtons translateMouseButtons(int s)
return ret;
}
-static Qt::MouseButton translateMouseButton(xcb_button_t s)
+Qt::MouseButton QXcbConnection::translateMouseButton(xcb_button_t s)
{
switch (s) {
case 1: return Qt::LeftButton;
@@ -944,39 +944,6 @@ static Qt::MouseButton translateMouseButton(xcb_button_t s)
}
}
-void QXcbConnection::handleButtonPress(xcb_generic_event_t *ev)
-{
- xcb_button_press_event_t *event = (xcb_button_press_event_t *)ev;
-
- // the event explicitly contains the state of the three first buttons,
- // the rest we need to manage ourselves
- m_buttons = (m_buttons & ~0x7) | translateMouseButtons(event->state);
- m_buttons |= translateMouseButton(event->detail);
- qCDebug(lcQpaXInput, "xcb: pressed mouse button %d, button state %X", event->detail, static_cast<unsigned int>(m_buttons));
-}
-
-void QXcbConnection::handleButtonRelease(xcb_generic_event_t *ev)
-{
- xcb_button_release_event_t *event = (xcb_button_release_event_t *)ev;
-
- // the event explicitly contains the state of the three first buttons,
- // the rest we need to manage ourselves
- m_buttons = (m_buttons & ~0x7) | translateMouseButtons(event->state);
- m_buttons &= ~translateMouseButton(event->detail);
- qCDebug(lcQpaXInput, "xcb: released mouse button %d, button state %X", event->detail, static_cast<unsigned int>(m_buttons));
-}
-
-void QXcbConnection::handleMotionNotify(xcb_generic_event_t *ev)
-{
- xcb_motion_notify_event_t *event = (xcb_motion_notify_event_t *)ev;
-
- m_buttons = (m_buttons & ~0x7) | translateMouseButtons(event->state);
-#ifdef Q_XCB_DEBUG
- qCDebug(lcQpaXInput, "xcb: moved mouse to %4d, %4d; button state %X",
- event->event_x, event->event_y, static_cast<unsigned int>(m_buttons));
-#endif
-}
-
#ifndef QT_NO_XKB
namespace {
typedef union {
@@ -1018,18 +985,35 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
switch (response_type) {
case XCB_EXPOSE:
HANDLE_PLATFORM_WINDOW_EVENT(xcb_expose_event_t, window, handleExposeEvent);
- case XCB_BUTTON_PRESS:
- m_keyboard->updateXKBStateFromCore(((xcb_button_press_event_t *)event)->state);
- handleButtonPress(event);
+
+ // press/release/motion is only delivered here when XI 2.2+ is _not_ in use
+ case XCB_BUTTON_PRESS: {
+ xcb_button_press_event_t *ev = (xcb_button_press_event_t *)event;
+ m_keyboard->updateXKBStateFromCore(ev->state);
+ // the event explicitly contains the state of the three first buttons,
+ // the rest we need to manage ourselves
+ m_buttons = (m_buttons & ~0x7) | translateMouseButtons(ev->state);
+ m_buttons |= translateMouseButton(ev->detail);
+ qCDebug(lcQpaXInput, "legacy mouse press, button %d state %X", ev->detail, static_cast<unsigned int>(m_buttons));
HANDLE_PLATFORM_WINDOW_EVENT(xcb_button_press_event_t, event, handleButtonPressEvent);
- case XCB_BUTTON_RELEASE:
- m_keyboard->updateXKBStateFromCore(((xcb_button_release_event_t *)event)->state);
- handleButtonRelease(event);
+ }
+ case XCB_BUTTON_RELEASE: {
+ xcb_button_release_event_t *ev = (xcb_button_release_event_t *)event;
+ m_keyboard->updateXKBStateFromCore(ev->state);
+ m_buttons = (m_buttons & ~0x7) | translateMouseButtons(ev->state);
+ m_buttons &= ~translateMouseButton(ev->detail);
+ qCDebug(lcQpaXInput, "legacy mouse release, button %d state %X", ev->detail, static_cast<unsigned int>(m_buttons));
HANDLE_PLATFORM_WINDOW_EVENT(xcb_button_release_event_t, event, handleButtonReleaseEvent);
- case XCB_MOTION_NOTIFY:
- m_keyboard->updateXKBStateFromCore(((xcb_motion_notify_event_t *)event)->state);
- handleMotionNotify(event);
+ }
+ case XCB_MOTION_NOTIFY: {
+ xcb_motion_notify_event_t *ev = (xcb_motion_notify_event_t *)event;
+ m_keyboard->updateXKBStateFromCore(ev->state);
+ m_buttons = (m_buttons & ~0x7) | translateMouseButtons(ev->state);
+ qCDebug(lcQpaXInput, "legacy mouse move %d,%d button %d state %X", ev->event_x, ev->event_y,
+ ev->detail, static_cast<unsigned int>(m_buttons));
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);
case XCB_MAP_NOTIFY:
@@ -1090,6 +1074,7 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
break;
#if defined(XCB_USE_XINPUT2)
case XCB_GE_GENERIC:
+ // Here the windowEventListener is invoked from xi2HandleEvent()
if (m_xi2Enabled)
xi2HandleEvent(reinterpret_cast<xcb_ge_event_t *>(event));
break;
@@ -1931,6 +1916,12 @@ void QXcbConnection::initializeXKB()
#endif
}
+bool QXcbConnection::xi2MouseEvents() const
+{
+ static bool mouseViaXI2 = !qEnvironmentVariableIsSet("QT_XCB_NO_XI2_MOUSE");
+ return mouseViaXI2;
+}
+
#if defined(XCB_USE_XINPUT2)
static int xi2ValuatorOffset(unsigned char *maskPtr, int maskLen, int number)
{
diff --git a/src/plugins/platforms/xcb/qxcbconnection.h b/src/plugins/platforms/xcb/qxcbconnection.h
index 466492ce42..0191b0e1ef 100644
--- a/src/plugins/platforms/xcb/qxcbconnection.h
+++ b/src/plugins/platforms/xcb/qxcbconnection.h
@@ -69,7 +69,8 @@
#endif
#endif
struct XInput2TouchDeviceData;
-#endif
+#endif // XCB_USE_XINPUT2
+
struct xcb_randr_get_output_info_reply_t;
//#define Q_XCB_DEBUG
@@ -347,6 +348,7 @@ public:
virtual void handleFocusInEvent(const xcb_focus_in_event_t *) {}
virtual void handleFocusOutEvent(const xcb_focus_out_event_t *) {}
virtual void handlePropertyNotifyEvent(const xcb_property_notify_event_t *) {}
+ virtual void handleXIMouseEvent(xcb_ge_event_t *) {}
virtual QXcbWindow *toWindow() { return 0; }
};
@@ -413,14 +415,14 @@ public:
void xi2Select(xcb_window_t window);
#endif
#ifdef XCB_USE_XINPUT21
- bool isUsingXInput21() const { return m_xi2Enabled && m_xi2Minor >= 1; }
+ bool isAtLeastXI21() const { return m_xi2Enabled && m_xi2Minor >= 1; }
#else
- bool isUsingXInput21() const { return false; }
+ bool isAtLeastXI21() const { return false; }
#endif
#ifdef XCB_USE_XINPUT22
- bool isUsingXInput22() const { return m_xi2Enabled && m_xi2Minor >= 2; }
+ bool isAtLeastXI22() const { return m_xi2Enabled && m_xi2Minor >= 2; }
#else
- bool isUsingXInput22() const { return false; }
+ bool isAtLeastXI22() const { return false; }
#endif
void sync();
@@ -457,7 +459,9 @@ public:
xcb_timestamp_t getTimestamp();
+ void setButton(Qt::MouseButton button, bool down) { if (down) m_buttons |= button; else m_buttons &= ~button; }
Qt::MouseButtons buttons() const { return m_buttons; }
+ Qt::MouseButton translateMouseButton(xcb_button_t s);
QXcbWindow *focusWindow() const { return m_focusWindow; }
void setFocusWindow(QXcbWindow *);
@@ -479,12 +483,19 @@ public:
void handleEnterEvent(const xcb_enter_notify_event_t *);
#endif
+#ifdef XCB_USE_XINPUT22
+ bool xi2SetMouseGrabEnabled(xcb_window_t w, bool grab);
+#endif
+ Qt::MouseButton xiToQtMouseButton(uint32_t b);
+
QXcbEventReader *eventReader() const { return m_reader; }
bool canGrab() const { return m_canGrabServer; }
QXcbGlIntegration *glIntegration() const { return m_glIntegration; }
+ bool xi2MouseEvents() const;
+
protected:
bool event(QEvent *e) Q_DECL_OVERRIDE;
@@ -512,9 +523,6 @@ private:
bool checkOutputIsPrimary(xcb_window_t rootWindow, xcb_randr_output_t output);
void initializeScreens();
void updateScreens(const xcb_randr_notify_event_t *event);
- void handleButtonPress(xcb_generic_event_t *event);
- void handleButtonRelease(xcb_generic_event_t *event);
- void handleMotionNotify(xcb_generic_event_t *event);
bool m_xi2Enabled;
int m_xi2Minor;
@@ -527,6 +535,9 @@ private:
void xi2HandleHierachyEvent(void *event);
void xi2HandleDeviceChangedEvent(void *event);
int m_xiOpCode, m_xiEventBase, m_xiErrorBase;
+#ifdef XCB_USE_XINPUT22
+ void xi2ProcessTouch(void *xiDevEvent, QXcbWindow *platformWindow);
+#endif // XCB_USE_XINPUT22
#ifndef QT_NO_TABLETEVENT
struct TabletData {
TabletData() : deviceId(0), pointerType(QTabletEvent::UnknownPointer),
@@ -546,10 +557,10 @@ private:
};
QHash<int, ValuatorClassInfo> valuatorInfo;
};
- bool xi2HandleTabletEvent(void *event, TabletData *tabletData);
+ bool xi2HandleTabletEvent(void *event, TabletData *tabletData, QXcbWindowEventListener *eventListener);
void xi2ReportTabletEvent(TabletData &tabletData, void *event);
QVector<TabletData> m_tabletData;
-#endif
+#endif // !QT_NO_TABLETEVENT
struct ScrollingDevice {
ScrollingDevice() : deviceId(0), verticalIndex(0), horizontalIndex(0), orientations(0), legacyOrientations(0) { }
int deviceId;
@@ -562,9 +573,7 @@ private:
void updateScrollingDevice(ScrollingDevice& scrollingDevice, int num_classes, void *classes);
void xi2HandleScrollEvent(void *event, ScrollingDevice &scrollingDevice);
QHash<int, ScrollingDevice> m_scrollingDevices;
-#endif // XCB_USE_XINPUT2
-#if defined(XCB_USE_XINPUT2)
static bool xi2GetValuatorValueIfSet(void *event, int valuatorNum, double *value);
static bool xi2PrepareXIGenericDeviceEvent(xcb_ge_event_t *event, int opCode);
#endif
@@ -636,6 +645,7 @@ private:
QByteArray m_startupId;
QXcbSystemTrayTracker *m_systemTrayTracker;
QXcbGlIntegration *m_glIntegration;
+ bool m_xiGrab;
friend class QXcbEventReader;
};
diff --git a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp
index d1b3ead11c..c7784ddb48 100644
--- a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp
+++ b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp
@@ -43,7 +43,6 @@
#include <X11/extensions/XInput2.h>
#include <X11/extensions/XI2proto.h>
-#define FINGER_MAX_WIDTH_MM 10
struct XInput2TouchDeviceData {
XInput2TouchDeviceData()
@@ -175,9 +174,11 @@ void QXcbConnection::xi2SetupDevices()
case XIKeyClass:
qCDebug(lcQpaXInputDevices) << " it's a keyboard";
break;
+#ifdef XCB_USE_XINPUT22
case XITouchClass:
// will be handled in deviceForId()
break;
+#endif
default:
qCDebug(lcQpaXInputDevices) << " has class" << devices[i].classes[c]->type;
break;
@@ -274,34 +275,37 @@ void QXcbConnection::xi2Select(xcb_window_t window)
unsigned char *xiBitMask = reinterpret_cast<unsigned char *>(&bitMask);
#ifdef XCB_USE_XINPUT22
- if (isUsingXInput22()) {
+ if (isAtLeastXI22()) {
bitMask |= XI_TouchBeginMask;
bitMask |= XI_TouchUpdateMask;
bitMask |= XI_TouchEndMask;
+ bitMask |= XI_PropertyEventMask; // for tablets
+ if (xi2MouseEvents()) {
+ // We want both mouse and touch through XI2 if touch is supported (>= 2.2).
+ // The plain xcb press and motion events will not be delivered after this.
+ bitMask |= XI_ButtonPressMask;
+ bitMask |= XI_ButtonReleaseMask;
+ bitMask |= XI_MotionMask;
+ qCDebug(lcQpaXInput, "XInput 2.2: Selecting press/release/motion events in addition to touch");
+ }
XIEventMask mask;
mask.mask_len = sizeof(bitMask);
mask.mask = xiBitMask;
- if (!m_touchDevices.isEmpty()) {
- // If we select for touch events on the master pointer, XInput2
- // will not synthesize mouse events. This means Qt must do it,
- // which is also preferable, since Qt can control better when
- // to do so.
- mask.deviceid = XIAllMasterDevices;
- Status result = XISelectEvents(xDisplay, window, &mask, 1);
- if (result != Success)
- qCDebug(lcQpaXInput, "XInput 2.2: failed to select touch events, window %x, result %d", window, result);
- }
+ // When xi2MouseEvents() is true (the default), pointer emulation for touch and tablet
+ // events will get disabled. This is preferable for touch, as Qt Quick handles touch events
+ // directly while for others QtGui synthesizes mouse events, not so much for tablets. For
+ // the latter we will synthesize the events ourselves.
+ mask.deviceid = XIAllMasterDevices;
+ Status result = XISelectEvents(xDisplay, window, &mask, 1);
+ if (result != Success)
+ qCDebug(lcQpaXInput, "XInput 2.2: failed to select pointer/touch events, window %x, result %d", window, result);
}
#endif // XCB_USE_XINPUT22
+ const bool pointerSelected = isAtLeastXI22() && xi2MouseEvents();
QSet<int> tabletDevices;
#ifndef QT_NO_TABLETEVENT
- // For each tablet, select some additional event types.
- // Press, motion, etc. events must never be selected for _all_ devices
- // as that would render the standard XCB_MOTION_NOTIFY and
- // similar handlers useless and we have no intention to infect
- // all the pure xcb code with Xlib-based XI2.
- if (!m_tabletData.isEmpty()) {
+ if (!m_tabletData.isEmpty() && !pointerSelected) {
unsigned int tabletBitMask;
unsigned char *xiTabletBitMask = reinterpret_cast<unsigned char *>(&tabletBitMask);
QVector<XIEventMask> xiEventMask(m_tabletData.count());
@@ -322,7 +326,8 @@ void QXcbConnection::xi2Select(xcb_window_t window)
#ifdef XCB_USE_XINPUT21
// Enable each scroll device
- if (!m_scrollingDevices.isEmpty()) {
+ if (!m_scrollingDevices.isEmpty() && !pointerSelected) {
+ // Only when XI2 mouse events are not enabled, otherwise motion and release are selected already.
QVector<XIEventMask> xiEventMask(m_scrollingDevices.size());
unsigned int scrollBitMask;
unsigned char *xiScrollBitMask = reinterpret_cast<unsigned char *>(&scrollBitMask);
@@ -359,8 +364,8 @@ void QXcbConnection::xi2Select(xcb_window_t window)
XInput2TouchDeviceData *QXcbConnection::touchDeviceForId(int id)
{
XInput2TouchDeviceData *dev = Q_NULLPTR;
- QHash<int, XInput2TouchDeviceData*>::const_iterator devIt = m_touchDevices.find(id);
- if ( devIt != m_touchDevices.end() ) {
+ QHash<int, XInput2TouchDeviceData*>::const_iterator devIt = m_touchDevices.constFind(id);
+ if (devIt != m_touchDevices.cend()) {
dev = devIt.value();
} else {
int nrDevices = 0;
@@ -424,7 +429,7 @@ XInput2TouchDeviceData *QXcbConnection::touchDeviceForId(int id)
dev->size.width() > 10000 || dev->size.height() > 10000)
dev->size = QSizeF(130, 110);
}
- if (!isUsingXInput22() || type == QTouchDevice::TouchPad)
+ if (!isAtLeastXI22() || type == QTouchDevice::TouchPad)
caps |= QTouchDevice::MouseEmulation;
if (type >= QTouchDevice::TouchScreen && type <= QTouchDevice::TouchPad) {
@@ -446,9 +451,9 @@ XInput2TouchDeviceData *QXcbConnection::touchDeviceForId(int id)
}
#if defined(XCB_USE_XINPUT21) || !defined(QT_NO_TABLETEVENT)
-static qreal fixed1616ToReal(FP1616 val)
+static inline qreal fixed1616ToReal(FP1616 val)
{
- return (qreal(val >> 16)) + (val & 0xFFFF) / (qreal)0xFFFF;
+ return qreal(val) / 0x10000;
}
#endif // defined(XCB_USE_XINPUT21) || !defined(QT_NO_TABLETEVENT)
@@ -467,163 +472,280 @@ void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event)
{
if (xi2PrepareXIGenericDeviceEvent(event, m_xiOpCode)) {
xXIGenericDeviceEvent *xiEvent = reinterpret_cast<xXIGenericDeviceEvent *>(event);
+ int sourceDeviceId = xiEvent->deviceid; // may be the master id
+ xXIDeviceEvent *xiDeviceEvent = 0;
+ QXcbWindowEventListener *eventListener = 0;
- if (xiEvent->evtype == XI_HierarchyChanged) {
+ switch (xiEvent->evtype) {
+ case XI_ButtonPress:
+ case XI_ButtonRelease:
+ case XI_Motion:
+ case XI_TouchBegin:
+ case XI_TouchUpdate:
+ case XI_TouchEnd:
+ {
+ xiDeviceEvent = reinterpret_cast<xXIDeviceEvent *>(event);
+ eventListener = windowEventListenerFromId(xiDeviceEvent->event);
+ if (eventListener) {
+ long result = 0;
+ if (eventListener->handleGenericEvent(reinterpret_cast<xcb_generic_event_t *>(event), &result))
+ return;
+ }
+ sourceDeviceId = xiDeviceEvent->sourceid; // use the actual device id instead of the master
+ break;
+ }
+ case XI_HierarchyChanged:
xi2HandleHierachyEvent(xiEvent);
return;
- }
- if (xiEvent->evtype == XI_DeviceChanged) {
+ case XI_DeviceChanged:
xi2HandleDeviceChangedEvent(xiEvent);
return;
+ default:
+ break;
}
#ifndef QT_NO_TABLETEVENT
for (int i = 0; i < m_tabletData.count(); ++i) {
- if (m_tabletData.at(i).deviceId == xiEvent->deviceid) {
- if (xi2HandleTabletEvent(xiEvent, &m_tabletData[i]))
+ if (m_tabletData.at(i).deviceId == sourceDeviceId) {
+ if (xi2HandleTabletEvent(xiEvent, &m_tabletData[i], eventListener))
return;
}
}
#endif // QT_NO_TABLETEVENT
#ifdef XCB_USE_XINPUT21
- QHash<int, ScrollingDevice>::iterator device = m_scrollingDevices.find(xiEvent->deviceid);
+ QHash<int, ScrollingDevice>::iterator device = m_scrollingDevices.find(sourceDeviceId);
if (device != m_scrollingDevices.end())
xi2HandleScrollEvent(xiEvent, device.value());
#endif // XCB_USE_XINPUT21
#ifdef XCB_USE_XINPUT22
- if (xiEvent->evtype == XI_TouchBegin || xiEvent->evtype == XI_TouchUpdate || xiEvent->evtype == XI_TouchEnd) {
- xXIDeviceEvent* xiDeviceEvent = reinterpret_cast<xXIDeviceEvent *>(event);
- if (Q_UNLIKELY(lcQpaXInput().isDebugEnabled()))
- qCDebug(lcQpaXInput, "XI2 touch event type %d seq %d detail %d pos %6.1f, %6.1f root pos %6.1f, %6.1f",
- event->event_type, xiEvent->sequenceNumber, xiDeviceEvent->detail,
- fixed1616ToReal(xiDeviceEvent->event_x), fixed1616ToReal(xiDeviceEvent->event_y),
- fixed1616ToReal(xiDeviceEvent->root_x), fixed1616ToReal(xiDeviceEvent->root_y) );
-
- if (QXcbWindow *platformWindow = platformWindowFromId(xiDeviceEvent->event)) {
- XInput2TouchDeviceData *dev = touchDeviceForId(xiDeviceEvent->sourceid);
- Q_ASSERT(dev);
- const bool firstTouch = m_touchPoints.isEmpty();
- if (xiEvent->evtype == XI_TouchBegin) {
- QWindowSystemInterface::TouchPoint tp;
- tp.id = xiDeviceEvent->detail % INT_MAX;
- tp.state = Qt::TouchPointPressed;
- tp.pressure = -1.0;
- m_touchPoints[tp.id] = tp;
- }
- QWindowSystemInterface::TouchPoint &touchPoint = m_touchPoints[xiDeviceEvent->detail];
- qreal x = fixed1616ToReal(xiDeviceEvent->root_x);
- qreal y = fixed1616ToReal(xiDeviceEvent->root_y);
- qreal nx = -1.0, ny = -1.0, w = 0.0, h = 0.0;
- QXcbScreen* screen = m_screens.at(0);
- for (int i = 0; i < dev->xiDeviceInfo->num_classes; ++i) {
- XIAnyClassInfo *classinfo = dev->xiDeviceInfo->classes[i];
- if (classinfo->type == XIValuatorClass) {
- XIValuatorClassInfo *vci = reinterpret_cast<XIValuatorClassInfo *>(classinfo);
- int n = vci->number;
- double value;
- if (!xi2GetValuatorValueIfSet(xiDeviceEvent, n, &value))
- continue;
- if (Q_UNLIKELY(lcQpaXInput().isDebugEnabled()))
- qCDebug(lcQpaXInput, " valuator %20s value %lf from range %lf -> %lf",
- atomName(vci->label).constData(), value, vci->min, vci->max );
- if (vci->label == atom(QXcbAtom::RelX)) {
- nx = valuatorNormalized(value, vci);
- } else if (vci->label == atom(QXcbAtom::RelY)) {
- ny = valuatorNormalized(value, vci);
- } else if (vci->label == atom(QXcbAtom::AbsX)) {
- nx = valuatorNormalized(value, vci);
- } else if (vci->label == atom(QXcbAtom::AbsY)) {
- ny = valuatorNormalized(value, vci);
- } else if (vci->label == atom(QXcbAtom::AbsMTPositionX)) {
- nx = valuatorNormalized(value, vci);
- } else if (vci->label == atom(QXcbAtom::AbsMTPositionY)) {
- ny = valuatorNormalized(value, vci);
- } else if (vci->label == atom(QXcbAtom::AbsMTTouchMajor)) {
- // Convert the value within its range as a fraction of a finger's max (contact patch)
- // width in mm, and from there to pixels depending on screen resolution
- w = valuatorNormalized(value, vci) * FINGER_MAX_WIDTH_MM *
- screen->geometry().width() / screen->physicalSize().width();
- } else if (vci->label == atom(QXcbAtom::AbsMTTouchMinor)) {
- h = valuatorNormalized(value, vci) * FINGER_MAX_WIDTH_MM *
- screen->geometry().height() / screen->physicalSize().height();
- } else if (vci->label == atom(QXcbAtom::AbsMTPressure) ||
- vci->label == atom(QXcbAtom::AbsPressure)) {
- touchPoint.pressure = valuatorNormalized(value, vci);
- }
- }
- }
- // If any value was not updated, use the last-known value.
- if (nx == -1.0) {
- x = touchPoint.area.center().x();
- nx = x / screen->geometry().width();
- }
- if (ny == -1.0) {
- y = touchPoint.area.center().y();
- ny = y / screen->geometry().height();
- }
- if (xiEvent->evtype != XI_TouchEnd) {
- if (w == 0.0)
- w = touchPoint.area.width();
- if (h == 0.0)
- h = touchPoint.area.height();
- }
-
- switch (xiEvent->evtype) {
- case XI_TouchBegin:
- if (firstTouch) {
- dev->firstPressedPosition = QPointF(x, y);
- dev->firstPressedNormalPosition = QPointF(nx, ny);
- }
- dev->pointPressedPosition.insert(touchPoint.id, QPointF(x, y));
- break;
- case XI_TouchUpdate:
- if (dev->qtTouchDevice->type() == QTouchDevice::TouchPad && dev->pointPressedPosition.value(touchPoint.id) == QPointF(x, y)) {
- qreal dx = (nx - dev->firstPressedNormalPosition.x()) *
- dev->size.width() * screen->geometry().width() / screen->physicalSize().width();
- qreal dy = (ny - dev->firstPressedNormalPosition.y()) *
- dev->size.height() * screen->geometry().height() / screen->physicalSize().height();
- x = dev->firstPressedPosition.x() + dx;
- y = dev->firstPressedPosition.y() + dy;
- touchPoint.state = Qt::TouchPointMoved;
- } else if (touchPoint.area.center() != QPoint(x, y)) {
- touchPoint.state = Qt::TouchPointMoved;
- dev->pointPressedPosition[touchPoint.id] = QPointF(x, y);
- }
- break;
- case XI_TouchEnd:
- touchPoint.state = Qt::TouchPointReleased;
- if (dev->qtTouchDevice->type() == QTouchDevice::TouchPad && dev->pointPressedPosition.value(touchPoint.id) == QPointF(x, y)) {
- qreal dx = (nx - dev->firstPressedNormalPosition.x()) *
- dev->size.width() * screen->geometry().width() / screen->physicalSize().width();
- qreal dy = (ny - dev->firstPressedNormalPosition.y()) *
- dev->size.width() * screen->geometry().width() / screen->physicalSize().width();
- x = dev->firstPressedPosition.x() + dx;
- y = dev->firstPressedPosition.y() + dy;
- }
- dev->pointPressedPosition.remove(touchPoint.id);
- }
- touchPoint.area = QRectF(x - w/2, y - h/2, w, h);
- touchPoint.normalPosition = QPointF(nx, ny);
+ if (xiDeviceEvent) {
+ switch (xiDeviceEvent->evtype) {
+ case XI_ButtonPress:
+ case XI_ButtonRelease:
+ case XI_Motion:
+ if (xi2MouseEvents() && eventListener)
+ eventListener->handleXIMouseEvent(event);
+ break;
+ case XI_TouchBegin:
+ case XI_TouchUpdate:
+ case XI_TouchEnd:
if (Q_UNLIKELY(lcQpaXInput().isDebugEnabled()))
- qCDebug(lcQpaXInput) << " touchpoint " << touchPoint.id << " state " << touchPoint.state << " pos norm " << touchPoint.normalPosition <<
- " area " << touchPoint.area << " pressure " << touchPoint.pressure;
- QWindowSystemInterface::handleTouchEvent(platformWindow->window(), xiEvent->time, dev->qtTouchDevice, m_touchPoints.values());
- if (touchPoint.state == Qt::TouchPointReleased)
- // If a touchpoint was released, we can forget it, because the ID won't be reused.
- m_touchPoints.remove(touchPoint.id);
- else
- // Make sure that we don't send TouchPointPressed/Moved in more than one QTouchEvent
- // with this touch point if the next XI2 event is about a different touch point.
- touchPoint.state = Qt::TouchPointStationary;
+ qCDebug(lcQpaXInput, "XI2 touch event type %d seq %d detail %d pos %6.1f, %6.1f root pos %6.1f, %6.1f on window %x",
+ event->event_type, xiDeviceEvent->sequenceNumber, xiDeviceEvent->detail,
+ fixed1616ToReal(xiDeviceEvent->event_x), fixed1616ToReal(xiDeviceEvent->event_y),
+ fixed1616ToReal(xiDeviceEvent->root_x), fixed1616ToReal(xiDeviceEvent->root_y),xiDeviceEvent->event);
+ if (QXcbWindow *platformWindow = platformWindowFromId(xiDeviceEvent->event))
+ xi2ProcessTouch(xiDeviceEvent, platformWindow);
+ break;
}
}
#endif // XCB_USE_XINPUT22
}
}
+#ifdef XCB_USE_XINPUT22
+void QXcbConnection::xi2ProcessTouch(void *xiDevEvent, QXcbWindow *platformWindow)
+{
+ xXIDeviceEvent *xiDeviceEvent = static_cast<xXIDeviceEvent *>(xiDevEvent);
+ XInput2TouchDeviceData *dev = touchDeviceForId(xiDeviceEvent->sourceid);
+ Q_ASSERT(dev);
+ const bool firstTouch = m_touchPoints.isEmpty();
+ if (xiDeviceEvent->evtype == XI_TouchBegin) {
+ QWindowSystemInterface::TouchPoint tp;
+ tp.id = xiDeviceEvent->detail % INT_MAX;
+ tp.state = Qt::TouchPointPressed;
+ tp.pressure = -1.0;
+ m_touchPoints[tp.id] = tp;
+ }
+ QWindowSystemInterface::TouchPoint &touchPoint = m_touchPoints[xiDeviceEvent->detail];
+ qreal x = fixed1616ToReal(xiDeviceEvent->root_x);
+ qreal y = fixed1616ToReal(xiDeviceEvent->root_y);
+ qreal nx = -1.0, ny = -1.0, d = 0.0;
+ QXcbScreen* screen = m_screens.at(0);
+ for (int i = 0; i < dev->xiDeviceInfo->num_classes; ++i) {
+ XIAnyClassInfo *classinfo = dev->xiDeviceInfo->classes[i];
+ if (classinfo->type == XIValuatorClass) {
+ XIValuatorClassInfo *vci = reinterpret_cast<XIValuatorClassInfo *>(classinfo);
+ int n = vci->number;
+ double value;
+ if (!xi2GetValuatorValueIfSet(xiDeviceEvent, n, &value))
+ continue;
+ if (Q_UNLIKELY(lcQpaXInput().isDebugEnabled()))
+ qCDebug(lcQpaXInput, " valuator %20s value %lf from range %lf -> %lf",
+ atomName(vci->label).constData(), value, vci->min, vci->max );
+ if (vci->label == atom(QXcbAtom::RelX)) {
+ nx = valuatorNormalized(value, vci);
+ } else if (vci->label == atom(QXcbAtom::RelY)) {
+ ny = valuatorNormalized(value, vci);
+ } else if (vci->label == atom(QXcbAtom::AbsX)) {
+ nx = valuatorNormalized(value, vci);
+ } else if (vci->label == atom(QXcbAtom::AbsY)) {
+ ny = valuatorNormalized(value, vci);
+ } else if (vci->label == atom(QXcbAtom::AbsMTPositionX)) {
+ nx = valuatorNormalized(value, vci);
+ } else if (vci->label == atom(QXcbAtom::AbsMTPositionY)) {
+ ny = valuatorNormalized(value, vci);
+ } else if (vci->label == atom(QXcbAtom::AbsMTTouchMajor)) {
+ d = valuatorNormalized(value, vci) * screen->geometry().width();
+ } else if (vci->label == atom(QXcbAtom::AbsMTPressure) ||
+ vci->label == atom(QXcbAtom::AbsPressure)) {
+ touchPoint.pressure = valuatorNormalized(value, vci);
+ }
+ }
+ }
+ // If any value was not updated, use the last-known value.
+ if (nx == -1.0) {
+ x = touchPoint.area.center().x();
+ nx = x / screen->geometry().width();
+ }
+ if (ny == -1.0) {
+ y = touchPoint.area.center().y();
+ ny = y / screen->geometry().height();
+ }
+ if (xiDeviceEvent->evtype != XI_TouchEnd) {
+ if (d == 0.0)
+ d = touchPoint.area.width();
+ }
+
+ switch (xiDeviceEvent->evtype) {
+ case XI_TouchBegin:
+ if (firstTouch) {
+ dev->firstPressedPosition = QPointF(x, y);
+ dev->firstPressedNormalPosition = QPointF(nx, ny);
+ }
+ dev->pointPressedPosition.insert(touchPoint.id, QPointF(x, y));
+
+ // Touches must be accepted when we are grabbing touch events. Otherwise the entire sequence
+ // will get replayed when the grab ends.
+ if (m_xiGrab) {
+ // XIAllowTouchEvents deadlocks with libXi < 1.7.4 (this has nothing to do with the XI2 versions like 2.2)
+ // http://lists.x.org/archives/xorg-devel/2014-July/043059.html
+#ifndef LIBXI_MAJOR
+ static bool allowTouchWarningShown = false;
+ if (!allowTouchWarningShown) {
+ allowTouchWarningShown = true;
+ qWarning("Skipping XIAllowTouchEvents() because it was not possible to detect libXi version at build time."
+ " Minimum libXi version required is 1.7.4."
+ " Expect issues with touch behavior.");
+ }
+#elif LIBXI_MAJOR == 1 && (LIBXI_MINOR < 7 || (LIBXI_MINOR == 7 && LIBXI_PATCH < 4))
+ static bool allowTouchWarningShown = false;
+ if (!allowTouchWarningShown) {
+ allowTouchWarningShown = true;
+ qWarning("Skipping XIAllowTouchEvents() due to not having libXi >= 1.7.4."
+ " libXi version at build time was %d.%d.%d."
+ " Expect issues with touch behavior.", LIBXI_MAJOR, LIBXI_MINOR, LIBXI_PATCH);
+ }
+#else
+ XIAllowTouchEvents(static_cast<Display *>(m_xlib_display), xiDeviceEvent->deviceid,
+ xiDeviceEvent->detail, xiDeviceEvent->event, XIAcceptTouch);
+#endif
+ }
+ break;
+ case XI_TouchUpdate:
+ if (dev->qtTouchDevice->type() == QTouchDevice::TouchPad && dev->pointPressedPosition.value(touchPoint.id) == QPointF(x, y)) {
+ qreal dx = (nx - dev->firstPressedNormalPosition.x()) *
+ dev->size.width() * screen->geometry().width() / screen->physicalSize().width();
+ qreal dy = (ny - dev->firstPressedNormalPosition.y()) *
+ dev->size.height() * screen->geometry().height() / screen->physicalSize().height();
+ x = dev->firstPressedPosition.x() + dx;
+ y = dev->firstPressedPosition.y() + dy;
+ touchPoint.state = Qt::TouchPointMoved;
+ } else if (touchPoint.area.center() != QPoint(x, y)) {
+ touchPoint.state = Qt::TouchPointMoved;
+ dev->pointPressedPosition[touchPoint.id] = QPointF(x, y);
+ }
+ break;
+ case XI_TouchEnd:
+ touchPoint.state = Qt::TouchPointReleased;
+ if (dev->qtTouchDevice->type() == QTouchDevice::TouchPad && dev->pointPressedPosition.value(touchPoint.id) == QPointF(x, y)) {
+ qreal dx = (nx - dev->firstPressedNormalPosition.x()) *
+ dev->size.width() * screen->geometry().width() / screen->physicalSize().width();
+ qreal dy = (ny - dev->firstPressedNormalPosition.y()) *
+ dev->size.width() * screen->geometry().width() / screen->physicalSize().width();
+ x = dev->firstPressedPosition.x() + dx;
+ y = dev->firstPressedPosition.y() + dy;
+ }
+ dev->pointPressedPosition.remove(touchPoint.id);
+ }
+ touchPoint.area = QRectF(x - d/2, y - d/2, d, d);
+ touchPoint.normalPosition = QPointF(nx, ny);
+
+ if (Q_UNLIKELY(lcQpaXInput().isDebugEnabled()))
+ qCDebug(lcQpaXInput) << " touchpoint " << touchPoint.id << " state " << touchPoint.state << " pos norm " << touchPoint.normalPosition <<
+ " area " << touchPoint.area << " pressure " << touchPoint.pressure;
+ QWindowSystemInterface::handleTouchEvent(platformWindow->window(), xiDeviceEvent->time, dev->qtTouchDevice, m_touchPoints.values());
+ if (touchPoint.state == Qt::TouchPointReleased)
+ // If a touchpoint was released, we can forget it, because the ID won't be reused.
+ m_touchPoints.remove(touchPoint.id);
+ else
+ // Make sure that we don't send TouchPointPressed/Moved in more than one QTouchEvent
+ // with this touch point if the next XI2 event is about a different touch point.
+ touchPoint.state = Qt::TouchPointStationary;
+}
+
+bool QXcbConnection::xi2SetMouseGrabEnabled(xcb_window_t w, bool grab)
+{
+ if (grab && !canGrab())
+ return false;
+
+ int num_devices = 0;
+ Display *xDisplay = static_cast<Display *>(xlib_display());
+ XIDeviceInfo *info = XIQueryDevice(xDisplay, XIAllMasterDevices, &num_devices);
+ if (!info)
+ return false;
+
+ XIEventMask evmask;
+ unsigned char mask[XIMaskLen(XI_LASTEVENT)];
+ evmask.mask = mask;
+ evmask.mask_len = sizeof(mask);
+ memset(mask, 0, sizeof(mask));
+ evmask.deviceid = XIAllMasterDevices;
+
+ XISetMask(mask, XI_ButtonPress);
+ XISetMask(mask, XI_ButtonRelease);
+ XISetMask(mask, XI_Motion);
+ XISetMask(mask, XI_TouchBegin);
+ XISetMask(mask, XI_TouchUpdate);
+ XISetMask(mask, XI_TouchEnd);
+
+ bool grabbed = true;
+ for (int i = 0; i < num_devices; i++) {
+ int id = info[i].deviceid, n = 0;
+ XIDeviceInfo *deviceInfo = XIQueryDevice(xDisplay, id, &n);
+ if (deviceInfo) {
+ const bool grabbable = deviceInfo->use != XIMasterKeyboard;
+ XIFreeDeviceInfo(deviceInfo);
+ if (!grabbable)
+ continue;
+ }
+ if (!grab) {
+ Status result = XIUngrabDevice(xDisplay, id, CurrentTime);
+ if (result != Success) {
+ grabbed = false;
+ qCDebug(lcQpaXInput, "XInput 2.2: failed to ungrab events for device %d (result %d)", id, result);
+ }
+ } else {
+ Status result = XIGrabDevice(xDisplay, id, w, CurrentTime, None, XIGrabModeAsync,
+ XIGrabModeAsync, False, &evmask);
+ if (result != Success) {
+ grabbed = false;
+ qCDebug(lcQpaXInput, "XInput 2.2: failed to grab events for device %d on window %x (result %d)", id, w, result);
+ }
+ }
+ }
+
+ XIFreeDeviceInfo(info);
+
+ m_xiGrab = grabbed;
+
+ return grabbed;
+}
+#endif // XCB_USE_XINPUT22
+
void QXcbConnection::xi2HandleHierachyEvent(void *event)
{
xXIHierarchyEvent *xiEvent = reinterpret_cast<xXIHierarchyEvent *>(event);
@@ -792,7 +914,8 @@ void QXcbConnection::xi2HandleScrollEvent(void *event, ScrollingDevice &scrollin
#endif // XCB_USE_XINPUT21
}
-static Qt::MouseButton xiToQtMouseButton(uint32_t b) {
+Qt::MouseButton QXcbConnection::xiToQtMouseButton(uint32_t b)
+{
switch (b) {
case 1: return Qt::LeftButton;
case 2: return Qt::MiddleButton;
@@ -839,20 +962,29 @@ static QTabletEvent::TabletDevice toolIdToTabletDevice(quint32 toolId) {
}
#ifndef QT_NO_TABLETEVENT
-bool QXcbConnection::xi2HandleTabletEvent(void *event, TabletData *tabletData)
+bool QXcbConnection::xi2HandleTabletEvent(void *event, TabletData *tabletData, QXcbWindowEventListener *eventListener)
{
bool handled = true;
Display *xDisplay = static_cast<Display *>(m_xlib_display);
xXIGenericDeviceEvent *xiEvent = static_cast<xXIGenericDeviceEvent *>(event);
+ xXIDeviceEvent *xiDeviceEvent = reinterpret_cast<xXIDeviceEvent *>(xiEvent);
+
+#ifdef XCB_USE_XINPUT22
+ // Synthesize mouse events since otherwise there are no mouse events from
+ // the pen on the XI 2.2+ path.
+ if (xi2MouseEvents() && eventListener)
+ eventListener->handleXIMouseEvent(reinterpret_cast<xcb_ge_event_t *>(event));
+#endif
+
switch (xiEvent->evtype) {
case XI_ButtonPress: {
- Qt::MouseButton b = xiToQtMouseButton(reinterpret_cast<xXIDeviceEvent *>(event)->detail);
+ Qt::MouseButton b = xiToQtMouseButton(xiDeviceEvent->detail);
tabletData->buttons |= b;
xi2ReportTabletEvent(*tabletData, xiEvent);
break;
}
case XI_ButtonRelease: {
- Qt::MouseButton b = xiToQtMouseButton(reinterpret_cast<xXIDeviceEvent *>(event)->detail);
+ Qt::MouseButton b = xiToQtMouseButton(xiDeviceEvent->detail);
tabletData->buttons ^= b;
xi2ReportTabletEvent(*tabletData, xiEvent);
break;
@@ -915,7 +1047,7 @@ bool QXcbConnection::xi2HandleTabletEvent(void *event, TabletData *tabletData)
// TODO maybe have a hash of tabletData->deviceId to device data so we can
// look up the tablet name here, and distinguish multiple tablets
qCDebug(lcQpaXInput, "XI2 proximity change on tablet %d (USB %x): last tool: %x id %x current tool: %x id %x TabletDevice %d",
- ev->deviceid, ptr[_WACSER_USB_ID], ptr[_WACSER_LAST_TOOL_SERIAL], ptr[_WACSER_LAST_TOOL_ID],
+ tabletData->deviceId, ptr[_WACSER_USB_ID], ptr[_WACSER_LAST_TOOL_SERIAL], ptr[_WACSER_LAST_TOOL_ID],
ptr[_WACSER_TOOL_SERIAL], ptr[_WACSER_TOOL_ID], tabletData->tool);
}
XFree(data);
@@ -979,7 +1111,7 @@ void QXcbConnection::xi2ReportTabletEvent(TabletData &tabletData, void *event)
if (Q_UNLIKELY(lcQpaXInput().isDebugEnabled()))
qCDebug(lcQpaXInput, "XI2 event on tablet %d with tool %d type %d seq %d detail %d pos %6.1f, %6.1f root pos %6.1f, %6.1f buttons 0x%x pressure %4.2lf tilt %d, %d rotation %6.2lf",
- ev->deviceid, tabletData.tool, ev->evtype, ev->sequenceNumber, ev->detail,
+ tabletData.deviceId, tabletData.tool, ev->evtype, ev->sequenceNumber, ev->detail,
fixed1616ToReal(ev->event_x), fixed1616ToReal(ev->event_y),
fixed1616ToReal(ev->root_x), fixed1616ToReal(ev->root_y),
(int)tabletData.buttons, pressure, xTilt, yTilt, rotation);
diff --git a/src/plugins/platforms/xcb/qxcbkeyboard.cpp b/src/plugins/platforms/xcb/qxcbkeyboard.cpp
index 5a7c2cef83..f3bffd2998 100644
--- a/src/plugins/platforms/xcb/qxcbkeyboard.cpp
+++ b/src/plugins/platforms/xcb/qxcbkeyboard.cpp
@@ -47,6 +47,12 @@
#include <stdio.h>
#include <X11/keysym.h>
+#ifdef XCB_USE_XINPUT22
+#include <X11/extensions/XI2proto.h>
+#undef KeyPress
+#undef KeyRelease
+#endif
+
#ifndef XK_ISO_Left_Tab
#define XK_ISO_Left_Tab 0xFE20
#endif
@@ -791,6 +797,31 @@ void QXcbKeyboard::updateXKBStateFromCore(quint16 state)
}
}
+void QXcbKeyboard::updateXKBStateFromXI(void *modInfo, void *groupInfo)
+{
+#ifdef XCB_USE_XINPUT22
+ if (m_config && !connection()->hasXKB()) {
+ xXIModifierInfo *mods = static_cast<xXIModifierInfo *>(modInfo);
+ xXIGroupInfo *group = static_cast<xXIGroupInfo *>(groupInfo);
+ const xkb_state_component newState = xkb_state_update_mask(xkb_state,
+ mods->base_mods,
+ mods->latched_mods,
+ mods->locked_mods,
+ group->base_group,
+ group->latched_group,
+ group->locked_group);
+
+ if ((newState & XKB_STATE_LAYOUT_EFFECTIVE) == XKB_STATE_LAYOUT_EFFECTIVE) {
+ //qWarning("TODO: Support KeyboardLayoutChange on QPA (QTBUG-27681)");
+ }
+ }
+#else
+ Q_UNUSED(modInfo);
+ Q_UNUSED(groupInfo);
+ Q_ASSERT(false); // this can't be
+#endif
+}
+
quint32 QXcbKeyboard::xkbModMask(quint16 state)
{
quint32 xkb_mask = 0;
diff --git a/src/plugins/platforms/xcb/qxcbkeyboard.h b/src/plugins/platforms/xcb/qxcbkeyboard.h
index 2281674e2f..d2e37d624c 100644
--- a/src/plugins/platforms/xcb/qxcbkeyboard.h
+++ b/src/plugins/platforms/xcb/qxcbkeyboard.h
@@ -68,6 +68,7 @@ public:
void updateXKBMods();
quint32 xkbModMask(quint16 state);
void updateXKBStateFromCore(quint16 state);
+ void updateXKBStateFromXI(void *modInfo, void *groupInfo);
#ifndef QT_NO_XKB
// when XKEYBOARD is present on the X server
int coreDeviceId() const { return core_device_id; }
diff --git a/src/plugins/platforms/xcb/qxcbmain.cpp b/src/plugins/platforms/xcb/qxcbmain.cpp
index 0c3e8b5cd3..4a07b66491 100644
--- a/src/plugins/platforms/xcb/qxcbmain.cpp
+++ b/src/plugins/platforms/xcb/qxcbmain.cpp
@@ -39,7 +39,7 @@ QT_BEGIN_NAMESPACE
class QXcbIntegrationPlugin : public QPlatformIntegrationPlugin
{
Q_OBJECT
- Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QPA.QPlatformIntegrationFactoryInterface.5.2" FILE "xcb.json")
+ Q_PLUGIN_METADATA(IID QPlatformIntegrationFactoryInterface_iid FILE "xcb.json")
public:
QPlatformIntegration *create(const QString&, const QStringList&, int &, char **) Q_DECL_OVERRIDE;
};
diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp
index 076cc1aa80..d2da137591 100644
--- a/src/plugins/platforms/xcb/qxcbwindow.cpp
+++ b/src/plugins/platforms/xcb/qxcbwindow.cpp
@@ -97,6 +97,7 @@
#if defined(XCB_USE_XINPUT2)
#include <X11/extensions/XInput2.h>
+#include <X11/extensions/XI2proto.h>
#endif
#define XCOORD_MAX 16383
@@ -2167,16 +2168,17 @@ void QXcbWindow::handleUnmapNotifyEvent(const xcb_unmap_notify_event_t *event)
}
}
-void QXcbWindow::handleButtonPressEvent(const xcb_button_press_event_t *event)
+void QXcbWindow::handleButtonPressEvent(int event_x, int event_y, int root_x, int root_y,
+ int detail, Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp)
{
- const bool isWheel = event->detail >= 4 && event->detail <= 7;
+ const bool isWheel = detail >= 4 && detail <= 7;
if (!isWheel && window() != QGuiApplication::focusWindow()) {
QWindow *w = static_cast<QWindowPrivate *>(QObjectPrivate::get(window()))->eventReceiver();
if (!(w->flags() & Qt::WindowDoesNotAcceptFocus))
w->requestActivate();
}
- updateNetWmUserTime(event->time);
+ updateNetWmUserTime(timestamp);
if (m_embedded) {
if (window() != QGuiApplication::focusWindow()) {
@@ -2187,53 +2189,125 @@ void QXcbWindow::handleButtonPressEvent(const xcb_button_press_event_t *event)
}
}
const int dpr = int(devicePixelRatio());
- QPoint local(event->event_x/dpr, event->event_y/dpr);
- QPoint global = xcbScreen()->mapFromNative(QPoint(event->root_x, event->root_y));
-
- Qt::KeyboardModifiers modifiers = connection()->keyboard()->translateModifiers(event->state);
+ QPoint local(event_x / dpr, event_y / dpr);
+ QPoint global = xcbScreen()->mapFromNative(QPoint(root_x, root_y));
if (isWheel) {
- if (!connection()->isUsingXInput21()) {
+ if (!connection()->isAtLeastXI21()) {
// Logic borrowed from qapplication_x11.cpp
- int delta = 120 * ((event->detail == 4 || event->detail == 6) ? 1 : -1);
- bool hor = (((event->detail == 4 || event->detail == 5)
+ int delta = 120 * ((detail == 4 || detail == 6) ? 1 : -1);
+ bool hor = (((detail == 4 || detail == 5)
&& (modifiers & Qt::AltModifier))
- || (event->detail == 6 || event->detail == 7));
+ || (detail == 6 || detail == 7));
- QWindowSystemInterface::handleWheelEvent(window(), event->time,
+ QWindowSystemInterface::handleWheelEvent(window(), timestamp,
local, global, delta, hor ? Qt::Horizontal : Qt::Vertical, modifiers);
}
return;
}
- handleMouseEvent(event->time, local, global, modifiers);
+ handleMouseEvent(timestamp, local, global, modifiers);
}
-void QXcbWindow::handleButtonReleaseEvent(const xcb_button_release_event_t *event)
+void QXcbWindow::handleButtonReleaseEvent(int event_x, int event_y, int root_x, int root_y,
+ int detail, Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp)
{
const int dpr = int(devicePixelRatio());
- QPoint local(event->event_x/dpr, event->event_y/dpr);
- QPoint global = xcbScreen()->mapFromNative(QPoint(event->root_x, event->root_y));
- Qt::KeyboardModifiers modifiers = connection()->keyboard()->translateModifiers(event->state);
+ QPoint local(event_x / dpr, event_y / dpr);
+ QPoint global = xcbScreen()->mapFromNative(QPoint(root_x, root_y));
- if (event->detail >= 4 && event->detail <= 7) {
+ if (detail >= 4 && detail <= 7) {
// mouse wheel, handled in handleButtonPressEvent()
return;
}
- handleMouseEvent(event->time, local, global, modifiers);
+ handleMouseEvent(timestamp, local, global, modifiers);
}
-void QXcbWindow::handleMotionNotifyEvent(const xcb_motion_notify_event_t *event)
+void QXcbWindow::handleMotionNotifyEvent(int event_x, int event_y, int root_x, int root_y,
+ Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp)
{
- const int dpr = int(devicePixelRatio());
- QPoint local(event->event_x/dpr, event->event_y/dpr);
if (!xcbScreen())
return;
- QPoint global = xcbScreen()->mapFromNative(QPoint(event->root_x, event->root_y));
+ const int dpr = int(devicePixelRatio());
+ QPoint local(event_x / dpr, event_y / dpr);
+ QPoint global = xcbScreen()->mapFromNative(QPoint(root_x, root_y));
+ handleMouseEvent(timestamp, local, global, modifiers);
+}
+
+// Handlers for plain xcb events. Used only when XI 2.2 or newer is not available.
+void QXcbWindow::handleButtonPressEvent(const xcb_button_press_event_t *event)
+{
Qt::KeyboardModifiers modifiers = connection()->keyboard()->translateModifiers(event->state);
+ handleButtonPressEvent(event->event_x, event->event_y, event->root_x, event->root_y, event->detail,
+ modifiers, event->time);
+}
- handleMouseEvent(event->time, local, global, modifiers);
+void QXcbWindow::handleButtonReleaseEvent(const xcb_button_release_event_t *event)
+{
+ Qt::KeyboardModifiers modifiers = connection()->keyboard()->translateModifiers(event->state);
+ handleButtonReleaseEvent(event->event_x, event->event_y, event->root_x, event->root_y, event->detail,
+ modifiers, event->time);
+}
+
+void QXcbWindow::handleMotionNotifyEvent(const xcb_motion_notify_event_t *event)
+{
+ Qt::KeyboardModifiers modifiers = connection()->keyboard()->translateModifiers(event->state);
+ handleMotionNotifyEvent(event->event_x, event->event_y, event->root_x, event->root_y, modifiers, event->time);
+}
+
+#ifdef XCB_USE_XINPUT22
+static inline int fixed1616ToInt(FP1616 val)
+{
+ return int((qreal(val >> 16)) + (val & 0xFFFF) / (qreal)0xFFFF);
+}
+#endif
+
+// With XI 2.2+ press/release/motion comes here instead of the above handlers.
+void QXcbWindow::handleXIMouseEvent(xcb_ge_event_t *event)
+{
+#ifdef XCB_USE_XINPUT22
+ QXcbConnection *conn = connection();
+ xXIDeviceEvent *ev = reinterpret_cast<xXIDeviceEvent *>(event);
+ const Qt::KeyboardModifiers modifiers = conn->keyboard()->translateModifiers(ev->mods.effective_mods);
+ const int event_x = fixed1616ToInt(ev->event_x);
+ const int event_y = fixed1616ToInt(ev->event_y);
+ const int root_x = fixed1616ToInt(ev->root_x);
+ const int root_y = fixed1616ToInt(ev->root_y);
+
+ conn->keyboard()->updateXKBStateFromXI(&ev->mods, &ev->group);
+
+ const Qt::MouseButton button = conn->xiToQtMouseButton(ev->detail);
+
+ if (ev->buttons_len > 0) {
+ unsigned char *buttonMask = (unsigned char *) &ev[1];
+ for (int i = 1; i <= 15; ++i)
+ conn->setButton(conn->translateMouseButton(i), XIMaskIsSet(buttonMask, i));
+ }
+
+ switch (ev->evtype) {
+ case XI_ButtonPress:
+ qCDebug(lcQpaXInput, "XI2 mouse press, button %d", button);
+ conn->setButton(button, true);
+ handleButtonPressEvent(event_x, event_y, root_x, root_y, ev->detail, modifiers, ev->time);
+ break;
+ case XI_ButtonRelease:
+ qCDebug(lcQpaXInput, "XI2 mouse release, button %d", button);
+ conn->setButton(button, false);
+ handleButtonReleaseEvent(event_x, event_y, root_x, root_y, ev->detail, modifiers, ev->time);
+ break;
+ case XI_Motion:
+ qCDebug(lcQpaXInput, "XI2 mouse motion %d,%d", event_x, event_y);
+ handleMotionNotifyEvent(event_x, event_y, root_x, root_y, modifiers, ev->time);
+ break;
+ default:
+ qWarning() << "Unrecognized XI2 mouse event" << ev->evtype;
+ break;
+ }
+#else
+ Q_UNUSED(event);
+ Q_ASSERT(false); // this can't be
+#endif
}
QXcbWindow *QXcbWindow::toWindow() { return this; }
@@ -2417,6 +2491,10 @@ bool QXcbWindow::setKeyboardGrabEnabled(bool grab)
bool QXcbWindow::setMouseGrabEnabled(bool grab)
{
+#ifdef XCB_USE_XINPUT22
+ if (connection()->xi2MouseEvents())
+ return connection()->xi2SetMouseGrabEnabled(m_window, grab);
+#endif
if (grab && !connection()->canGrab())
return false;
diff --git a/src/plugins/platforms/xcb/qxcbwindow.h b/src/plugins/platforms/xcb/qxcbwindow.h
index 52e8ac1459..07019223c8 100644
--- a/src/plugins/platforms/xcb/qxcbwindow.h
+++ b/src/plugins/platforms/xcb/qxcbwindow.h
@@ -132,6 +132,7 @@ public:
void handleFocusInEvent(const xcb_focus_in_event_t *event) Q_DECL_OVERRIDE;
void handleFocusOutEvent(const xcb_focus_out_event_t *event) Q_DECL_OVERRIDE;
void handlePropertyNotifyEvent(const xcb_property_notify_event_t *event) Q_DECL_OVERRIDE;
+ void handleXIMouseEvent(xcb_ge_event_t *) Q_DECL_OVERRIDE;
QXcbWindow *toWindow() Q_DECL_OVERRIDE;
@@ -210,6 +211,15 @@ protected:
void doFocusIn();
void doFocusOut();
+ void handleButtonPressEvent(int event_x, int event_y, int root_x, int root_y,
+ int detail, Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp);
+
+ void handleButtonReleaseEvent(int event_x, int event_y, int root_x, int root_y,
+ int detail, Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp);
+
+ void handleMotionNotifyEvent(int event_x, int event_y, int root_x, int root_y,
+ Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp);
+
xcb_window_t m_window;
QXcbScreen *m_xcbScreen;
diff --git a/src/plugins/platforms/xcb/xcb_qpa_lib.pro b/src/plugins/platforms/xcb/xcb_qpa_lib.pro
index fd704dd904..12987567ff 100644
--- a/src/plugins/platforms/xcb/xcb_qpa_lib.pro
+++ b/src/plugins/platforms/xcb/xcb_qpa_lib.pro
@@ -59,6 +59,11 @@ contains(QT_CONFIG, xcb-xlib) {
DEFINES += XCB_USE_XINPUT2
SOURCES += qxcbconnection_xi2.cpp
LIBS += -lXi
+ !isEmpty(QMAKE_LIBXI_VERSION_MAJOR) {
+ DEFINES += LIBXI_MAJOR=$$QMAKE_LIBXI_VERSION_MAJOR \
+ LIBXI_MINOR=$$QMAKE_LIBXI_VERSION_MINOR \
+ LIBXI_PATCH=$$QMAKE_LIBXI_VERSION_PATCH
+ }
}
}