#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>
#if DMX_STATE_DEBUG
#define DMXDBG0(f) dmxLog(dmxDebug,f)
#else
#define DMXDBG0(f)
#endif
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);
}
}
}
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;
}
void dmxCommonMouCtrl(DevicePtr pDev, PtrCtrl *ctrl)
{
GETPRIVFROMPDEV;
if (priv->be) {
dmxCommonMouSetCtrl(priv->display,
priv->mctrlset ? &priv->mctrl : NULL,
ctrl);
priv->mctrl = *ctrl;
priv->mctrlset = 1;
}
}
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);
}
}
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;
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;
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);
}
void dmxCommonKbdGetInfo(DevicePtr pDev, DMXLocalInitInfoPtr info)
{
#ifdef XKB
GETPRIVFROMPDEV;
GETDMXINPUTFROMPRIV;
char *pt;
dmxCommonSaveState(priv);
if (priv->xkb) {
info->names.keymap = NULL;
#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);
#endif
}
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;
}
void dmxCommonKbdOff(DevicePtr pDev)
{
GETPRIVFROMPDEV;
priv->eventMask &= ~DMX_KEYBOARD_EVENT_MASK;
XSelectInput(priv->display, priv->window, priv->eventMask);
dmxCommonRestoreState(priv);
}
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;
}
void dmxCommonOthOff(DevicePtr pDev)
{
GETPRIVFROMPDEV;
if (priv->xi) XCloseDevice(priv->display, priv->xi);
priv->xi = NULL;
}
void dmxCommonOthGetInfo(DevicePtr pDev, DMXLocalInitInfoPtr info)
{
GETPRIVFROMPDEV;
GETDMXINPUTFROMPRIV;
XExtensionVersion *ext;
XDeviceInfo *devices;
Display *display = priv->display;
int num;
int i, j, k;
int (*handler)(Display *, char *, char *);
if (!display && !(display = XOpenDisplay(dmxInput->name)))
return;
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:
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:
break;
case ProximityClass:
info->proximityClass = 1;
break;
case FocusClass:
info->focusClass = 1;
break;
case OtherClass:
break;
}
}
}
}
XFreeDeviceList(devices);
}
if (display != priv->display) XCloseDisplay(display);
}
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;
}
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;
}
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);
}
}
int dmxFindPointerScreen(int x, int y)
{
int i;
for (i = 0; i < dmxNumScreens; i++) {
if (x >= dixScreenOrigins[i].x
&& x < dixScreenOrigins[i].x + screenInfo.screens[i]->width
&& y >= dixScreenOrigins[i].y
&& y < dixScreenOrigins[i].y + screenInfo.screens[i]->height)
return i;
}
return -1;
}
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;
}
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");
#ifdef XKB
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);
}
}
}
#endif
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;
}
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");
#ifdef XKB
if (priv->xkb) {
*priv->xkb->indicators = priv->savedIndicators;
XkbSetIndicatorMap(priv->display, ~0, priv->xkb);
XkbFreeKeyboard(priv->xkb, 0, True);
priv->xkb = 0;
}
#endif
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");
for (tmp = GetTimeInMillis(); GetTimeInMillis() - tmp < 250;) {
usleep(250);
}
}
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;
}