on 8a93021 help sign in
unity8/plugins/Ubuntu/Gestures/TouchGate.cpp
275 lines 8.4 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>
22#include <TouchOwnershipEvent.h>
23#include <TouchRegistry.h>
25#if TOUCHGATE_DEBUG
26#define ugDebug(params) qDebug().nospace() << "[TouchGate(" << (void*)this << ")] " << params
27#include <DebugHelpers.h>
28#else // TOUCHGATE_DEBUG
29#define ugDebug(params) ((void)0)
30#endif // TOUCHGATE_DEBUG
32TouchGate::TouchGate(QQuickItem *parent)
33 : QQuickItem(parent)
35 connect(this, &QQuickItem::enabledChanged,
36 this, &TouchGate::onEnabledChanged);
39bool TouchGate::event(QEvent *e)
41 if (e->type() == TouchOwnershipEvent::touchOwnershipEventType()) {
42 touchOwnershipEvent(static_cast<TouchOwnershipEvent *>(e));
43 return true;
44 } else {
45 return QQuickItem::event(e);
46 }
49void TouchGate::touchEvent(QTouchEvent *event)
51 ugDebug("got touch event" << qPrintable(touchEventToString(event)));
52 event->accept();
54 const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
55 QList<QTouchEvent::TouchPoint> validTouchPoints;
56 bool ownsAllTouches = true;
57 for (int i = 0; i < touchPoints.count(); ++i) {
58 const QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
60 if (touchPoint.state() == Qt::TouchPointPressed) {
61 Q_ASSERT(!m_touchInfoMap.contains(touchPoint.id()));
62 m_touchInfoMap[touchPoint.id()].ownership = OwnershipRequested;
63 m_touchInfoMap[touchPoint.id()].ended = false;
64 TouchRegistry::instance()->requestTouchOwnership(touchPoint.id(), this);
65 }
67 if (m_touchInfoMap.contains(touchPoint.id())) {
68 validTouchPoints.append(touchPoint);
70 ownsAllTouches &= m_touchInfoMap[touchPoint.id()].ownership == OwnershipGranted;
72 if (touchPoint.state() == Qt::TouchPointReleased) {
73 m_touchInfoMap[touchPoint.id()].ended = true;
74 }
75 }
77 }
79 if (validTouchPoints.isEmpty()) {
80 // nothing to do.
81 return;
82 }
84 if (ownsAllTouches) {
85 if (m_storedEvents.isEmpty()) {
86 // let it pass through
87 removeTouchInfoForEndedTouches(validTouchPoints);
88 m_dispatcher.dispatch(event->device(), event->modifiers(), validTouchPoints,
89 event->window(), event->timestamp());
90 } else {
91 // Retain the event to ensure TouchGate dispatches them in order.
92 // Otherwise the current event would come before the stored ones, which are older.
93 ugDebug("Storing event because thouches " << qPrintable(oldestPendingTouchIdsString())
94 << " are still pending ownership.");
95 storeTouchEvent(event->device(), event->modifiers(), validTouchPoints,
96 event->window(), event->timestamp());
97 }
98 } else {
99 // Retain events that have unowned touches
100 storeTouchEvent(event->device(), event->modifiers(), validTouchPoints,
101 event->window(), event->timestamp());
102 }
105void TouchGate::touchOwnershipEvent(TouchOwnershipEvent *event)
107 // TODO: Optimization: batch those actions as TouchOwnershipEvents
108 // might come one right after the other.
110 if (m_touchInfoMap.contains(event->touchId())) {
111 TouchInfo &touchInfo = m_touchInfoMap[event->touchId()];
113 if (event->gained()) {
114 ugDebug("Got ownership of touch " << event->touchId());
115 touchInfo.ownership = OwnershipGranted;
116 } else {
117 ugDebug("Lost ownership of touch " << event->touchId());
118 m_touchInfoMap.remove(event->touchId());
119 removeTouchFromStoredEvents(event->touchId());
120 }
122 dispatchFullyOwnedEvents();
123 } else {
124 // Ignore it. It probably happened because the TouchGate got disabled
125 // between the time it requested ownership and the time it got it.
126 }
129bool TouchGate::isTouchPointOwned(int touchId) const
131 return m_touchInfoMap[touchId].ownership == OwnershipGranted;
134void TouchGate::storeTouchEvent(QTouchDevice *device,
135 Qt::KeyboardModifiers modifiers,
136 const QList<QTouchEvent::TouchPoint> &touchPoints,
137 QWindow *window,
138 ulong timestamp)
140 ugDebug("Storing" << touchPoints);
141 TouchEvent event(device, modifiers, touchPoints, window, timestamp);
142 m_storedEvents.append(std::move(event));
145void TouchGate::removeTouchFromStoredEvents(int touchId)
147 int i = 0;
148 while (i < m_storedEvents.count()) {
149 TouchEvent &event = m_storedEvents[i];
150 bool removed = event.removeTouch(touchId);
152 if (removed && event.touchPoints.isEmpty()) {
153 m_storedEvents.removeAt(i);
154 } else {
155 ++i;
156 }
157 }
160void TouchGate::dispatchFullyOwnedEvents()
162 while (!m_storedEvents.isEmpty() && eventIsFullyOwned(m_storedEvents.first())) {
163 TouchEvent event = m_storedEvents.takeFirst();
164 dispatchTouchEventToTarget(event);
165 }
168#if TOUCHGATE_DEBUG
169QString TouchGate::oldestPendingTouchIdsString()
171 Q_ASSERT(!m_storedEvents.isEmpty());
173 QString str;
175 const auto &touchPoints = m_storedEvents.first().touchPoints;
176 for (int i = 0; i < touchPoints.count(); ++i) {
177 if (!isTouchPointOwned(touchPoints[i].id())) {
178 if (!str.isEmpty()) {
179 str.append(", ");
180 }
181 str.append(QString::number(touchPoints[i].id()));
182 }
183 }
185 return str;
187#endif
189bool TouchGate::eventIsFullyOwned(const TouchGate::TouchEvent &event) const
191 for (int i = 0; i < event.touchPoints.count(); ++i) {
192 if (!isTouchPointOwned(event.touchPoints[i].id())) {
193 return false;
194 }
195 }
197 return true;
200void TouchGate::setTargetItem(QQuickItem *item)
202 // TODO: changing the target item while dispatch of touch events is taking place will
203 // create a mess
205 if (item == m_dispatcher.targetItem())
206 return;
208 m_dispatcher.setTargetItem(item);
209 Q_EMIT targetItemChanged(item);
212void TouchGate::dispatchTouchEventToTarget(const TouchEvent &event)
214 removeTouchInfoForEndedTouches(event.touchPoints);
215 m_dispatcher.dispatch(event.device,
216 event.modifiers,
217 event.touchPoints,
218 event.window,
219 event.timestamp);
222void TouchGate::removeTouchInfoForEndedTouches(const QList<QTouchEvent::TouchPoint> &touchPoints)
224 for (int i = 0; i < touchPoints.size(); ++i) {\
225 const QTouchEvent::TouchPoint &touchPoint = touchPoints.at(i);
227 if (touchPoint.state() == Qt::TouchPointReleased) {
228 Q_ASSERT(m_touchInfoMap.contains(touchPoint.id()));
229 Q_ASSERT(m_touchInfoMap[touchPoint.id()].ended);
230 Q_ASSERT(m_touchInfoMap[touchPoint.id()].ownership == OwnershipGranted);
231 m_touchInfoMap.remove(touchPoint.id());
232 }
233 }
236void TouchGate::onEnabledChanged()
238 ugDebug(" enabled = " << isEnabled());
239 if (!isEnabled()) {
240 reset();
241 }
244void TouchGate::reset()
246 m_storedEvents.clear();
247 m_touchInfoMap.clear();
248 m_dispatcher.reset();
251TouchGate::TouchEvent::TouchEvent(QTouchDevice *device,
252 Qt::KeyboardModifiers modifiers,
253 const QList<QTouchEvent::TouchPoint> &touchPoints,
254 QWindow *window,
255 ulong timestamp)
256 : device(device)
257 , modifiers(modifiers)
258 , touchPoints(touchPoints)
259 , window(window)
260 , timestamp(timestamp)
264bool TouchGate::TouchEvent::removeTouch(int touchId)
266 bool removed = false;
267 for (int i = 0; i < touchPoints.count() && !removed; ++i) {
268 if (touchPoints[i].id() == touchId) {
269 touchPoints.removeAt(i);
270 removed = true;
271 }
272 }
274 return removed;