/* * Copyright 2001-2003 Red Hat Inc., Durham, North Carolina. * * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation on the rights to use, copy, modify, merge, * publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice (including the * next paragraph) shall be included in all copies or substantial * portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NON-INFRINGEMENT. IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ /* * Authors: * David H. Dawes * Kevin E. Martin * Rickard E. (Rik) Faith */ /** \file * * This file implements common routines used by the backend and console * input devices. */ #ifdef HAVE_DMX_CONFIG_H #include #endif #define DMX_STATE_DEBUG 0 #include "dmxinputinit.h" #include "dmxcommon.h" #include "dmxconsole.h" #include "dmxprop.h" #include "dmxsync.h" #include "dmxmap.h" #include "inputstr.h" #include "input.h" #include #include "mipointer.h" #include "scrnintstr.h" #include /* For usleep() */ #if DMX_STATE_DEBUG #define DMXDBG0(f) dmxLog(dmxDebug,f) #else #define DMXDBG0(f) #endif /** Each device has a private area that is visible only from inside the * driver code. */ typedef struct _myPrivate { DMX_COMMON_PRIVATE; } myPrivate; static void dmxCommonKbdSetAR(Display * display, unsigned char *old, unsigned char *new) { XKeyboardControl kc; XKeyboardState ks; unsigned long mask = KBKey | KBAutoRepeatMode; int i, j; int minKeycode, maxKeycode; if (!old) { XGetKeyboardControl(display, &ks); old = (unsigned char *) ks.auto_repeats; } XDisplayKeycodes(display, &minKeycode, &maxKeycode); for (i = 1; i < 32; i++) { if (!old || old[i] != new[i]) { for (j = 0; j < 8; j++) { if ((new[i] & (1 << j)) != (old[i] & (1 << j))) { kc.key = i * 8 + j; kc.auto_repeat_mode = ((new[i] & (1 << j)) ? AutoRepeatModeOn : AutoRepeatModeOff); if (kc.key >= minKeycode && kc.key <= maxKeycode) XChangeKeyboardControl(display, mask, &kc); } } } } } static void dmxCommonKbdSetLeds(Display * display, unsigned long new) { int i; XKeyboardControl kc; for (i = 0; i < 32; i++) { kc.led = i + 1; kc.led_mode = (new & (1 << i)) ? LedModeOn : LedModeOff; XChangeKeyboardControl(display, KBLed | KBLedMode, &kc); } } static void dmxCommonKbdSetCtrl(Display * display, KeybdCtrl * old, KeybdCtrl * new) { XKeyboardControl kc; unsigned long mask = KBKeyClickPercent | KBAutoRepeatMode; if (!old || old->click != new->click || old->autoRepeat != new->autoRepeat) { kc.key_click_percent = new->click; kc.auto_repeat_mode = new->autoRepeat; XChangeKeyboardControl(display, mask, &kc); } dmxCommonKbdSetLeds(display, new->leds); dmxCommonKbdSetAR(display, old ? old->autoRepeats : NULL, new->autoRepeats); } static void dmxCommonMouSetCtrl(Display * display, PtrCtrl * old, PtrCtrl * new) { Bool do_accel, do_threshold; if (!old || old->num != new->num || old->den != new->den || old->threshold != new->threshold) { do_accel = (new->num > 0 && new->den > 0); do_threshold = (new->threshold > 0); if (do_accel || do_threshold) { XChangePointerControl(display, do_accel, do_threshold, new->num, new->den, new->threshold); } } } /** Update the keyboard control. */ void dmxCommonKbdCtrl(DevicePtr pDev, KeybdCtrl * ctrl) { GETPRIVFROMPDEV; if (!priv->stateSaved && priv->be) dmxCommonSaveState(priv); if (!priv->display || !priv->stateSaved) return; dmxCommonKbdSetCtrl(priv->display, priv->kctrlset ? &priv->kctrl : NULL, ctrl); priv->kctrl = *ctrl; priv->kctrlset = 1; } /** Update the mouse control. */ void dmxCommonMouCtrl(DevicePtr pDev, PtrCtrl * ctrl) { GETPRIVFROMPDEV; /* Don't set the acceleration for the * console, because that should be * controlled by the X server that the * console is running on. Otherwise, * the acceleration for the console * window would be unexpected for the * scale of the window. */ if (priv->be) { dmxCommonMouSetCtrl(priv->display, priv->mctrlset ? &priv->mctrl : NULL, ctrl); priv->mctrl = *ctrl; priv->mctrlset = 1; } } /** Sound they keyboard bell. */ void dmxCommonKbdBell(DevicePtr pDev, int percent, int volume, int pitch, int duration) { GETPRIVFROMPDEV; XKeyboardControl kc; XKeyboardState ks; unsigned long mask = KBBellPercent | KBBellPitch | KBBellDuration; if (!priv->be) XGetKeyboardControl(priv->display, &ks); kc.bell_percent = volume; kc.bell_pitch = pitch; kc.bell_duration = duration; XChangeKeyboardControl(priv->display, mask, &kc); XBell(priv->display, percent); if (!priv->be) { kc.bell_percent = ks.bell_percent; kc.bell_pitch = ks.bell_pitch; kc.bell_duration = ks.bell_duration; XChangeKeyboardControl(priv->display, mask, &kc); } } /** Get the keyboard mapping. */ void dmxCommonKbdGetMap(DevicePtr pDev, KeySymsPtr pKeySyms, CARD8 *pModMap) { GETPRIVFROMPDEV; int min_keycode; int max_keycode; int map_width; KeySym *keyboard_mapping; XModifierKeymap *modifier_mapping; int i, j; /* Compute pKeySyms. Cast * XGetKeyboardMapping because of * compiler warning on 64-bit machines. * We assume pointers to 32-bit and * 64-bit ints are the same. */ XDisplayKeycodes(priv->display, &min_keycode, &max_keycode); keyboard_mapping = (KeySym *) XGetKeyboardMapping(priv->display, min_keycode, max_keycode - min_keycode + 1, &map_width); pKeySyms->minKeyCode = min_keycode; pKeySyms->maxKeyCode = max_keycode; pKeySyms->mapWidth = map_width; pKeySyms->map = keyboard_mapping; /* Compute pModMap */ modifier_mapping = XGetModifierMapping(priv->display); for (i = 0; i < MAP_LENGTH; i++) pModMap[i] = 0; for (j = 0; j < 8; j++) { int max_keypermod = modifier_mapping->max_keypermod; for (i = 0; i < max_keypermod; i++) { CARD8 keycode = modifier_mapping->modifiermap[j * max_keypermod + i]; if (keycode) pModMap[keycode] |= 1 << j; } } XFreeModifiermap(modifier_mapping); } /** Fill in the XKEYBOARD parts of the \a info structure for the * specified \a pDev. */ void dmxCommonKbdGetInfo(DevicePtr pDev, DMXLocalInitInfoPtr info) { GETPRIVFROMPDEV; GETDMXINPUTFROMPRIV; char *pt; dmxCommonSaveState(priv); if (priv->xkb) { #define NAME(x) \ priv->xkb->names->x ? XGetAtomName(priv->display,priv->xkb->names->x) : NULL info->names.keycodes = NAME(keycodes); info->names.types = NAME(types); info->names.compat = NAME(compat); info->names.symbols = NAME(symbols); info->names.geometry = NAME(geometry); info->freenames = 1; #undef NAME dmxLogInput(dmxInput, "XKEYBOARD: keycodes = %s\n", info->names.keycodes); dmxLogInput(dmxInput, "XKEYBOARD: symbols = %s\n", info->names.symbols); dmxLogInput(dmxInput, "XKEYBOARD: geometry = %s\n", info->names.geometry); if ((pt = strchr(info->names.keycodes, '+'))) *pt = '\0'; } dmxCommonRestoreState(priv); } /** Turn \a pDev on (i.e., take input from \a pDev). */ int dmxCommonKbdOn(DevicePtr pDev) { GETPRIVFROMPDEV; if (priv->be) dmxCommonSaveState(priv); priv->eventMask |= DMX_KEYBOARD_EVENT_MASK; XSelectInput(priv->display, priv->window, priv->eventMask); if (priv->be) XSetInputFocus(priv->display, priv->window, RevertToPointerRoot, CurrentTime); return -1; } /** Turn \a pDev off. */ void dmxCommonKbdOff(DevicePtr pDev) { GETPRIVFROMPDEV; priv->eventMask &= ~DMX_KEYBOARD_EVENT_MASK; XSelectInput(priv->display, priv->window, priv->eventMask); dmxCommonRestoreState(priv); } /** Turn \a pDev on (i.e., take input from \a pDev). */ int dmxCommonOthOn(DevicePtr pDev) { GETPRIVFROMPDEV; GETDMXINPUTFROMPRIV; XEventClass event_list[DMX_MAX_XINPUT_EVENT_TYPES]; int event_type[DMX_MAX_XINPUT_EVENT_TYPES]; int count = 0; #define ADD(type) \ if (count < DMX_MAX_XINPUT_EVENT_TYPES) { \ type(priv->xi, event_type[count], event_list[count]); \ if (event_type[count]) { \ dmxMapInsert(dmxLocal, event_type[count], XI_##type); \ ++count; \ } \ } else { \ dmxLog(dmxWarning, "More than %d event types for %s\n", \ DMX_MAX_XINPUT_EVENT_TYPES, dmxInput->name); \ } if (!(priv->xi = XOpenDevice(priv->display, dmxLocal->deviceId))) { dmxLog(dmxWarning, "Cannot open %s device (id=%d) on %s\n", dmxLocal->deviceName ? dmxLocal->deviceName : "(unknown)", dmxLocal->deviceId, dmxInput->name); return -1; } ADD(DeviceKeyPress); ADD(DeviceKeyRelease); ADD(DeviceButtonPress); ADD(DeviceButtonRelease); ADD(DeviceMotionNotify); ADD(DeviceFocusIn); ADD(DeviceFocusOut); ADD(ProximityIn); ADD(ProximityOut); ADD(DeviceStateNotify); ADD(DeviceMappingNotify); ADD(ChangeDeviceNotify); XSelectExtensionEvent(priv->display, priv->window, event_list, count); return -1; } /** Turn \a pDev off. */ void dmxCommonOthOff(DevicePtr pDev) { GETPRIVFROMPDEV; if (priv->xi) XCloseDevice(priv->display, priv->xi); priv->xi = NULL; } /** Fill the \a info structure with information needed to initialize \a * pDev. */ void dmxCommonOthGetInfo(DevicePtr pDev, DMXLocalInitInfoPtr info) { GETPRIVFROMPDEV; GETDMXINPUTFROMPRIV; XExtensionVersion *ext; XDeviceInfo *devices; Display *display = priv->display; int num; int i, j, k; XextErrorHandler handler; if (!display && !(display = XOpenDisplay(dmxInput->name))) return; /* Print out information about the XInput Extension. */ handler = XSetExtensionErrorHandler(dmxInputExtensionErrorHandler); ext = XGetExtensionVersion(display, INAME); XSetExtensionErrorHandler(handler); if (ext && ext != (XExtensionVersion *) NoSuchExtension) { XFree(ext); devices = XListInputDevices(display, &num); for (i = 0; i < num; i++) { if (devices[i].id == (XID) dmxLocal->deviceId) { XAnyClassPtr any; XKeyInfoPtr ki; XButtonInfoPtr bi; XValuatorInfoPtr vi; for (j = 0, any = devices[i].inputclassinfo; j < devices[i].num_classes; any = (XAnyClassPtr) ((char *) any + any->length), j++) { switch (any->class) { case KeyClass: ki = (XKeyInfoPtr) any; info->keyboard = 1; info->keyClass = 1; info->keySyms.minKeyCode = ki->min_keycode; info->keySyms.maxKeyCode = ki->max_keycode; info->kbdFeedbackClass = 1; break; case ButtonClass: bi = (XButtonInfoPtr) any; info->buttonClass = 1; info->numButtons = bi->num_buttons; info->ptrFeedbackClass = 1; break; case ValuatorClass: /* This assume all axes are either * Absolute or Relative. */ vi = (XValuatorInfoPtr) any; info->valuatorClass = 1; if (vi->mode == Absolute) info->numAbsAxes = vi->num_axes; else info->numRelAxes = vi->num_axes; for (k = 0; k < vi->num_axes; k++) { info->res[k] = vi->axes[k].resolution; info->minres[k] = vi->axes[k].resolution; info->maxres[k] = vi->axes[k].resolution; info->minval[k] = vi->axes[k].min_value; info->maxval[k] = vi->axes[k].max_value; } break; case FeedbackClass: /* Only keyboard and pointer feedback * are handled at this time. */ break; case ProximityClass: info->proximityClass = 1; break; case FocusClass: info->focusClass = 1; break; case OtherClass: break; } } } } XFreeDeviceList(devices); } if (display != priv->display) XCloseDisplay(display); } /** Obtain the mouse button mapping. */ void dmxCommonMouGetMap(DevicePtr pDev, unsigned char *map, int *nButtons) { GETPRIVFROMPDEV; int i; *nButtons = XGetPointerMapping(priv->display, map, DMX_MAX_BUTTONS); for (i = 0; i <= *nButtons; i++) map[i] = i; } static void * dmxCommonXSelect(DMXScreenInfo * dmxScreen, void *closure) { myPrivate *priv = closure; XSelectInput(dmxScreen->beDisplay, dmxScreen->scrnWin, priv->eventMask); return NULL; } static void * dmxCommonAddEnabledDevice(DMXScreenInfo * dmxScreen, void *closure) { AddEnabledDevice(XConnectionNumber(dmxScreen->beDisplay)); return NULL; } static void * dmxCommonRemoveEnabledDevice(DMXScreenInfo * dmxScreen, void *closure) { RemoveEnabledDevice(XConnectionNumber(dmxScreen->beDisplay)); return NULL; } /** Turn \a pDev on (i.e., take input from \a pDev). */ int dmxCommonMouOn(DevicePtr pDev) { GETPRIVFROMPDEV; GETDMXINPUTFROMPRIV; priv->eventMask |= DMX_POINTER_EVENT_MASK; if (dmxShadowFB) { XWarpPointer(priv->display, priv->window, priv->window, 0, 0, 0, 0, priv->initPointerX, priv->initPointerY); dmxSync(&dmxScreens[dmxInput->scrnIdx], TRUE); } if (!priv->be) { XSelectInput(priv->display, priv->window, priv->eventMask); AddEnabledDevice(XConnectionNumber(priv->display)); } else { dmxPropertyIterate(priv->be, dmxCommonXSelect, priv); dmxPropertyIterate(priv->be, dmxCommonAddEnabledDevice, dmxInput); } return -1; } /** Turn \a pDev off. */ void dmxCommonMouOff(DevicePtr pDev) { GETPRIVFROMPDEV; GETDMXINPUTFROMPRIV; priv->eventMask &= ~DMX_POINTER_EVENT_MASK; if (!priv->be) { RemoveEnabledDevice(XConnectionNumber(priv->display)); XSelectInput(priv->display, priv->window, priv->eventMask); } else { dmxPropertyIterate(priv->be, dmxCommonRemoveEnabledDevice, dmxInput); dmxPropertyIterate(priv->be, dmxCommonXSelect, priv); } } /** Given the global coordinates \a x and \a y, determine the screen * with the lowest number on which those coordinates lie. If they are * not on any screen, return -1. The number returned is an index into * \a dmxScreenInfo and is between -1 and \a dmxNumScreens - 1, * inclusive. */ int dmxFindPointerScreen(int x, int y) { int i; for (i = 0; i < dmxNumScreens; i++) { ScreenPtr pScreen = screenInfo.screens[i]; if (x >= pScreen->x && x < pScreen->x + pScreen->width && y >= pScreen->y && y < pScreen->y + pScreen->height) return i; } return -1; } /** Returns a pointer to the private area for the device that comes just * prior to \a pDevice in the current \a dmxInput device list. This is * used as the private area for the current device in some situations * (e.g., when a keyboard and mouse form a pair that should share the * same private area). If the requested private area cannot be located, * then NULL is returned. */ pointer dmxCommonCopyPrivate(DeviceIntPtr pDevice) { GETDMXLOCALFROMPDEVICE; DMXInputInfo *dmxInput = &dmxInputs[dmxLocal->inputIdx]; int i; for (i = 0; i < dmxInput->numDevs; i++) if (dmxInput->devs[i] == dmxLocal && i) return dmxInput->devs[i - 1]->private; return NULL; } /** This routine saves and resets some important state for the backend * and console device drivers: * - the modifier map is saved and set to 0 (so DMX controls the LEDs) * - the key click, bell, led, and repeat masks are saved and set to the * values that DMX claims to be using * * This routine and #dmxCommonRestoreState are used when the pointer * enters and leaves the console window, or when the backend window is * active or not active (for a full-screen window, this only happens at * server startup and server shutdown). */ void dmxCommonSaveState(pointer private) { GETPRIVFROMPRIVATE; XKeyboardState ks; unsigned long i; XModifierKeymap *modmap; if (dmxInput->console) priv = dmxInput->devs[0]->private; if (!priv->display || priv->stateSaved) return; DMXDBG0("dmxCommonSaveState\n"); if (dmxUseXKB && (priv->xkb = XkbAllocKeyboard())) { if (XkbGetIndicatorMap(priv->display, XkbAllIndicatorsMask, priv->xkb) || XkbGetNames(priv->display, XkbAllNamesMask, priv->xkb)) { dmxLogInput(dmxInput, "Could not get XKB information\n"); XkbFreeKeyboard(priv->xkb, 0, True); priv->xkb = NULL; } else { if (priv->xkb->indicators) { priv->savedIndicators = *priv->xkb->indicators; for (i = 0; i < XkbNumIndicators; i++) if (priv->xkb->indicators->phys_indicators & (1 << i)) { priv->xkb->indicators->maps[i].flags = XkbIM_NoAutomatic; } XkbSetIndicatorMap(priv->display, ~0, priv->xkb); } } } XGetKeyboardControl(priv->display, &ks); priv->savedKctrl.click = ks.key_click_percent; priv->savedKctrl.bell = ks.bell_percent; priv->savedKctrl.bell_pitch = ks.bell_pitch; priv->savedKctrl.bell_duration = ks.bell_duration; priv->savedKctrl.leds = ks.led_mask; priv->savedKctrl.autoRepeat = ks.global_auto_repeat; for (i = 0; i < 32; i++) priv->savedKctrl.autoRepeats[i] = ks.auto_repeats[i]; dmxCommonKbdSetCtrl(priv->display, &priv->savedKctrl, &priv->dmxLocal->kctrl); priv->savedModMap = XGetModifierMapping(priv->display); modmap = XNewModifiermap(0); XSetModifierMapping(priv->display, modmap); if (dmxInput->scrnIdx != -1) dmxSync(&dmxScreens[dmxInput->scrnIdx], TRUE); XFreeModifiermap(modmap); priv->stateSaved = 1; } /** This routine restores all the information saved by #dmxCommonSaveState. */ void dmxCommonRestoreState(pointer private) { GETPRIVFROMPRIVATE; int retcode = -1; CARD32 start; if (dmxInput->console) priv = dmxInput->devs[0]->private; if (!priv->stateSaved) return; priv->stateSaved = 0; DMXDBG0("dmxCommonRestoreState\n"); if (priv->xkb) { *priv->xkb->indicators = priv->savedIndicators; XkbSetIndicatorMap(priv->display, ~0, priv->xkb); XkbFreeKeyboard(priv->xkb, 0, True); priv->xkb = 0; } for (start = GetTimeInMillis(); GetTimeInMillis() - start < 5000;) { CARD32 tmp; retcode = XSetModifierMapping(priv->display, priv->savedModMap); if (retcode == MappingSuccess) break; if (retcode == MappingBusy) dmxLogInput(dmxInput, "Keyboard busy, waiting\n"); else dmxLogInput(dmxInput, "Keyboard error, waiting\n"); /* Don't generate X11 protocol for a bit */ for (tmp = GetTimeInMillis(); GetTimeInMillis() - tmp < 250;) { usleep(250); /* This ends up sleeping only until * the next key press generates an * interruption. We make the delay * relatively short in case the user * pressed they keys quickly. */ } } if (retcode != MappingSuccess) dmxLog(dmxWarning, "Unable to restore keyboard modifier state!\n"); XFreeModifiermap(priv->savedModMap); priv->savedModMap = NULL; dmxCommonKbdSetCtrl(priv->display, NULL, &priv->savedKctrl); priv->kctrlset = 0; /* Invalidate copy */ }