From c4fee9d2ecd6fdd4c05b04c9f02b23f10f2938b8 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 4 Dec 2012 15:03:37 +1000 Subject: [PATCH 01/11] xkb: always post XTest button up when the physical button released (#28808) Regression introduced by commit 2decff6393a44b56d80d53570718f95354fde454 xkb: ProcesssPointerEvent must work on the VCP if it gets the VCP XTest buttons must be released when a physical button is released. This was fixed in 14327858391ebe929b806efb53ad79e789361883, but 2decff6393a44b56d80d53570718f95354fde454 changed a condition that this code didn't get triggered anymore. "dev" for pointer events is now always the VCP which doesn't have a xkbi struct. So move this condition out and always trigger the XTest released for button events. Signed-off-by: Peter Hutterer Tested-by: Frank Roscher --- xkb/xkbAccessX.c | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/xkb/xkbAccessX.c b/xkb/xkbAccessX.c index 21df85d94..963150294 100644 --- a/xkb/xkbAccessX.c +++ b/xkb/xkbAccessX.c @@ -723,23 +723,22 @@ ProcessPointerEvent(InternalEvent *ev, DeviceIntPtr mouse) changed |= XkbPointerButtonMask; } else if (event->type == ET_ButtonRelease) { - if (xkbi) { - xkbi->lockedPtrButtons &= ~(1 << (event->detail.key & 0x7)); + if (IsMaster(dev)) { + DeviceIntPtr source; + int rc; - if (IsMaster(dev)) { - DeviceIntPtr source; - int rc; - - rc = dixLookupDevice(&source, event->sourceid, serverClient, - DixWriteAccess); - if (rc != Success) - ErrorF("[xkb] bad sourceid '%d' on button release event.\n", - event->sourceid); - else if (!IsXTestDevice(source, GetMaster(dev, MASTER_POINTER))) - XkbFakeDeviceButton(dev, FALSE, event->detail.key); - } + rc = dixLookupDevice(&source, event->sourceid, serverClient, + DixWriteAccess); + if (rc != Success) + ErrorF("[xkb] bad sourceid '%d' on button release event.\n", + event->sourceid); + else if (!IsXTestDevice(source, GetMaster(dev, MASTER_POINTER))) + XkbFakeDeviceButton(dev, FALSE, event->detail.key); } + if (xkbi) + xkbi->lockedPtrButtons &= ~(1 << (event->detail.key & 0x7)); + changed |= XkbPointerButtonMask; } From 5daa442fe15d9a87112a2def673c99a5f2506dcf Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 4 Dec 2012 15:28:14 +1000 Subject: [PATCH 02/11] xkb: only post a XTest release if the XTest device has the button down Signed-off-by: Peter Hutterer --- xkb/xkbAccessX.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/xkb/xkbAccessX.c b/xkb/xkbAccessX.c index 963150294..13051e034 100644 --- a/xkb/xkbAccessX.c +++ b/xkb/xkbAccessX.c @@ -732,8 +732,13 @@ ProcessPointerEvent(InternalEvent *ev, DeviceIntPtr mouse) if (rc != Success) ErrorF("[xkb] bad sourceid '%d' on button release event.\n", event->sourceid); - else if (!IsXTestDevice(source, GetMaster(dev, MASTER_POINTER))) - XkbFakeDeviceButton(dev, FALSE, event->detail.key); + else if (!IsXTestDevice(source, GetMaster(dev, MASTER_POINTER))) { + DeviceIntPtr xtest_device; + + xtest_device = GetXTestDevice(GetMaster(dev, MASTER_POINTER)); + if (button_is_down(xtest_device, ev->device_event.detail.button, BUTTON_PROCESSED)) + XkbFakeDeviceButton(dev, FALSE, event->detail.key); + } } if (xkbi) From 36740d02b9ca117f1404e077367fbbbe271a17d6 Mon Sep 17 00:00:00 2001 From: Daniel Martin Date: Fri, 7 Dec 2012 19:38:55 +0100 Subject: [PATCH 03/11] xfixes: Fix minor number in QueryVersion Due to a typo the major version number was passed as minor version to version_compare(). Regression-from: ffd4874798ba54f86acac75779a15b4babeaa5f3 Signed-off-by: Daniel Martin Reviewed-by: Peter Hutterer Signed-off-by: Peter Hutterer --- xfixes/xfixes.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xfixes/xfixes.c b/xfixes/xfixes.c index 52c57dfab..48af9ea6e 100644 --- a/xfixes/xfixes.c +++ b/xfixes/xfixes.c @@ -74,7 +74,7 @@ ProcXFixesQueryVersion(ClientPtr client) if (version_compare(stuff->majorVersion, stuff->minorVersion, SERVER_XFIXES_MAJOR_VERSION, - SERVER_XFIXES_MAJOR_VERSION) < 0) { + SERVER_XFIXES_MINOR_VERSION) < 0) { rep.majorVersion = stuff->majorVersion; rep.minorVersion = stuff->minorVersion; } From ead21f9426122536adfb4787ac181008ae83cd4b Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 19 Nov 2012 16:16:10 +1000 Subject: [PATCH 04/11] =?UTF-8?q?Xi:=20fix=20typo=20"mechansims"=20?= =?UTF-8?q?=E2=86=92=20"mechanisms"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Peter Hutterer --- Xi/exevents.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Xi/exevents.c b/Xi/exevents.c index 2caf98c25..ae126dfd7 100644 --- a/Xi/exevents.c +++ b/Xi/exevents.c @@ -1409,7 +1409,7 @@ DeliverTouchEmulatedEvent(DeviceIntPtr dev, TouchPointInfoPtr ti, ptrev->device_event.corestate = event_get_corestate(dev, kbd); if (grab) { - /* this side-steps the usual activation mechansims, but... */ + /* this side-steps the usual activation mechanisms, but... */ if (ev->any.type == ET_TouchBegin && !dev->deviceGrab.grab) ActivatePassiveGrab(dev, grab, ptrev, ev); /* also delivers the event */ else { From 146f48c2934fc85ec095496da5c8f0102bc7f5b5 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 22 Nov 2012 13:49:34 +1000 Subject: [PATCH 05/11] dix: don't call ProcessInputEvents() when accepting/rejecting touches TouchListenerAcceptReject may be called during normal event processing, but ProcessInputEvents is not reentrant and calling it here smashes the event queue. Signed-off-by: Peter Hutterer --- dix/touch.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/dix/touch.c b/dix/touch.c index 29ba17194..f4a93c684 100644 --- a/dix/touch.c +++ b/dix/touch.c @@ -987,8 +987,6 @@ TouchListenerAcceptReject(DeviceIntPtr dev, TouchPointInfoPtr ti, int listener, for (i = 0; i < nev; i++) mieqProcessDeviceEvent(dev, events + i, NULL); - ProcessInputEvents(); - FreeEventList(events, GetMaximumEventsNum()); return nev ? Success : BadMatch; From bc1f90a615018c05994fae3e678dd2341256cd82 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 26 Nov 2012 12:23:54 +1000 Subject: [PATCH 06/11] dix: only reject active grabs on ungrab and do it before actually ungrabbing An active grab ungrabbing is the same as rejecting the grab, since the client is no longer interested in those events. So reject any touch grab, but do so before actually deactivating since we're interested in the TouchEnd for the current grabbing client. A passive grab otoh is _not_ like rejecting a grab, since it deactivates automatically when the touch ends. Signed-off-by: Peter Hutterer --- dix/events.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/dix/events.c b/dix/events.c index 3282ef818..03ed106ed 100644 --- a/dix/events.c +++ b/dix/events.c @@ -1503,11 +1503,20 @@ DeactivatePointerGrab(DeviceIntPtr mouse) { GrabPtr grab = mouse->deviceGrab.grab; DeviceIntPtr dev; + Bool wasPassive = mouse->deviceGrab.fromPassiveGrab; Bool wasImplicit = (mouse->deviceGrab.fromPassiveGrab && mouse->deviceGrab.implicitGrab); XID grab_resource = grab->resource; int i; + /* If an explicit grab was deactivated, we must remove it from the head of + * all the touches' listener lists. */ + for (i = 0; !wasPassive && mouse->touch && i < mouse->touch->num_touches; i++) { + TouchPointInfoPtr ti = mouse->touch->touches + i; + if (ti->active && TouchResourceIsOwner(ti, grab_resource)) + TouchListenerAcceptReject(mouse, ti, 0, XIRejectTouch); + } + TouchRemovePointerGrab(mouse); mouse->valuator->motionHintWindow = NullWindow; From ece8157a59751b3ed492fb2e1eb8d5f20221e195 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 26 Nov 2012 15:14:19 +1000 Subject: [PATCH 07/11] dix: when deactivating pointer-only grabs, don't emulate TouchEnd events A client with a pointer grab on a touch device must reject the touch when detactivating the grab while the touch is active. However, such a rejecting must not trigger a ButtonRelease event to be emulated and sent to the client. Set the grabbing listener's state to HAS_END, so we simply skip delivery to that client. Signed-off-by: Peter Hutterer --- dix/events.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/dix/events.c b/dix/events.c index 03ed106ed..31f8d8700 100644 --- a/dix/events.c +++ b/dix/events.c @@ -1513,8 +1513,15 @@ DeactivatePointerGrab(DeviceIntPtr mouse) * all the touches' listener lists. */ for (i = 0; !wasPassive && mouse->touch && i < mouse->touch->num_touches; i++) { TouchPointInfoPtr ti = mouse->touch->touches + i; - if (ti->active && TouchResourceIsOwner(ti, grab_resource)) + if (ti->active && TouchResourceIsOwner(ti, grab_resource)) { + /* Rejecting will generate a TouchEnd, but we must not + emulate a ButtonRelease here. So pretend the listener + already has the end event */ + if (grab->grabtype == CORE || grab->grabtype == XI || + !xi2mask_isset(dev->deviceGrab.grab->xi2mask, dev, XI_TouchBegin)) + ti->listeners[0].state = LISTENER_HAS_END; TouchListenerAcceptReject(mouse, ti, 0, XIRejectTouch); + } } TouchRemovePointerGrab(mouse); From 00def5144557cfe8bf535f926212a8e084dc7cf6 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 26 Nov 2012 14:55:13 +1000 Subject: [PATCH 08/11] Xi: if a TouchEnd appears on a actively grabbing client, always accept Once the TouchEnd appears on the device, the touch is done. If the client still has a pointer grab, accept it to avoid clients with TouchOwnership selections to wait indefinitely for the actual touch event. Signed-off-by: Peter Hutterer --- Xi/exevents.c | 55 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/Xi/exevents.c b/Xi/exevents.c index ae126dfd7..4c1aeb4da 100644 --- a/Xi/exevents.c +++ b/Xi/exevents.c @@ -1566,32 +1566,41 @@ ProcessTouchEvent(InternalEvent *ev, DeviceIntPtr dev) else ti = TouchFindByClientID(dev, touchid); - /* Under the following circumstances we create a new touch record for an - * existing touch: - * - * - The touch may be pointer emulated - * - An explicit grab is active on the device - * - The grab is a pointer grab - * - * This allows for an explicit grab to receive pointer events for an already - * active touch. - */ - if (!ti && type != ET_TouchBegin && emulate_pointer && - dev->deviceGrab.grab && !dev->deviceGrab.fromPassiveGrab && + /* Active pointer grab */ + if (emulate_pointer && dev->deviceGrab.grab && !dev->deviceGrab.fromPassiveGrab && (dev->deviceGrab.grab->grabtype == CORE || dev->deviceGrab.grab->grabtype == XI || - !xi2mask_isset(dev->deviceGrab.grab->xi2mask, dev, XI_TouchBegin))) { - ti = TouchBeginTouch(dev, ev->device_event.sourceid, touchid, - emulate_pointer); - if (!ti) { - DebugF("[Xi] %s: Failed to create new dix record for explicitly " - "grabbed touchpoint %d\n", - dev->name, touchid); - return; - } + !xi2mask_isset(dev->deviceGrab.grab->xi2mask, dev, XI_TouchBegin))) + { + /* Active pointer grab on touch point and we get a TouchEnd - claim this + * touchpoint accepted, otherwise clients waiting for ownership will + * wait on this touchpoint until this client ungrabs, or the cows come + * home, whichever is earlier */ + if (ti && type == ET_TouchEnd) + TouchListenerAcceptReject(dev, ti, 0, XIAcceptTouch); + else if (!ti && type != ET_TouchBegin) { + /* Under the following circumstances we create a new touch record for an + * existing touch: + * + * - The touch may be pointer emulated + * - An explicit grab is active on the device + * - The grab is a pointer grab + * + * This allows for an explicit grab to receive pointer events for an already + * active touch. + */ + ti = TouchBeginTouch(dev, ev->device_event.sourceid, touchid, + emulate_pointer); + if (!ti) { + DebugF("[Xi] %s: Failed to create new dix record for explicitly " + "grabbed touchpoint %d\n", + dev->name, touchid); + return; + } - TouchBuildSprite(dev, ti, ev); - TouchSetupListeners(dev, ti, ev); + TouchBuildSprite(dev, ti, ev); + TouchSetupListeners(dev, ti, ev); + } } if (!ti) { From 08da994a08bb74afae81176c56fb525d0439274b Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 26 Nov 2012 12:33:29 +1000 Subject: [PATCH 09/11] dix: add FIXME, TouchRemovePointerGrab does nothing Signed-off-by: Peter Hutterer --- dix/touch.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dix/touch.c b/dix/touch.c index f4a93c684..d890b6227 100644 --- a/dix/touch.c +++ b/dix/touch.c @@ -915,6 +915,8 @@ TouchRemovePointerGrab(DeviceIntPtr dev) ti = TouchFindByClientID(dev, ev->touchid); if (!ti) return; + + /* FIXME: missing a bit of code here... */ } /* As touch grabs don't turn into active grabs with their own resources, we From a7c97d737ef0d14ec97869082decd049637cfe7a Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 20 Nov 2012 11:38:39 +1000 Subject: [PATCH 10/11] dix: split xi2_mask_isset into a per-device function For touch selection conflicts, we need to check not only if the mask is set for the device, but if it is set for only that specific device (regardless of XIAll*Devices) Signed-off-by: Peter Hutterer --- dix/inpututils.c | 30 +++++++++++++++++++++--------- include/inpututils.h | 1 + test/xi2/xi2.c | 6 ++++++ 3 files changed, 28 insertions(+), 9 deletions(-) diff --git a/dix/inpututils.c b/dix/inpututils.c index eb2222a07..9e38e1742 100644 --- a/dix/inpututils.c +++ b/dix/inpututils.c @@ -1015,6 +1015,21 @@ xi2mask_free(XI2Mask **mask) *mask = NULL; } +/** + * Test if the bit for event type is set for this device only. + * + * @return TRUE if the bit is set, FALSE otherwise + */ +Bool +xi2mask_isset_for_device(XI2Mask *mask, const DeviceIntPtr dev, int event_type) +{ + BUG_WARN(dev->id < 0); + BUG_WARN(dev->id >= mask->nmasks); + BUG_WARN(bits_to_bytes(event_type + 1) > mask->mask_size); + + return BitIsOn(mask->masks[dev->id], event_type); +} + /** * Test if the bit for event type is set for this device, or the * XIAllDevices/XIAllMasterDevices (if applicable) is set. @@ -1026,15 +1041,12 @@ xi2mask_isset(XI2Mask *mask, const DeviceIntPtr dev, int event_type) { int set = 0; - BUG_WARN(dev->id < 0); - BUG_WARN(dev->id >= mask->nmasks); - BUG_WARN(bits_to_bytes(event_type + 1) > mask->mask_size); - - set = ! !BitIsOn(mask->masks[XIAllDevices], event_type); - if (!set) - set = ! !BitIsOn(mask->masks[dev->id], event_type); - if (!set && IsMaster(dev)) - set = ! !BitIsOn(mask->masks[XIAllMasterDevices], event_type); + if (xi2mask_isset_for_device(mask, inputInfo.all_devices, event_type)) + set = 1; + else if (xi2mask_isset_for_device(mask, dev, event_type)) + set = 1; + else if (IsMaster(dev) && xi2mask_isset_for_device(mask, inputInfo.all_master_devices, event_type)) + set = 1; return set; } diff --git a/include/inpututils.h b/include/inpututils.h index cd9a4de82..53c96ba1c 100644 --- a/include/inpututils.h +++ b/include/inpututils.h @@ -57,6 +57,7 @@ XI2Mask *xi2mask_new(void); XI2Mask *xi2mask_new_with_size(size_t, size_t); /* don't use it */ void xi2mask_free(XI2Mask **mask); Bool xi2mask_isset(XI2Mask *mask, const DeviceIntPtr dev, int event_type); +Bool xi2mask_isset_for_device(XI2Mask *mask, const DeviceIntPtr dev, int event_type); void xi2mask_set(XI2Mask *mask, int deviceid, int event_type); void xi2mask_zero(XI2Mask *mask, int deviceid); void xi2mask_merge(XI2Mask *dest, const XI2Mask *source); diff --git a/test/xi2/xi2.c b/test/xi2/xi2.c index 6ee705293..1cdad1dbd 100644 --- a/test/xi2/xi2.c +++ b/test/xi2/xi2.c @@ -36,8 +36,14 @@ xi2mask_test(void) XI2Mask *xi2mask = NULL, *mergemask = NULL; unsigned char *mask; DeviceIntRec dev; + DeviceIntRec all_devices, all_master_devices; int i; + all_devices.id = XIAllDevices; + inputInfo.all_devices = &all_devices; + all_master_devices.id = XIAllMasterDevices; + inputInfo.all_master_devices = &all_master_devices; + /* size >= nmasks * 2 for the test cases below */ xi2mask = xi2mask_new_with_size(MAXDEVICES + 2, (MAXDEVICES + 2) * 2); assert(xi2mask); From 39f19b3f3b8c9b714e70e339dfb0083ff629ab2a Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 20 Nov 2012 11:48:31 +1000 Subject: [PATCH 11/11] Xi: fix touch event selction conflicts (#57301) There are limits on which client may select for touch events on a given window, with restrictions being that no two clients can select on the same device, but narrower selections are allowed, i.e. if one client has XIAllDevices, a second client may still select for device X. The current code had a dependency on which client selected first and which device, resulting in inconsistencies when selecting for events. Fix that, responding with the right errors regardless of who selected what first. X.Org Bug 57301 Signed-off-by: Peter Hutterer --- Xi/xiselectev.c | 80 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 56 insertions(+), 24 deletions(-) diff --git a/Xi/xiselectev.c b/Xi/xiselectev.c index ab1b6245f..45a996e4c 100644 --- a/Xi/xiselectev.c +++ b/Xi/xiselectev.c @@ -36,6 +36,57 @@ #include "xiselectev.h" +/** + * Ruleset: + * - if A has XIAllDevices, B may select on device X + * - If A has XIAllDevices, B may select on XIAllMasterDevices + * - If A has XIAllMasterDevices, B may select on device X + * - If A has XIAllMasterDevices, B may select on XIAllDevices + * - if A has device X, B may select on XIAllDevices/XIAllMasterDevices + */ +static int check_for_touch_selection_conflicts(ClientPtr B, WindowPtr win, int deviceid) +{ + OtherInputMasks *inputMasks = wOtherInputMasks(win); + InputClients *A = NULL; + + if (inputMasks) + A = inputMasks->inputClients; + for (; A; A = A->next) { + DeviceIntPtr tmp; + + if (CLIENT_ID(A->resource) == B->index) + continue; + + if (deviceid == XIAllDevices) + tmp = inputInfo.all_devices; + else if (deviceid == XIAllMasterDevices) + tmp = inputInfo.all_master_devices; + else + dixLookupDevice(&tmp, deviceid, serverClient, DixReadAccess); + if (!tmp) + return BadImplementation; /* this shouldn't happen */ + + /* A has XIAllDevices */ + if (xi2mask_isset_for_device(A->xi2mask, inputInfo.all_devices, XI_TouchBegin)) { + if (deviceid == XIAllDevices) + return BadAccess; + } + + /* A has XIAllMasterDevices */ + if (xi2mask_isset_for_device(A->xi2mask, inputInfo.all_master_devices, XI_TouchBegin)) { + if (deviceid == XIAllMasterDevices) + return BadAccess; + } + + /* A has this device */ + if (xi2mask_isset_for_device(A->xi2mask, tmp, XI_TouchBegin)) + return BadAccess; + } + + return Success; +} + + /** * Check the given mask (in len bytes) for invalid mask bits. * Invalid mask bits are any bits above XI2LastEvent. @@ -169,30 +220,11 @@ ProcXISelectEvents(ClientPtr client) * same devices, including master devices. * XXX: This breaks if a device goes from floating to attached. */ if (BitIsOn(bits, XI_TouchBegin)) { - OtherInputMasks *inputMasks = wOtherInputMasks(win); - InputClients *iclient = NULL; - - if (inputMasks) - iclient = inputMasks->inputClients; - for (; iclient; iclient = iclient->next) { - DeviceIntPtr tmp; - - if (CLIENT_ID(iclient->resource) == client->index) - continue; - - if (evmask->deviceid == XIAllDevices) - tmp = inputInfo.all_devices; - else if (evmask->deviceid == XIAllMasterDevices) - tmp = inputInfo.all_master_devices; - else - dixLookupDevice(&tmp, evmask->deviceid, serverClient, - DixReadAccess); - if (!tmp) - return BadImplementation; /* this shouldn't happen */ - - if (xi2mask_isset(iclient->xi2mask, tmp, XI_TouchBegin)) - return BadAccess; - } + rc = check_for_touch_selection_conflicts(client, + win, + evmask->deviceid); + if (rc != Success) + return rc; } }