summaryrefslogtreecommitdiff
path: root/Xi
diff options
context:
space:
mode:
authorChase Douglas <chase.douglas@canonical.com>2012-04-10 17:12:42 -0700
committerPeter Hutterer <peter.hutterer@who-t.net>2012-04-16 11:30:02 +1000
commit32ece7c09bf0ebc3d99b4078aacebbd44314776a (patch)
treed5d0a2623e367cc589c3f0bc5346bd6a3cdb280a /Xi
parent163b0f375d73c05873fb341652de3ed347337828 (diff)
Ensure sequential touches are pointer emulated sequentially
Issue: * Two sequential touches (i.e. down, up, down, up) * Both are grabbed by a touch grab * Both have a second listener in the form of a pointer grab or selection * The second and first touches are rejected in that order The first touch must be pointer emulated before the second touch, so the second touch must be paused until the first touch is rejected or accepted and all events are delivered to pointer clients. This change ensures all pointer emulated events are emitted sequentially. It necessarily imposes a delay on further touch events when pointer grabs and selections are used, but there is no way around it. Signed-off-by: Chase Douglas <chase.douglas@canonical.com> Reviewed-by: Peter Hutterer <peter.hutterer@who-t.net> Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Diffstat (limited to 'Xi')
-rw-r--r--Xi/exevents.c75
1 files changed, 74 insertions, 1 deletions
diff --git a/Xi/exevents.c b/Xi/exevents.c
index a843e0300..c05c22604 100644
--- a/Xi/exevents.c
+++ b/Xi/exevents.c
@@ -1111,6 +1111,48 @@ EmitTouchEnd(DeviceIntPtr dev, TouchPointInfoPtr ti, int flags, XID resource)
}
/**
+ * Find the oldest touch that still has a pointer emulation client.
+ *
+ * Pointer emulation can only be performed for the oldest touch. Otherwise, the
+ * order of events seen by the client will be wrong. This function helps us find
+ * the next touch to be emulated.
+ *
+ * @param dev The device to find touches for.
+ */
+static TouchPointInfoPtr
+FindOldestPointerEmulatedTouch(DeviceIntPtr dev)
+{
+ TouchPointInfoPtr oldest = NULL;
+ int i;
+
+ for (i = 0; i < dev->touch->num_touches; i++) {
+ TouchPointInfoPtr ti = dev->touch->touches + i;
+ int j;
+
+ if (!ti->active || !ti->emulate_pointer)
+ continue;
+
+ for (j = 0; j < ti->num_listeners; j++) {
+ if (ti->listeners[j].type == LISTENER_POINTER_GRAB ||
+ ti->listeners[j].type == LISTENER_POINTER_REGULAR)
+ break;
+ }
+ if (j == ti->num_listeners)
+ continue;
+
+ if (!oldest) {
+ oldest = ti;
+ continue;
+ }
+
+ if (oldest->client_id - ti->client_id < UINT_MAX / 2)
+ oldest = ti;
+ }
+
+ return oldest;
+}
+
+/**
* If the current owner has rejected the event, deliver the
* TouchOwnership/TouchBegin to the next item in the sprite stack.
*/
@@ -1123,8 +1165,16 @@ TouchPuntToNextOwner(DeviceIntPtr dev, TouchPointInfoPtr ti,
ti->listeners[0].state == LISTENER_EARLY_ACCEPT)
DeliverTouchEvents(dev, ti, (InternalEvent *) ev,
ti->listeners[0].listener);
- else if (ti->listeners[0].state == LISTENER_AWAITING_BEGIN)
+ else if (ti->listeners[0].state == LISTENER_AWAITING_BEGIN) {
+ /* We can't punt to a pointer listener unless all older pointer
+ * emulated touches have been seen already. */
+ if ((ti->listeners[0].type == LISTENER_POINTER_GRAB ||
+ ti->listeners[0].type == LISTENER_POINTER_REGULAR) &&
+ ti != FindOldestPointerEmulatedTouch(dev))
+ return;
+
TouchEventHistoryReplay(ti, dev, ti->listeners[0].listener);
+ }
/* If we've just removed the last grab and the touch has physically
* ended, send a TouchEnd event too and finalise the touch. */
@@ -1139,6 +1189,25 @@ TouchPuntToNextOwner(DeviceIntPtr dev, TouchPointInfoPtr ti,
}
/**
+ * Check the oldest touch to see if it needs to be replayed to its pointer
+ * owner.
+ *
+ * Touch event propagation is paused if it hits a pointer listener while an
+ * older touch with a pointer listener is waiting on accept or reject. This
+ * function will restart propagation of a paused touch if needed.
+ *
+ * @param dev The device to check touches for.
+ */
+static void
+CheckOldestTouch(DeviceIntPtr dev)
+{
+ TouchPointInfoPtr oldest = FindOldestPointerEmulatedTouch(dev);
+
+ if (oldest && oldest->listeners[0].state == LISTENER_AWAITING_BEGIN)
+ TouchPuntToNextOwner(dev, oldest, NULL);
+}
+
+/**
* Process a touch rejection.
*
* @param sourcedev The source device of the touch sequence.
@@ -1169,6 +1238,7 @@ TouchRejected(DeviceIntPtr sourcedev, TouchPointInfoPtr ti, XID resource,
* finish, then we can just kill it now. */
if (ti->num_listeners == 1 && ti->pending_finish) {
TouchEndTouch(sourcedev, ti);
+ CheckOldestTouch(sourcedev);
return;
}
@@ -1184,6 +1254,8 @@ TouchRejected(DeviceIntPtr sourcedev, TouchPointInfoPtr ti, XID resource,
* the TouchOwnership or TouchBegin event to the new owner. */
if (ev && ti->num_listeners > 0 && was_owner)
TouchPuntToNextOwner(sourcedev, ti, ev);
+
+ CheckOldestTouch(sourcedev);
}
/**
@@ -1391,6 +1463,7 @@ DeliverTouchEmulatedEvent(DeviceIntPtr dev, TouchPointInfoPtr ti,
!dev->button->buttonsDown &&
dev->deviceGrab.fromPassiveGrab && GrabIsPointerGrab(grab)) {
(*dev->deviceGrab.DeactivateGrab) (dev);
+ CheckOldestTouch(dev);
return Success;
}
}