Imported Upstream version 1.15.1
[deb_xorg-server.git] / hw / dmx / input / dmxcommon.c
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. */
70 typedef struct _myPrivate {
71 DMX_COMMON_PRIVATE;
72 } myPrivate;
73
74 static void
75 dmxCommonKbdSetAR(Display * display, 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
105 static void
106 dmxCommonKbdSetLeds(Display * display, unsigned long new)
107 {
108 int i;
109 XKeyboardControl kc;
110
111 for (i = 0; i < 32; i++) {
112 kc.led = i + 1;
113 kc.led_mode = (new & (1 << i)) ? LedModeOn : LedModeOff;
114 XChangeKeyboardControl(display, KBLed | KBLedMode, &kc);
115 }
116 }
117
118 static void
119 dmxCommonKbdSetCtrl(Display * display, KeybdCtrl * old, KeybdCtrl * new)
120 {
121 XKeyboardControl kc;
122 unsigned long mask = KBKeyClickPercent | KBAutoRepeatMode;
123
124 if (!old || old->click != new->click || old->autoRepeat != new->autoRepeat) {
125
126 kc.key_click_percent = new->click;
127 kc.auto_repeat_mode = new->autoRepeat;
128
129 XChangeKeyboardControl(display, mask, &kc);
130 }
131
132 dmxCommonKbdSetLeds(display, new->leds);
133 dmxCommonKbdSetAR(display, old ? old->autoRepeats : NULL, new->autoRepeats);
134 }
135
136 static void
137 dmxCommonMouSetCtrl(Display * display, PtrCtrl * old, PtrCtrl * new)
138 {
139 Bool do_accel, do_threshold;
140
141 if (!old
142 || old->num != new->num
143 || old->den != new->den || old->threshold != new->threshold) {
144 do_accel = (new->num > 0 && new->den > 0);
145 do_threshold = (new->threshold > 0);
146 if (do_accel || do_threshold) {
147 XChangePointerControl(display, do_accel, do_threshold,
148 new->num, new->den, new->threshold);
149 }
150 }
151 }
152
153 /** Update the keyboard control. */
154 void
155 dmxCommonKbdCtrl(DevicePtr pDev, KeybdCtrl * ctrl)
156 {
157 GETPRIVFROMPDEV;
158
159 if (!priv->stateSaved && priv->be)
160 dmxCommonSaveState(priv);
161 if (!priv->display || !priv->stateSaved)
162 return;
163 dmxCommonKbdSetCtrl(priv->display,
164 priv->kctrlset ? &priv->kctrl : NULL, ctrl);
165 priv->kctrl = *ctrl;
166 priv->kctrlset = 1;
167 }
168
169 /** Update the mouse control. */
170 void
171 dmxCommonMouCtrl(DevicePtr pDev, PtrCtrl * ctrl)
172 {
173 GETPRIVFROMPDEV;
174
175 /* Don't set the acceleration for the
176 * console, because that should be
177 * controlled by the X server that the
178 * console is running on. Otherwise,
179 * the acceleration for the console
180 * window would be unexpected for the
181 * scale of the window. */
182 if (priv->be) {
183 dmxCommonMouSetCtrl(priv->display,
184 priv->mctrlset ? &priv->mctrl : NULL, ctrl);
185 priv->mctrl = *ctrl;
186 priv->mctrlset = 1;
187 }
188 }
189
190 /** Sound they keyboard bell. */
191 void
192 dmxCommonKbdBell(DevicePtr pDev, int percent,
193 int volume, int pitch, int duration)
194 {
195 GETPRIVFROMPDEV;
196 XKeyboardControl kc;
197 XKeyboardState ks;
198 unsigned long mask = KBBellPercent | KBBellPitch | KBBellDuration;
199
200 if (!priv->be)
201 XGetKeyboardControl(priv->display, &ks);
202 kc.bell_percent = volume;
203 kc.bell_pitch = pitch;
204 kc.bell_duration = duration;
205 XChangeKeyboardControl(priv->display, mask, &kc);
206 XBell(priv->display, percent);
207 if (!priv->be) {
208 kc.bell_percent = ks.bell_percent;
209 kc.bell_pitch = ks.bell_pitch;
210 kc.bell_duration = ks.bell_duration;
211 XChangeKeyboardControl(priv->display, mask, &kc);
212 }
213 }
214
215 /** Get the keyboard mapping. */
216 void
217 dmxCommonKbdGetMap(DevicePtr pDev, KeySymsPtr pKeySyms, CARD8 *pModMap)
218 {
219 GETPRIVFROMPDEV;
220 int min_keycode;
221 int max_keycode;
222 int map_width;
223 KeySym *keyboard_mapping;
224 XModifierKeymap *modifier_mapping;
225 int i, j;
226
227 /* Compute pKeySyms. Cast
228 * XGetKeyboardMapping because of
229 * compiler warning on 64-bit machines.
230 * We assume pointers to 32-bit and
231 * 64-bit ints are the same. */
232 XDisplayKeycodes(priv->display, &min_keycode, &max_keycode);
233 keyboard_mapping = (KeySym *) XGetKeyboardMapping(priv->display,
234 min_keycode,
235 max_keycode
236 - min_keycode + 1,
237 &map_width);
238 pKeySyms->minKeyCode = min_keycode;
239 pKeySyms->maxKeyCode = max_keycode;
240 pKeySyms->mapWidth = map_width;
241 pKeySyms->map = keyboard_mapping;
242
243 /* Compute pModMap */
244 modifier_mapping = XGetModifierMapping(priv->display);
245 for (i = 0; i < MAP_LENGTH; i++)
246 pModMap[i] = 0;
247 for (j = 0; j < 8; j++) {
248 int max_keypermod = modifier_mapping->max_keypermod;
249
250 for (i = 0; i < max_keypermod; i++) {
251 CARD8 keycode =
252 modifier_mapping->modifiermap[j * max_keypermod + i];
253 if (keycode)
254 pModMap[keycode] |= 1 << j;
255 }
256 }
257 XFreeModifiermap(modifier_mapping);
258 }
259
260 /** Fill in the XKEYBOARD parts of the \a info structure for the
261 * specified \a pDev. */
262 void
263 dmxCommonKbdGetInfo(DevicePtr pDev, DMXLocalInitInfoPtr info)
264 {
265 GETPRIVFROMPDEV;
266 GETDMXINPUTFROMPRIV;
267 char *pt;
268
269 dmxCommonSaveState(priv);
270 if (priv->xkb) {
271 #define NAME(x) \
272 priv->xkb->names->x ? XGetAtomName(priv->display,priv->xkb->names->x) : NULL
273 info->names.keycodes = NAME(keycodes);
274 info->names.types = NAME(types);
275 info->names.compat = NAME(compat);
276 info->names.symbols = NAME(symbols);
277 info->names.geometry = NAME(geometry);
278 info->freenames = 1;
279 #undef NAME
280 dmxLogInput(dmxInput,
281 "XKEYBOARD: keycodes = %s\n", info->names.keycodes);
282 dmxLogInput(dmxInput,
283 "XKEYBOARD: symbols = %s\n", info->names.symbols);
284 dmxLogInput(dmxInput,
285 "XKEYBOARD: geometry = %s\n", info->names.geometry);
286 if ((pt = strchr(info->names.keycodes, '+')))
287 *pt = '\0';
288 }
289 dmxCommonRestoreState(priv);
290 }
291
292 /** Turn \a pDev on (i.e., take input from \a pDev). */
293 int
294 dmxCommonKbdOn(DevicePtr pDev)
295 {
296 GETPRIVFROMPDEV;
297 if (priv->be)
298 dmxCommonSaveState(priv);
299 priv->eventMask |= DMX_KEYBOARD_EVENT_MASK;
300 XSelectInput(priv->display, priv->window, priv->eventMask);
301 if (priv->be)
302 XSetInputFocus(priv->display, priv->window, RevertToPointerRoot,
303 CurrentTime);
304 return -1;
305 }
306
307 /** Turn \a pDev off. */
308 void
309 dmxCommonKbdOff(DevicePtr pDev)
310 {
311 GETPRIVFROMPDEV;
312 priv->eventMask &= ~DMX_KEYBOARD_EVENT_MASK;
313 XSelectInput(priv->display, priv->window, priv->eventMask);
314 dmxCommonRestoreState(priv);
315 }
316
317 /** Turn \a pDev on (i.e., take input from \a pDev). */
318 int
319 dmxCommonOthOn(DevicePtr pDev)
320 {
321 GETPRIVFROMPDEV;
322 GETDMXINPUTFROMPRIV;
323 XEventClass event_list[DMX_MAX_XINPUT_EVENT_TYPES];
324 int event_type[DMX_MAX_XINPUT_EVENT_TYPES];
325 int count = 0;
326
327 #define ADD(type) \
328 if (count < DMX_MAX_XINPUT_EVENT_TYPES) { \
329 type(priv->xi, event_type[count], event_list[count]); \
330 if (event_type[count]) { \
331 dmxMapInsert(dmxLocal, event_type[count], XI_##type); \
332 ++count; \
333 } \
334 } else { \
335 dmxLog(dmxWarning, "More than %d event types for %s\n", \
336 DMX_MAX_XINPUT_EVENT_TYPES, dmxInput->name); \
337 }
338
339 if (!(priv->xi = XOpenDevice(priv->display, dmxLocal->deviceId))) {
340 dmxLog(dmxWarning, "Cannot open %s device (id=%d) on %s\n",
341 dmxLocal->deviceName ? dmxLocal->deviceName : "(unknown)",
342 dmxLocal->deviceId, dmxInput->name);
343 return -1;
344 }
345 ADD(DeviceKeyPress);
346 ADD(DeviceKeyRelease);
347 ADD(DeviceButtonPress);
348 ADD(DeviceButtonRelease);
349 ADD(DeviceMotionNotify);
350 ADD(DeviceFocusIn);
351 ADD(DeviceFocusOut);
352 ADD(ProximityIn);
353 ADD(ProximityOut);
354 ADD(DeviceStateNotify);
355 ADD(DeviceMappingNotify);
356 ADD(ChangeDeviceNotify);
357 XSelectExtensionEvent(priv->display, priv->window, event_list, count);
358
359 return -1;
360 }
361
362 /** Turn \a pDev off. */
363 void
364 dmxCommonOthOff(DevicePtr pDev)
365 {
366 GETPRIVFROMPDEV;
367
368 if (priv->xi)
369 XCloseDevice(priv->display, priv->xi);
370 priv->xi = NULL;
371 }
372
373 /** Fill the \a info structure with information needed to initialize \a
374 * pDev. */
375 void
376 dmxCommonOthGetInfo(DevicePtr pDev, DMXLocalInitInfoPtr info)
377 {
378 GETPRIVFROMPDEV;
379 GETDMXINPUTFROMPRIV;
380 XExtensionVersion *ext;
381 XDeviceInfo *devices;
382 Display *display = priv->display;
383 int num;
384 int i, j, k;
385 XextErrorHandler handler;
386
387 if (!display && !(display = XOpenDisplay(dmxInput->name)))
388 return;
389
390 /* Print out information about the XInput Extension. */
391 handler = XSetExtensionErrorHandler(dmxInputExtensionErrorHandler);
392 ext = XGetExtensionVersion(display, INAME);
393 XSetExtensionErrorHandler(handler);
394
395 if (ext && ext != (XExtensionVersion *) NoSuchExtension) {
396 XFree(ext);
397 devices = XListInputDevices(display, &num);
398 for (i = 0; i < num; i++) {
399 if (devices[i].id == (XID) dmxLocal->deviceId) {
400 XAnyClassPtr any;
401 XKeyInfoPtr ki;
402 XButtonInfoPtr bi;
403 XValuatorInfoPtr vi;
404
405 for (j = 0, any = devices[i].inputclassinfo;
406 j < devices[i].num_classes;
407 any = (XAnyClassPtr) ((char *) any + any->length), j++) {
408 switch (any->class) {
409 case KeyClass:
410 ki = (XKeyInfoPtr) any;
411 info->keyboard = 1;
412 info->keyClass = 1;
413 info->keySyms.minKeyCode = ki->min_keycode;
414 info->keySyms.maxKeyCode = ki->max_keycode;
415 info->kbdFeedbackClass = 1;
416 break;
417 case ButtonClass:
418 bi = (XButtonInfoPtr) any;
419 info->buttonClass = 1;
420 info->numButtons = bi->num_buttons;
421 info->ptrFeedbackClass = 1;
422 break;
423 case ValuatorClass:
424 /* This assume all axes are either
425 * Absolute or Relative. */
426 vi = (XValuatorInfoPtr) any;
427 info->valuatorClass = 1;
428 if (vi->mode == Absolute)
429 info->numAbsAxes = vi->num_axes;
430 else
431 info->numRelAxes = vi->num_axes;
432 for (k = 0; k < vi->num_axes; k++) {
433 info->res[k] = vi->axes[k].resolution;
434 info->minres[k] = vi->axes[k].resolution;
435 info->maxres[k] = vi->axes[k].resolution;
436 info->minval[k] = vi->axes[k].min_value;
437 info->maxval[k] = vi->axes[k].max_value;
438 }
439 break;
440 case FeedbackClass:
441 /* Only keyboard and pointer feedback
442 * are handled at this time. */
443 break;
444 case ProximityClass:
445 info->proximityClass = 1;
446 break;
447 case FocusClass:
448 info->focusClass = 1;
449 break;
450 case OtherClass:
451 break;
452 }
453 }
454 }
455 }
456 XFreeDeviceList(devices);
457 }
458 if (display != priv->display)
459 XCloseDisplay(display);
460 }
461
462 /** Obtain the mouse button mapping. */
463 void
464 dmxCommonMouGetMap(DevicePtr pDev, unsigned char *map, int *nButtons)
465 {
466 GETPRIVFROMPDEV;
467 int i;
468
469 *nButtons = XGetPointerMapping(priv->display, map, DMX_MAX_BUTTONS);
470 for (i = 0; i <= *nButtons; i++)
471 map[i] = i;
472 }
473
474 static void *
475 dmxCommonXSelect(DMXScreenInfo * dmxScreen, void *closure)
476 {
477 myPrivate *priv = closure;
478
479 XSelectInput(dmxScreen->beDisplay, dmxScreen->scrnWin, priv->eventMask);
480 return NULL;
481 }
482
483 static void *
484 dmxCommonAddEnabledDevice(DMXScreenInfo * dmxScreen, void *closure)
485 {
486 AddEnabledDevice(XConnectionNumber(dmxScreen->beDisplay));
487 return NULL;
488 }
489
490 static void *
491 dmxCommonRemoveEnabledDevice(DMXScreenInfo * dmxScreen, void *closure)
492 {
493 RemoveEnabledDevice(XConnectionNumber(dmxScreen->beDisplay));
494 return NULL;
495 }
496
497 /** Turn \a pDev on (i.e., take input from \a pDev). */
498 int
499 dmxCommonMouOn(DevicePtr pDev)
500 {
501 GETPRIVFROMPDEV;
502 GETDMXINPUTFROMPRIV;
503
504 priv->eventMask |= DMX_POINTER_EVENT_MASK;
505 if (!priv->be) {
506 XSelectInput(priv->display, priv->window, priv->eventMask);
507 AddEnabledDevice(XConnectionNumber(priv->display));
508 }
509 else {
510 dmxPropertyIterate(priv->be, dmxCommonXSelect, priv);
511 dmxPropertyIterate(priv->be, dmxCommonAddEnabledDevice, dmxInput);
512 }
513
514 return -1;
515 }
516
517 /** Turn \a pDev off. */
518 void
519 dmxCommonMouOff(DevicePtr pDev)
520 {
521 GETPRIVFROMPDEV;
522 GETDMXINPUTFROMPRIV;
523
524 priv->eventMask &= ~DMX_POINTER_EVENT_MASK;
525 if (!priv->be) {
526 RemoveEnabledDevice(XConnectionNumber(priv->display));
527 XSelectInput(priv->display, priv->window, priv->eventMask);
528 }
529 else {
530 dmxPropertyIterate(priv->be, dmxCommonRemoveEnabledDevice, dmxInput);
531 dmxPropertyIterate(priv->be, dmxCommonXSelect, priv);
532 }
533 }
534
535 /** Given the global coordinates \a x and \a y, determine the screen
536 * with the lowest number on which those coordinates lie. If they are
537 * not on any screen, return -1. The number returned is an index into
538 * \a dmxScreenInfo and is between -1 and \a dmxNumScreens - 1,
539 * inclusive. */
540 int
541 dmxFindPointerScreen(int x, int y)
542 {
543 int i;
544
545 for (i = 0; i < dmxNumScreens; i++) {
546 ScreenPtr pScreen = screenInfo.screens[i];
547
548 if (x >= pScreen->x && x < pScreen->x + pScreen->width &&
549 y >= pScreen->y && y < pScreen->y + pScreen->height)
550 return i;
551 }
552 return -1;
553 }
554
555 /** Returns a pointer to the private area for the device that comes just
556 * prior to \a pDevice in the current \a dmxInput device list. This is
557 * used as the private area for the current device in some situations
558 * (e.g., when a keyboard and mouse form a pair that should share the
559 * same private area). If the requested private area cannot be located,
560 * then NULL is returned. */
561 pointer
562 dmxCommonCopyPrivate(DeviceIntPtr pDevice)
563 {
564 GETDMXLOCALFROMPDEVICE;
565 DMXInputInfo *dmxInput = &dmxInputs[dmxLocal->inputIdx];
566 int i;
567
568 for (i = 0; i < dmxInput->numDevs; i++)
569 if (dmxInput->devs[i] == dmxLocal && i)
570 return dmxInput->devs[i - 1]->private;
571 return NULL;
572 }
573
574 /** This routine saves and resets some important state for the backend
575 * and console device drivers:
576 * - the modifier map is saved and set to 0 (so DMX controls the LEDs)
577 * - the key click, bell, led, and repeat masks are saved and set to the
578 * values that DMX claims to be using
579 *
580 * This routine and #dmxCommonRestoreState are used when the pointer
581 * enters and leaves the console window, or when the backend window is
582 * active or not active (for a full-screen window, this only happens at
583 * server startup and server shutdown).
584 */
585 void
586 dmxCommonSaveState(pointer private)
587 {
588 GETPRIVFROMPRIVATE;
589 XKeyboardState ks;
590 unsigned long i;
591 XModifierKeymap *modmap;
592
593 if (dmxInput->console)
594 priv = dmxInput->devs[0]->private;
595 if (!priv->display || priv->stateSaved)
596 return;
597 DMXDBG0("dmxCommonSaveState\n");
598 if (dmxUseXKB && (priv->xkb = XkbAllocKeyboard())) {
599 if (XkbGetIndicatorMap(priv->display, XkbAllIndicatorsMask, priv->xkb)
600 || XkbGetNames(priv->display, XkbAllNamesMask, priv->xkb)) {
601 dmxLogInput(dmxInput, "Could not get XKB information\n");
602 XkbFreeKeyboard(priv->xkb, 0, True);
603 priv->xkb = NULL;
604 }
605 else {
606 if (priv->xkb->indicators) {
607 priv->savedIndicators = *priv->xkb->indicators;
608 for (i = 0; i < XkbNumIndicators; i++)
609 if (priv->xkb->indicators->phys_indicators & (1 << i)) {
610 priv->xkb->indicators->maps[i].flags
611 = XkbIM_NoAutomatic;
612 }
613 XkbSetIndicatorMap(priv->display, ~0, priv->xkb);
614 }
615 }
616 }
617
618 XGetKeyboardControl(priv->display, &ks);
619 priv->savedKctrl.click = ks.key_click_percent;
620 priv->savedKctrl.bell = ks.bell_percent;
621 priv->savedKctrl.bell_pitch = ks.bell_pitch;
622 priv->savedKctrl.bell_duration = ks.bell_duration;
623 priv->savedKctrl.leds = ks.led_mask;
624 priv->savedKctrl.autoRepeat = ks.global_auto_repeat;
625 for (i = 0; i < 32; i++)
626 priv->savedKctrl.autoRepeats[i] = ks.auto_repeats[i];
627
628 dmxCommonKbdSetCtrl(priv->display, &priv->savedKctrl,
629 &priv->dmxLocal->kctrl);
630
631 priv->savedModMap = XGetModifierMapping(priv->display);
632
633 modmap = XNewModifiermap(0);
634 XSetModifierMapping(priv->display, modmap);
635 if (dmxInput->scrnIdx != -1)
636 dmxSync(&dmxScreens[dmxInput->scrnIdx], TRUE);
637 XFreeModifiermap(modmap);
638
639 priv->stateSaved = 1;
640 }
641
642 /** This routine restores all the information saved by #dmxCommonSaveState. */
643 void
644 dmxCommonRestoreState(pointer private)
645 {
646 GETPRIVFROMPRIVATE;
647 int retcode = -1;
648 CARD32 start;
649
650 if (dmxInput->console)
651 priv = dmxInput->devs[0]->private;
652 if (!priv->stateSaved)
653 return;
654 priv->stateSaved = 0;
655
656 DMXDBG0("dmxCommonRestoreState\n");
657 if (priv->xkb) {
658 *priv->xkb->indicators = priv->savedIndicators;
659 XkbSetIndicatorMap(priv->display, ~0, priv->xkb);
660 XkbFreeKeyboard(priv->xkb, 0, True);
661 priv->xkb = 0;
662 }
663
664 for (start = GetTimeInMillis(); GetTimeInMillis() - start < 5000;) {
665 CARD32 tmp;
666
667 retcode = XSetModifierMapping(priv->display, priv->savedModMap);
668 if (retcode == MappingSuccess)
669 break;
670 if (retcode == MappingBusy)
671 dmxLogInput(dmxInput, "Keyboard busy, waiting\n");
672 else
673 dmxLogInput(dmxInput, "Keyboard error, waiting\n");
674
675 /* Don't generate X11 protocol for a bit */
676 for (tmp = GetTimeInMillis(); GetTimeInMillis() - tmp < 250;) {
677 usleep(250); /* This ends up sleeping only until
678 * the next key press generates an
679 * interruption. We make the delay
680 * relatively short in case the user
681 * pressed they keys quickly. */
682 }
683
684 }
685 if (retcode != MappingSuccess)
686 dmxLog(dmxWarning, "Unable to restore keyboard modifier state!\n");
687
688 XFreeModifiermap(priv->savedModMap);
689 priv->savedModMap = NULL;
690
691 dmxCommonKbdSetCtrl(priv->display, NULL, &priv->savedKctrl);
692 priv->kctrlset = 0; /* Invalidate copy */
693 }