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.
This commit is contained in:
Peter Hutterer 2007-11-15 10:41:34 +10:30
parent cc4586df60
commit 64711a0948
3 changed files with 192 additions and 82 deletions

View File

@ -76,7 +76,9 @@ SOFTWARE.
#include "listdev.h" /* for CopySwapXXXClass */ #include "listdev.h" /* for CopySwapXXXClass */
#ifdef XKB #ifdef XKB
#include <X11/extensions/XKBproto.h>
#include "xkbsrv.h" #include "xkbsrv.h"
extern Bool XkbCopyKeymap(XkbDescPtr src, XkbDescPtr dst, Bool sendNotifies);
#endif #endif
#define WID(w) ((w) ? ((w)->drawable.id) : 0) #define WID(w) ((w) ? ((w)->drawable.id) : 0)
@ -102,6 +104,154 @@ RegisterOtherDevice(DeviceIntPtr device)
device->public.realInputProc = ProcessOtherEvent; 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 static void
ChangeMasterDeviceClasses(DeviceIntPtr device, ChangeMasterDeviceClasses(DeviceIntPtr device,
deviceClassesChangedEvent *dcce) deviceClassesChangedEvent *dcce)
@ -119,21 +269,31 @@ ChangeMasterDeviceClasses(DeviceIntPtr device,
dcce->num_classes = 0; dcce->num_classes = 0;
master->public.devicePrivate = device->public.devicePrivate; master->public.devicePrivate = device->public.devicePrivate;
master->key = device->key;
master->valuator = device->valuator; if (master->key)
master->button = device->button; xfree(master->key->modifierKeyMap);
master->focus = device->focus; #ifdef XKB
master->proximity = device->proximity; if (master->key && master->key->xkbInfo)
master->absolute = device->absolute; XkbFreeInfo(master->key->xkbInfo);
master->kbdfeed = device->kbdfeed; #endif
master->ptrfeed = device->ptrfeed; xfree(master->key); master->key = NULL;
master->intfeed = device->intfeed; xfree(master->valuator); master->valuator = NULL;
master->stringfeed = device->stringfeed; xfree(master->button); master->button = NULL;
master->bell = device->bell; xfree(master->focus); master->focus = NULL;
master->leds = device->leds; 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 */ /* event is already correct size, see comment in GetPointerEvents */
classbuff = (char*)&dcce[1]; classbuff = (char*)&dcce[1];
/* we don't actually swap if there's a NullClient, swapping is done /* we don't actually swap if there's a NullClient, swapping is done
* later when event is delivered. */ * later when event is delivered. */
CopySwapClasses(NullClient, master, &dcce->num_classes, &classbuff); CopySwapClasses(NullClient, master, &dcce->num_classes, &classbuff);
@ -159,9 +319,9 @@ UpdateDeviceState(DeviceIntPtr device, xEvent* xE, int count)
int key = 0, int key = 0,
bit = 0; bit = 0;
KeyClassPtr k = device->key; KeyClassPtr k = NULL;
ButtonClassPtr b = device->button; ButtonClassPtr b = NULL;
ValuatorClassPtr v = device->valuator; ValuatorClassPtr v = NULL;
deviceValuator *xV = (deviceValuator *) xE; deviceValuator *xV = (deviceValuator *) xE;
BYTE *kptr = NULL; BYTE *kptr = NULL;
CARD16 modifiers = 0, CARD16 modifiers = 0,
@ -181,6 +341,11 @@ UpdateDeviceState(DeviceIntPtr device, xEvent* xE, int count)
if (xE->u.u.type == GenericEvent) if (xE->u.u.type == GenericEvent)
return DEFAULT; return DEFAULT;
k = device->key;
v = device->valuator;
b = device->button;
if (xE->u.u.type != DeviceValuator) if (xE->u.u.type != DeviceValuator)
{ {
key = xE->u.u.detail; key = xE->u.u.detail;
@ -272,10 +437,9 @@ UpdateDeviceState(DeviceIntPtr device, xEvent* xE, int count)
*kptr |= bit; *kptr |= bit;
if (device->valuator) if (device->valuator)
device->valuator->motionHintWindow = NullWindow; device->valuator->motionHintWindow = NullWindow;
if (!device->isMaster) b->buttonsDown++;
b->buttonsDown++;
b->motionMask = DeviceButtonMotionMask; b->motionMask = DeviceButtonMotionMask;
if (!device->isMaster && !b->map[key]) /* bit already unset for MDs */ if (!b->map[key])
return DONT_PROCESS; return DONT_PROCESS;
if (b->map[key] <= 5) if (b->map[key] <= 5)
b->state |= (Button1Mask >> 1) << b->map[key]; b->state |= (Button1Mask >> 1) << b->map[key];
@ -290,9 +454,7 @@ UpdateDeviceState(DeviceIntPtr device, xEvent* xE, int count)
*kptr &= ~bit; *kptr &= ~bit;
if (device->valuator) if (device->valuator)
device->valuator->motionHintWindow = NullWindow; device->valuator->motionHintWindow = NullWindow;
if (!device->isMaster) if (b->buttonsDown >= 1 && !--b->buttonsDown)
b->buttonsDown--;
if (b->buttonsDown >= 1 && !b->buttonsDown)
b->motionMask = 0; b->motionMask = 0;
if (!b->map[key]) if (!b->map[key])
return DONT_PROCESS; return DONT_PROCESS;
@ -323,9 +485,9 @@ ProcessOtherEvent(xEventPtr xE, DeviceIntPtr device, int count)
GrabPtr grab = device->deviceGrab.grab; GrabPtr grab = device->deviceGrab.grab;
Bool deactivateDeviceGrab = FALSE; Bool deactivateDeviceGrab = FALSE;
int key = 0, rootX, rootY; int key = 0, rootX, rootY;
ButtonClassPtr b = device->button; ButtonClassPtr b;
KeyClassPtr k = device->key; KeyClassPtr k;
ValuatorClassPtr v = device->valuator; ValuatorClassPtr v;
deviceValuator *xV = (deviceValuator *) xE; deviceValuator *xV = (deviceValuator *) xE;
BOOL sendCore = FALSE; BOOL sendCore = FALSE;
xEvent core; xEvent core;
@ -336,6 +498,10 @@ ProcessOtherEvent(xEventPtr xE, DeviceIntPtr device, int count)
if (ret == DONT_PROCESS) if (ret == DONT_PROCESS)
return; return;
v = device->valuator;
b = device->button;
k = device->key;
coretype = XItoCoreType(xE->u.u.type); coretype = XItoCoreType(xE->u.u.type);
if (device->isMaster && device->coreEvents && coretype) if (device->isMaster && device->coreEvents && coretype)
sendCore = TRUE; sendCore = TRUE;

View File

@ -865,63 +865,6 @@ GetProximityEvents(EventList *events, DeviceIntPtr pDev, int type,
return num_events; 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. * Note that pDev was the last function to send a core pointer event.
* Currently a no-op. * Currently a no-op.

View File

@ -458,7 +458,6 @@ extern int GetMotionHistory(
unsigned long stop, unsigned long stop,
ScreenPtr pScreen); ScreenPtr pScreen);
extern void SwitchCoreKeyboard(DeviceIntPtr pDev);
extern void SwitchCorePointer(DeviceIntPtr pDev); extern void SwitchCorePointer(DeviceIntPtr pDev);
extern DeviceIntPtr LookupDeviceIntRec( extern DeviceIntPtr LookupDeviceIntRec(
@ -483,6 +482,8 @@ extern DeviceIntPtr NextFreePointerDevice(void);
extern int AllocMasterDevice(char* name, extern int AllocMasterDevice(char* name,
DeviceIntPtr* ptr, DeviceIntPtr* ptr,
DeviceIntPtr* keybd); DeviceIntPtr* keybd);
extern void DeepCopyDeviceClasses(DeviceIntPtr from,
DeviceIntPtr to);
/* Window/device based access control */ /* Window/device based access control */
extern Bool ACRegisterClient(ClientPtr client); extern Bool ACRegisterClient(ClientPtr client);