1/*
2 * Copyright 2001-2003 Red Hat Inc., Durham, North Carolina.
3 *
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation on the rights to use, copy, modify, merge,
10 * publish, distribute, sublicense, and/or sell copies of the Software,
11 * and to permit persons to whom the Software is furnished to do so,
12 * subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial
16 * portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 * NON-INFRINGEMENT.  IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS
22 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
23 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
24 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 * SOFTWARE.
26 */
27
28/*
29 * Authors:
30 *   David H. Dawes <dawes@xfree86.org>
31 *   Kevin E. Martin <kem@redhat.com>
32 *   Rickard E. (Rik) Faith <faith@redhat.com>
33 */
34
35/** \file
36 *
37 * This file implements common routines used by the backend and console
38 * input devices.
39 */
40
41#ifdef HAVE_DMX_CONFIG_H
42#include <dmx-config.h>
43#endif
44
45#define DMX_STATE_DEBUG 0
46
47#include "dmxinputinit.h"
48#include "dmxcommon.h"
49#include "dmxconsole.h"
50#include "dmxprop.h"
51#include "dmxsync.h"
52#include "dmxmap.h"
53
54#include "inputstr.h"
55#include "input.h"
56#include <X11/keysym.h>
57#include "mipointer.h"
58#include "scrnintstr.h"
59
60#include <unistd.h>             /* For usleep() */
61
62#if DMX_STATE_DEBUG
63#define DMXDBG0(f)               dmxLog(dmxDebug,f)
64#else
65#define DMXDBG0(f)
66#endif
67
68/** Each device has a private area that is visible only from inside the
69 * driver code. */
70typedef struct _myPrivate {
71    DMX_COMMON_PRIVATE;
72} myPrivate;
73
74static void dmxCommonKbdSetAR(Display *display,
75                              unsigned char *old, unsigned char *new)
76{
77    XKeyboardControl kc;
78    XKeyboardState   ks;
79    unsigned long    mask = KBKey | KBAutoRepeatMode;
80    int              i, j;
81    int              minKeycode, maxKeycode;
82
83    if (!old) {
84        XGetKeyboardControl(display, &ks);
85        old = (unsigned char *)ks.auto_repeats;
86    }
87
88    XDisplayKeycodes(display, &minKeycode, &maxKeycode);
89    for (i = 1; i < 32; i++) {
90        if (!old || old[i] != new[i]) {
91            for (j = 0; j < 8; j++) {
92                if ((new[i] & (1 << j)) != (old[i] & (1 << j))) {
93                    kc.key              = i * 8 + j;
94                    kc.auto_repeat_mode = ((new[i] & (1 << j))
95                                           ? AutoRepeatModeOn
96                                           : AutoRepeatModeOff);
97                    if (kc.key >= minKeycode && kc.key <= maxKeycode)
98                        XChangeKeyboardControl(display, mask, &kc);
99                }
100            }
101        }
102    }
103}
104
105static void dmxCommonKbdSetLeds(Display *display, unsigned long new)
106{
107    int              i;
108    XKeyboardControl kc;
109
110    for (i = 0; i < 32; i++) {
111        kc.led      = i + 1;
112        kc.led_mode = (new & (1 << i)) ? LedModeOn : LedModeOff;
113        XChangeKeyboardControl(display, KBLed | KBLedMode, &kc);
114    }
115}
116
117static void dmxCommonKbdSetCtrl(Display *display,
118                                KeybdCtrl *old, KeybdCtrl *new)
119{
120    XKeyboardControl kc;
121    unsigned long    mask = KBKeyClickPercent | KBAutoRepeatMode;
122
123    if (!old
124        || old->click         != new->click
125        || old->autoRepeat    != new->autoRepeat) {
126
127        kc.key_click_percent = new->click;
128        kc.auto_repeat_mode  = new->autoRepeat;
129
130        XChangeKeyboardControl(display, mask, &kc);
131    }
132
133    dmxCommonKbdSetLeds(display, new->leds);
134    dmxCommonKbdSetAR(display, old ? old->autoRepeats : NULL,
135                      new->autoRepeats);
136}
137
138static void dmxCommonMouSetCtrl(Display *display, PtrCtrl *old, PtrCtrl *new)
139{
140    Bool do_accel, do_threshold;
141
142    if (!old
143        || old->num != new->num
144        || old->den != new->den
145        || old->threshold != new->threshold) {
146        do_accel     = (new->num > 0 && new->den > 0);
147        do_threshold = (new->threshold > 0);
148        if (do_accel || do_threshold) {
149            XChangePointerControl(display, do_accel, do_threshold,
150                                  new->num, new->den, new->threshold);
151        }
152    }
153}
154
155/** Update the keyboard control. */
156void dmxCommonKbdCtrl(DevicePtr pDev, KeybdCtrl *ctrl)
157{
158    GETPRIVFROMPDEV;
159
160    if (!priv->stateSaved && priv->be) dmxCommonSaveState(priv);
161    if (!priv->display || !priv->stateSaved) return;
162    dmxCommonKbdSetCtrl(priv->display,
163                        priv->kctrlset ? &priv->kctrl : NULL,
164                        ctrl);
165    priv->kctrl    = *ctrl;
166    priv->kctrlset = 1;
167}
168
169/** Update the mouse control. */
170void dmxCommonMouCtrl(DevicePtr pDev, PtrCtrl *ctrl)
171{
172    GETPRIVFROMPDEV;
173
174                                /* Don't set the acceleration for the
175                                 * console, because that should be
176                                 * controlled by the X server that the
177                                 * console is running on.  Otherwise,
178                                 * the acceleration for the console
179                                 * window would be unexpected for the
180                                 * scale of the window. */
181    if (priv->be) {
182        dmxCommonMouSetCtrl(priv->display,
183                            priv->mctrlset ? &priv->mctrl : NULL,
184                            ctrl);
185        priv->mctrl    = *ctrl;
186        priv->mctrlset = 1;
187    }
188}
189
190/** Sound they keyboard bell. */
191void dmxCommonKbdBell(DevicePtr pDev, int percent,
192                      int volume, int pitch, int duration)
193{
194    GETPRIVFROMPDEV;
195    XKeyboardControl kc;
196    XKeyboardState   ks;
197    unsigned long    mask = KBBellPercent | KBBellPitch | KBBellDuration;
198
199    if (!priv->be) XGetKeyboardControl(priv->display, &ks);
200    kc.bell_percent  = volume;
201    kc.bell_pitch    = pitch;
202    kc.bell_duration = duration;
203    XChangeKeyboardControl(priv->display, mask, &kc);
204    XBell(priv->display, percent);
205    if (!priv->be) {
206        kc.bell_percent  = ks.bell_percent;
207        kc.bell_pitch    = ks.bell_pitch;
208        kc.bell_duration = ks.bell_duration;
209        XChangeKeyboardControl(priv->display, mask, &kc);
210    }
211}
212
213/** Get the keyboard mapping. */
214void dmxCommonKbdGetMap(DevicePtr pDev, KeySymsPtr pKeySyms, CARD8 *pModMap)
215{
216    GETPRIVFROMPDEV;
217    int             min_keycode;
218    int             max_keycode;
219    int             map_width;
220    KeySym          *keyboard_mapping;
221    XModifierKeymap *modifier_mapping;
222    int             i, j;
223
224                                /* Compute pKeySyms.  Cast
225                                 * XGetKeyboardMapping because of
226                                 * compiler warning on 64-bit machines.
227                                 * We assume pointers to 32-bit and
228                                 * 64-bit ints are the same. */
229    XDisplayKeycodes(priv->display, &min_keycode, &max_keycode);
230    keyboard_mapping     = (KeySym *)XGetKeyboardMapping(priv->display,
231                                                         min_keycode,
232                                                         max_keycode
233                                                         - min_keycode + 1,
234                                                         &map_width);
235    pKeySyms->minKeyCode = min_keycode;
236    pKeySyms->maxKeyCode = max_keycode;
237    pKeySyms->mapWidth   = map_width;
238    pKeySyms->map        = keyboard_mapping;
239
240
241                                /* Compute pModMap  */
242    modifier_mapping     = XGetModifierMapping(priv->display);
243    for (i = 0; i < MAP_LENGTH; i++)
244        pModMap[i] = 0;
245    for (j = 0; j < 8; j++) {
246        int max_keypermod = modifier_mapping->max_keypermod;
247
248        for (i = 0; i < max_keypermod; i++) {
249            CARD8 keycode = modifier_mapping->modifiermap[j*max_keypermod + i];
250            if (keycode)
251                pModMap[keycode] |= 1 << j;
252        }
253    }
254    XFreeModifiermap(modifier_mapping);
255}
256
257/** Fill in the XKEYBOARD parts of the \a info structure for the
258 * specified \a pDev. */
259void dmxCommonKbdGetInfo(DevicePtr pDev, DMXLocalInitInfoPtr info)
260{
261    GETPRIVFROMPDEV;
262    GETDMXINPUTFROMPRIV;
263    char *pt;
264
265    dmxCommonSaveState(priv);
266    if (priv->xkb) {
267#define NAME(x) \
268 priv->xkb->names->x ? XGetAtomName(priv->display,priv->xkb->names->x) : NULL
269        info->names.keycodes = NAME(keycodes);
270        info->names.types    = NAME(types);
271        info->names.compat   = NAME(compat);
272        info->names.symbols  = NAME(symbols);
273        info->names.geometry = NAME(geometry);
274        info->freenames      = 1;
275#undef NAME
276        dmxLogInput(dmxInput,
277                    "XKEYBOARD: keycodes = %s\n", info->names.keycodes);
278        dmxLogInput(dmxInput,
279                    "XKEYBOARD: symbols  = %s\n", info->names.symbols);
280        dmxLogInput(dmxInput,
281                    "XKEYBOARD: geometry = %s\n", info->names.geometry);
282        if ((pt = strchr(info->names.keycodes, '+'))) *pt = '\0';
283    }
284    dmxCommonRestoreState(priv);
285}
286
287/** Turn \a pDev on (i.e., take input from \a pDev). */
288int dmxCommonKbdOn(DevicePtr pDev)
289{
290    GETPRIVFROMPDEV;
291    if (priv->be) dmxCommonSaveState(priv);
292    priv->eventMask |= DMX_KEYBOARD_EVENT_MASK;
293    XSelectInput(priv->display, priv->window, priv->eventMask);
294    if (priv->be)
295        XSetInputFocus(priv->display, priv->window, RevertToPointerRoot,
296                       CurrentTime);
297    return -1;
298}
299
300/** Turn \a pDev off. */
301void dmxCommonKbdOff(DevicePtr pDev)
302{
303    GETPRIVFROMPDEV;
304    priv->eventMask &= ~DMX_KEYBOARD_EVENT_MASK;
305    XSelectInput(priv->display, priv->window, priv->eventMask);
306    dmxCommonRestoreState(priv);
307}
308
309/** Turn \a pDev on (i.e., take input from \a pDev). */
310int dmxCommonOthOn(DevicePtr pDev)
311{
312    GETPRIVFROMPDEV;
313    GETDMXINPUTFROMPRIV;
314    XEventClass event_list[DMX_MAX_XINPUT_EVENT_TYPES];
315    int         event_type[DMX_MAX_XINPUT_EVENT_TYPES];
316    int         count = 0;
317
318#define ADD(type)                                                            \
319    if (count < DMX_MAX_XINPUT_EVENT_TYPES) {                                \
320        type(priv->xi, event_type[count], event_list[count]);                \
321        if (event_type[count]) {                                             \
322            dmxMapInsert(dmxLocal, event_type[count], XI_##type);            \
323            ++count;                                                         \
324        }                                                                    \
325    } else {                                                                 \
326        dmxLog(dmxWarning, "More than %d event types for %s\n",              \
327               DMX_MAX_XINPUT_EVENT_TYPES, dmxInput->name);                  \
328    }
329
330    if (!(priv->xi = XOpenDevice(priv->display, dmxLocal->deviceId))) {
331        dmxLog(dmxWarning, "Cannot open %s device (id=%d) on %s\n",
332               dmxLocal->deviceName ? dmxLocal->deviceName : "(unknown)",
333               dmxLocal->deviceId, dmxInput->name);
334        return -1;
335    }
336    ADD(DeviceKeyPress);
337    ADD(DeviceKeyRelease);
338    ADD(DeviceButtonPress);
339    ADD(DeviceButtonRelease);
340    ADD(DeviceMotionNotify);
341    ADD(DeviceFocusIn);
342    ADD(DeviceFocusOut);
343    ADD(ProximityIn);
344    ADD(ProximityOut);
345    ADD(DeviceStateNotify);
346    ADD(DeviceMappingNotify);
347    ADD(ChangeDeviceNotify);
348    XSelectExtensionEvent(priv->display, priv->window, event_list, count);
349
350    return -1;
351}
352
353/** Turn \a pDev off. */
354void dmxCommonOthOff(DevicePtr pDev)
355{
356    GETPRIVFROMPDEV;
357
358    if (priv->xi) XCloseDevice(priv->display, priv->xi);
359    priv->xi = NULL;
360}
361
362/** Fill the \a info structure with information needed to initialize \a
363 * pDev. */
364void dmxCommonOthGetInfo(DevicePtr pDev, DMXLocalInitInfoPtr info)
365{
366    GETPRIVFROMPDEV;
367    GETDMXINPUTFROMPRIV;
368    XExtensionVersion    *ext;
369    XDeviceInfo          *devices;
370    Display              *display = priv->display;
371    int                  num;
372    int                  i, j, k;
373    XextErrorHandler     handler;
374
375    if (!display && !(display = XOpenDisplay(dmxInput->name)))
376        return;
377
378    /* Print out information about the XInput Extension. */
379    handler = XSetExtensionErrorHandler(dmxInputExtensionErrorHandler);
380    ext     = XGetExtensionVersion(display, INAME);
381    XSetExtensionErrorHandler(handler);
382
383    if (ext && ext != (XExtensionVersion *)NoSuchExtension) {
384        XFree(ext);
385        devices = XListInputDevices(display, &num);
386        for (i = 0; i < num; i++) {
387            if (devices[i].id == (XID)dmxLocal->deviceId) {
388                XAnyClassPtr     any;
389                XKeyInfoPtr      ki;
390                XButtonInfoPtr   bi;
391                XValuatorInfoPtr vi;
392                for (j = 0, any = devices[i].inputclassinfo;
393                     j < devices[i].num_classes;
394                     any = (XAnyClassPtr)((char *)any + any->length), j++) {
395                    switch (any->class) {
396                    case KeyClass:
397                        ki = (XKeyInfoPtr)any;
398                        info->keyboard           = 1;
399                        info->keyClass           = 1;
400                        info->keySyms.minKeyCode = ki->min_keycode;
401                        info->keySyms.maxKeyCode = ki->max_keycode;
402                        info->kbdFeedbackClass   = 1;
403                        break;
404                    case ButtonClass:
405                        bi = (XButtonInfoPtr)any;
406                        info->buttonClass        = 1;
407                        info->numButtons         = bi->num_buttons;
408                        info->ptrFeedbackClass   = 1;
409                        break;
410                    case ValuatorClass:
411                                /* This assume all axes are either
412                                 * Absolute or Relative. */
413                        vi = (XValuatorInfoPtr)any;
414                        info->valuatorClass      = 1;
415                        if (vi->mode == Absolute)
416                            info->numAbsAxes     = vi->num_axes;
417                        else
418                            info->numRelAxes     = vi->num_axes;
419                        for (k = 0; k < vi->num_axes; k++) {
420                            info->res[k]         = vi->axes[k].resolution;
421                            info->minres[k]      = vi->axes[k].resolution;
422                            info->maxres[k]      = vi->axes[k].resolution;
423                            info->minval[k]      = vi->axes[k].min_value;
424                            info->maxval[k]      = vi->axes[k].max_value;
425                        }
426                        break;
427                    case FeedbackClass:
428                                /* Only keyboard and pointer feedback
429                                 * are handled at this time. */
430                        break;
431                    case ProximityClass:
432                        info->proximityClass     = 1;
433                        break;
434                    case FocusClass:
435                        info->focusClass         = 1;
436                        break;
437                    case OtherClass:
438                        break;
439                    }
440                }
441            }
442        }
443        XFreeDeviceList(devices);
444    }
445    if (display != priv->display) XCloseDisplay(display);
446}
447
448/** Obtain the mouse button mapping. */
449void dmxCommonMouGetMap(DevicePtr pDev, unsigned char *map, int *nButtons)
450{
451    GETPRIVFROMPDEV;
452    int i;
453
454    *nButtons = XGetPointerMapping(priv->display, map, DMX_MAX_BUTTONS);
455    for (i = 0; i <= *nButtons; i++) map[i] = i;
456}
457
458static void *dmxCommonXSelect(DMXScreenInfo *dmxScreen, void *closure)
459{
460    myPrivate *priv = closure;
461    XSelectInput(dmxScreen->beDisplay, dmxScreen->scrnWin, priv->eventMask);
462    return NULL;
463}
464
465static void *dmxCommonAddEnabledDevice(DMXScreenInfo *dmxScreen, void *closure)
466{
467    AddEnabledDevice(XConnectionNumber(dmxScreen->beDisplay));
468    return NULL;
469}
470
471static void *dmxCommonRemoveEnabledDevice(DMXScreenInfo *dmxScreen,
472                                          void *closure)
473{
474    RemoveEnabledDevice(XConnectionNumber(dmxScreen->beDisplay));
475    return NULL;
476}
477
478/** Turn \a pDev on (i.e., take input from \a pDev). */
479int dmxCommonMouOn(DevicePtr pDev)
480{
481    GETPRIVFROMPDEV;
482    GETDMXINPUTFROMPRIV;
483
484    priv->eventMask |= DMX_POINTER_EVENT_MASK;
485    if (dmxShadowFB) {
486        XWarpPointer(priv->display, priv->window, priv->window,
487                     0, 0, 0, 0,
488                     priv->initPointerX,
489                     priv->initPointerY);
490        dmxSync(&dmxScreens[dmxInput->scrnIdx], TRUE);
491    }
492    if (!priv->be) {
493        XSelectInput(priv->display, priv->window, priv->eventMask);
494        AddEnabledDevice(XConnectionNumber(priv->display));
495    } else {
496        dmxPropertyIterate(priv->be, dmxCommonXSelect, priv);
497        dmxPropertyIterate(priv->be, dmxCommonAddEnabledDevice, dmxInput);
498    }
499
500    return -1;
501}
502
503/** Turn \a pDev off. */
504void dmxCommonMouOff(DevicePtr pDev)
505{
506    GETPRIVFROMPDEV;
507    GETDMXINPUTFROMPRIV;
508
509    priv->eventMask &= ~DMX_POINTER_EVENT_MASK;
510    if (!priv->be) {
511        RemoveEnabledDevice(XConnectionNumber(priv->display));
512        XSelectInput(priv->display, priv->window, priv->eventMask);
513    } else {
514        dmxPropertyIterate(priv->be, dmxCommonRemoveEnabledDevice, dmxInput);
515        dmxPropertyIterate(priv->be, dmxCommonXSelect, priv);
516    }
517}
518
519/** Given the global coordinates \a x and \a y, determine the screen
520 * with the lowest number on which those coordinates lie.  If they are
521 * not on any screen, return -1.  The number returned is an index into
522 * \a dmxScreenInfo and is between -1 and \a dmxNumScreens - 1,
523 * inclusive. */
524int dmxFindPointerScreen(int x, int y)
525{
526    int i;
527
528    for (i = 0; i < dmxNumScreens; i++) {
529	ScreenPtr pScreen = screenInfo.screens[i];
530	if (x >= pScreen->x && x < pScreen->x + pScreen->width &&
531	    y >= pScreen->y && y < pScreen->y + pScreen->height)
532	    return i;
533    }
534    return -1;
535}
536
537/** Returns a pointer to the private area for the device that comes just
538 * prior to \a pDevice in the current \a dmxInput device list.  This is
539 * used as the private area for the current device in some situations
540 * (e.g., when a keyboard and mouse form a pair that should share the
541 * same private area).  If the requested private area cannot be located,
542 * then NULL is returned. */
543pointer dmxCommonCopyPrivate(DeviceIntPtr pDevice)
544{
545    GETDMXLOCALFROMPDEVICE;
546    DMXInputInfo *dmxInput = &dmxInputs[dmxLocal->inputIdx];
547    int          i;
548
549    for (i = 0; i < dmxInput->numDevs; i++)
550        if (dmxInput->devs[i] == dmxLocal && i)
551            return dmxInput->devs[i-1]->private;
552    return NULL;
553}
554
555/** This routine saves and resets some important state for the backend
556 * and console device drivers:
557 * - the modifier map is saved and set to 0 (so DMX controls the LEDs)
558 * - the key click, bell, led, and repeat masks are saved and set to the
559 * values that DMX claims to be using
560 *
561 * This routine and #dmxCommonRestoreState are used when the pointer
562 * enters and leaves the console window, or when the backend window is
563 * active or not active (for a full-screen window, this only happens at
564 * server startup and server shutdown).
565 */
566void dmxCommonSaveState(pointer private)
567{
568    GETPRIVFROMPRIVATE;
569    XKeyboardState   ks;
570    unsigned long    i;
571    XModifierKeymap  *modmap;
572
573    if (dmxInput->console) priv = dmxInput->devs[0]->private;
574    if (!priv->display || priv->stateSaved) return;
575    DMXDBG0("dmxCommonSaveState\n");
576    if (dmxUseXKB && (priv->xkb = XkbAllocKeyboard())) {
577        if (XkbGetIndicatorMap(priv->display, XkbAllIndicatorsMask, priv->xkb)
578            || XkbGetNames(priv->display, XkbAllNamesMask, priv->xkb)) {
579            dmxLogInput(dmxInput, "Could not get XKB information\n");
580            XkbFreeKeyboard(priv->xkb, 0, True);
581            priv->xkb = NULL;
582        } else {
583            if (priv->xkb->indicators) {
584                priv->savedIndicators = *priv->xkb->indicators;
585                for (i = 0; i < XkbNumIndicators; i++)
586                    if (priv->xkb->indicators->phys_indicators & (1 << i)) {
587                        priv->xkb->indicators->maps[i].flags
588                            = XkbIM_NoAutomatic;
589                    }
590                XkbSetIndicatorMap(priv->display, ~0, priv->xkb);
591            }
592        }
593    }
594
595    XGetKeyboardControl(priv->display, &ks);
596    priv->savedKctrl.click              = ks.key_click_percent;
597    priv->savedKctrl.bell               = ks.bell_percent;
598    priv->savedKctrl.bell_pitch         = ks.bell_pitch;
599    priv->savedKctrl.bell_duration      = ks.bell_duration;
600    priv->savedKctrl.leds               = ks.led_mask;
601    priv->savedKctrl.autoRepeat         = ks.global_auto_repeat;
602    for (i = 0; i < 32; i++)
603        priv->savedKctrl.autoRepeats[i] = ks.auto_repeats[i];
604
605    dmxCommonKbdSetCtrl(priv->display, &priv->savedKctrl,
606                        &priv->dmxLocal->kctrl);
607
608    priv->savedModMap                   = XGetModifierMapping(priv->display);
609
610    modmap                              = XNewModifiermap(0);
611    XSetModifierMapping(priv->display, modmap);
612    if (dmxInput->scrnIdx != -1)
613        dmxSync(&dmxScreens[dmxInput->scrnIdx], TRUE);
614    XFreeModifiermap(modmap);
615
616    priv->stateSaved = 1;
617}
618
619/** This routine restores all the information saved by #dmxCommonSaveState. */
620void dmxCommonRestoreState(pointer private)
621{
622    GETPRIVFROMPRIVATE;
623    int retcode = -1;
624    CARD32 start;
625
626    if (dmxInput->console)
627        priv = dmxInput->devs[0]->private;
628    if (!priv->stateSaved)
629        return;
630    priv->stateSaved = 0;
631
632    DMXDBG0("dmxCommonRestoreState\n");
633    if (priv->xkb) {
634        *priv->xkb->indicators = priv->savedIndicators;
635        XkbSetIndicatorMap(priv->display, ~0, priv->xkb);
636        XkbFreeKeyboard(priv->xkb, 0, True);
637        priv->xkb = 0;
638    }
639
640    for (start = GetTimeInMillis(); GetTimeInMillis() - start < 5000;) {
641        CARD32 tmp;
642
643        retcode = XSetModifierMapping(priv->display, priv->savedModMap);
644        if (retcode == MappingSuccess)
645            break;
646        if (retcode == MappingBusy)
647            dmxLogInput(dmxInput, "Keyboard busy, waiting\n");
648        else
649            dmxLogInput(dmxInput, "Keyboard error, waiting\n");
650
651                                /* Don't generate X11 protocol for a bit */
652        for (tmp = GetTimeInMillis(); GetTimeInMillis() - tmp < 250;) {
653            usleep(250);            /* This ends up sleeping only until
654                                     * the next key press generates an
655                                     * interruption.  We make the delay
656                                     * relatively short in case the user
657                                     * pressed they keys quickly. */
658        }
659
660    }
661    if (retcode != MappingSuccess)
662        dmxLog(dmxWarning, "Unable to restore keyboard modifier state!\n");
663
664    XFreeModifiermap(priv->savedModMap);
665    priv->savedModMap = NULL;
666
667    dmxCommonKbdSetCtrl(priv->display, NULL, &priv->savedKctrl);
668    priv->kctrlset = 0;         /* Invalidate copy */
669}
670