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