From 64711a094893e83764bbeda538c6e877ebe2af79 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 15 Nov 2007 10:41:34 +1030 Subject: [PATCH] Xi: When switching MD classes, make a deep copy instead of pointer flip. Turns out it's really really hard synchronising device state across multiple duplicated events if they all share the same struct. So instead of doing so, when the SD changes deep-copy all it's classes into the MD. The MD then has the same capabilities, but the state can be set separately. This should fix xkb, key state, repeat etc. problems. Updating the device state allows us to remove the SwitchCoreKeyboard from the event gathering, it's all done during event processing now. --- Xi/exevents.c | 214 ++++++++++++++++++++++++++++++++++++++++++------ dix/getevents.c | 57 ------------- include/input.h | 3 +- 3 files changed, 192 insertions(+), 82 deletions(-) diff --git a/Xi/exevents.c b/Xi/exevents.c index 91035c7cb..1bf6c5179 100644 --- a/Xi/exevents.c +++ b/Xi/exevents.c @@ -76,7 +76,9 @@ SOFTWARE. #include "listdev.h" /* for CopySwapXXXClass */ #ifdef XKB +#include #include "xkbsrv.h" +extern Bool XkbCopyKeymap(XkbDescPtr src, XkbDescPtr dst, Bool sendNotifies); #endif #define WID(w) ((w) ? ((w)->drawable.id) : 0) @@ -102,6 +104,154 @@ RegisterOtherDevice(DeviceIntPtr device) device->public.realInputProc = ProcessOtherEvent; } +/** + * Copy the device->key into master->key and send a mapping notify to the + * clients if appropriate. + * master->key needs to be allocated by the caller. + * + * Device is the slave device. If it is attached to a master device, we may + * need to send a mapping notify to the client because it causes the MD + * to change state. + * + * Mapping notify needs to be sent in the following cases: + * - different slave device on same master + * - different master + * + * XXX: They way how the code is we also send a map notify if the slave device + * stays the same, but the master changes. This isn't really necessary though. + * + * XXX: this gives you funny behaviour with the ClientPointer. When a + * MappingNotify is sent to the client, the client usually responds with a + * GetKeyboardMapping. This will retrieve the ClientPointer's keyboard + * mapping, regardless of which keyboard sent the last mapping notify request. + * So depending on the CP setting, your keyboard may change layout in each + * app... + * + * This code is basically the old SwitchCoreKeyboard. + */ + +static void +CopyKeyClass(DeviceIntPtr device, DeviceIntPtr master) +{ + static DeviceIntPtr lastMapNotifyDevice = NULL; + KeyClassPtr mk, dk; /* master, device */ + BOOL sendNotify = FALSE; + int i; + + if (device == master) + return; + + dk = device->key; + mk = master->key; + + if (master->devPrivates[CoreDevicePrivatesIndex].ptr != device) { + memcpy(mk->modifierMap, dk->modifierMap, MAP_LENGTH); + + mk->modifierKeyMap = xcalloc(8, dk->maxKeysPerModifier); + if (!mk->modifierKeyMap) + FatalError("[Xi] no memory for class shift.\n"); + memcpy(mk->modifierKeyMap, dk->modifierKeyMap, + (8 * dk->maxKeysPerModifier)); + + mk->maxKeysPerModifier = dk->maxKeysPerModifier; + mk->curKeySyms.minKeyCode = dk->curKeySyms.minKeyCode; + mk->curKeySyms.maxKeyCode = dk->curKeySyms.maxKeyCode; + SetKeySymsMap(&mk->curKeySyms, &dk->curKeySyms); + + /* + * Copy state from the extended keyboard to core. If you omit this, + * holding Ctrl on keyboard one, and pressing Q on keyboard two, will + * cause your app to quit. This feels wrong to me, hence the below + * code. + * + * XXX: If you synthesise core modifier events, the state will get + * clobbered here. You'll have to work out something sensible + * to fix that. Good luck. + */ + +#define KEYBOARD_MASK (ShiftMask | LockMask | ControlMask | Mod1Mask | \ + Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask) + mk->state &= ~(KEYBOARD_MASK); + mk->state |= (dk->state & KEYBOARD_MASK); +#undef KEYBOARD_MASK + for (i = 0; i < 8; i++) + mk->modifierKeyCount[i] = dk->modifierKeyCount[i]; + +#ifdef XKB + if (!mk->xkbInfo || !mk->xkbInfo->desc) + XkbInitDevice(master); + if (!noXkbExtension && dk->xkbInfo && dk->xkbInfo->desc) { + if (!XkbCopyKeymap(dk->xkbInfo->desc, mk->xkbInfo->desc, True)) + FatalError("Couldn't pivot keymap from device to core!\n"); + } +#endif + + master->devPrivates[CoreDevicePrivatesIndex].ptr = device; + sendNotify = TRUE; + } else if (lastMapNotifyDevice != master) + sendNotify = TRUE; + + if (sendNotify) + { + SendMappingNotify(master, MappingKeyboard, + mk->curKeySyms.minKeyCode, + (mk->curKeySyms.maxKeyCode - + mk->curKeySyms.minKeyCode), + serverClient); + lastMapNotifyDevice = master; + } +} + +_X_EXPORT void +DeepCopyDeviceClasses(DeviceIntPtr from, DeviceIntPtr to) +{ +#define ALLOC_COPY_CLASS_IF(field, type) \ + if (from->field)\ + { \ + to->field = xcalloc(1, sizeof(type)); \ + if (!to->field) \ + FatalError("[Xi] no memory for class shift.\n"); \ + memcpy(to->field, from->field, sizeof(type)); \ + } + + ALLOC_COPY_CLASS_IF(key, KeyClassRec); + if (to->key) + { +#ifdef XKB + to->key->xkbInfo = NULL; +#endif + CopyKeyClass(from, to); + } + + if (from->valuator) + { + ValuatorClassPtr v; + to->valuator = xalloc(sizeof(ValuatorClassRec) + + from->valuator->numAxes * sizeof(AxisInfo) + + from->valuator->numAxes * sizeof(unsigned int)); + v = to->valuator; + if (!v) + FatalError("[Xi] no memory for class shift.\n"); + memcpy(v, from->valuator, sizeof(ValuatorClassRec)); + v->axes = (AxisInfoPtr)&v[1]; + memcpy(v->axes, from->valuator->axes, v->numAxes * sizeof(AxisInfo)); + } + + ALLOC_COPY_CLASS_IF(button, ButtonClassRec); + /* XXX: XkbAction needs to be copied */ + ALLOC_COPY_CLASS_IF(focus, FocusClassRec); + ALLOC_COPY_CLASS_IF(proximity, ProximityClassRec); + ALLOC_COPY_CLASS_IF(absolute, AbsoluteClassRec); + ALLOC_COPY_CLASS_IF(kbdfeed, KbdFeedbackClassRec); + /* XXX: XkbSrvLedInfo needs to be copied*/ + ALLOC_COPY_CLASS_IF(ptrfeed, PtrFeedbackClassRec); + ALLOC_COPY_CLASS_IF(intfeed, IntegerFeedbackClassRec); + ALLOC_COPY_CLASS_IF(stringfeed, StringFeedbackClassRec); + ALLOC_COPY_CLASS_IF(bell, BellFeedbackClassRec); + ALLOC_COPY_CLASS_IF(leds, LedFeedbackClassRec); + /* XXX: XkbSrvLedInfo needs to be copied. */ +} + static void ChangeMasterDeviceClasses(DeviceIntPtr device, deviceClassesChangedEvent *dcce) @@ -119,21 +269,31 @@ ChangeMasterDeviceClasses(DeviceIntPtr device, dcce->num_classes = 0; master->public.devicePrivate = device->public.devicePrivate; - master->key = device->key; - master->valuator = device->valuator; - master->button = device->button; - master->focus = device->focus; - master->proximity = device->proximity; - master->absolute = device->absolute; - master->kbdfeed = device->kbdfeed; - master->ptrfeed = device->ptrfeed; - master->intfeed = device->intfeed; - master->stringfeed = device->stringfeed; - master->bell = device->bell; - master->leds = device->leds; + + if (master->key) + xfree(master->key->modifierKeyMap); +#ifdef XKB + if (master->key && master->key->xkbInfo) + XkbFreeInfo(master->key->xkbInfo); +#endif + xfree(master->key); master->key = NULL; + xfree(master->valuator); master->valuator = NULL; + xfree(master->button); master->button = NULL; + xfree(master->focus); master->focus = NULL; + xfree(master->proximity); master->proximity = NULL; + xfree(master->absolute); master->absolute = NULL; + xfree(master->kbdfeed); master->kbdfeed = NULL; + xfree(master->ptrfeed); master->ptrfeed = NULL; + xfree(master->stringfeed); master->stringfeed = NULL; + xfree(master->bell); master->bell = NULL; + xfree(master->leds); master->leds = NULL; + xfree(master->intfeed); master->intfeed = NULL; + + DeepCopyDeviceClasses(device, master); /* event is already correct size, see comment in GetPointerEvents */ classbuff = (char*)&dcce[1]; + /* we don't actually swap if there's a NullClient, swapping is done * later when event is delivered. */ CopySwapClasses(NullClient, master, &dcce->num_classes, &classbuff); @@ -159,9 +319,9 @@ UpdateDeviceState(DeviceIntPtr device, xEvent* xE, int count) int key = 0, bit = 0; - KeyClassPtr k = device->key; - ButtonClassPtr b = device->button; - ValuatorClassPtr v = device->valuator; + KeyClassPtr k = NULL; + ButtonClassPtr b = NULL; + ValuatorClassPtr v = NULL; deviceValuator *xV = (deviceValuator *) xE; BYTE *kptr = NULL; CARD16 modifiers = 0, @@ -181,6 +341,11 @@ UpdateDeviceState(DeviceIntPtr device, xEvent* xE, int count) if (xE->u.u.type == GenericEvent) return DEFAULT; + k = device->key; + v = device->valuator; + b = device->button; + + if (xE->u.u.type != DeviceValuator) { key = xE->u.u.detail; @@ -272,10 +437,9 @@ UpdateDeviceState(DeviceIntPtr device, xEvent* xE, int count) *kptr |= bit; if (device->valuator) device->valuator->motionHintWindow = NullWindow; - if (!device->isMaster) - b->buttonsDown++; + b->buttonsDown++; b->motionMask = DeviceButtonMotionMask; - if (!device->isMaster && !b->map[key]) /* bit already unset for MDs */ + if (!b->map[key]) return DONT_PROCESS; if (b->map[key] <= 5) b->state |= (Button1Mask >> 1) << b->map[key]; @@ -290,9 +454,7 @@ UpdateDeviceState(DeviceIntPtr device, xEvent* xE, int count) *kptr &= ~bit; if (device->valuator) device->valuator->motionHintWindow = NullWindow; - if (!device->isMaster) - b->buttonsDown--; - if (b->buttonsDown >= 1 && !b->buttonsDown) + if (b->buttonsDown >= 1 && !--b->buttonsDown) b->motionMask = 0; if (!b->map[key]) return DONT_PROCESS; @@ -323,9 +485,9 @@ ProcessOtherEvent(xEventPtr xE, DeviceIntPtr device, int count) GrabPtr grab = device->deviceGrab.grab; Bool deactivateDeviceGrab = FALSE; int key = 0, rootX, rootY; - ButtonClassPtr b = device->button; - KeyClassPtr k = device->key; - ValuatorClassPtr v = device->valuator; + ButtonClassPtr b; + KeyClassPtr k; + ValuatorClassPtr v; deviceValuator *xV = (deviceValuator *) xE; BOOL sendCore = FALSE; xEvent core; @@ -336,6 +498,10 @@ ProcessOtherEvent(xEventPtr xE, DeviceIntPtr device, int count) if (ret == DONT_PROCESS) return; + v = device->valuator; + b = device->button; + k = device->key; + coretype = XItoCoreType(xE->u.u.type); if (device->isMaster && device->coreEvents && coretype) sendCore = TRUE; diff --git a/dix/getevents.c b/dix/getevents.c index b0211b61a..20beff370 100644 --- a/dix/getevents.c +++ b/dix/getevents.c @@ -865,63 +865,6 @@ GetProximityEvents(EventList *events, DeviceIntPtr pDev, int type, return num_events; } - -/** - * pDev is the slave device that is about to send an event. If it is attached - * to a master device, then we need to send a mapping notify to the client. - * To do so, we need to remember the last master device that sent a mapping - * event. - * - * Mapping notify needs to be sent in the following cases: - * - different slave device on same master - * - different master - * - * Call this just before processInputProc. - * - * XXX: They way how the code is we also send a map notify if the slave device - * stays the same, but the master changes. This isn't really necessary though. - * - * XXX: this gives you funny behaviour with the ClientPointer. When a - * MappingNotify is sent to the client, the client usually responds with a - * GetKeyboardMapping. This will retrieve the ClientPointer's keyboard - * mapping, regardless of which keyboard sent the last mapping notify request. - * So depending on the CP setting, your keyboard may change layout in each - * app... - */ -_X_EXPORT void -SwitchCoreKeyboard(DeviceIntPtr pDev) -{ - static DeviceIntPtr lastMapNotifyDevice = NULL; - DeviceIntPtr master; - KeyClassPtr ckeyc; - int i = 0; - BOOL sendNotify = FALSE; - - if (pDev->isMaster || !pDev->u.master) - return; - - master = pDev->u.master; - ckeyc = master->key; - - if (master->devPrivates[CoreDevicePrivatesIndex].ptr != pDev) { - master->devPrivates[CoreDevicePrivatesIndex].ptr = pDev; - sendNotify = TRUE; - } - - if (lastMapNotifyDevice != master) - sendNotify = TRUE; - - if (sendNotify) - { - SendMappingNotify(pDev, MappingKeyboard, ckeyc->curKeySyms.minKeyCode, - (ckeyc->curKeySyms.maxKeyCode - - ckeyc->curKeySyms.minKeyCode), - serverClient); - lastMapNotifyDevice = master; - } -} - - /** * Note that pDev was the last function to send a core pointer event. * Currently a no-op. diff --git a/include/input.h b/include/input.h index dbf6aee20..91ce4eeef 100644 --- a/include/input.h +++ b/include/input.h @@ -458,7 +458,6 @@ extern int GetMotionHistory( unsigned long stop, ScreenPtr pScreen); -extern void SwitchCoreKeyboard(DeviceIntPtr pDev); extern void SwitchCorePointer(DeviceIntPtr pDev); extern DeviceIntPtr LookupDeviceIntRec( @@ -483,6 +482,8 @@ extern DeviceIntPtr NextFreePointerDevice(void); extern int AllocMasterDevice(char* name, DeviceIntPtr* ptr, DeviceIntPtr* keybd); +extern void DeepCopyDeviceClasses(DeviceIntPtr from, + DeviceIntPtr to); /* Window/device based access control */ extern Bool ACRegisterClient(ClientPtr client);