Unity 8
MouseTouchAdaptor.cpp
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  */
18 
19 #include "MouseTouchAdaptor.h"
20 
21 #include <qpa/qwindowsysteminterface.h>
22 
23 #include <QCoreApplication>
24 #include <QMouseEvent>
25 #include <QTest>
26 
27 using QTest::QTouchEventSequence;
28 
29 namespace {
30 MouseTouchAdaptor *g_instance = nullptr;
31 
32 Qt::MouseButton translateMouseButton(xcb_button_t detail)
33 {
34  switch (detail) {
35  case 1: return Qt::LeftButton;
36  case 2: return Qt::MidButton;
37  case 3: return Qt::RightButton;
38  // Button values 4-7 are Wheel events
39  default: return Qt::NoButton;
40  }
41 }
42 } // end of anonymous namespace
43 
44 MouseTouchAdaptor::MouseTouchAdaptor()
45  : QObject(nullptr), m_leftButtonIsPressed(false), m_enabled(true)
46 {
47  QCoreApplication::instance()->installNativeEventFilter(this);
48 
49  m_touchDevice = new QTouchDevice;
50  m_touchDevice->setType(QTouchDevice::TouchScreen);
51  QWindowSystemInterface::registerTouchDevice(m_touchDevice);
52 }
53 
54 MouseTouchAdaptor::~MouseTouchAdaptor()
55 {
56  g_instance = nullptr;
57 }
58 
59 MouseTouchAdaptor* MouseTouchAdaptor::instance()
60 {
61  if (!g_instance) {
62  g_instance = new MouseTouchAdaptor;
63  }
64 
65  return g_instance;
66 }
67 
68 bool MouseTouchAdaptor::nativeEventFilter(const QByteArray & eventType,
69  void * message, long * /*result*/)
70 {
71  if (!m_enabled) {
72  return false;
73  }
74 
75  if (eventType != "xcb_generic_event_t") {
76  // wrong backend.
77  qWarning("MouseTouchAdaptor: XCB backend not in use. Adaptor inoperative!");
78  return false;
79  }
80 
81  xcb_generic_event_t *xcbEvent = static_cast<xcb_generic_event_t *>(message);
82 
83  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;
93  default:
94  return false;
95  break;
96  };
97 }
98 
99 bool MouseTouchAdaptor::handleButtonPress(xcb_button_press_event_t *pressEvent)
100 {
101  Qt::MouseButton button = translateMouseButton(pressEvent->detail);
102 
103  // Just eat the event if it wasn't a left mouse press
104  if (button != Qt::LeftButton)
105  return true;
106 
107  QWindow *targetWindow = findQWindowWithXWindowID(static_cast<WId>(pressEvent->event));
108 
109  QPoint windowPos(pressEvent->event_x / targetWindow->devicePixelRatio(), pressEvent->event_y / targetWindow->devicePixelRatio());
110 
111  QTouchEventSequence touchEvent = QTest::touchEvent(targetWindow, m_touchDevice,
112  false /* autoCommit */);
113  touchEvent.press(0 /* touchId */, windowPos);
114  touchEvent.commit(false /* processEvents */);
115 
116  m_leftButtonIsPressed = true;
117  return true;
118 }
119 
120 bool MouseTouchAdaptor::handleButtonRelease(xcb_button_release_event_t *releaseEvent)
121 {
122  Qt::MouseButton button = translateMouseButton(releaseEvent->detail);
123 
124  // Just eat the event if it wasn't a left mouse release
125  if (button != Qt::LeftButton)
126  return true;
127 
128  QWindow *targetWindow = findQWindowWithXWindowID(static_cast<WId>(releaseEvent->event));
129 
130  QPoint windowPos(releaseEvent->event_x / targetWindow->devicePixelRatio(), releaseEvent->event_y / targetWindow->devicePixelRatio());
131 
132  QTouchEventSequence touchEvent = QTest::touchEvent(targetWindow, m_touchDevice,
133  false /* autoCommit */);
134  touchEvent.release(0 /* touchId */, windowPos);
135  touchEvent.commit(false /* processEvents */);
136 
137  m_leftButtonIsPressed = false;
138  return true;
139 }
140 
141 bool MouseTouchAdaptor::handleMotionNotify(xcb_motion_notify_event_t *event)
142 {
143  if (!m_leftButtonIsPressed) {
144  return true;
145  }
146 
147  QWindow *targetWindow = findQWindowWithXWindowID(static_cast<WId>(event->event));
148 
149  QPoint windowPos(event->event_x / targetWindow->devicePixelRatio(), event->event_y / targetWindow->devicePixelRatio());
150 
151  QTouchEventSequence touchEvent = QTest::touchEvent(targetWindow, m_touchDevice,
152  false /* autoCommit */);
153  touchEvent.move(0 /* touchId */, windowPos);
154  touchEvent.commit(false /* processEvents */);
155 
156  return true;
157 }
158 
159 QWindow *MouseTouchAdaptor::findQWindowWithXWindowID(WId windowId)
160 {
161  QWindowList windowList = QGuiApplication::topLevelWindows();
162  QWindow *foundWindow = nullptr;
163 
164  int i = 0;
165  while (!foundWindow && i < windowList.count()) {
166  QWindow *window = windowList[i];
167  if (window->winId() == windowId) {
168  foundWindow = window;
169  } else {
170  ++i;
171  }
172  }
173 
174  Q_ASSERT(foundWindow);
175  return foundWindow;
176 }
177 
178 bool MouseTouchAdaptor::enabled() const
179 {
180  return m_enabled;
181 }
182 
183 void MouseTouchAdaptor::setEnabled(bool value)
184 {
185  if (value != m_enabled) {
186  m_enabled = value;
187  Q_EMIT enabledChanged(value);
188  }
189 }