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>
This commit is contained in:
Chase Douglas 2012-04-10 17:12:42 -07:00 committed by Peter Hutterer
parent 163b0f375d
commit 32ece7c09b
1 changed files with 74 additions and 1 deletions

View File

@ -1110,6 +1110,48 @@ EmitTouchEnd(DeviceIntPtr dev, TouchPointInfoPtr ti, int flags, XID resource)
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
* 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. */
@ -1138,6 +1188,25 @@ TouchPuntToNextOwner(DeviceIntPtr dev, TouchPointInfoPtr 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.
*
@ -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;
}
}