@@ -16,14 +16,56 @@
16 * Authored by: Daniel d'Andrada <daniel.dandrada@canonical.com>
16 * Authored by: Daniel d'Andrada <daniel.dandrada@canonical.com>
20+/* Some parts of the code were copied from the XCB platform plugin of the Qt Toolkit,
21+ * which is under the following license:
24+/****************************************************************************
26+** Copyright (C) 2015 The Qt Company Ltd.
27+** Contact: http://www.qt.io/licensing/
29+** This file is part of the .
31+** $QT_BEGIN_LICENSE:LGPL21$
32+** Commercial License Usage
33+** Licensees holding valid commercial Qt licenses may use this file in
34+** accordance with the commercial license agreement provided with the
35+** Software or, alternatively, in accordance with the terms contained in
36+** a written agreement between you and The Qt Company. For licensing terms
37+** and conditions see http://www.qt.io/terms-conditions. For further
38+** information use the contact form at http://www.qt.io/contact-us.
40+** GNU Lesser General Public License Usage
41+** Alternatively, this file may be used under the terms of the GNU Lesser
42+** General Public License version 2.1 or version 3 as published by the Free
43+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
44+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
45+** following information to ensure the GNU Lesser General Public License
46+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
47+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
49+** As a special exception, The Qt Company gives you certain additional
50+** rights. These rights are described in The Qt Company LGPL Exception
51+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
55+****************************************************************************/
19#include "MouseTouchAdaptor.h"
57#include "MouseTouchAdaptor.h"
59+#include <qpa/qplatformnativeinterface.h>
21#include <qpa/qwindowsysteminterface.h>
60#include <qpa/qwindowsysteminterface.h>
23#include <QCoreApplication>
62#include <QCoreApplication>
24#include <QMouseEvent>
63#include <QMouseEvent>
25#include <QTest>
64#include <QTest>
66+#include <X11/extensions/XInput2.h>
67+#include <X11/extensions/XI2proto.h>
27using QTest::QTouchEventSequence;
69using QTest::QTouchEventSequence;
29namespace {
71namespace {
@@ -49,6 +91,8 @@ MouseTouchAdaptor::MouseTouchAdaptor()
49 m_touchDevice = new QTouchDevice;
91 m_touchDevice = new QTouchDevice;
50 m_touchDevice->setType(QTouchDevice::TouchScreen);
92 m_touchDevice->setType(QTouchDevice::TouchScreen);
51 QWindowSystemInterface::registerTouchDevice(m_touchDevice);
93 QWindowSystemInterface::registerTouchDevice(m_touchDevice);
54MouseTouchAdaptor::~MouseTouchAdaptor()
98MouseTouchAdaptor::~MouseTouchAdaptor()
@@ -65,9 +109,114 @@ MouseTouchAdaptor* MouseTouchAdaptor::instance()
65 return g_instance;
109 return g_instance;
112+void MouseTouchAdaptor::fetchXInput2Info()
114+ QPlatformNativeInterface *nativeInterface = qGuiApp->platformNativeInterface();
115+ Display *xDisplay = static_cast<Display*>(nativeInterface->nativeResourceForIntegration("Display"));
116+ if (xDisplay && XQueryExtension(xDisplay, "XInputExtension", &m_xiOpCode, &m_xiEventBase, &m_xiErrorBase)) {
118+ m_xi2Minor = 2; // try 2.2 first, needed for TouchBegin/Update/End
119+ if (XIQueryVersion(xDisplay, &xiMajor, &m_xi2Minor) == BadRequest) {
120+ m_xi2Minor = 1; // for smooth scrolling 2.1 is enough
121+ if (XIQueryVersion(xDisplay, &xiMajor, &m_xi2Minor) == BadRequest) {
122+ m_xi2Minor = 0; // for tablet support 2.0 is enough
123+ m_xi2Enabled = XIQueryVersion(xDisplay, &xiMajor, &m_xi2Minor) != BadRequest;
125+ m_xi2Enabled = true;
127+ m_xi2Enabled = true;
132+// Starting from the xcb version 1.9.3 struct xcb_ge_event_t has changed:
133+// - "pad0" became "extension"
134+// - "pad1" and "pad" became "pad0"
135+// New and old version of this struct share the following fields:
136+// NOTE: API might change again in the next release of xcb in which case this comment will
137+// need to be updated to reflect the reality.
138+typedef struct qt_xcb_ge_event_t {
139+ uint8_t response_type;
143+ uint16_t event_type;
146+bool xi2PrepareXIGenericDeviceEvent(xcb_ge_event_t *ev, int opCode)
148+ qt_xcb_ge_event_t *event = (qt_xcb_ge_event_t *)ev;
149+ // xGenericEvent has "extension" on the second byte, the same is true for xcb_ge_event_t starting from
150+ // the xcb version 1.9.3, prior to that it was called "pad0".
151+ if (event->extension == opCode) {
152+ // xcb event structs contain stuff that wasn't on the wire, the full_sequence field
153+ // adds an extra 4 bytes and generic events cookie data is on the wire right after the standard 32 bytes.
154+ // Move this data back to have the same layout in memory as it was on the wire
155+ // and allow casting, overwriting the full_sequence field.
156+ memmove((char*) event + 32, (char*) event + 36, event->length * 4);
162+static inline qreal fixed1616ToReal(FP1616 val)
164+ return qreal(val) / 0x10000;
167+bool MouseTouchAdaptor::xi2HandleEvent(xcb_ge_event_t *event)
169+ if (!xi2PrepareXIGenericDeviceEvent(event, m_xiOpCode)) {
173+ xXIGenericDeviceEvent *xiEvent = reinterpret_cast<xXIGenericDeviceEvent *>(event);
174+ xXIDeviceEvent *xiDeviceEvent = 0;
176+ switch (xiEvent->evtype) {
177+ case XI_ButtonPress:
178+ case XI_ButtonRelease:
180+ xiDeviceEvent = reinterpret_cast<xXIDeviceEvent *>(event);
186+ if (!xiDeviceEvent) {
190+ switch (xiDeviceEvent->evtype) {
191+ case XI_ButtonPress:
192+ return handleButtonPress(
193+ static_cast<WId>(xiDeviceEvent->event),
194+ xiDeviceEvent->detail,
195+ fixed1616ToReal(xiDeviceEvent->event_x),
196+ fixed1616ToReal(xiDeviceEvent->event_y));
197+ case XI_ButtonRelease:
198+ return handleButtonRelease(
199+ static_cast<WId>(xiDeviceEvent->event),
200+ xiDeviceEvent->detail,
201+ fixed1616ToReal(xiDeviceEvent->event_x),
202+ fixed1616ToReal(xiDeviceEvent->event_y));
204+ return handleMotionNotify(
205+ static_cast<WId>(xiDeviceEvent->event),
206+ fixed1616ToReal(xiDeviceEvent->event_x),
207+ fixed1616ToReal(xiDeviceEvent->event_y));
68bool MouseTouchAdaptor::nativeEventFilter(const QByteArray & eventType,
215bool MouseTouchAdaptor::nativeEventFilter(const QByteArray & eventType,
69 void * message, long * /*result*/)
216 void * message, long * /*result*/)
218+ static int eventCount = 0;
71 if (!m_enabled) {
220 if (!m_enabled) {
72 return false;
221 return false;
@@ -81,32 +230,42 @@ bool MouseTouchAdaptor::nativeEventFilter(const QByteArray & eventType,
81 xcb_generic_event_t *xcbEvent = static_cast<xcb_generic_event_t *>(message);
230 xcb_generic_event_t *xcbEvent = static_cast<xcb_generic_event_t *>(message);
83 switch (xcbEvent->response_type & ~0x80) {
232 switch (xcbEvent->response_type & ~0x80) {
84− case XCB_BUTTON_PRESS:
233+ case XCB_BUTTON_PRESS: {
85− return handleButtonPress(reinterpret_cast<xcb_button_press_event_t *>(xcbEvent));
234+ auto pressEvent = reinterpret_cast<xcb_button_press_event_t *>(xcbEvent);
86− break;
235+ return handleButtonPress(static_cast<WId>(pressEvent->event), pressEvent->detail,
236+ pressEvent->event_x, pressEvent->event_y);
87− case XCB_BUTTON_RELEASE:
238+ case XCB_BUTTON_RELEASE: {
88− return handleButtonRelease(reinterpret_cast<xcb_button_release_event_t *>(xcbEvent));
239+ auto releaseEvent = reinterpret_cast<xcb_button_release_event_t *>(xcbEvent);
89− break;
240+ return handleButtonRelease(static_cast<WId>(releaseEvent->event), releaseEvent->detail,
241+ releaseEvent->event_x, releaseEvent->event_y);
90− case XCB_MOTION_NOTIFY:
243+ case XCB_MOTION_NOTIFY: {
91− return handleMotionNotify(reinterpret_cast<xcb_motion_notify_event_t *>(xcbEvent));
244+ auto motionEvent = reinterpret_cast<xcb_motion_notify_event_t *>(xcbEvent);
92− break;
245+ return handleMotionNotify(static_cast<WId>(motionEvent->event), motionEvent->event_x, motionEvent->event_y);
247+ case XCB_GE_GENERIC:
249+ return xi2HandleEvent(reinterpret_cast<xcb_ge_event_t *>(xcbEvent));
94 return false;
254 return false;
99−bool MouseTouchAdaptor::handleButtonPress(xcb_button_press_event_t *pressEvent)
258+bool MouseTouchAdaptor::handleButtonPress(WId windowId, uint32_t detail, int x, int y)
101− Qt::MouseButton button = translateMouseButton(pressEvent->detail);
260+ Qt::MouseButton button = translateMouseButton(detail);
103 // Just eat the event if it wasn't a left mouse press
262 // Just eat the event if it wasn't a left mouse press
104 if (button != Qt::LeftButton)
263 if (button != Qt::LeftButton)
105 return true;
264 return true;
107− QWindow *targetWindow = findQWindowWithXWindowID(static_cast<WId>(pressEvent->event));
266+ QWindow *targetWindow = findQWindowWithXWindowID(windowId);
109− QPoint windowPos(pressEvent->event_x / targetWindow->devicePixelRatio(), pressEvent->event_y / targetWindow->devicePixelRatio());
268+ QPoint windowPos(x / targetWindow->devicePixelRatio(), y / targetWindow->devicePixelRatio());
111 QTouchEventSequence touchEvent = QTest::touchEvent(targetWindow, m_touchDevice,
270 QTouchEventSequence touchEvent = QTest::touchEvent(targetWindow, m_touchDevice,
112 false /* autoCommit */);
271 false /* autoCommit */);
@@ -117,17 +276,17 @@ bool MouseTouchAdaptor::handleButtonPress(xcb_button_press_event_t *pressEvent)
117 return true;
276 return true;
120−bool MouseTouchAdaptor::handleButtonRelease(xcb_button_release_event_t *releaseEvent)
279+bool MouseTouchAdaptor::handleButtonRelease(WId windowId, uint32_t detail, int x, int y)
122− Qt::MouseButton button = translateMouseButton(releaseEvent->detail);
281+ Qt::MouseButton button = translateMouseButton(detail);
124 // Just eat the event if it wasn't a left mouse release
283 // Just eat the event if it wasn't a left mouse release
125 if (button != Qt::LeftButton)
284 if (button != Qt::LeftButton)
126 return true;
285 return true;
128− QWindow *targetWindow = findQWindowWithXWindowID(static_cast<WId>(releaseEvent->event));
287+ QWindow *targetWindow = findQWindowWithXWindowID(windowId);
130− QPoint windowPos(releaseEvent->event_x / targetWindow->devicePixelRatio(), releaseEvent->event_y / targetWindow->devicePixelRatio());
289+ QPoint windowPos(x / targetWindow->devicePixelRatio(), y / targetWindow->devicePixelRatio());
132 QTouchEventSequence touchEvent = QTest::touchEvent(targetWindow, m_touchDevice,
291 QTouchEventSequence touchEvent = QTest::touchEvent(targetWindow, m_touchDevice,
133 false /* autoCommit */);
292 false /* autoCommit */);
@@ -138,15 +297,15 @@ bool MouseTouchAdaptor::handleButtonRelease(xcb_button_release_event_t *releaseE
138 return true;
297 return true;
141−bool MouseTouchAdaptor::handleMotionNotify(xcb_motion_notify_event_t *event)
300+bool MouseTouchAdaptor::handleMotionNotify(WId windowId, int x, int y)
143 if (!m_leftButtonIsPressed) {
302 if (!m_leftButtonIsPressed) {
144 return true;
303 return true;
147− QWindow *targetWindow = findQWindowWithXWindowID(static_cast<WId>(event->event));
306+ QWindow *targetWindow = findQWindowWithXWindowID(windowId);
149− QPoint windowPos(event->event_x / targetWindow->devicePixelRatio(), event->event_y / targetWindow->devicePixelRatio());
308+ QPoint windowPos(x / targetWindow->devicePixelRatio(), y / targetWindow->devicePixelRatio());
151 QTouchEventSequence touchEvent = QTest::touchEvent(targetWindow, m_touchDevice,
310 QTouchEventSequence touchEvent = QTest::touchEvent(targetWindow, m_touchDevice,
152 false /* autoCommit */);
311 false /* autoCommit */);