on xenial help sign in
Update MouseTouchAdaptor to XInput2, as per Qt 5.5
commitb71c30f47dc0b15380ab4a66836297db6a4d5f27 authorDaniel d'Andrada <daniel.dandrada@canonical.com> dateTue 15 Dec 2015, 17:55 (11 years ago) parente9f921b
5 files changed+198−25
debian/control+4 src/CMakeLists.txt+1−1 src/MouseTouchAdaptor.cpp+180−21 src/MouseTouchAdaptor.h+10−3 tests/utils/modules/Unity/Test/CMakeLists.txt+3
debian/control+4
@@ -31,7 +31,11 @@ Build-Depends: cmake,
3131 libudev-dev,
3232 libunity-api-dev (>= 7.104),
3333 libusermetricsoutput1-dev,
34+# Need those X11 libs touch emulation from mouse events in manual QML tests on a X11 desktop
3435 libxcb1-dev,
36+ libx11-dev
37+ libxi-dev,
38+# End of X11 libs
3539 pkg-config,
3640 python3-all:any,
3741 python3-setuptools,
src/CMakeLists.txt+1−1
@@ -30,7 +30,7 @@ add_executable(${SHELL_APP}
3030
3131qt5_use_modules(${SHELL_APP} Gui Qml Quick Test)
3232
33−pkg_check_modules(NEEDED_LIBS REQUIRED xcb libandroid-properties)
33+pkg_check_modules(NEEDED_LIBS REQUIRED x11 xcb xi libandroid-properties)
3434if (NOT "${NEEDED_LIBS_INCLUDE_DIRS}" STREQUAL "")
3535 set_target_properties(${SHELL_APP} PROPERTIES INCLUDE_DIRECTORIES ${NEEDED_LIBS_INCLUDE_DIRS})
3636endif()
src/MouseTouchAdaptor.cpp+180−21
@@ -16,14 +16,56 @@
1616 * Authored by: Daniel d'Andrada <daniel.dandrada@canonical.com>
1717 */
1818
19+
20+/* Some parts of the code were copied from the XCB platform plugin of the Qt Toolkit,
21+ * which is under the following license:
22+ */
23+
24+/****************************************************************************
25+**
26+** Copyright (C) 2015 The Qt Company Ltd.
27+** Contact: http://www.qt.io/licensing/
28+**
29+** This file is part of the .
30+**
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.
39+**
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.
48+**
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.
52+**
53+** $QT_END_LICENSE$
54+**
55+****************************************************************************/
56+
1957#include "MouseTouchAdaptor.h"
2058
59+#include <qpa/qplatformnativeinterface.h>
2160#include <qpa/qwindowsysteminterface.h>
2261
2362#include <QCoreApplication>
2463#include <QMouseEvent>
2564#include <QTest>
2665
66+#include <X11/extensions/XInput2.h>
67+#include <X11/extensions/XI2proto.h>
68+
2769using QTest::QTouchEventSequence;
2870
2971namespace {
@@ -49,6 +91,8 @@ MouseTouchAdaptor::MouseTouchAdaptor()
4991 m_touchDevice = new QTouchDevice;
5092 m_touchDevice->setType(QTouchDevice::TouchScreen);
5193 QWindowSystemInterface::registerTouchDevice(m_touchDevice);
94+
95+ fetchXInput2Info();
5296}
5397
5498MouseTouchAdaptor::~MouseTouchAdaptor()
@@ -65,9 +109,114 @@ MouseTouchAdaptor* MouseTouchAdaptor::instance()
65109 return g_instance;
66110}
67111
112+void MouseTouchAdaptor::fetchXInput2Info()
113+{
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)) {
117+ int xiMajor = 2;
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;
124+ } else
125+ m_xi2Enabled = true;
126+ } else {
127+ m_xi2Enabled = true;
128+ }
129+ }
130+}
131+
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;
140+ uint8_t extension;
141+ uint16_t sequence;
142+ uint32_t length;
143+ uint16_t event_type;
144+} qt_xcb_ge_event_t;
145+
146+bool xi2PrepareXIGenericDeviceEvent(xcb_ge_event_t *ev, int opCode)
147+{
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);
157+ return true;
158+ }
159+ return false;
160+}
161+
162+static inline qreal fixed1616ToReal(FP1616 val)
163+{
164+ return qreal(val) / 0x10000;
165+}
166+
167+bool MouseTouchAdaptor::xi2HandleEvent(xcb_ge_event_t *event)
168+{
169+ if (!xi2PrepareXIGenericDeviceEvent(event, m_xiOpCode)) {
170+ return false;
171+ }
172+
173+ xXIGenericDeviceEvent *xiEvent = reinterpret_cast<xXIGenericDeviceEvent *>(event);
174+ xXIDeviceEvent *xiDeviceEvent = 0;
175+
176+ switch (xiEvent->evtype) {
177+ case XI_ButtonPress:
178+ case XI_ButtonRelease:
179+ case XI_Motion:
180+ xiDeviceEvent = reinterpret_cast<xXIDeviceEvent *>(event);
181+ break;
182+ default:
183+ break;
184+ }
185+
186+ if (!xiDeviceEvent) {
187+ return false;
188+ }
189+
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));
203+ case XI_Motion:
204+ return handleMotionNotify(
205+ static_cast<WId>(xiDeviceEvent->event),
206+ fixed1616ToReal(xiDeviceEvent->event_x),
207+ fixed1616ToReal(xiDeviceEvent->event_y));
208+ return true;
209+ default:
210+ return false;
211+ }
212+}
213+
214+
68215bool MouseTouchAdaptor::nativeEventFilter(const QByteArray & eventType,
69216 void * message, long * /*result*/)
70217{
218+ static int eventCount = 0;
219+ eventCount++;
71220 if (!m_enabled) {
72221 return false;
73222 }
@@ -81,32 +230,42 @@ bool MouseTouchAdaptor::nativeEventFilter(const QByteArray & eventType,
81230 xcb_generic_event_t *xcbEvent = static_cast<xcb_generic_event_t *>(message);
82231
83232 switch (xcbEvent->response_type & ~0x80) {
84− case XCB_BUTTON_PRESS:
85− return handleButtonPress(reinterpret_cast<xcb_button_press_event_t *>(xcbEvent));
86− break;
87− case XCB_BUTTON_RELEASE:
88− return handleButtonRelease(reinterpret_cast<xcb_button_release_event_t *>(xcbEvent));
89− break;
90− case XCB_MOTION_NOTIFY:
91− return handleMotionNotify(reinterpret_cast<xcb_motion_notify_event_t *>(xcbEvent));
92− break;
233+ case XCB_BUTTON_PRESS: {
234+ auto pressEvent = reinterpret_cast<xcb_button_press_event_t *>(xcbEvent);
235+ return handleButtonPress(static_cast<WId>(pressEvent->event), pressEvent->detail,
236+ pressEvent->event_x, pressEvent->event_y);
237+ }
238+ case XCB_BUTTON_RELEASE: {
239+ auto releaseEvent = reinterpret_cast<xcb_button_release_event_t *>(xcbEvent);
240+ return handleButtonRelease(static_cast<WId>(releaseEvent->event), releaseEvent->detail,
241+ releaseEvent->event_x, releaseEvent->event_y);
242+ }
243+ case XCB_MOTION_NOTIFY: {
244+ auto motionEvent = reinterpret_cast<xcb_motion_notify_event_t *>(xcbEvent);
245+ return handleMotionNotify(static_cast<WId>(motionEvent->event), motionEvent->event_x, motionEvent->event_y);
246+ }
247+ case XCB_GE_GENERIC:
248+ if (m_xi2Enabled) {
249+ return xi2HandleEvent(reinterpret_cast<xcb_ge_event_t *>(xcbEvent));
250+ } else {
251+ return false;
252+ }
93253 default:
94254 return false;
95− break;
96255 };
97256}
98257
99−bool MouseTouchAdaptor::handleButtonPress(xcb_button_press_event_t *pressEvent)
258+bool MouseTouchAdaptor::handleButtonPress(WId windowId, uint32_t detail, int x, int y)
100259{
101− Qt::MouseButton button = translateMouseButton(pressEvent->detail);
260+ Qt::MouseButton button = translateMouseButton(detail);
102261
103262 // Just eat the event if it wasn't a left mouse press
104263 if (button != Qt::LeftButton)
105264 return true;
106265
107− QWindow *targetWindow = findQWindowWithXWindowID(static_cast<WId>(pressEvent->event));
266+ QWindow *targetWindow = findQWindowWithXWindowID(windowId);
108267
109− QPoint windowPos(pressEvent->event_x / targetWindow->devicePixelRatio(), pressEvent->event_y / targetWindow->devicePixelRatio());
268+ QPoint windowPos(x / targetWindow->devicePixelRatio(), y / targetWindow->devicePixelRatio());
110269
111270 QTouchEventSequence touchEvent = QTest::touchEvent(targetWindow, m_touchDevice,
112271 false /* autoCommit */);
@@ -117,17 +276,17 @@ bool MouseTouchAdaptor::handleButtonPress(xcb_button_press_event_t *pressEvent)
117276 return true;
118277}
119278
120−bool MouseTouchAdaptor::handleButtonRelease(xcb_button_release_event_t *releaseEvent)
279+bool MouseTouchAdaptor::handleButtonRelease(WId windowId, uint32_t detail, int x, int y)
121280{
122− Qt::MouseButton button = translateMouseButton(releaseEvent->detail);
281+ Qt::MouseButton button = translateMouseButton(detail);
123282
124283 // Just eat the event if it wasn't a left mouse release
125284 if (button != Qt::LeftButton)
126285 return true;
127286
128− QWindow *targetWindow = findQWindowWithXWindowID(static_cast<WId>(releaseEvent->event));
287+ QWindow *targetWindow = findQWindowWithXWindowID(windowId);
129288
130− QPoint windowPos(releaseEvent->event_x / targetWindow->devicePixelRatio(), releaseEvent->event_y / targetWindow->devicePixelRatio());
289+ QPoint windowPos(x / targetWindow->devicePixelRatio(), y / targetWindow->devicePixelRatio());
131290
132291 QTouchEventSequence touchEvent = QTest::touchEvent(targetWindow, m_touchDevice,
133292 false /* autoCommit */);
@@ -138,15 +297,15 @@ bool MouseTouchAdaptor::handleButtonRelease(xcb_button_release_event_t *releaseE
138297 return true;
139298}
140299
141−bool MouseTouchAdaptor::handleMotionNotify(xcb_motion_notify_event_t *event)
300+bool MouseTouchAdaptor::handleMotionNotify(WId windowId, int x, int y)
142301{
143302 if (!m_leftButtonIsPressed) {
144303 return true;
145304 }
146305
147− QWindow *targetWindow = findQWindowWithXWindowID(static_cast<WId>(event->event));
306+ QWindow *targetWindow = findQWindowWithXWindowID(windowId);
148307
149− QPoint windowPos(event->event_x / targetWindow->devicePixelRatio(), event->event_y / targetWindow->devicePixelRatio());
308+ QPoint windowPos(x / targetWindow->devicePixelRatio(), y / targetWindow->devicePixelRatio());
150309
151310 QTouchEventSequence touchEvent = QTest::touchEvent(targetWindow, m_touchDevice,
152311 false /* autoCommit */);
src/MouseTouchAdaptor.h+10−3
@@ -21,6 +21,7 @@
2121
2222#include <QtCore/QAbstractNativeEventFilter>
2323#include <QWindow>
24+
2425#include <xcb/xcb.h>
2526
2627class QMouseEvent;
@@ -47,16 +48,22 @@ Q_SIGNALS:
4748
4849private:
4950 MouseTouchAdaptor();
51+ void fetchXInput2Info();
52+ bool xi2HandleEvent(xcb_ge_event_t *event);
5053
51− bool handleButtonPress(xcb_button_press_event_t *pressEvent);
52− bool handleButtonRelease(xcb_button_release_event_t *releaseEvent);
53− bool handleMotionNotify(xcb_motion_notify_event_t *event);
54+ bool handleButtonPress(WId windowId, uint32_t detail, int x, int y);
55+ bool handleButtonRelease(WId windowId, uint32_t detail, int x, int y);
56+ bool handleMotionNotify(WId windowId, int x, int y);
5457 QWindow *findQWindowWithXWindowID(WId windowId);
5558
5659 QTouchDevice *m_touchDevice;
5760 bool m_leftButtonIsPressed;
5861
5962 bool m_enabled;
63+
64+ bool m_xi2Enabled{false};
65+ int m_xi2Minor{-1};
66+ int m_xiOpCode, m_xiEventBase, m_xiErrorBase;
6067};
6168
6269#endif // MOUSE_TOUCH_ADAPTOR_H
tests/utils/modules/Unity/Test/CMakeLists.txt+3
@@ -19,6 +19,9 @@ set(UnityTestQML_SOURCES
1919add_library(UnityTestQml MODULE ${UnityTestQML_SOURCES})
2020target_link_libraries(UnityTestQml UbuntuGestures)
2121
22+pkg_check_modules(MOUSETOUCHADAPTOR_LIBS REQUIRED x11 xcb xi)
23+target_link_libraries(UnityTestQml ${MOUSETOUCHADAPTOR_LIBS_LDFLAGS})
24+
2225qt5_use_modules(UnityTestQml Core Quick Test)
2326
2427export_qmlfiles(Unity.Test Unity/Test)