on vivid help sign in
unity8/src/MouseTouchAdaptor.cpp
392 lines 13 kB history raw
1/*
2 * Copyright (C) 2013,2015 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Daniel d'Andrada <daniel.dandrada@canonical.com>
17 */
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 */
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****************************************************************************/
57#include "MouseTouchAdaptor.h"
59#include <qpa/qplatformnativeinterface.h>
60#include <qpa/qwindowsysteminterface.h>
62#include <QCoreApplication>
63#include <QMouseEvent>
64#include <QTest>
66#include <X11/extensions/XInput2.h>
67#include <X11/extensions/XI2proto.h>
69using QTest::QTouchEventSequence;
71namespace {
72MouseTouchAdaptor *g_instance = nullptr;
74const Qt::KeyboardModifiers TRI_PRESS_MODIFIER = Qt::ShiftModifier|Qt::ControlModifier|Qt::AltModifier;
76Qt::MouseButton translateMouseButton(xcb_button_t detail)
78 switch (detail) {
79 case 1: return Qt::LeftButton;
80 case 2: return Qt::MidButton;
81 case 3: return Qt::RightButton;
82 // Button values 4-7 are Wheel events
83 default: return Qt::NoButton;
84 }
87Qt::KeyboardModifiers translateMofidier(uint32_t mod)
89 Qt::KeyboardModifiers qtMod = Qt::NoModifier;
91 if (mod & 0x01) qtMod |= Qt::ShiftModifier;
92 if (mod & 0x04) qtMod |= Qt::ControlModifier;
93 if (mod & 0x08) qtMod |= Qt::AltModifier;
94 if (mod & 0x80) qtMod |= Qt::MetaModifier;
96 return qtMod;
98} // end of anonymous namespace
100MouseTouchAdaptor::MouseTouchAdaptor()
101 : QObject(nullptr)
102 , m_leftButtonIsPressed(false)
103 , m_triPressModifier(false)
104 , m_enabled(true)
106 QCoreApplication::instance()->installNativeEventFilter(this);
108 m_touchDevice = new QTouchDevice;
109 m_touchDevice->setType(QTouchDevice::TouchScreen);
110 QWindowSystemInterface::registerTouchDevice(m_touchDevice);
112 fetchXInput2Info();
115MouseTouchAdaptor::~MouseTouchAdaptor()
117 g_instance = nullptr;
120MouseTouchAdaptor* MouseTouchAdaptor::instance()
122 if (!g_instance) {
123 g_instance = new MouseTouchAdaptor;
124 }
126 return g_instance;
129void MouseTouchAdaptor::fetchXInput2Info()
131 QPlatformNativeInterface *nativeInterface = qGuiApp->platformNativeInterface();
132 Display *xDisplay = static_cast<Display*>(nativeInterface->nativeResourceForIntegration("Display"));
133 if (xDisplay && XQueryExtension(xDisplay, "XInputExtension", &m_xiOpCode, &m_xiEventBase, &m_xiErrorBase)) {
134 int xiMajor = 2;
135 m_xi2Minor = 2; // try 2.2 first, needed for TouchBegin/Update/End
136 if (XIQueryVersion(xDisplay, &xiMajor, &m_xi2Minor) == BadRequest) {
137 m_xi2Minor = 1; // for smooth scrolling 2.1 is enough
138 if (XIQueryVersion(xDisplay, &xiMajor, &m_xi2Minor) == BadRequest) {
139 m_xi2Minor = 0; // for tablet support 2.0 is enough
140 m_xi2Enabled = XIQueryVersion(xDisplay, &xiMajor, &m_xi2Minor) != BadRequest;
141 } else
142 m_xi2Enabled = true;
143 } else {
144 m_xi2Enabled = true;
145 }
146 }
149// Starting from the xcb version 1.9.3 struct xcb_ge_event_t has changed:
150// - "pad0" became "extension"
151// - "pad1" and "pad" became "pad0"
152// New and old version of this struct share the following fields:
153// NOTE: API might change again in the next release of xcb in which case this comment will
154// need to be updated to reflect the reality.
155typedef struct qt_xcb_ge_event_t {
156 uint8_t response_type;
157 uint8_t extension;
158 uint16_t sequence;
159 uint32_t length;
160 uint16_t event_type;
161} qt_xcb_ge_event_t;
163bool xi2PrepareXIGenericDeviceEvent(xcb_ge_event_t *ev, int opCode)
165 qt_xcb_ge_event_t *event = (qt_xcb_ge_event_t *)ev;
166 // xGenericEvent has "extension" on the second byte, the same is true for xcb_ge_event_t starting from
167 // the xcb version 1.9.3, prior to that it was called "pad0".
168 if (event->extension == opCode) {
169 // xcb event structs contain stuff that wasn't on the wire, the full_sequence field
170 // adds an extra 4 bytes and generic events cookie data is on the wire right after the standard 32 bytes.
171 // Move this data back to have the same layout in memory as it was on the wire
172 // and allow casting, overwriting the full_sequence field.
173 memmove((char*) event + 32, (char*) event + 36, event->length * 4);
174 return true;
175 }
176 return false;
179static inline qreal fixed1616ToReal(FP1616 val)
181 return qreal(val) / 0x10000;
184bool MouseTouchAdaptor::xi2HandleEvent(xcb_ge_event_t *event)
186 if (!xi2PrepareXIGenericDeviceEvent(event, m_xiOpCode)) {
187 return false;
188 }
190 xXIGenericDeviceEvent *xiEvent = reinterpret_cast<xXIGenericDeviceEvent *>(event);
191 xXIDeviceEvent *xiDeviceEvent = 0;
193 switch (xiEvent->evtype) {
194 case XI_ButtonPress:
195 case XI_ButtonRelease:
196 case XI_Motion:
197 xiDeviceEvent = reinterpret_cast<xXIDeviceEvent *>(event);
198 break;
199 default:
200 break;
201 }
203 if (!xiDeviceEvent) {
204 return false;
205 }
207 switch (xiDeviceEvent->evtype) {
208 case XI_ButtonPress:
209 return handleButtonPress(
210 static_cast<WId>(xiDeviceEvent->event),
211 xiDeviceEvent->detail,
212 xiDeviceEvent->mods.base_mods,
213 fixed1616ToReal(xiDeviceEvent->event_x),
214 fixed1616ToReal(xiDeviceEvent->event_y));
215 case XI_ButtonRelease:
216 return handleButtonRelease(
217 static_cast<WId>(xiDeviceEvent->event),
218 xiDeviceEvent->detail,
219 xiDeviceEvent->mods.base_mods,
220 fixed1616ToReal(xiDeviceEvent->event_x),
221 fixed1616ToReal(xiDeviceEvent->event_y));
222 case XI_Motion:
223 return handleMotionNotify(
224 static_cast<WId>(xiDeviceEvent->event),
225 xiDeviceEvent->mods.base_mods,
226 fixed1616ToReal(xiDeviceEvent->event_x),
227 fixed1616ToReal(xiDeviceEvent->event_y));
228 return true;
229 default:
230 return false;
231 }
235bool MouseTouchAdaptor::nativeEventFilter(const QByteArray & eventType,
236 void * message, long * /*result*/)
238 static int eventCount = 0;
239 eventCount++;
240 if (!m_enabled) {
241 return false;
242 }
244 if (eventType != "xcb_generic_event_t") {
245 // wrong backend.
246 qWarning("MouseTouchAdaptor: XCB backend not in use. Adaptor inoperative!");
247 return false;
248 }
250 xcb_generic_event_t *xcbEvent = static_cast<xcb_generic_event_t *>(message);
252 switch (xcbEvent->response_type & ~0x80) {
253 case XCB_BUTTON_PRESS: {
254 auto pressEvent = reinterpret_cast<xcb_button_press_event_t *>(xcbEvent);
255 return handleButtonPress(static_cast<WId>(pressEvent->event), pressEvent->detail, 0,
256 pressEvent->event_x, pressEvent->event_y);
257 }
258 case XCB_BUTTON_RELEASE: {
259 auto releaseEvent = reinterpret_cast<xcb_button_release_event_t *>(xcbEvent);
260 return handleButtonRelease(static_cast<WId>(releaseEvent->event), releaseEvent->detail, 0,
261 releaseEvent->event_x, releaseEvent->event_y);
262 }
263 case XCB_MOTION_NOTIFY: {
264 auto motionEvent = reinterpret_cast<xcb_motion_notify_event_t *>(xcbEvent);
265 return handleMotionNotify(static_cast<WId>(motionEvent->event), 0,
266 motionEvent->event_x, motionEvent->event_y);
267 }
268 case XCB_GE_GENERIC:
269 if (m_xi2Enabled) {
270 return xi2HandleEvent(reinterpret_cast<xcb_ge_event_t *>(xcbEvent));
271 } else {
272 return false;
273 }
274 default:
275 return false;
276 };
279bool MouseTouchAdaptor::handleButtonPress(WId windowId, uint32_t detail, uint32_t modifiers, int x, int y)
281 Qt::MouseButton button = translateMouseButton(detail);
282 Qt::KeyboardModifiers qtMod = translateMofidier(modifiers);
284 // Just eat the event if it wasn't a left mouse press
285 if (button != Qt::LeftButton)
286 return true;
288 QWindow *targetWindow = findQWindowWithXWindowID(windowId);
290 QPoint windowPos(x / targetWindow->devicePixelRatio(), y / targetWindow->devicePixelRatio());
292 QTouchEventSequence touchEvent = QTest::touchEvent(targetWindow, m_touchDevice,
293 false /* autoCommit */);
294 touchEvent.press(0 /* touchId */, windowPos);
295 if (qtMod == TRI_PRESS_MODIFIER) {
296 touchEvent.press(1, windowPos);
297 touchEvent.press(2, windowPos);
298 m_triPressModifier = true;
299 }
300 touchEvent.commit(false /* processEvents */);
302 m_leftButtonIsPressed = true;
303 return true;
306bool MouseTouchAdaptor::handleButtonRelease(WId windowId, uint32_t detail, uint32_t, int x, int y)
308 Qt::MouseButton button = translateMouseButton(detail);
310 // Don't eat the event if it wasn't a left mouse press
311 if (button != Qt::LeftButton)
312 return false;
314 QWindow *targetWindow = findQWindowWithXWindowID(windowId);
316 QPoint windowPos(x / targetWindow->devicePixelRatio(), y / targetWindow->devicePixelRatio());
318 QTouchEventSequence touchEvent = QTest::touchEvent(targetWindow, m_touchDevice,
319 false /* autoCommit */);
320 touchEvent.release(0 /* touchId */, windowPos);
321 if (m_triPressModifier) {
322 touchEvent.release(1, windowPos);
323 touchEvent.release(2, windowPos);
324 }
325 touchEvent.commit(false /* processEvents */);
327 m_leftButtonIsPressed = false;
328 m_triPressModifier = false;
329 return true;
332bool MouseTouchAdaptor::handleMotionNotify(WId windowId, uint32_t modifiers, int x, int y)
334 if (!m_leftButtonIsPressed) {
335 return true;
336 }
337 Qt::KeyboardModifiers qtMod = translateMofidier(modifiers);
339 QWindow *targetWindow = findQWindowWithXWindowID(windowId);
341 QPoint windowPos(x / targetWindow->devicePixelRatio(), y / targetWindow->devicePixelRatio());
343 QTouchEventSequence touchEvent = QTest::touchEvent(targetWindow, m_touchDevice,
344 false /* autoCommit */);
345 touchEvent.move(0 /* touchId */, windowPos);
346 if (m_triPressModifier) {
347 if (qtMod == TRI_PRESS_MODIFIER) {
348 touchEvent.move(1, windowPos);
349 touchEvent.move(2, windowPos);
350 } else {
351 // released modifiers
352 touchEvent.release(1, windowPos);
353 touchEvent.release(2, windowPos);
354 m_triPressModifier = false;
355 }
356 }
357 touchEvent.commit(false /* processEvents */);
359 return true;
362QWindow *MouseTouchAdaptor::findQWindowWithXWindowID(WId windowId)
364 QWindowList windowList = QGuiApplication::topLevelWindows();
365 QWindow *foundWindow = nullptr;
367 int i = 0;
368 while (!foundWindow && i < windowList.count()) {
369 QWindow *window = windowList[i];
370 if (window->winId() == windowId) {
371 foundWindow = window;
372 } else {
373 ++i;
374 }
375 }
377 Q_ASSERT(foundWindow);
378 return foundWindow;
381bool MouseTouchAdaptor::enabled() const
383 return m_enabled;
386void MouseTouchAdaptor::setEnabled(bool value)
388 if (value != m_enabled) {
389 m_enabled = value;
390 Q_EMIT enabledChanged(value);
391 }