on 8633fc7 help sign in
unity8/plugins/Ubuntu/Gestures/TouchGate.cpp
285 lines 8.7 kB history raw
1/*
2 * Copyright (C) 2014-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 */
17#include "TouchGate.h"
19#include <QCoreApplication>
20#include <QDebug>
21#include <QQuickWindow>
23#include <TouchOwnershipEvent.h>
24#include <TouchRegistry.h>
26#if TOUCHGATE_DEBUG
27#define ugDebug(params) qDebug().nospace() << "[TouchGate(" << (void*)this << ")] " << params
28#include <DebugHelpers.h>
29#else // TOUCHGATE_DEBUG
30#define ugDebug(params) ((void)0)
31#endif // TOUCHGATE_DEBUG
33TouchGate::TouchGate(QQuickItem *parent)
34 : QQuickItem(parent)
36 connect(this, &QQuickItem::enabledChanged,
37 this, &TouchGate::onEnabledChanged);
40bool TouchGate::event(QEvent *e)
42 if (e->type() == TouchOwnershipEvent::touchOwnershipEventType()) {
43 touchOwnershipEvent(static_cast<TouchOwnershipEvent *>(e));
44 return true;
45 } else {
46 return QQuickItem::event(e);
47 }
50void TouchGate::touchEvent(QTouchEvent *event)
52 ugDebug("got touch event" << qPrintable(touchEventToString(event)));
53 event->accept();
55 const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
56 QList<QTouchEvent::TouchPoint> validTouchPoints;
57 bool ownsAllTouches = true;
58 for (int i = 0; i < touchPoints.count(); ++i) {
59 const QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
61 if (touchPoint.state() == Qt::TouchPointPressed) {
62 Q_ASSERT(!m_touchInfoMap.contains(touchPoint.id()));
63 m_touchInfoMap[touchPoint.id()].ownership = OwnershipRequested;
64 m_touchInfoMap[touchPoint.id()].ended = false;
65 TouchRegistry::instance()->requestTouchOwnership(touchPoint.id(), this);
66 }
68 if (m_touchInfoMap.contains(touchPoint.id())) {
69 validTouchPoints.append(touchPoint);
71 ownsAllTouches &= m_touchInfoMap[touchPoint.id()].ownership == OwnershipGranted;
73 if (touchPoint.state() == Qt::TouchPointReleased) {
74 m_touchInfoMap[touchPoint.id()].ended = true;
75 }
76 }
78 }
80 if (validTouchPoints.isEmpty()) {
81 // nothing to do.
82 return;
83 }
85 if (ownsAllTouches) {
86 if (m_storedEvents.isEmpty()) {
87 // let it pass through
88 removeTouchInfoForEndedTouches(validTouchPoints);
89 m_dispatcher.dispatch(event->device(), event->modifiers(), validTouchPoints,
90 event->window(), event->timestamp());
91 } else {
92 // Retain the event to ensure TouchGate dispatches them in order.
93 // Otherwise the current event would come before the stored ones, which are older.
94 ugDebug("Storing event because thouches " << qPrintable(oldestPendingTouchIdsString())
95 << " are still pending ownership.");
96 storeTouchEvent(event->device(), event->modifiers(), validTouchPoints,
97 event->window(), event->timestamp());
98 }
99 } else {
100 // Retain events that have unowned touches
101 storeTouchEvent(event->device(), event->modifiers(), validTouchPoints,
102 event->window(), event->timestamp());
103 }
106void TouchGate::itemChange(ItemChange change, const ItemChangeData &value)
108 if (change == QQuickItem::ItemSceneChange) {
109 if (value.window != nullptr) {
110 value.window->installEventFilter(TouchRegistry::instance());
111 }
112 }
115void TouchGate::touchOwnershipEvent(TouchOwnershipEvent *event)
117 // TODO: Optimization: batch those actions as TouchOwnershipEvents
118 // might come one right after the other.
120 if (m_touchInfoMap.contains(event->touchId())) {
121 TouchInfo &touchInfo = m_touchInfoMap[event->touchId()];
123 if (event->gained()) {
124 ugDebug("Got ownership of touch " << event->touchId());
125 touchInfo.ownership = OwnershipGranted;
126 } else {
127 ugDebug("Lost ownership of touch " << event->touchId());
128 m_touchInfoMap.remove(event->touchId());
129 removeTouchFromStoredEvents(event->touchId());
130 }
132 dispatchFullyOwnedEvents();
133 } else {
134 // Ignore it. It probably happened because the TouchGate got disabled
135 // between the time it requested ownership and the time it got it.
136 }
139bool TouchGate::isTouchPointOwned(int touchId) const
141 return m_touchInfoMap[touchId].ownership == OwnershipGranted;
144void TouchGate::storeTouchEvent(QTouchDevice *device,
145 Qt::KeyboardModifiers modifiers,
146 const QList<QTouchEvent::TouchPoint> &touchPoints,
147 QWindow *window,
148 ulong timestamp)
150 ugDebug("Storing" << touchPoints);
151 TouchEvent event(device, modifiers, touchPoints, window, timestamp);
152 m_storedEvents.append(std::move(event));
155void TouchGate::removeTouchFromStoredEvents(int touchId)
157 int i = 0;
158 while (i < m_storedEvents.count()) {
159 TouchEvent &event = m_storedEvents[i];
160 bool removed = event.removeTouch(touchId);
162 if (removed && event.touchPoints.isEmpty()) {
163 m_storedEvents.removeAt(i);
164 } else {
165 ++i;
166 }
167 }
170void TouchGate::dispatchFullyOwnedEvents()
172 while (!m_storedEvents.isEmpty() && eventIsFullyOwned(m_storedEvents.first())) {
173 TouchEvent event = m_storedEvents.takeFirst();
174 dispatchTouchEventToTarget(event);
175 }
178#if TOUCHGATE_DEBUG
179QString TouchGate::oldestPendingTouchIdsString()
181 Q_ASSERT(!m_storedEvents.isEmpty());
183 QString str;
185 const auto &touchPoints = m_storedEvents.first().touchPoints;
186 for (int i = 0; i < touchPoints.count(); ++i) {
187 if (!isTouchPointOwned(touchPoints[i].id())) {
188 if (!str.isEmpty()) {
189 str.append(", ");
190 }
191 str.append(QString::number(touchPoints[i].id()));
192 }
193 }
195 return str;
197#endif
199bool TouchGate::eventIsFullyOwned(const TouchGate::TouchEvent &event) const
201 for (int i = 0; i < event.touchPoints.count(); ++i) {
202 if (!isTouchPointOwned(event.touchPoints[i].id())) {
203 return false;
204 }
205 }
207 return true;
210void TouchGate::setTargetItem(QQuickItem *item)
212 // TODO: changing the target item while dispatch of touch events is taking place will
213 // create a mess
215 if (item == m_dispatcher.targetItem())
216 return;
218 m_dispatcher.setTargetItem(item);
219 Q_EMIT targetItemChanged(item);
222void TouchGate::dispatchTouchEventToTarget(const TouchEvent &event)
224 removeTouchInfoForEndedTouches(event.touchPoints);
225 m_dispatcher.dispatch(event.device,
226 event.modifiers,
227 event.touchPoints,
228 event.window,
229 event.timestamp);
232void TouchGate::removeTouchInfoForEndedTouches(const QList<QTouchEvent::TouchPoint> &touchPoints)
234 for (int i = 0; i < touchPoints.size(); ++i) {\
235 const QTouchEvent::TouchPoint &touchPoint = touchPoints.at(i);
237 if (touchPoint.state() == Qt::TouchPointReleased) {
238 Q_ASSERT(m_touchInfoMap.contains(touchPoint.id()));
239 Q_ASSERT(m_touchInfoMap[touchPoint.id()].ended);
240 Q_ASSERT(m_touchInfoMap[touchPoint.id()].ownership == OwnershipGranted);
241 m_touchInfoMap.remove(touchPoint.id());
242 }
243 }
246void TouchGate::onEnabledChanged()
248 ugDebug(" enabled = " << isEnabled());
249 if (!isEnabled()) {
250 reset();
251 }
254void TouchGate::reset()
256 m_storedEvents.clear();
257 m_touchInfoMap.clear();
258 m_dispatcher.reset();
261TouchGate::TouchEvent::TouchEvent(QTouchDevice *device,
262 Qt::KeyboardModifiers modifiers,
263 const QList<QTouchEvent::TouchPoint> &touchPoints,
264 QWindow *window,
265 ulong timestamp)
266 : device(device)
267 , modifiers(modifiers)
268 , touchPoints(touchPoints)
269 , window(window)
270 , timestamp(timestamp)
274bool TouchGate::TouchEvent::removeTouch(int touchId)
276 bool removed = false;
277 for (int i = 0; i < touchPoints.count() && !removed; ++i) {
278 if (touchPoints[i].id() == touchId) {
279 touchPoints.removeAt(i);
280 removed = true;
281 }
282 }
284 return removed;