aboutsummaryrefslogtreecommitdiffstats
path: root/src/MouseTouchAdaptor.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/MouseTouchAdaptor.cpp')
-rw-r--r--src/MouseTouchAdaptor.cpp276
1 files changed, 276 insertions, 0 deletions
diff --git a/src/MouseTouchAdaptor.cpp b/src/MouseTouchAdaptor.cpp
new file mode 100644
index 00000000..12bbddd0
--- /dev/null
+++ b/src/MouseTouchAdaptor.cpp
@@ -0,0 +1,276 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Pelagicore AG
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Triton IVI UI.
+**
+** $QT_BEGIN_LICENSE:GPL-QTAS$
+** Commercial License Usage
+** Licensees holding valid commercial Qt Automotive Suite licenses may use
+** this file in accordance with the commercial license agreement provided
+** with the Software or, alternatively, in accordance with the terms
+** contained in a written agreement between you and The Qt Company. For
+** licensing terms and conditions see https://www.qt.io/terms-conditions.
+** For further information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+** SPDX-License-Identifier: GPL-3.0
+**
+****************************************************************************/
+
+#include "MouseTouchAdaptor.h"
+
+#include <QGuiApplication>
+#include <QMouseEvent>
+#include <QTest>
+
+#include <qpa/qplatformnativeinterface.h>
+#include <qpa/qwindowsysteminterface.h>
+
+#include <X11/extensions/XInput2.h>
+#include <X11/extensions/XI2proto.h>
+
+Q_LOGGING_CATEGORY(mouseTouchAdaptor, "mousetouchadaptor")
+
+using QTest::QTouchEventSequence;
+
+namespace {
+
+Qt::MouseButton xcbButtonToQtMouseButton(xcb_button_t detail)
+{
+ switch (detail) {
+ case 1: return Qt::LeftButton;
+ case 2: return Qt::MidButton;
+ case 3: return Qt::RightButton;
+ // don't care about the rest
+ default: return Qt::NoButton;
+ }
+}
+
+void xi2PrepareXIGenericDeviceEvent(xcb_ge_event_t *event)
+{
+ // xcb event structs contain stuff that wasn't on the wire, the full_sequence field
+ // adds an extra 4 bytes and generic events cookie data is on the wire right after the standard 32 bytes.
+ // Move this data back to have the same layout in memory as it was on the wire
+ // and allow casting, overwriting the full_sequence field.
+ memmove((char*) event + 32, (char*) event + 36, event->length * 4);
+}
+
+qreal fixed1616ToReal(FP1616 val)
+{
+ return qreal(val) / 0x10000;
+}
+
+} // anonymous namespace
+
+MouseTouchAdaptor *MouseTouchAdaptor::m_instance = nullptr;
+
+MouseTouchAdaptor *MouseTouchAdaptor::instance()
+{
+ if (!m_instance) {
+ new MouseTouchAdaptor;
+ }
+ return m_instance;
+}
+
+MouseTouchAdaptor::MouseTouchAdaptor()
+{
+ Q_ASSERT(!m_instance);
+ m_instance = this;
+
+ qGuiApp->installNativeEventFilter(this);
+
+ // Create a fake touch device to deliver our synthesized events
+ m_touchDevice = new QTouchDevice;
+ m_touchDevice->setType(QTouchDevice::TouchScreen);
+ QWindowSystemInterface::registerTouchDevice(m_touchDevice);
+
+ queryForXInput2();
+}
+
+MouseTouchAdaptor::~MouseTouchAdaptor()
+{
+ Q_ASSERT(m_instance);
+ m_instance = nullptr;
+}
+
+void MouseTouchAdaptor::queryForXInput2()
+{
+ QPlatformNativeInterface *nativeInterface = qGuiApp->platformNativeInterface();
+ Display *xDisplay = static_cast<Display*>(nativeInterface->nativeResourceForIntegration("Display"));
+
+ int xiOpCode, xiEventBase, xiErrorBase;
+ if (xDisplay && XQueryExtension(xDisplay, "XInputExtension", &xiOpCode, &xiEventBase, &xiErrorBase)) {
+ // 2.0 is enough for our needs
+ int xiMajor = 2;
+ int xi2Minor = 0;
+ m_haveXInput2 = XIQueryVersion(xDisplay, &xiMajor, &xi2Minor) != BadRequest;
+ }
+}
+
+bool MouseTouchAdaptor::nativeEventFilter(const QByteArray &eventType, void *message, long * /*result*/)
+{
+ if (eventType != "xcb_generic_event_t") {
+ qCDebug(mouseTouchAdaptor) << "Non XCB native event received. Ignoring.";
+ return false;
+ }
+
+ xcb_generic_event_t *xcbEvent = static_cast<xcb_generic_event_t *>(message);
+
+ switch (xcbEvent->response_type & ~0x80) {
+ case XCB_BUTTON_PRESS: {
+ auto xcbPress = reinterpret_cast<xcb_button_press_event_t *>(xcbEvent);
+ return handleButtonPress(static_cast<WId>(xcbPress->event), xcbPress->detail, 0,
+ xcbPress->event_x, xcbPress->event_y);
+ }
+ case XCB_BUTTON_RELEASE: {
+ auto xcbRelease = reinterpret_cast<xcb_button_release_event_t *>(xcbEvent);
+ return handleButtonRelease(static_cast<WId>(xcbRelease->event), xcbRelease->detail, 0,
+ xcbRelease->event_x, xcbRelease->event_y);
+ }
+ case XCB_MOTION_NOTIFY: {
+ auto xcbMotion = reinterpret_cast<xcb_motion_notify_event_t *>(xcbEvent);
+ return handleMotionNotify(static_cast<WId>(xcbMotion->event), 0,
+ xcbMotion->event_x, xcbMotion->event_y);
+ }
+ case XCB_GE_GENERIC:
+ if (m_haveXInput2) {
+ return handleXI2Event(reinterpret_cast<xcb_ge_event_t *>(xcbEvent));
+ } else {
+ return false;
+ }
+ default:
+ return false;
+ };
+}
+
+bool MouseTouchAdaptor::handleButtonPress(WId windowId, uint32_t detail, uint32_t /*modifiers*/, int x, int y)
+{
+ Qt::MouseButton button = xcbButtonToQtMouseButton(detail);
+
+ // Filter out the other mouse buttons
+ if (button != Qt::LeftButton)
+ return true;
+
+ QWindow *targetWindow = findQWindowWithXWindowID(windowId);
+
+ QPoint windowPos(x / targetWindow->devicePixelRatio(), y / targetWindow->devicePixelRatio());
+
+ QTouchEventSequence touchEvent = QTest::touchEvent(targetWindow, m_touchDevice, false /* autoCommit */);
+ touchEvent.press(0 /* touchId */, windowPos);
+ touchEvent.commit(false /* processEvents */);
+
+ m_leftButtonIsPressed = true;
+ return true;
+}
+
+bool MouseTouchAdaptor::handleButtonRelease(WId windowId, uint32_t detail, uint32_t, int x, int y)
+{
+ Qt::MouseButton button = xcbButtonToQtMouseButton(detail);
+
+ // Don't eat the event if it wasn't a left mouse press
+ if (button != Qt::LeftButton)
+ return false;
+
+ QWindow *targetWindow = findQWindowWithXWindowID(windowId);
+
+ QPoint windowPos(x / targetWindow->devicePixelRatio(), y / targetWindow->devicePixelRatio());
+
+ QTouchEventSequence touchEvent = QTest::touchEvent(targetWindow, m_touchDevice, false /* autoCommit */);
+ touchEvent.release(0 /* touchId */, windowPos);
+ touchEvent.commit(false /* processEvents */);
+
+ m_leftButtonIsPressed = false;
+ return true;
+}
+
+bool MouseTouchAdaptor::handleMotionNotify(WId windowId, uint32_t /*modifiers*/, int x, int y)
+{
+ if (!m_leftButtonIsPressed)
+ return true;
+
+ QWindow *targetWindow = findQWindowWithXWindowID(windowId);
+
+ QPoint windowPos(x / targetWindow->devicePixelRatio(), y / targetWindow->devicePixelRatio());
+
+ QTouchEventSequence touchEvent = QTest::touchEvent(targetWindow, m_touchDevice, false /* autoCommit */);
+ touchEvent.move(0 /* touchId */, windowPos);
+ touchEvent.commit(false /* processEvents */);
+
+ return true;
+}
+
+QWindow *MouseTouchAdaptor::findQWindowWithXWindowID(WId windowId)
+{
+ QWindowList windowList = QGuiApplication::topLevelWindows();
+ QWindow *foundWindow = nullptr;
+
+ int i = 0;
+ while (!foundWindow && i < windowList.count()) {
+ QWindow *window = windowList[i];
+ if (window->winId() == windowId)
+ foundWindow = window;
+ else
+ ++i;
+ }
+
+ Q_ASSERT(foundWindow);
+ return foundWindow;
+}
+
+bool MouseTouchAdaptor::handleXI2Event(xcb_ge_event_t *event)
+{
+ xi2PrepareXIGenericDeviceEvent(event);
+ xXIGenericDeviceEvent *xiEvent = reinterpret_cast<xXIGenericDeviceEvent *>(event);
+ xXIDeviceEvent *xiDeviceEvent = 0;
+
+ switch (xiEvent->evtype) {
+ case XI_ButtonPress:
+ case XI_ButtonRelease:
+ case XI_Motion:
+ xiDeviceEvent = reinterpret_cast<xXIDeviceEvent *>(event);
+ break;
+ default:
+ break;
+ }
+
+ if (!xiDeviceEvent)
+ return false;
+
+ switch (xiDeviceEvent->evtype) {
+ case XI_ButtonPress:
+ return handleButtonPress(
+ static_cast<WId>(xiDeviceEvent->event),
+ xiDeviceEvent->detail,
+ xiDeviceEvent->mods.base_mods,
+ fixed1616ToReal(xiDeviceEvent->event_x),
+ fixed1616ToReal(xiDeviceEvent->event_y));
+ case XI_ButtonRelease:
+ return handleButtonRelease(
+ static_cast<WId>(xiDeviceEvent->event),
+ xiDeviceEvent->detail,
+ xiDeviceEvent->mods.base_mods,
+ fixed1616ToReal(xiDeviceEvent->event_x),
+ fixed1616ToReal(xiDeviceEvent->event_y));
+ case XI_Motion:
+ return handleMotionNotify(
+ static_cast<WId>(xiDeviceEvent->event),
+ xiDeviceEvent->mods.base_mods,
+ fixed1616ToReal(xiDeviceEvent->event_x),
+ fixed1616ToReal(xiDeviceEvent->event_y));
+ return true;
+ default:
+ return false;
+ }
+}