428261197a
Tested by ajacoutot@, krw@, shadchin@ and jasper@ on various configurations including multihead with both zaphod and xrandr.
670 lines
23 KiB
C
670 lines
23 KiB
C
/*
|
|
* 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 <dawes@xfree86.org>
|
|
* Kevin E. Martin <kem@redhat.com>
|
|
* Rickard E. (Rik) Faith <faith@redhat.com>
|
|
*/
|
|
|
|
/** \file
|
|
*
|
|
* This file implements common routines used by the backend and console
|
|
* input devices.
|
|
*/
|
|
|
|
#ifdef HAVE_DMX_CONFIG_H
|
|
#include <dmx-config.h>
|
|
#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 <X11/keysym.h>
|
|
#include "mipointer.h"
|
|
#include "scrnintstr.h"
|
|
|
|
#include <unistd.h> /* 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 */
|
|
}
|