diff options
author | Peter Hutterer <peter.hutterer@who-t.net> | 2010-06-29 12:12:53 +1000 |
---|---|---|
committer | Peter Hutterer <peter.hutterer@who-t.net> | 2010-07-01 14:05:39 +1000 |
commit | 69ac909878ef80bb74c4a9ca4150eda66debd754 (patch) | |
tree | fa48683fa3fac7ae01d41b0b136da8f61b9c93d7 | |
parent | 09645864f5a52882eee51c801b3e610d683e7147 (diff) |
xkb: merge lockedPtrButtons state from all attached SDs.
Problem:
lockedPtrButtons keeps the state of the buttons locked by a PointerKeys button
press. Unconditionally clearing the bits may cause stuck buttons in this
sequence of events:
1. type Shift + NumLock to enable PointerKeys
2. type 0/Ins on keypad to emulate Button 1 press
→ button1 press event to client
3. press and release button 1 on physical mouse
→ button1 release event to client
Button 1 on the MD is now stuck and cannot be released.
Cause:
XKB PointerKeys button events are posted through the XTEST pointer device.
Once a press is generated, the XTEST device's button is down. The DIX merges
the button state of all attached SDs, hence the MD will have a button down
while the XTEST device has a button down.
PointerKey button events are only generated on the master device to avoid
duplicate events (see XkbFakeDeviceButton()). If the MD has the
lockedPtrButtons bit cleared by a release event on a physical device, no
such event is generated when a keyboard device triggers the PointerKey
ButtonRelease trigger. Since the event - if generated - is posted through
the XTEST pointer device, lack of a generated ButtonRelease event on the
XTEST pointer device means the button is never released, resulting in the
stuck button observed above.
Solution:
This patch merges the MD's lockedPtrButtons with the one of all attached
slave devices on release events. Thus, as long as one attached keyboard has
a lockedPtrButtons bit set, this bit is kept in the MD. Once a PointerKey
button is released on all keyboards, the matching release event is emulated
from the MD through the XTEST pointer device, thus also releasing the button
in the DIX.
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
-rw-r--r-- | include/xkbsrv.h | 3 | ||||
-rw-r--r-- | xkb/xkbAccessX.c | 18 | ||||
-rw-r--r-- | xkb/xkbActions.c | 8 | ||||
-rw-r--r-- | xkb/xkbUtils.c | 26 |
4 files changed, 54 insertions, 1 deletions
diff --git a/include/xkbsrv.h b/include/xkbsrv.h index 5847e6395..956b2249b 100644 --- a/include/xkbsrv.h +++ b/include/xkbsrv.h @@ -924,6 +924,9 @@ extern int XkbGetEffectiveGroup( XkbStatePtr /* xkbstate */, CARD8 /* keycode */); +extern void XkbMergeLockedPtrBtns( + DeviceIntPtr /* master */); + #include "xkbfile.h" #include "xkbrules.h" diff --git a/xkb/xkbAccessX.c b/xkb/xkbAccessX.c index 6d17c75b1..81f959677 100644 --- a/xkb/xkbAccessX.c +++ b/xkb/xkbAccessX.c @@ -707,8 +707,24 @@ DeviceEvent *event = &ev->device_event; changed |= XkbPointerButtonMask; } else if (event->type == ET_ButtonRelease) { - if (xkbi) + if (xkbi) { xkbi->lockedPtrButtons&= ~(1 << (event->detail.key & 0x7)); + + /* Merge this MD's lockedPtrButtons with the one of all + * attached slave devices. + * The DIX uses a merged button state for MDs, not + * releasing buttons until the last SD has released + * thenm. If we unconditionally clear the + * lockedPtrButtons bit on the MD, a PointerKeys button + * release on the SD keyboard won't generate the required fake button + * event on the XTEST pointer, thus never processing the + * button event in the DIX and the XTEST pointer's + * buttons stay down - result is a stuck button. + */ + if (IsMaster(dev)) + XkbMergeLockedPtrBtns(dev); + } + changed |= XkbPointerButtonMask; } diff --git a/xkb/xkbActions.c b/xkb/xkbActions.c index d1149a196..b4b839558 100644 --- a/xkb/xkbActions.c +++ b/xkb/xkbActions.c @@ -634,6 +634,14 @@ _XkbFilterPointerBtn( XkbSrvInfoPtr xkbi, } xkbi->lockedPtrButtons&= ~(1<<button); + if (IsMaster(xkbi->device)) + { + XkbMergeLockedPtrBtns(xkbi->device); + /* One SD still has lock set, don't post event */ + if ((xkbi->lockedPtrButtons & (1 << button)) != 0) + break; + } + /* fallthrough */ case XkbSA_PtrBtn: XkbFakeDeviceButton(xkbi->device, 0, button); diff --git a/xkb/xkbUtils.c b/xkb/xkbUtils.c index 3344e5088..14dc784b8 100644 --- a/xkb/xkbUtils.c +++ b/xkb/xkbUtils.c @@ -2094,3 +2094,29 @@ XkbGetEffectiveGroup(XkbSrvInfoPtr xkbi, XkbStatePtr xkbState, CARD8 keycode) return effectiveGroup; } + +/* Merge the lockedPtrButtons from all attached SDs for the given master + * device into the MD's state. + */ +void +XkbMergeLockedPtrBtns(DeviceIntPtr master) +{ + DeviceIntPtr d = inputInfo.devices; + XkbSrvInfoPtr xkbi = NULL; + + if (!IsMaster(master)) + return; + + if (!master->key) + return; + + xkbi = master->key->xkbInfo; + xkbi->lockedPtrButtons = 0; + + for (; d; d = d->next) { + if (IsMaster(d) || GetMaster(d, MASTER_KEYBOARD) != master || !d->key) + continue; + + xkbi->lockedPtrButtons |= d->key->xkbInfo->lockedPtrButtons; + } +} |