2
* Copyright (C) 2016 Canonical, Ltd.
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.
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.
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/>.
16
* Authored by: Zsombor Egri <zsombor.egri@canonical.com>
19
/* Some parts of the code were copied from the XCB platform plugin of the Qt Toolkit,
20
* which is under the following license:
23
/****************************************************************************
25
** Copyright (C) 2015 The Qt Company Ltd.
26
** Contact: http://www.qt.io/licensing/
28
** This file is part of the .
30
** $QT_BEGIN_LICENSE:LGPL21$
31
** Commercial License Usage
32
** Licensees holding valid commercial Qt licenses may use this file in
33
** accordance with the commercial license agreement provided with the
34
** Software or, alternatively, in accordance with the terms contained in
35
** a written agreement between you and The Qt Company. For licensing terms
36
** and conditions see http://www.qt.io/terms-conditions. For further
37
** information use the contact form at http://www.qt.io/contact-us.
39
** GNU Lesser General Public License Usage
40
** Alternatively, this file may be used under the terms of the GNU Lesser
41
** General Public License version 2.1 or version 3 as published by the Free
42
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
43
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
44
** following information to ensure the GNU Lesser General Public License
45
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
46
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
48
** As a special exception, The Qt Company gives you certain additional
49
** rights. These rights are described in The Qt Company LGPL Exception
50
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
54
****************************************************************************/
56
#include "mousetouchadaptor_p.h"
58
#include <qpa/qplatformnativeinterface.h>
60
#include <QCoreApplication>
61
#include <QMouseEvent>
64
#include <X11/extensions/XInput2.h>
65
#include <X11/extensions/XI2proto.h>
67
using QTest::QTouchEventSequence;
71
const Qt::KeyboardModifiers TRI_PRESS_MODIFIER = Qt::ShiftModifier|Qt::ControlModifier|Qt::AltModifier;
73
Qt::MouseButton translateMouseButton(xcb_button_t detail)
76
case 1: return Qt::LeftButton;
77
case 2: return Qt::MidButton;
78
case 3: return Qt::RightButton;
79
// Button values 4-7 are Wheel events
80
default: return Qt::NoButton;
84
Qt::KeyboardModifiers translateMofidier(uint32_t mod)
86
Qt::KeyboardModifiers qtMod = Qt::NoModifier;
88
if (mod & 0x01) qtMod |= Qt::ShiftModifier;
89
if (mod & 0x04) qtMod |= Qt::ControlModifier;
90
if (mod & 0x08) qtMod |= Qt::AltModifier;
91
if (mod & 0x80) qtMod |= Qt::MetaModifier;
96
} // end of anonymous namespace
99
namespace UbuntuToolkit {
101
X11MouseTouchAdaptorPrivate::X11MouseTouchAdaptorPrivate()
102
: m_leftButtonIsPressed(false)
103
, m_triPressModifier(false)
108
void X11MouseTouchAdaptorPrivate::init()
110
QPlatformNativeInterface *nativeInterface = qGuiApp->platformNativeInterface();
111
Display *xDisplay = static_cast<Display*>(nativeInterface->nativeResourceForIntegration("Display"));
112
if (xDisplay && XQueryExtension(xDisplay, "XInputExtension", &m_xiOpCode, &m_xiEventBase, &m_xiErrorBase)) {
114
m_xi2Minor = 2; // try 2.2 first, needed for TouchBegin/Update/End
115
if (XIQueryVersion(xDisplay, &xiMajor, &m_xi2Minor) == BadRequest) {
116
m_xi2Minor = 1; // for smooth scrolling 2.1 is enough
117
if (XIQueryVersion(xDisplay, &xiMajor, &m_xi2Minor) == BadRequest) {
118
m_xi2Minor = 0; // for tablet support 2.0 is enough
119
m_xi2Enabled = XIQueryVersion(xDisplay, &xiMajor, &m_xi2Minor) != BadRequest;
127
QCoreApplication::instance()->installNativeEventFilter(this);
130
void X11MouseTouchAdaptorPrivate::setEnabled(bool value)
132
if (value != enabled) {
134
Q_EMIT q_func()->enabledChanged(value);
138
// Starting from the xcb version 1.9.3 struct xcb_ge_event_t has changed:
139
// - "pad0" became "extension"
140
// - "pad1" and "pad" became "pad0"
141
// New and old version of this struct share the following fields:
142
// NOTE: API might change again in the next release of xcb in which case this comment will
143
// need to be updated to reflect the reality.
144
typedef struct qt_xcb_ge_event_t {
145
uint8_t response_type;
152
bool xi2PrepareXIGenericDeviceEvent(xcb_ge_event_t *ev, int opCode)
154
qt_xcb_ge_event_t *event = (qt_xcb_ge_event_t *)ev;
155
// xGenericEvent has "extension" on the second byte, the same is true for xcb_ge_event_t starting from
156
// the xcb version 1.9.3, prior to that it was called "pad0".
157
if (event->extension == opCode) {
158
// xcb event structs contain stuff that wasn't on the wire, the full_sequence field
159
// adds an extra 4 bytes and generic events cookie data is on the wire right after the standard 32 bytes.
160
// Move this data back to have the same layout in memory as it was on the wire
161
// and allow casting, overwriting the full_sequence field.
162
memmove((char*) event + 32, (char*) event + 36, event->length * 4);
168
static inline qreal fixed1616ToReal(FP1616 val)
170
return qreal(val) / 0x10000;
173
bool X11MouseTouchAdaptorPrivate::xi2HandleEvent(xcb_ge_event_t *event)
175
if (!xi2PrepareXIGenericDeviceEvent(event, m_xiOpCode)) {
179
xXIGenericDeviceEvent *xiEvent = reinterpret_cast<xXIGenericDeviceEvent *>(event);
180
xXIDeviceEvent *xiDeviceEvent = 0;
182
switch (xiEvent->evtype) {
184
case XI_ButtonRelease:
186
xiDeviceEvent = reinterpret_cast<xXIDeviceEvent *>(event);
192
if (!xiDeviceEvent) {
196
switch (xiDeviceEvent->evtype) {
198
return handleButtonPress(
199
static_cast<WId>(xiDeviceEvent->event),
200
xiDeviceEvent->detail,
201
xiDeviceEvent->mods.base_mods,
202
fixed1616ToReal(xiDeviceEvent->event_x),
203
fixed1616ToReal(xiDeviceEvent->event_y));
204
case XI_ButtonRelease:
205
return handleButtonRelease(
206
static_cast<WId>(xiDeviceEvent->event),
207
xiDeviceEvent->detail,
208
xiDeviceEvent->mods.base_mods,
209
fixed1616ToReal(xiDeviceEvent->event_x),
210
fixed1616ToReal(xiDeviceEvent->event_y));
212
return handleMotionNotify(
213
static_cast<WId>(xiDeviceEvent->event),
214
xiDeviceEvent->mods.base_mods,
215
fixed1616ToReal(xiDeviceEvent->event_x),
216
fixed1616ToReal(xiDeviceEvent->event_y));
223
bool X11MouseTouchAdaptorPrivate::nativeEventFilter(const QByteArray &eventType, void *message, long *result)
226
static int eventCount = 0;
232
if (eventType != "xcb_generic_event_t") {
234
qWarning("MouseTouchAdaptor: XCB backend not in use. Adaptor inoperative!");
238
xcb_generic_event_t *xcbEvent = static_cast<xcb_generic_event_t *>(message);
240
switch (xcbEvent->response_type & ~0x80) {
241
case XCB_BUTTON_PRESS: {
242
auto pressEvent = reinterpret_cast<xcb_button_press_event_t *>(xcbEvent);
243
return handleButtonPress(static_cast<WId>(pressEvent->event), pressEvent->detail, 0,
244
pressEvent->event_x, pressEvent->event_y);
246
case XCB_BUTTON_RELEASE: {
247
auto releaseEvent = reinterpret_cast<xcb_button_release_event_t *>(xcbEvent);
248
return handleButtonRelease(static_cast<WId>(releaseEvent->event), releaseEvent->detail, 0,
249
releaseEvent->event_x, releaseEvent->event_y);
251
case XCB_MOTION_NOTIFY: {
252
auto motionEvent = reinterpret_cast<xcb_motion_notify_event_t *>(xcbEvent);
253
return handleMotionNotify(static_cast<WId>(motionEvent->event), 0,
254
motionEvent->event_x, motionEvent->event_y);
258
return xi2HandleEvent(reinterpret_cast<xcb_ge_event_t *>(xcbEvent));
267
bool X11MouseTouchAdaptorPrivate::handleButtonPress(WId windowId, uint32_t detail, uint32_t modifiers, int x, int y)
269
Qt::MouseButton button = translateMouseButton(detail);
270
Qt::KeyboardModifiers qtMod = translateMofidier(modifiers);
272
// Just eat the event if it wasn't a left mouse press
273
if (button != Qt::LeftButton)
276
QWindow *targetWindow = findQWindowWithXWindowID(windowId);
278
QPoint windowPos(x / targetWindow->devicePixelRatio(), y / targetWindow->devicePixelRatio());
280
QTouchEventSequence touchEvent = QTest::touchEvent(targetWindow, MouseTouchAdaptor::touchDevice(),
281
false /* autoCommit */);
282
touchEvent.press(0 /* touchId */, windowPos);
283
if (qtMod == TRI_PRESS_MODIFIER) {
284
touchEvent.press(1, windowPos);
285
touchEvent.press(2, windowPos);
286
m_triPressModifier = true;
288
touchEvent.commit(false /* processEvents */);
290
m_leftButtonIsPressed = true;
294
bool X11MouseTouchAdaptorPrivate::handleButtonRelease(WId windowId, uint32_t detail, uint32_t, int x, int y)
296
Qt::MouseButton button = translateMouseButton(detail);
298
// Don't eat the event if it wasn't a left mouse press
299
if (button != Qt::LeftButton)
302
QWindow *targetWindow = findQWindowWithXWindowID(windowId);
304
QPoint windowPos(x / targetWindow->devicePixelRatio(), y / targetWindow->devicePixelRatio());
306
QTouchEventSequence touchEvent = QTest::touchEvent(targetWindow, MouseTouchAdaptor::touchDevice(),
307
false /* autoCommit */);
308
touchEvent.release(0 /* touchId */, windowPos);
309
if (m_triPressModifier) {
310
touchEvent.release(1, windowPos);
311
touchEvent.release(2, windowPos);
313
touchEvent.commit(false /* processEvents */);
315
m_leftButtonIsPressed = false;
316
m_triPressModifier = false;
320
bool X11MouseTouchAdaptorPrivate::handleMotionNotify(WId windowId, uint32_t modifiers, int x, int y)
322
if (!m_leftButtonIsPressed) {
325
Qt::KeyboardModifiers qtMod = translateMofidier(modifiers);
327
QWindow *targetWindow = findQWindowWithXWindowID(windowId);
329
QPoint windowPos(x / targetWindow->devicePixelRatio(), y / targetWindow->devicePixelRatio());
331
QTouchEventSequence touchEvent = QTest::touchEvent(targetWindow, MouseTouchAdaptor::touchDevice(),
332
false /* autoCommit */);
333
touchEvent.move(0 /* touchId */, windowPos);
334
if (m_triPressModifier) {
335
if (qtMod == TRI_PRESS_MODIFIER) {
336
touchEvent.move(1, windowPos);
337
touchEvent.move(2, windowPos);
339
// released modifiers
340
touchEvent.release(1, windowPos);
341
touchEvent.release(2, windowPos);
342
m_triPressModifier = false;
345
touchEvent.commit(false /* processEvents */);
350
} // namespace UbuntuToolkit