~bzoltan/ubuntu-ui-toolkit/inputMainFocus

« back to all changes in this revision

Viewing changes to src/Ubuntu/UbuntuToolkit/mousetouchadaptor_x11.cpp

  • Committer: CI Train Bot
  • Author(s): Zoltán Balogh, Tim Peeters, Zsombor Egri, Christian Dywan, Andrea Bernabei
  • Date: 2016-04-12 11:12:39 UTC
  • mfrom: (1000.868.47 OTA11-landing-2016-04-08)
  • Revision ID: ci-train-bot@canonical.com-20160412111239-g60hc86tzxtrprn3
OTA11-landing-2016-04-08

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2016 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: Zsombor Egri <zsombor.egri@canonical.com>
 
17
 */
 
18
 
 
19
/* Some parts of the code were copied from the XCB platform plugin of the Qt Toolkit,
 
20
 * which is under the following license:
 
21
 */
 
22
 
 
23
/****************************************************************************
 
24
**
 
25
** Copyright (C) 2015 The Qt Company Ltd.
 
26
** Contact: http://www.qt.io/licensing/
 
27
**
 
28
** This file is part of the .
 
29
**
 
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.
 
38
**
 
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.
 
47
**
 
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.
 
51
**
 
52
** $QT_END_LICENSE$
 
53
**
 
54
****************************************************************************/
 
55
 
 
56
#include "mousetouchadaptor_p.h"
 
57
 
 
58
#include <qpa/qplatformnativeinterface.h>
 
59
 
 
60
#include <QCoreApplication>
 
61
#include <QMouseEvent>
 
62
#include <QTest>
 
63
 
 
64
#include <X11/extensions/XInput2.h>
 
65
#include <X11/extensions/XI2proto.h>
 
66
 
 
67
using QTest::QTouchEventSequence;
 
68
 
 
69
namespace {
 
70
 
 
71
const Qt::KeyboardModifiers TRI_PRESS_MODIFIER = Qt::ShiftModifier|Qt::ControlModifier|Qt::AltModifier;
 
72
 
 
73
Qt::MouseButton translateMouseButton(xcb_button_t detail)
 
74
{
 
75
    switch (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;
 
81
    }
 
82
}
 
83
 
 
84
Qt::KeyboardModifiers translateMofidier(uint32_t mod)
 
85
{
 
86
    Qt::KeyboardModifiers qtMod = Qt::NoModifier;
 
87
 
 
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;
 
92
 
 
93
    return qtMod;
 
94
}
 
95
 
 
96
} // end of anonymous namespace
 
97
 
 
98
 
 
99
namespace UbuntuToolkit {
 
100
 
 
101
X11MouseTouchAdaptorPrivate::X11MouseTouchAdaptorPrivate()
 
102
    : m_leftButtonIsPressed(false)
 
103
    , m_triPressModifier(false)
 
104
{
 
105
    enabled = true;
 
106
}
 
107
 
 
108
void X11MouseTouchAdaptorPrivate::init()
 
109
{
 
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)) {
 
113
        int xiMajor = 2;
 
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;
 
120
            } else
 
121
                m_xi2Enabled = true;
 
122
        } else {
 
123
            m_xi2Enabled = true;
 
124
        }
 
125
    }
 
126
 
 
127
    QCoreApplication::instance()->installNativeEventFilter(this);
 
128
}
 
129
 
 
130
void X11MouseTouchAdaptorPrivate::setEnabled(bool value)
 
131
{
 
132
    if (value != enabled) {
 
133
        enabled = value;
 
134
        Q_EMIT q_func()->enabledChanged(value);
 
135
    }
 
136
}
 
137
 
 
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;
 
146
    uint8_t  extension;
 
147
    uint16_t sequence;
 
148
    uint32_t length;
 
149
    uint16_t event_type;
 
150
} qt_xcb_ge_event_t;
 
151
 
 
152
bool xi2PrepareXIGenericDeviceEvent(xcb_ge_event_t *ev, int opCode)
 
153
{
 
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);
 
163
        return true;
 
164
    }
 
165
    return false;
 
166
}
 
167
 
 
168
static inline qreal fixed1616ToReal(FP1616 val)
 
169
{
 
170
    return qreal(val) / 0x10000;
 
171
}
 
172
 
 
173
bool X11MouseTouchAdaptorPrivate::xi2HandleEvent(xcb_ge_event_t *event)
 
174
{
 
175
    if (!xi2PrepareXIGenericDeviceEvent(event, m_xiOpCode)) {
 
176
        return false;
 
177
    }
 
178
 
 
179
    xXIGenericDeviceEvent *xiEvent = reinterpret_cast<xXIGenericDeviceEvent *>(event);
 
180
    xXIDeviceEvent *xiDeviceEvent = 0;
 
181
 
 
182
    switch (xiEvent->evtype) {
 
183
    case XI_ButtonPress:
 
184
    case XI_ButtonRelease:
 
185
    case XI_Motion:
 
186
        xiDeviceEvent = reinterpret_cast<xXIDeviceEvent *>(event);
 
187
        break;
 
188
    default:
 
189
        break;
 
190
    }
 
191
 
 
192
    if (!xiDeviceEvent) {
 
193
        return false;
 
194
    }
 
195
 
 
196
    switch (xiDeviceEvent->evtype) {
 
197
    case XI_ButtonPress:
 
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));
 
211
    case XI_Motion:
 
212
        return handleMotionNotify(
 
213
                static_cast<WId>(xiDeviceEvent->event),
 
214
                xiDeviceEvent->mods.base_mods,
 
215
                fixed1616ToReal(xiDeviceEvent->event_x),
 
216
                fixed1616ToReal(xiDeviceEvent->event_y));
 
217
        return true;
 
218
    default:
 
219
        return false;
 
220
    }
 
221
}
 
222
 
 
223
bool X11MouseTouchAdaptorPrivate::nativeEventFilter(const QByteArray &eventType, void *message, long *result)
 
224
{
 
225
    Q_UNUSED(result);
 
226
    static int eventCount = 0;
 
227
    eventCount++;
 
228
    if (!enabled) {
 
229
        return false;
 
230
    }
 
231
 
 
232
    if (eventType != "xcb_generic_event_t") {
 
233
        // wrong backend.
 
234
        qWarning("MouseTouchAdaptor: XCB backend not in use. Adaptor inoperative!");
 
235
        return false;
 
236
    }
 
237
 
 
238
    xcb_generic_event_t *xcbEvent = static_cast<xcb_generic_event_t *>(message);
 
239
 
 
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);
 
245
        }
 
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);
 
250
        }
 
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);
 
255
        }
 
256
        case XCB_GE_GENERIC:
 
257
            if (m_xi2Enabled) {
 
258
                return xi2HandleEvent(reinterpret_cast<xcb_ge_event_t *>(xcbEvent));
 
259
            } else {
 
260
                return false;
 
261
            }
 
262
        default:
 
263
            return false;
 
264
    };
 
265
}
 
266
 
 
267
bool X11MouseTouchAdaptorPrivate::handleButtonPress(WId windowId, uint32_t detail, uint32_t modifiers, int x, int y)
 
268
{
 
269
    Qt::MouseButton button = translateMouseButton(detail);
 
270
    Qt::KeyboardModifiers qtMod = translateMofidier(modifiers);
 
271
 
 
272
    // Just eat the event if it wasn't a left mouse press
 
273
    if (button != Qt::LeftButton)
 
274
        return true;
 
275
 
 
276
    QWindow *targetWindow = findQWindowWithXWindowID(windowId);
 
277
 
 
278
    QPoint windowPos(x / targetWindow->devicePixelRatio(), y / targetWindow->devicePixelRatio());
 
279
 
 
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;
 
287
    }
 
288
    touchEvent.commit(false /* processEvents */);
 
289
 
 
290
    m_leftButtonIsPressed = true;
 
291
    return true;
 
292
}
 
293
 
 
294
bool X11MouseTouchAdaptorPrivate::handleButtonRelease(WId windowId, uint32_t detail, uint32_t, int x, int y)
 
295
{
 
296
    Qt::MouseButton button = translateMouseButton(detail);
 
297
 
 
298
    // Don't eat the event if it wasn't a left mouse press
 
299
    if (button != Qt::LeftButton)
 
300
        return false;
 
301
 
 
302
    QWindow *targetWindow = findQWindowWithXWindowID(windowId);
 
303
 
 
304
    QPoint windowPos(x / targetWindow->devicePixelRatio(), y / targetWindow->devicePixelRatio());
 
305
 
 
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);
 
312
    }
 
313
    touchEvent.commit(false /* processEvents */);
 
314
 
 
315
    m_leftButtonIsPressed = false;
 
316
    m_triPressModifier = false;
 
317
    return true;
 
318
}
 
319
 
 
320
bool X11MouseTouchAdaptorPrivate::handleMotionNotify(WId windowId, uint32_t modifiers, int x, int y)
 
321
{
 
322
    if (!m_leftButtonIsPressed) {
 
323
        return true;
 
324
    }
 
325
    Qt::KeyboardModifiers qtMod = translateMofidier(modifiers);
 
326
 
 
327
    QWindow *targetWindow = findQWindowWithXWindowID(windowId);
 
328
 
 
329
    QPoint windowPos(x / targetWindow->devicePixelRatio(), y / targetWindow->devicePixelRatio());
 
330
 
 
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);
 
338
        } else {
 
339
            // released modifiers
 
340
            touchEvent.release(1, windowPos);
 
341
            touchEvent.release(2, windowPos);
 
342
            m_triPressModifier = false;
 
343
        }
 
344
    }
 
345
    touchEvent.commit(false /* processEvents */);
 
346
 
 
347
    return true;
 
348
}
 
349
 
 
350
} // namespace UbuntuToolkit