on xenial help sign in
TouchRegistry: remove null candidates from list of candidates Fixes: #1473492

Approved by: Albert Astals Cid

commit47c389c08b7f909509c26b0aa23a5098c6bac3d1 authorDaniel d'Andrada <daniel.dandrada@canonical.com> dateWed 29 Jul 2015, 12:38 (11 years ago) parentf92e7acdb1d2cf
3 files changed+111−15
libs/UbuntuGestures/TouchRegistry.cpp+60−14 libs/UbuntuGestures/TouchRegistry.h+4−1 tests/libs/UbuntuGestures/tst_TouchRegistry.cpp+47
libs/UbuntuGestures/TouchRegistry.cpp+60−14
@@ -271,6 +271,8 @@ void TouchRegistry::addCandidateOwnerForTouch(int id, QQuickItem *candidate)
271271 this, &TouchRegistry::rejectCandidateOwnerForTouch);
272272
273273 touchInfo->candidates.append(candidateInfo);
274+
275+ connect(candidate, &QObject::destroyed, this, [=](){ pruneNullCandidatesForTouch(id); });
274276}
275277
276278void TouchRegistry::addTouchWatcher(int touchId, QQuickItem *watcher)
@@ -296,25 +298,53 @@ void TouchRegistry::removeCandidateOwnerForTouch(int id, QQuickItem *candidate)
296298 Pool<TouchInfo>::Iterator touchInfo = findTouchInfo(id);
297299 if (!touchInfo) { qFatal("TouchRegistry: Failed to find TouchInfo"); }
298300
299− int indexRemoved = -1;
300301
301302 // TODO: check if the candidate is in fact the owner of the touch
302303
303− for (int i = 0; i < touchInfo->candidates.count() && indexRemoved == -1; ++i) {
304− CandidateInfo &candidateInfo = touchInfo->candidates[i];
305− if (candidateInfo.item == candidate) {
306− Q_ASSERT(i > 0 || candidateInfo.state == CandidateInfo::Undecided);
307− if (i == 0 && candidateInfo.state != CandidateInfo::Undecided) {
308− qCritical("TouchRegistry: touch owner is being removed.");
309− }
310− delete candidateInfo.inactivityTimer;
311− candidateInfo.inactivityTimer = nullptr;
312− touchInfo->candidates.removeAt(i);
313− indexRemoved = i;
304+ bool removed = false;
305+ for (int i = 0; i < touchInfo->candidates.count() && !removed; ++i) {
306+ if (touchInfo->candidates[i].item == candidate) {
307+ removeCandidateOwnerForTouchByIndex(touchInfo, i);
308+ removed = true;
314309 }
315310 }
311+}
312+
313+void TouchRegistry::pruneNullCandidatesForTouch(int touchId)
314+{
315+ #if TOUCHREGISTRY_DEBUG
316+ UG_DEBUG << "pruneNullCandidatesForTouch touchId" << touchId;
317+ #endif
318+
319+ Pool<TouchInfo>::Iterator touchInfo = findTouchInfo(touchId);
320+ if (!touchInfo) {
321+ // doesn't matter as touch is already gone.
322+ return;
323+ }
316324
317− if (indexRemoved == 0) {
325+ int i = 0;
326+ while (i < touchInfo->candidates.count()) {
327+ if (touchInfo->candidates[i].item.isNull()) {
328+ removeCandidateOwnerForTouchByIndex(touchInfo, i);
329+ } else {
330+ ++i;
331+ }
332+ }
333+}
334+
335+void TouchRegistry::removeCandidateOwnerForTouchByIndex(Pool<TouchRegistry::TouchInfo>::Iterator &touchInfo,
336+ int candidateIndex)
337+{
338+ // TODO: check if the candidate is in fact the owner of the touch
339+
340+ Q_ASSERT(candidateIndex < touchInfo->candidates.count());
341+
342+ if (candidateIndex == 0 && touchInfo->candidates[candidateIndex].state != CandidateInfo::Undecided) {
343+ qCritical("TouchRegistry: touch owner is being removed.");
344+ }
345+ removeCandidateHelper(touchInfo, candidateIndex);
346+
347+ if (candidateIndex == 0) {
318348 // the top candidate has been removed. if the new top candidate
319349 // wants the touch let him know he's now the owner.
320350 if (touchInfo->isOwned()) {
@@ -359,6 +389,7 @@ void TouchRegistry::requestTouchOwnership(int id, QQuickItem *candidate)
359389 touchInfo->candidates.append(candidateInfo);
360390 // it's the last one
361391 candidateIndex = touchInfo->candidates.count() - 1;
392+ connect(candidate, &QObject::destroyed, this, [=](){ pruneNullCandidatesForTouch(id); });
362393 }
363394
364395 // If it's the top candidate it means it's now the owner. Let
@@ -430,7 +461,7 @@ void TouchRegistry::rejectCandidateOwnerForTouch(int id, QQuickItem *candidate)
430461 QCoreApplication::sendEvent(candidate, &lostOwnershipEvent);
431462 }
432463
433− touchInfo->candidates.removeAt(rejectedCandidateIndex);
464+ removeCandidateHelper(touchInfo, rejectedCandidateIndex);
434465
435466 if (rejectedCandidateIndex == 0) {
436467 // the top candidate has been removed. if the new top candidate
@@ -441,6 +472,21 @@ void TouchRegistry::rejectCandidateOwnerForTouch(int id, QQuickItem *candidate)
441472 }
442473}
443474
475+void TouchRegistry::removeCandidateHelper(Pool<TouchInfo>::Iterator &touchInfo, int candidateIndex)
476+{
477+ {
478+ CandidateInfo &candidateInfo = touchInfo->candidates[candidateIndex];
479+
480+ delete candidateInfo.inactivityTimer;
481+ candidateInfo.inactivityTimer = nullptr;
482+
483+ if (candidateInfo.item) {
484+ disconnect(candidateInfo.item.data(), nullptr, this, nullptr);
485+ }
486+ }
487+ touchInfo->candidates.removeAt(candidateIndex);
488+}
489+
444490////////////////////////////////////// TouchRegistry::TouchInfo ////////////////////////////////////
445491
446492TouchRegistry::TouchInfo::TouchInfo(int id)
libs/UbuntuGestures/TouchRegistry.h+4−1
@@ -149,7 +149,6 @@ private:
149149 // yet because of undecided candidates higher up.
150150 InterimOwner = 2
151151 } state;
152− // TODO: Prune candidates that become null and resolve ownership accordingly.
153152 QPointer<QQuickItem> item;
154153 QPointer<UbuntuGestures::CandidateInactivityTimer> inactivityTimer;
155154 };
@@ -172,6 +171,10 @@ private:
172171 QList<QPointer<QQuickItem>> watchers;
173172 };
174173
174+ void pruneNullCandidatesForTouch(int touchId);
175+ void removeCandidateOwnerForTouchByIndex(Pool<TouchInfo>::Iterator &touchInfo, int candidateIndex);
176+ void removeCandidateHelper(Pool<TouchInfo>::Iterator &touchInfo, int candidateIndex);
177+
175178 Pool<TouchInfo>::Iterator findTouchInfo(int id);
176179
177180 void deliverTouchUpdatesToUndecidedCandidatesAndWatchers(const QTouchEvent *event);
tests/libs/UbuntuGestures/tst_TouchRegistry.cpp+47
@@ -67,6 +67,7 @@ private Q_SLOTS:
6767 void rejectingTouchfterItsEnd();
6868 void removeOldUndecidedCandidates();
6969 void interimOwnerWontGetUnownedTouchEvents();
70+ void candidateVanishes();
7071
7172private:
7273 TouchRegistry *touchRegistry;
@@ -817,6 +818,52 @@ void tst_TouchRegistry::interimOwnerWontGetUnownedTouchEvents()
817818 QCOMPARE(interimOwner.unownedTouchEvents.count(), 0);
818819}
819820
821+/*
822+ Regression test for https://bugs.launchpad.net/ubuntu/+source/unity8/+bug/1473492
823+
824+ Bug caused by candidates getting deleted before ownership resolution
825+ */
826+void tst_TouchRegistry::candidateVanishes()
827+{
828+ DummyCandidate mainCandidate;
829+ DummyCandidate *missingCandidate = new DummyCandidate;
830+
831+ {
832+ QList<QTouchEvent::TouchPoint> touchPoints;
833+ touchPoints.append(QTouchEvent::TouchPoint(0));
834+ touchPoints[0].setState(Qt::TouchPointPressed);
835+ QTouchEvent touchEvent(QEvent::TouchBegin,
836+ 0 /* device */,
837+ Qt::NoModifier,
838+ Qt::TouchPointPressed,
839+ touchPoints);
840+ touchRegistry->update(&touchEvent);
841+ }
842+
843+ touchRegistry->addCandidateOwnerForTouch(0, &mainCandidate);
844+ touchRegistry->addCandidateOwnerForTouch(0, missingCandidate);
845+
846+ {
847+ QList<QTouchEvent::TouchPoint> touchPoints;
848+ touchPoints.append(QTouchEvent::TouchPoint(0));
849+ touchPoints[0].setState(Qt::TouchPointMoved);
850+ QTouchEvent touchEvent(QEvent::TouchUpdate,
851+ 0 /* device */,
852+ Qt::NoModifier,
853+ Qt::TouchPointMoved,
854+ touchPoints);
855+ touchRegistry->update(&touchEvent);
856+ }
857+
858+ // This candidate suddenly disappears without having removed its candidacy.
859+ delete missingCandidate;
860+ missingCandidate = nullptr;
861+
862+ touchRegistry->requestTouchOwnership(0, &mainCandidate);
863+
864+ QVERIFY(mainCandidate.ownedTouches.contains(0));
865+}
866+
820867////////////// TouchMemento //////////
821868
822869TouchMemento::TouchMemento(const QTouchEvent *touchEvent)