First import
[xorg_rtime.git] / xorg-server-1.4 / hw / dmx / input / dmxcommon.c
blobe77bb791887431375712dbbe7ccba2d3569c08f3
1 /*
2 * Copyright 2001-2003 Red Hat Inc., Durham, North Carolina.
4 * All Rights Reserved.
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:
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.
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.
29 * Authors:
30 * David H. Dawes <dawes@xfree86.org>
31 * Kevin E. Martin <kem@redhat.com>
32 * Rickard E. (Rik) Faith <faith@redhat.com>
35 /** \file
37 * This file implements common routines used by the backend and console
38 * input devices.
41 #ifdef HAVE_DMX_CONFIG_H
42 #include <dmx-config.h>
43 #endif
45 #define DMX_STATE_DEBUG 0
47 #include "dmxinputinit.h"
48 #include "dmxcommon.h"
49 #include "dmxconsole.h"
50 #include "dmxprop.h"
51 #include "dmxsync.h"
52 #include "dmxmap.h"
54 #include "inputstr.h"
55 #include "input.h"
56 #include <X11/keysym.h>
57 #include "mipointer.h"
58 #include "scrnintstr.h"
60 #include <unistd.h> /* For usleep() */
62 #if DMX_STATE_DEBUG
63 #define DMXDBG0(f) dmxLog(dmxDebug,f)
64 #else
65 #define DMXDBG0(f)
66 #endif
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;
74 static void dmxCommonKbdSetAR(Display *display,
75 unsigned char *old, unsigned char *new)
77 XKeyboardControl kc;
78 XKeyboardState ks;
79 unsigned long mask = KBKey | KBAutoRepeatMode;
80 int i, j;
81 int minKeycode, maxKeycode;
83 if (!old) {
84 XGetKeyboardControl(display, &ks);
85 old = (unsigned char *)ks.auto_repeats;
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);
105 static void dmxCommonKbdSetLeds(Display *display, unsigned long new)
107 int i;
108 XKeyboardControl kc;
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);
117 static void dmxCommonKbdSetCtrl(Display *display,
118 KeybdCtrl *old, KeybdCtrl *new)
120 XKeyboardControl kc;
121 unsigned long mask = KBKeyClickPercent | KBAutoRepeatMode;
123 if (!old
124 || old->click != new->click
125 || old->autoRepeat != new->autoRepeat) {
127 kc.key_click_percent = new->click;
128 kc.auto_repeat_mode = new->autoRepeat;
130 XChangeKeyboardControl(display, mask, &kc);
133 dmxCommonKbdSetLeds(display, new->leds);
134 dmxCommonKbdSetAR(display, old ? old->autoRepeats : NULL,
135 new->autoRepeats);
138 static void dmxCommonMouSetCtrl(Display *display, PtrCtrl *old, PtrCtrl *new)
140 Bool do_accel, do_threshold;
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);
155 /** Update the keyboard control. */
156 void dmxCommonKbdCtrl(DevicePtr pDev, KeybdCtrl *ctrl)
158 GETPRIVFROMPDEV;
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;
169 /** Update the mouse control. */
170 void dmxCommonMouCtrl(DevicePtr pDev, PtrCtrl *ctrl)
172 GETPRIVFROMPDEV;
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;
190 /** Sound they keyboard bell. */
191 void dmxCommonKbdBell(DevicePtr pDev, int percent,
192 int volume, int pitch, int duration)
194 GETPRIVFROMPDEV;
195 XKeyboardControl kc;
196 XKeyboardState ks;
197 unsigned long mask = KBBellPercent | KBBellPitch | KBBellDuration;
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);
213 /** Get the keyboard mapping. */
214 void dmxCommonKbdGetMap(DevicePtr pDev, KeySymsPtr pKeySyms, CARD8 *pModMap)
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;
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;
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;
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;
254 XFreeModifiermap(modifier_mapping);
257 /** Fill in the XKEYBOARD parts of the \a info structure for the
258 * specified \a pDev. */
259 void dmxCommonKbdGetInfo(DevicePtr pDev, DMXLocalInitInfoPtr info)
261 #ifdef XKB
262 GETPRIVFROMPDEV;
263 GETDMXINPUTFROMPRIV;
264 char *pt;
266 dmxCommonSaveState(priv);
267 if (priv->xkb) {
268 info->names.keymap = NULL;
269 #define NAME(x) \
270 priv->xkb->names->x ? XGetAtomName(priv->display,priv->xkb->names->x) : NULL
271 info->names.keycodes = NAME(keycodes);
272 info->names.types = NAME(types);
273 info->names.compat = NAME(compat);
274 info->names.symbols = NAME(symbols);
275 info->names.geometry = NAME(geometry);
276 info->freenames = 1;
277 #undef NAME
278 dmxLogInput(dmxInput,
279 "XKEYBOARD: keycodes = %s\n", info->names.keycodes);
280 dmxLogInput(dmxInput,
281 "XKEYBOARD: symbols = %s\n", info->names.symbols);
282 dmxLogInput(dmxInput,
283 "XKEYBOARD: geometry = %s\n", info->names.geometry);
284 if ((pt = strchr(info->names.keycodes, '+'))) *pt = '\0';
286 dmxCommonRestoreState(priv);
287 #endif
290 /** Turn \a pDev on (i.e., take input from \a pDev). */
291 int dmxCommonKbdOn(DevicePtr pDev)
293 GETPRIVFROMPDEV;
294 if (priv->be) dmxCommonSaveState(priv);
295 priv->eventMask |= DMX_KEYBOARD_EVENT_MASK;
296 XSelectInput(priv->display, priv->window, priv->eventMask);
297 if (priv->be)
298 XSetInputFocus(priv->display, priv->window, RevertToPointerRoot,
299 CurrentTime);
300 return -1;
303 /** Turn \a pDev off. */
304 void dmxCommonKbdOff(DevicePtr pDev)
306 GETPRIVFROMPDEV;
307 priv->eventMask &= ~DMX_KEYBOARD_EVENT_MASK;
308 XSelectInput(priv->display, priv->window, priv->eventMask);
309 dmxCommonRestoreState(priv);
312 /** Turn \a pDev on (i.e., take input from \a pDev). */
313 int dmxCommonOthOn(DevicePtr pDev)
315 GETPRIVFROMPDEV;
316 GETDMXINPUTFROMPRIV;
317 XEventClass event_list[DMX_MAX_XINPUT_EVENT_TYPES];
318 int event_type[DMX_MAX_XINPUT_EVENT_TYPES];
319 int count = 0;
321 #define ADD(type) \
322 if (count < DMX_MAX_XINPUT_EVENT_TYPES) { \
323 type(priv->xi, event_type[count], event_list[count]); \
324 if (event_type[count]) { \
325 dmxMapInsert(dmxLocal, event_type[count], XI_##type); \
326 ++count; \
328 } else { \
329 dmxLog(dmxWarning, "More than %d event types for %s\n", \
330 DMX_MAX_XINPUT_EVENT_TYPES, dmxInput->name); \
333 if (!(priv->xi = XOpenDevice(priv->display, dmxLocal->deviceId))) {
334 dmxLog(dmxWarning, "Cannot open %s device (id=%d) on %s\n",
335 dmxLocal->deviceName ? dmxLocal->deviceName : "(unknown)",
336 dmxLocal->deviceId, dmxInput->name);
337 return -1;
339 ADD(DeviceKeyPress);
340 ADD(DeviceKeyRelease);
341 ADD(DeviceButtonPress);
342 ADD(DeviceButtonRelease);
343 ADD(DeviceMotionNotify);
344 ADD(DeviceFocusIn);
345 ADD(DeviceFocusOut);
346 ADD(ProximityIn);
347 ADD(ProximityOut);
348 ADD(DeviceStateNotify);
349 ADD(DeviceMappingNotify);
350 ADD(ChangeDeviceNotify);
351 XSelectExtensionEvent(priv->display, priv->window, event_list, count);
353 return -1;
356 /** Turn \a pDev off. */
357 void dmxCommonOthOff(DevicePtr pDev)
359 GETPRIVFROMPDEV;
361 if (priv->xi) XCloseDevice(priv->display, priv->xi);
362 priv->xi = NULL;
365 /** Fill the \a info structure with information needed to initialize \a
366 * pDev. */
367 void dmxCommonOthGetInfo(DevicePtr pDev, DMXLocalInitInfoPtr info)
369 GETPRIVFROMPDEV;
370 GETDMXINPUTFROMPRIV;
371 XExtensionVersion *ext;
372 XDeviceInfo *devices;
373 Display *display = priv->display;
374 int num;
375 int i, j, k;
376 int (*handler)(Display *, char *, char *);
378 if (!display && !(display = XOpenDisplay(dmxInput->name)))
379 return;
381 /* Print out information about the XInput Extension. */
382 handler = XSetExtensionErrorHandler(dmxInputExtensionErrorHandler);
383 ext = XGetExtensionVersion(display, INAME);
384 XSetExtensionErrorHandler(handler);
386 if (ext && ext != (XExtensionVersion *)NoSuchExtension) {
387 XFree(ext);
388 devices = XListInputDevices(display, &num);
389 for (i = 0; i < num; i++) {
390 if (devices[i].id == (XID)dmxLocal->deviceId) {
391 XAnyClassPtr any;
392 XKeyInfoPtr ki;
393 XButtonInfoPtr bi;
394 XValuatorInfoPtr vi;
395 for (j = 0, any = devices[i].inputclassinfo;
396 j < devices[i].num_classes;
397 any = (XAnyClassPtr)((char *)any + any->length), j++) {
398 switch (any->class) {
399 case KeyClass:
400 ki = (XKeyInfoPtr)any;
401 info->keyboard = 1;
402 info->keyClass = 1;
403 info->keySyms.minKeyCode = ki->min_keycode;
404 info->keySyms.maxKeyCode = ki->max_keycode;
405 info->kbdFeedbackClass = 1;
406 break;
407 case ButtonClass:
408 bi = (XButtonInfoPtr)any;
409 info->buttonClass = 1;
410 info->numButtons = bi->num_buttons;
411 info->ptrFeedbackClass = 1;
412 break;
413 case ValuatorClass:
414 /* This assume all axes are either
415 * Absolute or Relative. */
416 vi = (XValuatorInfoPtr)any;
417 info->valuatorClass = 1;
418 if (vi->mode == Absolute)
419 info->numAbsAxes = vi->num_axes;
420 else
421 info->numRelAxes = vi->num_axes;
422 for (k = 0; k < vi->num_axes; k++) {
423 info->res[k] = vi->axes[k].resolution;
424 info->minres[k] = vi->axes[k].resolution;
425 info->maxres[k] = vi->axes[k].resolution;
426 info->minval[k] = vi->axes[k].min_value;
427 info->maxval[k] = vi->axes[k].max_value;
429 break;
430 case FeedbackClass:
431 /* Only keyboard and pointer feedback
432 * are handled at this time. */
433 break;
434 case ProximityClass:
435 info->proximityClass = 1;
436 break;
437 case FocusClass:
438 info->focusClass = 1;
439 break;
440 case OtherClass:
441 break;
446 XFreeDeviceList(devices);
448 if (display != priv->display) XCloseDisplay(display);
451 /** Obtain the mouse button mapping. */
452 void dmxCommonMouGetMap(DevicePtr pDev, unsigned char *map, int *nButtons)
454 GETPRIVFROMPDEV;
455 int i;
457 *nButtons = XGetPointerMapping(priv->display, map, DMX_MAX_BUTTONS);
458 for (i = 0; i <= *nButtons; i++) map[i] = i;
461 static void *dmxCommonXSelect(DMXScreenInfo *dmxScreen, void *closure)
463 myPrivate *priv = closure;
464 XSelectInput(dmxScreen->beDisplay, dmxScreen->scrnWin, priv->eventMask);
465 return NULL;
468 static void *dmxCommonAddEnabledDevice(DMXScreenInfo *dmxScreen, void *closure)
470 AddEnabledDevice(XConnectionNumber(dmxScreen->beDisplay));
471 return NULL;
474 static void *dmxCommonRemoveEnabledDevice(DMXScreenInfo *dmxScreen,
475 void *closure)
477 RemoveEnabledDevice(XConnectionNumber(dmxScreen->beDisplay));
478 return NULL;
481 /** Turn \a pDev on (i.e., take input from \a pDev). */
482 int dmxCommonMouOn(DevicePtr pDev)
484 GETPRIVFROMPDEV;
485 GETDMXINPUTFROMPRIV;
487 priv->eventMask |= DMX_POINTER_EVENT_MASK;
488 if (dmxShadowFB) {
489 XWarpPointer(priv->display, priv->window, priv->window,
490 0, 0, 0, 0,
491 priv->initPointerX,
492 priv->initPointerY);
493 dmxSync(&dmxScreens[dmxInput->scrnIdx], TRUE);
495 if (!priv->be) {
496 XSelectInput(priv->display, priv->window, priv->eventMask);
497 AddEnabledDevice(XConnectionNumber(priv->display));
498 } else {
499 dmxPropertyIterate(priv->be, dmxCommonXSelect, priv);
500 dmxPropertyIterate(priv->be, dmxCommonAddEnabledDevice, dmxInput);
503 return -1;
506 /** Turn \a pDev off. */
507 void dmxCommonMouOff(DevicePtr pDev)
509 GETPRIVFROMPDEV;
510 GETDMXINPUTFROMPRIV;
512 priv->eventMask &= ~DMX_POINTER_EVENT_MASK;
513 if (!priv->be) {
514 RemoveEnabledDevice(XConnectionNumber(priv->display));
515 XSelectInput(priv->display, priv->window, priv->eventMask);
516 } else {
517 dmxPropertyIterate(priv->be, dmxCommonRemoveEnabledDevice, dmxInput);
518 dmxPropertyIterate(priv->be, dmxCommonXSelect, priv);
522 /** Given the global coordinates \a x and \a y, determine the screen
523 * with the lowest number on which those coordinates lie. If they are
524 * not on any screen, return -1. The number returned is an index into
525 * #dmxScreenInfo and is between -1 and #dmxNumScreens - 1,
526 * inclusive. */
527 int dmxFindPointerScreen(int x, int y)
529 int i;
531 for (i = 0; i < dmxNumScreens; i++) {
532 if (x >= dixScreenOrigins[i].x
533 && x < dixScreenOrigins[i].x + screenInfo.screens[i]->width
534 && y >= dixScreenOrigins[i].y
535 && y < dixScreenOrigins[i].y + screenInfo.screens[i]->height)
536 return i;
538 return -1;
541 /** Returns a pointer to the private area for the device that comes just
542 * prior to \a pDevice in the current \a dmxInput device list. This is
543 * used as the private area for the current device in some situations
544 * (e.g., when a keyboard and mouse form a pair that should share the
545 * same private area). If the requested private area cannot be located,
546 * then NULL is returned. */
547 pointer dmxCommonCopyPrivate(DeviceIntPtr pDevice)
549 GETDMXLOCALFROMPDEVICE;
550 DMXInputInfo *dmxInput = &dmxInputs[dmxLocal->inputIdx];
551 int i;
553 for (i = 0; i < dmxInput->numDevs; i++)
554 if (dmxInput->devs[i] == dmxLocal && i)
555 return dmxInput->devs[i-1]->private;
556 return NULL;
559 /** This routine saves and resets some important state for the backend
560 * and console device drivers:
561 * - the modifier map is saved and set to 0 (so DMX controls the LEDs)
562 * - the key click, bell, led, and repeat masks are saved and set to the
563 * values that DMX claims to be using
565 * This routine and #dmxCommonRestoreState are used when the pointer
566 * enters and leaves the console window, or when the backend window is
567 * active or not active (for a full-screen window, this only happens at
568 * server startup and server shutdown).
570 void dmxCommonSaveState(pointer private)
572 GETPRIVFROMPRIVATE;
573 XKeyboardState ks;
574 unsigned long i;
575 XModifierKeymap *modmap;
577 if (dmxInput->console) priv = dmxInput->devs[0]->private;
578 if (!priv->display || priv->stateSaved) return;
579 DMXDBG0("dmxCommonSaveState\n");
580 #ifdef XKB
581 if (dmxUseXKB && (priv->xkb = XkbAllocKeyboard())) {
582 if (XkbGetIndicatorMap(priv->display, XkbAllIndicatorsMask, priv->xkb)
583 || XkbGetNames(priv->display, XkbAllNamesMask, priv->xkb)) {
584 dmxLogInput(dmxInput, "Could not get XKB information\n");
585 XkbFreeKeyboard(priv->xkb, 0, True);
586 priv->xkb = NULL;
587 } else {
588 if (priv->xkb->indicators) {
589 priv->savedIndicators = *priv->xkb->indicators;
590 for (i = 0; i < XkbNumIndicators; i++)
591 if (priv->xkb->indicators->phys_indicators & (1 << i)) {
592 priv->xkb->indicators->maps[i].flags
593 = XkbIM_NoAutomatic;
595 XkbSetIndicatorMap(priv->display, ~0, priv->xkb);
599 #endif
601 XGetKeyboardControl(priv->display, &ks);
602 priv->savedKctrl.click = ks.key_click_percent;
603 priv->savedKctrl.bell = ks.bell_percent;
604 priv->savedKctrl.bell_pitch = ks.bell_pitch;
605 priv->savedKctrl.bell_duration = ks.bell_duration;
606 priv->savedKctrl.leds = ks.led_mask;
607 priv->savedKctrl.autoRepeat = ks.global_auto_repeat;
608 for (i = 0; i < 32; i++)
609 priv->savedKctrl.autoRepeats[i] = ks.auto_repeats[i];
611 dmxCommonKbdSetCtrl(priv->display, &priv->savedKctrl,
612 &priv->dmxLocal->kctrl);
614 priv->savedModMap = XGetModifierMapping(priv->display);
616 modmap = XNewModifiermap(0);
617 XSetModifierMapping(priv->display, modmap);
618 if (dmxInput->scrnIdx != -1)
619 dmxSync(&dmxScreens[dmxInput->scrnIdx], TRUE);
620 XFreeModifiermap(modmap);
622 priv->stateSaved = 1;
625 /** This routine restores all the information saved by #dmxCommonSaveState. */
626 void dmxCommonRestoreState(pointer private)
628 GETPRIVFROMPRIVATE;
629 int retcode = -1;
630 CARD32 start;
632 if (dmxInput->console)
633 priv = dmxInput->devs[0]->private;
634 if (!priv->stateSaved)
635 return;
636 priv->stateSaved = 0;
638 DMXDBG0("dmxCommonRestoreState\n");
639 #ifdef XKB
640 if (priv->xkb) {
641 *priv->xkb->indicators = priv->savedIndicators;
642 XkbSetIndicatorMap(priv->display, ~0, priv->xkb);
643 XkbFreeKeyboard(priv->xkb, 0, True);
644 priv->xkb = 0;
646 #endif
648 for (start = GetTimeInMillis(); GetTimeInMillis() - start < 5000;) {
649 CARD32 tmp;
651 retcode = XSetModifierMapping(priv->display, priv->savedModMap);
652 if (retcode == MappingSuccess)
653 break;
654 if (retcode == MappingBusy)
655 dmxLogInput(dmxInput, "Keyboard busy, waiting\n");
656 else
657 dmxLogInput(dmxInput, "Keyboard error, waiting\n");
659 /* Don't generate X11 protocol for a bit */
660 for (tmp = GetTimeInMillis(); GetTimeInMillis() - tmp < 250;) {
661 usleep(250); /* This ends up sleeping only until
662 * the next key press generates an
663 * interruption. We make the delay
664 * relatively short in case the user
665 * pressed they keys quickly. */
669 if (retcode != MappingSuccess)
670 dmxLog(dmxWarning, "Unable to restore keyboard modifier state!\n");
672 XFreeModifiermap(priv->savedModMap);
673 priv->savedModMap = NULL;
675 dmxCommonKbdSetCtrl(priv->display, NULL, &priv->savedKctrl);
676 priv->kctrlset = 0; /* Invalidate copy */