on d496e45 help sign in
unity8/plugins/Ubuntu/Gestures/TouchGate.cpp
287 lines 8.8 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 <UbuntuGestures/private/touchownershipevent_p.h>
24#include <UbuntuGestures/private/touchregistry_p.h>
26#if TOUCHGATE_DEBUG
27#define ugDebug(params) qDebug().nospace() << "[TouchGate(" << (void*)this << ")] " << params
28#include <UbuntuGestures/private/debughelpers_p.h>
29#else // TOUCHGATE_DEBUG
30#define ugDebug(params) ((void)0)
31#endif // TOUCHGATE_DEBUG
33UG_USE_NAMESPACE
35TouchGate::TouchGate(QQuickItem *parent)
36 : QQuickItem(parent)
38 connect(this, &QQuickItem::enabledChanged,
39 this, &TouchGate::onEnabledChanged);
42bool TouchGate::event(QEvent *e)
44 if (e->type() == TouchOwnershipEvent::touchOwnershipEventType()) {
45 touchOwnershipEvent(static_cast<TouchOwnershipEvent*>(e));
46 return true;
47 } else {
48 return QQuickItem::event(e);
49 }
52void TouchGate::touchEvent(QTouchEvent *event)
54 ugDebug("got touch event" << qPrintable(touchEventToString(event)));
55 event->accept();
57 const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
58 QList<QTouchEvent::TouchPoint> validTouchPoints;
59 bool ownsAllTouches = true;
60 for (int i = 0; i < touchPoints.count(); ++i) {
61 const QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
63 if (touchPoint.state() == Qt::TouchPointPressed) {
64// Q_ASSERT(!m_touchInfoMap.contains(touchPoint.id()));
65 m_touchInfoMap[touchPoint.id()].ownership = OwnershipRequested;
66 m_touchInfoMap[touchPoint.id()].ended = false;
67 TouchRegistry::instance()->requestTouchOwnership(touchPoint.id(), this);
68 }
70 if (m_touchInfoMap.contains(touchPoint.id())) {
71 validTouchPoints.append(touchPoint);
73 ownsAllTouches &= m_touchInfoMap[touchPoint.id()].ownership == OwnershipGranted;
75 if (touchPoint.state() == Qt::TouchPointReleased) {
76 m_touchInfoMap[touchPoint.id()].ended = true;
77 }
78 }
80 }
82 if (validTouchPoints.isEmpty()) {
83 // nothing to do.
84 return;
85 }
87 if (ownsAllTouches) {
88 if (m_storedEvents.isEmpty()) {
89 // let it pass through
90 removeTouchInfoForEndedTouches(validTouchPoints);
91 m_dispatcher.dispatch(event->device(), event->modifiers(), validTouchPoints,
92 event->window(), event->timestamp());
93 } else {
94 // Retain the event to ensure TouchGate dispatches them in order.
95 // Otherwise the current event would come before the stored ones, which are older.
96 ugDebug("Storing event because thouches " << qPrintable(oldestPendingTouchIdsString())
97 << " are still pending ownership.");
98 storeTouchEvent(event->device(), event->modifiers(), validTouchPoints,
99 event->window(), event->timestamp());
100 }
101 } else {
102 // Retain events that have unowned touches
103 storeTouchEvent(event->device(), event->modifiers(), validTouchPoints,
104 event->window(), event->timestamp());
105 }
108void TouchGate::itemChange(ItemChange change, const ItemChangeData &value)
110 if (change == QQuickItem::ItemSceneChange) {
111 if (value.window != nullptr) {
112 value.window->installEventFilter(TouchRegistry::instance());
113 }
114 }
117void TouchGate::touchOwnershipEvent(TouchOwnershipEvent *event)
119 // TODO: Optimization: batch those actions as TouchOwnershipEvents
120 // might come one right after the other.
122 if (m_touchInfoMap.contains(event->touchId())) {
123 TouchInfo &touchInfo = m_touchInfoMap[event->touchId()];
125 if (event->gained()) {
126 ugDebug("Got ownership of touch " << event->touchId());
127 touchInfo.ownership = OwnershipGranted;
128 } else {
129 ugDebug("Lost ownership of touch " << event->touchId());
130 m_touchInfoMap.remove(event->touchId());
131 removeTouchFromStoredEvents(event->touchId());
132 }
134 dispatchFullyOwnedEvents();
135 } else {
136 // Ignore it. It probably happened because the TouchGate got disabled
137 // between the time it requested ownership and the time it got it.
138 }
141bool TouchGate::isTouchPointOwned(int touchId) const
143 return m_touchInfoMap[touchId].ownership == OwnershipGranted;
146void TouchGate::storeTouchEvent(QTouchDevice *device,
147 Qt::KeyboardModifiers modifiers,
148 const QList<QTouchEvent::TouchPoint> &touchPoints,
149 QWindow *window,
150 ulong timestamp)
152 ugDebug("Storing" << touchPoints);
153 TouchEvent event(device, modifiers, touchPoints, window, timestamp);
154 m_storedEvents.append(std::move(event));
157void TouchGate::removeTouchFromStoredEvents(int touchId)
159 int i = 0;
160 while (i < m_storedEvents.count()) {
161 TouchEvent &event = m_storedEvents[i];
162 bool removed = event.removeTouch(touchId);
164 if (removed && event.touchPoints.isEmpty()) {
165 m_storedEvents.removeAt(i);
166 } else {
167 ++i;
168 }
169 }
172void TouchGate::dispatchFullyOwnedEvents()
174 while (!m_storedEvents.isEmpty() && eventIsFullyOwned(m_storedEvents.first())) {
175 TouchEvent event = m_storedEvents.takeFirst();
176 dispatchTouchEventToTarget(event);
177 }
180#if TOUCHGATE_DEBUG
181QString TouchGate::oldestPendingTouchIdsString()
183 Q_ASSERT(!m_storedEvents.isEmpty());
185 QString str;
187 const auto &touchPoints = m_storedEvents.first().touchPoints;
188 for (int i = 0; i < touchPoints.count(); ++i) {
189 if (!isTouchPointOwned(touchPoints[i].id())) {
190 if (!str.isEmpty()) {
191 str.append(", ");
192 }
193 str.append(QString::number(touchPoints[i].id()));
194 }
195 }
197 return str;
199#endif
201bool TouchGate::eventIsFullyOwned(const TouchGate::TouchEvent &event) const
203 for (int i = 0; i < event.touchPoints.count(); ++i) {
204 if (!isTouchPointOwned(event.touchPoints[i].id())) {
205 return false;
206 }
207 }
209 return true;
212void TouchGate::setTargetItem(QQuickItem *item)
214 // TODO: changing the target item while dispatch of touch events is taking place will
215 // create a mess
217 if (item == m_dispatcher.targetItem())
218 return;
220 m_dispatcher.setTargetItem(item);
221 Q_EMIT targetItemChanged(item);
224void TouchGate::dispatchTouchEventToTarget(const TouchEvent &event)
226 removeTouchInfoForEndedTouches(event.touchPoints);
227 m_dispatcher.dispatch(event.device,
228 event.modifiers,
229 event.touchPoints,
230 event.window,
231 event.timestamp);
234void TouchGate::removeTouchInfoForEndedTouches(const QList<QTouchEvent::TouchPoint> &touchPoints)
236 for (int i = 0; i < touchPoints.size(); ++i) {\
237 const QTouchEvent::TouchPoint &touchPoint = touchPoints.at(i);
239 if (touchPoint.state() == Qt::TouchPointReleased) {
240 Q_ASSERT(m_touchInfoMap.contains(touchPoint.id()));
241 Q_ASSERT(m_touchInfoMap[touchPoint.id()].ended);
242 Q_ASSERT(m_touchInfoMap[touchPoint.id()].ownership == OwnershipGranted);
243 m_touchInfoMap.remove(touchPoint.id());
244 }
245 }
248void TouchGate::onEnabledChanged()
250 ugDebug(" enabled = " << isEnabled());
251 if (!isEnabled()) {
252 reset();
253 }
256void TouchGate::reset()
258 m_storedEvents.clear();
259 m_touchInfoMap.clear();
260 m_dispatcher.reset();
263TouchGate::TouchEvent::TouchEvent(QTouchDevice *device,
264 Qt::KeyboardModifiers modifiers,
265 const QList<QTouchEvent::TouchPoint> &touchPoints,
266 QWindow *window,
267 ulong timestamp)
268 : device(device)
269 , modifiers(modifiers)
270 , touchPoints(touchPoints)
271 , window(window)
272 , timestamp(timestamp)
276bool TouchGate::TouchEvent::removeTouch(int touchId)
278 bool removed = false;
279 for (int i = 0; i < touchPoints.count() && !removed; ++i) {
280 if (touchPoints[i].id() == touchId) {
281 touchPoints.removeAt(i);
282 removed = true;
283 }
284 }
286 return removed;