Commit 32ece7c0 authored by Chase Douglas's avatar Chase Douglas Committed by Peter Hutterer

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: default avatarChase Douglas <chase.douglas@canonical.com>
Reviewed-by: Peter Hutterer's avatarPeter Hutterer <peter.hutterer@who-t.net>
Signed-off-by: Peter Hutterer's avatarPeter Hutterer <peter.hutterer@who-t.net>
parent 163b0f37
...@@ -1110,6 +1110,48 @@ EmitTouchEnd(DeviceIntPtr dev, TouchPointInfoPtr ti, int flags, XID resource) ...@@ -1110,6 +1110,48 @@ EmitTouchEnd(DeviceIntPtr dev, TouchPointInfoPtr ti, int flags, XID resource)
FreeEventList(tel, GetMaximumEventsNum()); FreeEventList(tel, GetMaximumEventsNum());
} }
/**
* 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 * If the current owner has rejected the event, deliver the
* TouchOwnership/TouchBegin to the next item in the sprite stack. * TouchOwnership/TouchBegin to the next item in the sprite stack.
...@@ -1123,8 +1165,16 @@ TouchPuntToNextOwner(DeviceIntPtr dev, TouchPointInfoPtr ti, ...@@ -1123,8 +1165,16 @@ TouchPuntToNextOwner(DeviceIntPtr dev, TouchPointInfoPtr ti,
ti->listeners[0].state == LISTENER_EARLY_ACCEPT) ti->listeners[0].state == LISTENER_EARLY_ACCEPT)
DeliverTouchEvents(dev, ti, (InternalEvent *) ev, DeliverTouchEvents(dev, ti, (InternalEvent *) ev,
ti->listeners[0].listener); 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); TouchEventHistoryReplay(ti, dev, ti->listeners[0].listener);
}
/* If we've just removed the last grab and the touch has physically /* If we've just removed the last grab and the touch has physically
* ended, send a TouchEnd event too and finalise the touch. */ * ended, send a TouchEnd event too and finalise the touch. */
...@@ -1138,6 +1188,25 @@ TouchPuntToNextOwner(DeviceIntPtr dev, TouchPointInfoPtr ti, ...@@ -1138,6 +1188,25 @@ TouchPuntToNextOwner(DeviceIntPtr dev, TouchPointInfoPtr ti,
ActivateEarlyAccept(dev, ti); ActivateEarlyAccept(dev, 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. * Process a touch rejection.
* *
...@@ -1169,6 +1238,7 @@ TouchRejected(DeviceIntPtr sourcedev, TouchPointInfoPtr ti, XID resource, ...@@ -1169,6 +1238,7 @@ TouchRejected(DeviceIntPtr sourcedev, TouchPointInfoPtr ti, XID resource,
* finish, then we can just kill it now. */ * finish, then we can just kill it now. */
if (ti->num_listeners == 1 && ti->pending_finish) { if (ti->num_listeners == 1 && ti->pending_finish) {
TouchEndTouch(sourcedev, ti); TouchEndTouch(sourcedev, ti);
CheckOldestTouch(sourcedev);
return; return;
} }
...@@ -1184,6 +1254,8 @@ TouchRejected(DeviceIntPtr sourcedev, TouchPointInfoPtr ti, XID resource, ...@@ -1184,6 +1254,8 @@ TouchRejected(DeviceIntPtr sourcedev, TouchPointInfoPtr ti, XID resource,
* the TouchOwnership or TouchBegin event to the new owner. */ * the TouchOwnership or TouchBegin event to the new owner. */
if (ev && ti->num_listeners > 0 && was_owner) if (ev && ti->num_listeners > 0 && was_owner)
TouchPuntToNextOwner(sourcedev, ti, ev); TouchPuntToNextOwner(sourcedev, ti, ev);
CheckOldestTouch(sourcedev);
} }
/** /**
...@@ -1391,6 +1463,7 @@ DeliverTouchEmulatedEvent(DeviceIntPtr dev, TouchPointInfoPtr ti, ...@@ -1391,6 +1463,7 @@ DeliverTouchEmulatedEvent(DeviceIntPtr dev, TouchPointInfoPtr ti,
!dev->button->buttonsDown && !dev->button->buttonsDown &&
dev->deviceGrab.fromPassiveGrab && GrabIsPointerGrab(grab)) { dev->deviceGrab.fromPassiveGrab && GrabIsPointerGrab(grab)) {
(*dev->deviceGrab.DeactivateGrab) (dev); (*dev->deviceGrab.DeactivateGrab) (dev);
CheckOldestTouch(dev);
return Success; return Success;
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment