From deab888bb3bb2a56963da50ff551bd66fbd858a1 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 29 Jun 2010 13:49:27 +1000 Subject: [PATCH 1/5] xkb: Mark switch case fallthrough with comment. Signed-off-by: Peter Hutterer --- xkb/xkbActions.c | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-) diff --git a/xkb/xkbActions.c b/xkb/xkbActions.c index 4c7bce2..6a7f36d 100644 --- a/xkb/xkbActions.c +++ b/xkb/xkbActions.c @@ -625,6 +625,8 @@ _XkbFilterPointerBtn( XkbSrvInfoPtr xkbi, break; } xkbi->lockedPtrButtons&= ~(1<device, 0, button); break; -- 1.7.1 From 50b6311dbd2594acc36d6856fdde8623459f1374 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 29 Jun 2010 12:12:53 +1000 Subject: [PATCH 2/5] xkb: merge lockedPtrButtons state from all attached SDs. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- include/xkbsrv.h | 3 +++ xkb/xkbAccessX.c | 18 +++++++++++++++++- xkb/xkbActions.c | 8 ++++++++ xkb/xkbUtils.c | 26 ++++++++++++++++++++++++++ 4 files changed, 54 insertions(+), 1 deletions(-) diff --git a/include/xkbsrv.h b/include/xkbsrv.h index c0cd501..f0db0e4 100644 --- a/include/xkbsrv.h +++ b/include/xkbsrv.h @@ -933,6 +933,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 be1dcee..e3fdc06 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 6a7f36d..ab52b6a 100644 --- a/xkb/xkbActions.c +++ b/xkb/xkbActions.c @@ -626,6 +626,14 @@ _XkbFilterPointerBtn( XkbSrvInfoPtr xkbi, } xkbi->lockedPtrButtons&= ~(1<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: XkbDDXFakeDeviceButton(xkbi->device, 0, button); diff --git a/xkb/xkbUtils.c b/xkb/xkbUtils.c index b1e0e55..d7d1935 100644 --- a/xkb/xkbUtils.c +++ b/xkb/xkbUtils.c @@ -2190,3 +2190,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; + } +} -- 1.7.1 From 4a4224f5d786035af88c251a9ee177217e8f77fd Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 14 Apr 2010 10:54:29 +1000 Subject: [PATCH 3/5] xkb: rename XkbFakeDeviceButton and XkbFakeDeviceMotion, move into xkbActions.c The name XkbDDXFakeDeviceButton and XkbDDXFakeDeviceMotion is somewhat misleading, there's no DDX involved in the game at all anymore. This removes XkbFakeDeviceMotion and XkbFakeDeviceButton from the API where it arguably shouldn't have been in the first place. Signed-off-by: Peter Hutterer Reviewed-by: Daniel Stone Reviewed-by: Dan Nicholson --- include/xkbsrv.h | 13 ------- xkb/Makefile.am | 4 +-- xkb/ddxDevBtn.c | 69 -------------------------------------- xkb/ddxFakeMtn.c | 64 ----------------------------------- xkb/xkbActions.c | 97 ++++++++++++++++++++++++++++++++++++++++++++++------- 5 files changed, 85 insertions(+), 162 deletions(-) delete mode 100644 xkb/ddxDevBtn.c delete mode 100644 xkb/ddxFakeMtn.c diff --git a/include/xkbsrv.h b/include/xkbsrv.h index f0db0e4..d1cbd1a 100644 --- a/include/xkbsrv.h +++ b/include/xkbsrv.h @@ -768,19 +768,6 @@ extern _X_EXPORT void XkbDDXUpdateDeviceIndicators( CARD32 /* newState */ ); -extern _X_EXPORT void XkbDDXFakePointerMotion( - DeviceIntPtr /* dev */, - unsigned int /* flags */, - int /* x */, - int /* y */ -); - -extern _X_EXPORT void XkbDDXFakeDeviceButton( - DeviceIntPtr /* dev */, - Bool /* press */, - int /* button */ -); - extern _X_EXPORT int XkbDDXTerminateServer( DeviceIntPtr /* dev */, KeyCode /* key */, diff --git a/xkb/Makefile.am b/xkb/Makefile.am index e54ce59..fb3ccbf 100644 --- a/xkb/Makefile.am +++ b/xkb/Makefile.am @@ -5,11 +5,9 @@ AM_CFLAGS = $(DIX_CFLAGS) DDX_SRCS = \ ddxBeep.c \ ddxCtrls.c \ - ddxFakeMtn.c \ ddxLEDs.c \ ddxLoad.c \ - ddxList.c \ - ddxDevBtn.c + ddxList.c DIX_SRCS = \ xkb.c \ diff --git a/xkb/ddxDevBtn.c b/xkb/ddxDevBtn.c deleted file mode 100644 index b8a1255..0000000 --- a/xkb/ddxDevBtn.c +++ /dev/null @@ -1,69 +0,0 @@ -/************************************************************ -Copyright (c) 1995 by Silicon Graphics Computer Systems, Inc. - -Permission to use, copy, modify, and distribute this -software and its documentation for any purpose and without -fee is hereby granted, provided that the above copyright -notice appear in all copies and that both that copyright -notice and this permission notice appear in supporting -documentation, and that the name of Silicon Graphics not be -used in advertising or publicity pertaining to distribution -of the software without specific prior written permission. -Silicon Graphics makes no representation about the suitability -of this software for any purpose. It is provided "as is" -without any express or implied warranty. - -SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS -SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON -GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL -DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, -DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE -OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH -THE USE OR PERFORMANCE OF THIS SOFTWARE. - -********************************************************/ - -#ifdef HAVE_DIX_CONFIG_H -#include -#endif - -#include "inputstr.h" -#include -#include "mi.h" - -void -XkbDDXFakeDeviceButton(DeviceIntPtr dev,Bool press,int button) -{ - EventListPtr events; - int nevents, i; - DeviceIntPtr ptr; - - /* If dev is a slave device, and the SD is attached, do nothing. If we'd - * post through the attached master pointer we'd get duplicate events. - * - * if dev is a master keyboard, post through the XTEST device - * - * if dev is a floating slave, post through the device itself. - */ - - if (IsMaster(dev)) - ptr = GetXTestDevice(GetMaster(dev, MASTER_POINTER)); - else if (!dev->u.master) - ptr = dev; - else - return; - - events = InitEventList(GetMaximumEventsNum()); - OsBlockSignals(); - nevents = GetPointerEvents(events, ptr, - press ? ButtonPress : ButtonRelease, button, - 0 /* flags */, 0 /* first */, - 0 /* num_val */, NULL); - OsReleaseSignals(); - - for (i = 0; i < nevents; i++) - mieqProcessDeviceEvent(ptr, (InternalEvent*)events[i].event, NULL); - - FreeEventList(events, GetMaximumEventsNum()); -} diff --git a/xkb/ddxFakeMtn.c b/xkb/ddxFakeMtn.c deleted file mode 100644 index b383716..0000000 --- a/xkb/ddxFakeMtn.c +++ /dev/null @@ -1,64 +0,0 @@ -/************************************************************ -Copyright (c) 1993 by Silicon Graphics Computer Systems, Inc. - -Permission to use, copy, modify, and distribute this -software and its documentation for any purpose and without -fee is hereby granted, provided that the above copyright -notice appear in all copies and that both that copyright -notice and this permission notice appear in supporting -documentation, and that the name of Silicon Graphics not be -used in advertising or publicity pertaining to distribution -of the software without specific prior written permission. -Silicon Graphics makes no representation about the suitability -of this software for any purpose. It is provided "as is" -without any express or implied warranty. - -SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS -SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON -GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL -DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, -DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE -OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH -THE USE OR PERFORMANCE OF THIS SOFTWARE. - -********************************************************/ - -#ifdef HAVE_DIX_CONFIG_H -#include -#endif - -#include "inputstr.h" -#include -#include "mi.h" - -void -XkbDDXFakePointerMotion(DeviceIntPtr dev, unsigned flags,int x,int y) -{ - EventListPtr events; - int nevents, i; - DeviceIntPtr ptr; - int gpe_flags = 0; - - if (!dev->u.master) - ptr = dev; - else - ptr = GetXTestDevice(GetMaster(dev, MASTER_POINTER)); - - if (flags & XkbSA_MoveAbsoluteX || flags & XkbSA_MoveAbsoluteY) - gpe_flags = POINTER_ABSOLUTE; - else - gpe_flags = POINTER_RELATIVE; - - events = InitEventList(GetMaximumEventsNum()); - OsBlockSignals(); - nevents = GetPointerEvents(events, ptr, - MotionNotify, 0, - gpe_flags, 0, 2, (int[]){x, y}); - OsReleaseSignals(); - - for (i = 0; i < nevents; i++) - mieqProcessDeviceEvent(ptr, (InternalEvent*)events[i].event, NULL); - - FreeEventList(events, GetMaximumEventsNum()); -} diff --git a/xkb/xkbActions.c b/xkb/xkbActions.c index ab52b6a..2817e39 100644 --- a/xkb/xkbActions.c +++ b/xkb/xkbActions.c @@ -40,11 +40,15 @@ THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include "xkb.h" #include +#include "mi.h" #define EXTENSION_EVENT_BASE 64 static int xkbDevicePrivateKeyIndex; DevPrivateKey xkbDevicePrivateKey = &xkbDevicePrivateKeyIndex; +static void XkbFakeDeviceButton(DeviceIntPtr dev,Bool press,int button); +static void XkbFakePointerMotion(DeviceIntPtr dev, unsigned flags,int x,int y); + void xkbUnwrapProc(DeviceIntPtr device, DeviceHandleProc proc, pointer data) @@ -479,7 +483,7 @@ int dx,dy; dx= xkbi->mouseKeysDX; dy= xkbi->mouseKeysDY; } - XkbDDXFakePointerMotion(xkbi->device, xkbi->mouseKeysFlags,dx,dy); + XkbFakePointerMotion(xkbi->device, xkbi->mouseKeysFlags,dx,dy); return xkbi->desc->ctrls->mk_interval; } @@ -507,7 +511,7 @@ Bool accel; accel= ((pAction->ptr.flags&XkbSA_NoAcceleration)==0); x= XkbPtrActionX(&pAction->ptr); y= XkbPtrActionY(&pAction->ptr); - XkbDDXFakePointerMotion(xkbi->device, pAction->ptr.flags,x,y); + XkbFakePointerMotion(xkbi->device, pAction->ptr.flags,x,y); AccessXCancelRepeatKey(xkbi,keycode); xkbi->mouseKeysAccel= accel&& (xkbi->desc->ctrls->enabled_ctrls&XkbMouseKeysAccelMask); @@ -554,7 +558,7 @@ _XkbFilterPointerBtn( XkbSrvInfoPtr xkbi, ((pAction->btn.flags&XkbSA_LockNoLock)==0)) { xkbi->lockedPtrButtons|= (1<device, 1, button); + XkbFakeDeviceButton(xkbi->device, 1, button); filter->upAction.type= XkbSA_NoAction; } break; @@ -565,12 +569,12 @@ _XkbFilterPointerBtn( XkbSrvInfoPtr xkbi, if (pAction->btn.count>0) { nClicks= pAction->btn.count; for (i=0;idevice, 1, button); - XkbDDXFakeDeviceButton(xkbi->device, 0, button); + XkbFakeDeviceButton(xkbi->device, 1, button); + XkbFakeDeviceButton(xkbi->device, 0, button); } filter->upAction.type= XkbSA_NoAction; } - else XkbDDXFakeDeviceButton(xkbi->device, 1, button); + else XkbFakeDeviceButton(xkbi->device, 1, button); } break; case XkbSA_SetPtrDflt: @@ -636,7 +640,7 @@ _XkbFilterPointerBtn( XkbSrvInfoPtr xkbi, /* fallthrough */ case XkbSA_PtrBtn: - XkbDDXFakeDeviceButton(xkbi->device, 0, button); + XkbFakeDeviceButton(xkbi->device, 0, button); break; } filter->active = 0; @@ -974,7 +978,7 @@ int button; if ((pAction->devbtn.flags&XkbSA_LockNoLock)|| BitIsOn(dev->button->down, button)) return 0; - XkbDDXFakeDeviceButton(dev,TRUE,button); + XkbFakeDeviceButton(dev,TRUE,button); filter->upAction.type= XkbSA_NoAction; break; case XkbSA_DeviceBtn: @@ -982,12 +986,12 @@ int button; int nClicks,i; nClicks= pAction->btn.count; for (i=0;iupAction.type= XkbSA_NoAction; } - else XkbDDXFakeDeviceButton(dev,TRUE,button); + else XkbFakeDeviceButton(dev,TRUE,button); break; } } @@ -1006,10 +1010,10 @@ int button; if ((filter->upAction.devbtn.flags&XkbSA_LockNoUnlock)|| !BitIsOn(dev->button->down, button)) return 0; - XkbDDXFakeDeviceButton(dev,FALSE,button); + XkbFakeDeviceButton(dev,FALSE,button); break; case XkbSA_DeviceBtn: - XkbDDXFakeDeviceButton(dev,FALSE,button); + XkbFakeDeviceButton(dev,FALSE,button); break; } filter->active = 0; @@ -1326,3 +1330,70 @@ xkbStateNotify sn; return; } +static void +XkbFakePointerMotion(DeviceIntPtr dev, unsigned flags,int x,int y) +{ + EventListPtr events; + int nevents, i; + DeviceIntPtr ptr; + int gpe_flags = 0; + + if (!dev->u.master) + ptr = dev; + else + ptr = GetXTestDevice(GetMaster(dev, MASTER_POINTER)); + + if (flags & XkbSA_MoveAbsoluteX || flags & XkbSA_MoveAbsoluteY) + gpe_flags = POINTER_ABSOLUTE; + else + gpe_flags = POINTER_RELATIVE; + + events = InitEventList(GetMaximumEventsNum()); + OsBlockSignals(); + nevents = GetPointerEvents(events, ptr, + MotionNotify, 0, + gpe_flags, 0, 2, (int[]){x, y}); + OsReleaseSignals(); + + for (i = 0; i < nevents; i++) + mieqProcessDeviceEvent(ptr, (InternalEvent*)events[i].event, NULL); + + FreeEventList(events, GetMaximumEventsNum()); +} + +static void +XkbFakeDeviceButton(DeviceIntPtr dev,Bool press,int button) +{ + EventListPtr events; + int nevents, i; + DeviceIntPtr ptr; + + /* If dev is a slave device, and the SD is attached, do nothing. If we'd + * post through the attached master pointer we'd get duplicate events. + * + * if dev is a master keyboard, post through the XTEST device + * + * if dev is a floating slave, post through the device itself. + */ + + if (IsMaster(dev)) + ptr = GetXTestDevice(GetMaster(dev, MASTER_POINTER)); + else if (!dev->u.master) + ptr = dev; + else + return; + + events = InitEventList(GetMaximumEventsNum()); + OsBlockSignals(); + nevents = GetPointerEvents(events, ptr, + press ? ButtonPress : ButtonRelease, button, + 0 /* flags */, 0 /* first */, + 0 /* num_val */, NULL); + OsReleaseSignals(); + + + for (i = 0; i < nevents; i++) + mieqProcessDeviceEvent(ptr, (InternalEvent*)events[i].event, NULL); + + FreeEventList(events, GetMaximumEventsNum()); +} -- 1.7.1 From dcb46252f959893f1934232698e2ae26390a8a5b Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 29 Jun 2010 15:24:51 +1000 Subject: [PATCH 4/5] xkb: emulate PointerKeys events only on the master device. This patch replicates the behaviour for button events. Only generate a PointerKeys motion event on the master device, not on the slave device. Fixes the current issue of PointerKey motion events generating key events as well. Signed-off-by: Peter Hutterer --- xkb/xkbActions.c | 9 ++++----- 1 files changed, 4 insertions(+), 5 deletions(-) diff --git a/xkb/xkbActions.c b/xkb/xkbActions.c index 2817e39..391c375 100644 --- a/xkb/xkbActions.c +++ b/xkb/xkbActions.c @@ -496,9 +496,6 @@ _XkbFilterPointerMove( XkbSrvInfoPtr xkbi, int x,y; Bool accel; - if (xkbi->device == inputInfo.keyboard) - return 0; - if (filter->keycode==0) { /* initial press */ filter->keycode = keycode; filter->active = 1; @@ -1338,10 +1335,12 @@ XkbFakePointerMotion(DeviceIntPtr dev, unsigned flags,int x,int y) DeviceIntPtr ptr; int gpe_flags = 0; - if (!dev->u.master) + if (IsMaster(dev)) + ptr = GetXTestDevice(GetMaster(dev, MASTER_POINTER)); + else if (!dev->u.master) ptr = dev; else - ptr = GetXTestDevice(GetMaster(dev, MASTER_POINTER)); + return; if (flags & XkbSA_MoveAbsoluteX || flags & XkbSA_MoveAbsoluteY) gpe_flags = POINTER_ABSOLUTE; -- 1.7.1 From 40941fb2e9ae763add7b74850e8a0471ac754db6 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 1 Jul 2010 12:44:57 +1000 Subject: [PATCH 5/5] xkb: release XTEST pointer buttons on physical releases. (#28808) If a button release event is posted for the MD pointer, post a release event through the matching XTEST device. This way, a client who posts a button press through the XTEST extension cannot inadvertedly lock the button. This behaviour is required for historical reasons, until server 1.7 the core pointer would release a button press on physical events, regardless of the XTEST state. Clients seem to rely on this behaviour, causing seemingly stuck grabs. The merged behaviour is kept for multiple keyboard PointerKey events, if two physical keyboards hold the button down as a result of PointerKey actions, the button is not released until the last keyboard releases the button. X.Org Bug 28808 Signed-off-by: Peter Hutterer --- include/xkbsrv.h | 6 ++++++ xkb/xkbAccessX.c | 23 ++++++++++------------- xkb/xkbActions.c | 4 ++-- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/include/xkbsrv.h b/include/xkbsrv.h index d1cbd1a..a96ca56 100644 --- a/include/xkbsrv.h +++ b/include/xkbsrv.h @@ -923,6 +923,12 @@ extern int XkbGetEffectiveGroup( extern void XkbMergeLockedPtrBtns( DeviceIntPtr /* master */); +extern void XkbFakeDeviceButton( + DeviceIntPtr /* dev */, + int /* press */, + int /* button */); + + #include "xkbfile.h" #include "xkbrules.h" diff --git a/xkb/xkbAccessX.c b/xkb/xkbAccessX.c index e3fdc06..d3f9652 100644 --- a/xkb/xkbAccessX.c +++ b/xkb/xkbAccessX.c @@ -710,19 +710,16 @@ DeviceEvent *event = &ev->device_event; 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); + 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); + } } changed |= XkbPointerButtonMask; diff --git a/xkb/xkbActions.c b/xkb/xkbActions.c index 391c375..5d40199 100644 --- a/xkb/xkbActions.c +++ b/xkb/xkbActions.c @@ -46,7 +46,7 @@ THE USE OR PERFORMANCE OF THIS SOFTWARE. static int xkbDevicePrivateKeyIndex; DevPrivateKey xkbDevicePrivateKey = &xkbDevicePrivateKeyIndex; -static void XkbFakeDeviceButton(DeviceIntPtr dev,Bool press,int button); +void XkbFakeDeviceButton(DeviceIntPtr dev,Bool press,int button); static void XkbFakePointerMotion(DeviceIntPtr dev, unsigned flags,int x,int y); void @@ -1360,7 +1360,7 @@ XkbFakePointerMotion(DeviceIntPtr dev, unsigned flags,int x,int y) FreeEventList(events, GetMaximumEventsNum()); } -static void +void XkbFakeDeviceButton(DeviceIntPtr dev,Bool press,int button) { EventListPtr events; -- 1.7.1 From 7273832bcdc6f43e9a5a8fdbb56844466efb710a Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 23 Jul 2010 11:46:30 +1000 Subject: [PATCH 1/3] xkb: post-fix PointerKeys button events with a DeviceChangedEvent. commit 14327858391ebe929b806efb53ad79e789361883 xkb: release XTEST pointer buttons on physical releases. (#28808) revealed a bug with the XTEST/PointerKeys interaction. Events resulting from PointerKeys are injected into the event processing stream, not appended to the event queue. The events generated for the fake button press include a DeviceChangedEvent (DCE), a raw button event and the button event itself. The DCE causes the master to switch classes to the attached XTEST pointer device. Once the fake button is processed, normal event processing continues with events in the EQ. The master still contains the XTEST classes, causing some events to be dropped if e.g. the number of valuators of the event in the queue exceeds the XTEST device's number of valuators. Example: the EQ contains the following events, processed one-by-one, left to right. [DCE (dev)][Btn down][Btn up][Motion][Motion][...] ^ XkbFakeDeviceButton injects [DCE (XTEST)][Btn up] Thus the event sequence processed looks like this: [DCE (dev)][Btn down][Btn up][DCE (XTEST)][Btn up][Motion][Motion][...] The first DCE causes the master to switch to the device. The button up event injects a DCE to the XTEST device, causing the following Motion events to be processed with the master still being on XTEST classes. This patch post-fixes the injected event sequence with a DCE to restore the classes of the original slave device, resulting in an event sequence like this: [DCE (dev)][Btn down][Btn up][DCE (XTEST)][Btn up][DCE (dev)][Motion][Motion] Note that this is a simplified description. The event sequence injected by the PointerKeys code is injected for the master device only and the matching slave device that caused the injection has already finished processing on the slave. Furthermore, the injection happens as part of the the XKB layer, before the unwrapping of the processInputProc takes us into the DIX where the DCE is actually handled. Bug reproducible with a device that reports more than 2 valuators. Simply cause button releases on the device and wait for a "too many valuators" warning message. Signed-off-by: Peter Hutterer --- xkb/xkbActions.c | 26 +++++++++++++++++++------- 1 files changed, 19 insertions(+), 7 deletions(-) diff --git a/xkb/xkbActions.c b/xkb/xkbActions.c index 5d40199..2afd46d 100644 --- a/xkb/xkbActions.c +++ b/xkb/xkbActions.c @@ -1365,34 +1365,46 @@ XkbFakeDeviceButton(DeviceIntPtr dev,Bool press,int button) { EventListPtr events; int nevents, i; - DeviceIntPtr ptr; + DeviceIntPtr ptr, mpointer, lastSlave; /* If dev is a slave device, and the SD is attached, do nothing. If we'd * post through the attached master pointer we'd get duplicate events. * * if dev is a master keyboard, post through the XTEST device - * * if dev is a floating slave, post through the device itself. + * + * The event is injected into the event processing, not the EQ. Thus, + * ensure that we restore the master after the event sequence to the + * original set of classes. Otherwise, the master remains on the XTEST + * classes and drops events that don't fit into the XTEST layout (e.g. + * events with more than 2 valuators). + * To do so, we remember the lastSlave that posted through the master + * and add a DeviceChangedEvent to the end of the list. */ - if (IsMaster(dev)) - ptr = GetXTestDevice(GetMaster(dev, MASTER_POINTER)); - else if (!dev->u.master) + if (IsMaster(dev)) { + mpointer = GetMaster(dev, MASTER_POINTER); + lastSlave = mpointer->u.lastSlave; + ptr = GetXTestDevice(mpointer); + } else if (!dev->u.master) ptr = dev; else return; - events = InitEventList(GetMaximumEventsNum()); + events = InitEventList(GetMaximumEventsNum() + 1); OsBlockSignals(); nevents = GetPointerEvents(events, ptr, press ? ButtonPress : ButtonRelease, button, 0 /* flags */, 0 /* first */, 0 /* num_val */, NULL); + if (IsMaster(dev) && (lastSlave && lastSlave != ptr)) + CreateClassesChangedEvent(&events[nevents++], mpointer, + lastSlave, DEVCHANGE_POINTER_EVENT); OsReleaseSignals(); for (i = 0; i < nevents; i++) mieqProcessDeviceEvent(ptr, (InternalEvent*)events[i].event, NULL); - FreeEventList(events, GetMaximumEventsNum()); + FreeEventList(events, GetMaximumEventsNum() + 1); } -- 1.7.2 From 817e031a996a5f5aa16fc789d7e570cc589d96cb Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 28 Jul 2010 14:24:59 +1000 Subject: [PATCH 3/3] Xi: reset the unused classes pointer after copying After copying the unused_classes into the device, reset the original pointer. Otherwise we have two pointers pointing to the same field and both get freed on device removal. Some classes already have this behaviour since 51c8fd69. Signed-off-by: Peter Hutterer --- Xi/exevents.c | 6 ++++++ 1 files changed, 6 insertions(+), 0 deletions(-) diff --git a/Xi/exevents.c b/Xi/exevents.c index 566b0ef..a6160dd 100644 --- a/Xi/exevents.c +++ b/Xi/exevents.c @@ -227,6 +227,7 @@ DeepCopyFeedbackClasses(DeviceIntPtr from, DeviceIntPtr to) classes = dixLookupPrivate(&to->devPrivates, UnusedClassesPrivateKey); to->intfeed = classes->intfeed; + classes->intfeed = NULL; } i = &to->intfeed; @@ -263,6 +264,7 @@ DeepCopyFeedbackClasses(DeviceIntPtr from, DeviceIntPtr to) classes = dixLookupPrivate(&to->devPrivates, UnusedClassesPrivateKey); to->stringfeed = classes->stringfeed; + classes->stringfeed = NULL; } s = &to->stringfeed; @@ -299,6 +301,7 @@ DeepCopyFeedbackClasses(DeviceIntPtr from, DeviceIntPtr to) classes = dixLookupPrivate(&to->devPrivates, UnusedClassesPrivateKey); to->bell = classes->bell; + classes->bell = NULL; } b = &to->bell; @@ -336,6 +339,7 @@ DeepCopyFeedbackClasses(DeviceIntPtr from, DeviceIntPtr to) classes = dixLookupPrivate(&to->devPrivates, UnusedClassesPrivateKey); to->leds = classes->leds; + classes->leds = NULL; } l = &to->leds; @@ -387,6 +391,7 @@ DeepCopyKeyboardClasses(DeviceIntPtr from, DeviceIntPtr to) to->kbdfeed = classes->kbdfeed; if (!to->kbdfeed) InitKeyboardDeviceStruct(to, NULL, NULL, NULL); + classes->kbdfeed = NULL; } k = &to->kbdfeed; @@ -517,6 +522,7 @@ DeepCopyPointerClasses(DeviceIntPtr from, DeviceIntPtr to) classes = dixLookupPrivate(&to->devPrivates, UnusedClassesPrivateKey); to->ptrfeed = classes->ptrfeed; + classes->ptrfeed = NULL; } p = &to->ptrfeed; -- 1.7.2