xserver-multidpi/hw/dmx/input/dmxinputinit.c
Chase Douglas 65c0fc81eb Add support for per-axis valuator modes (Relative/Absolute)
The XI2 protocol supports per-axis modes, but the server so far does
not. This change adds support in the server.

A complication is the fact that XI1 does not support per-axis modes.
The solution provided here is to set a per-device mode that defines the
mode of at least the first two valuators (X and Y). Note that initializing
the first two axes to a different mode than the device mode will fail.

For XI1 events, any axes following the first two that have the same mode
will be sent to clients, up to the first axis that has a different mode.
Thus, if a device has relative, then absolute, then relative mode axes,
only the first block of relative axes will be sent over XI1.

Since the XI2 protocol supports per-axis modes, all axes are sent to the
client.

Signed-off-by: Chase Douglas <chase.douglas@canonical.com>
2010-10-22 13:37:57 +10:00

1307 lines
45 KiB
C

/*
* Copyright 2002-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:
* Rickard E. (Rik) Faith <faith@redhat.com>
*
*/
/** \file
* This file provides generic input support. Functions here set up
* input and lead to the calling of low-level device drivers for
* input. */
#ifdef HAVE_DMX_CONFIG_H
#include <dmx-config.h>
#endif
#define DMX_WINDOW_DEBUG 0
#include "dmxinputinit.h"
#include "dmxextension.h" /* For dmxInputCount */
#include "dmxdummy.h"
#include "dmxbackend.h"
#include "dmxconsole.h"
#include "dmxcommon.h"
#include "dmxevents.h"
#include "dmxmotion.h"
#include "dmxprop.h"
#include "config/dmxconfig.h"
#include "dmxcursor.h"
#include "lnx-keyboard.h"
#include "lnx-ms.h"
#include "lnx-ps2.h"
#include "usb-keyboard.h"
#include "usb-mouse.h"
#include "usb-other.h"
#include "usb-common.h"
#include "dmxsigio.h"
#include "dmxarg.h"
#include "inputstr.h"
#include "input.h"
#include "mipointer.h"
#include "windowstr.h"
#include "mi.h"
#include "xkbsrv.h"
#include <X11/extensions/XI.h>
#include <X11/extensions/XIproto.h>
#include "exevents.h"
#include "extinit.h"
DMXLocalInputInfoPtr dmxLocalCorePointer, dmxLocalCoreKeyboard;
static DMXLocalInputInfoRec DMXDummyMou = {
"dummy-mou", DMX_LOCAL_MOUSE, DMX_LOCAL_TYPE_LOCAL, 1,
NULL, NULL, NULL, NULL, NULL, dmxDummyMouGetInfo
};
static DMXLocalInputInfoRec DMXDummyKbd = {
"dummy-kbd", DMX_LOCAL_KEYBOARD, DMX_LOCAL_TYPE_LOCAL, 1,
NULL, NULL, NULL, NULL, NULL, dmxDummyKbdGetInfo
};
static DMXLocalInputInfoRec DMXBackendMou = {
"backend-mou", DMX_LOCAL_MOUSE, DMX_LOCAL_TYPE_BACKEND, 2,
dmxBackendCreatePrivate, dmxBackendDestroyPrivate,
dmxBackendInit, NULL, dmxBackendLateReInit, dmxBackendMouGetInfo,
dmxCommonMouOn, dmxCommonMouOff, dmxBackendUpdatePosition,
NULL, NULL, NULL,
dmxBackendCollectEvents, dmxBackendProcessInput, dmxBackendFunctions, NULL,
dmxCommonMouCtrl
};
static DMXLocalInputInfoRec DMXBackendKbd = {
"backend-kbd", DMX_LOCAL_KEYBOARD, DMX_LOCAL_TYPE_BACKEND,
1, /* With backend-mou or console-mou */
dmxCommonCopyPrivate, NULL,
dmxBackendInit, NULL, NULL, dmxBackendKbdGetInfo,
dmxCommonKbdOn, dmxCommonKbdOff, NULL,
NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, dmxCommonKbdCtrl, dmxCommonKbdBell
};
static DMXLocalInputInfoRec DMXConsoleMou = {
"console-mou", DMX_LOCAL_MOUSE, DMX_LOCAL_TYPE_CONSOLE, 2,
dmxConsoleCreatePrivate, dmxConsoleDestroyPrivate,
dmxConsoleInit, dmxConsoleReInit, NULL, dmxConsoleMouGetInfo,
dmxCommonMouOn, dmxCommonMouOff, dmxConsoleUpdatePosition,
NULL, NULL, NULL,
dmxConsoleCollectEvents, NULL, dmxConsoleFunctions, dmxConsoleUpdateInfo,
dmxCommonMouCtrl
};
static DMXLocalInputInfoRec DMXConsoleKbd = {
"console-kbd", DMX_LOCAL_KEYBOARD, DMX_LOCAL_TYPE_CONSOLE,
1, /* With backend-mou or console-mou */
dmxCommonCopyPrivate, NULL,
dmxConsoleInit, dmxConsoleReInit, NULL, dmxConsoleKbdGetInfo,
dmxCommonKbdOn, dmxCommonKbdOff, NULL,
NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, dmxCommonKbdCtrl, dmxCommonKbdBell
};
static DMXLocalInputInfoRec DMXCommonOth = {
"common-oth", DMX_LOCAL_OTHER, DMX_LOCAL_TYPE_COMMON, 1,
dmxCommonCopyPrivate, NULL,
NULL, NULL, NULL, dmxCommonOthGetInfo,
dmxCommonOthOn, dmxCommonOthOff
};
static DMXLocalInputInfoRec DMXLocalDevices[] = {
/* Dummy drivers that can compile on any OS */
#ifdef __linux__
/* Linux-specific drivers */
{
"kbd", DMX_LOCAL_KEYBOARD, DMX_LOCAL_TYPE_LOCAL, 1,
kbdLinuxCreatePrivate, kbdLinuxDestroyPrivate,
kbdLinuxInit, NULL, NULL, kbdLinuxGetInfo,
kbdLinuxOn, kbdLinuxOff, NULL,
kbdLinuxVTPreSwitch, kbdLinuxVTPostSwitch, kbdLinuxVTSwitch,
kbdLinuxRead, NULL, NULL, NULL,
NULL, kbdLinuxCtrl, kbdLinuxBell
},
{
"ms", DMX_LOCAL_MOUSE, DMX_LOCAL_TYPE_LOCAL, 1,
msLinuxCreatePrivate, msLinuxDestroyPrivate,
msLinuxInit, NULL, NULL, msLinuxGetInfo,
msLinuxOn, msLinuxOff, NULL,
msLinuxVTPreSwitch, msLinuxVTPostSwitch, NULL,
msLinuxRead
},
{
"ps2", DMX_LOCAL_MOUSE, DMX_LOCAL_TYPE_LOCAL, 1,
ps2LinuxCreatePrivate, ps2LinuxDestroyPrivate,
ps2LinuxInit, NULL, NULL, ps2LinuxGetInfo,
ps2LinuxOn, ps2LinuxOff, NULL,
ps2LinuxVTPreSwitch, ps2LinuxVTPostSwitch, NULL,
ps2LinuxRead
},
#endif
#ifdef __linux__
/* USB drivers, currently only for
Linux, but relatively easy to port to
other OSs */
{
"usb-kbd", DMX_LOCAL_KEYBOARD, DMX_LOCAL_TYPE_LOCAL, 1,
usbCreatePrivate, usbDestroyPrivate,
kbdUSBInit, NULL, NULL, kbdUSBGetInfo,
kbdUSBOn, usbOff, NULL,
NULL, NULL, NULL,
kbdUSBRead, NULL, NULL, NULL,
NULL, kbdUSBCtrl
},
{
"usb-mou", DMX_LOCAL_MOUSE, DMX_LOCAL_TYPE_LOCAL, 1,
usbCreatePrivate, usbDestroyPrivate,
mouUSBInit, NULL, NULL, mouUSBGetInfo,
mouUSBOn, usbOff, NULL,
NULL, NULL, NULL,
mouUSBRead
},
{
"usb-oth", DMX_LOCAL_OTHER, DMX_LOCAL_TYPE_LOCAL, 1,
usbCreatePrivate, usbDestroyPrivate,
othUSBInit, NULL, NULL, othUSBGetInfo,
othUSBOn, usbOff, NULL,
NULL, NULL, NULL,
othUSBRead
},
#endif
{
"dummy-mou", DMX_LOCAL_MOUSE, DMX_LOCAL_TYPE_LOCAL, 1,
NULL, NULL, NULL, NULL, NULL, dmxDummyMouGetInfo
},
{
"dummy-kbd", DMX_LOCAL_KEYBOARD, DMX_LOCAL_TYPE_LOCAL, 1,
NULL, NULL, NULL, NULL, NULL, dmxDummyKbdGetInfo
},
{ NULL } /* Must be last */
};
#if 11 /*BP*/
void
DDXRingBell(int volume, int pitch, int duration)
{
/* NO-OP */
}
/* taken from kdrive/src/kinput.c: */
static void
dmxKbdCtrl (DeviceIntPtr pDevice, KeybdCtrl *ctrl)
{
#if 0
KdKeyboardInfo *ki;
for (ki = kdKeyboards; ki; ki = ki->next) {
if (ki->dixdev && ki->dixdev->id == pDevice->id)
break;
}
if (!ki || !ki->dixdev || ki->dixdev->id != pDevice->id || !ki->driver)
return;
KdSetLeds(ki, ctrl->leds);
ki->bellPitch = ctrl->bell_pitch;
ki->bellDuration = ctrl->bell_duration;
#endif
}
/* taken from kdrive/src/kinput.c: */
static void
dmxBell(int volume, DeviceIntPtr pDev, pointer arg, int something)
{
#if 0
KeybdCtrl *ctrl = arg;
KdKeyboardInfo *ki = NULL;
for (ki = kdKeyboards; ki; ki = ki->next) {
if (ki->dixdev && ki->dixdev->id == pDev->id)
break;
}
if (!ki || !ki->dixdev || ki->dixdev->id != pDev->id || !ki->driver)
return;
KdRingBell(ki, volume, ctrl->bell_pitch, ctrl->bell_duration);
#endif
}
#endif /*BP*/
static void _dmxChangePointerControl(DMXLocalInputInfoPtr dmxLocal,
PtrCtrl *ctrl)
{
if (!dmxLocal) return;
dmxLocal->mctrl = *ctrl;
if (dmxLocal->mCtrl) dmxLocal->mCtrl(&dmxLocal->pDevice->public, ctrl);
}
/** Change the pointer control information for the \a pDevice. If the
* device sends core events, then also change the control information
* for all of the pointer devices that send core events. */
void dmxChangePointerControl(DeviceIntPtr pDevice, PtrCtrl *ctrl)
{
GETDMXLOCALFROMPDEVICE;
int i, j;
if (dmxLocal->sendsCore) { /* Do for all core devices */
for (i = 0; i < dmxNumInputs; i++) {
DMXInputInfo *dmxInput = &dmxInputs[i];
if (dmxInput->detached) continue;
for (j = 0; j < dmxInput->numDevs; j++)
if (dmxInput->devs[j]->sendsCore)
_dmxChangePointerControl(dmxInput->devs[j], ctrl);
}
} else { /* Do for this device only */
_dmxChangePointerControl(dmxLocal, ctrl);
}
}
static void _dmxKeyboardKbdCtrlProc(DMXLocalInputInfoPtr dmxLocal,
KeybdCtrl *ctrl)
{
dmxLocal->kctrl = *ctrl;
if (dmxLocal->kCtrl) {
dmxLocal->kCtrl(&dmxLocal->pDevice->public, ctrl);
if (dmxLocal->pDevice->kbdfeed) {
XkbEventCauseRec cause;
XkbSetCauseUnknown(&cause);
/* Generate XKB events, as necessary */
XkbUpdateIndicators(dmxLocal->pDevice, XkbAllIndicatorsMask, False,
NULL, &cause);
}
}
}
/** Change the keyboard control information for the \a pDevice. If the
* device sends core events, then also change the control information
* for all of the keyboard devices that send core events. */
void dmxKeyboardKbdCtrlProc(DeviceIntPtr pDevice, KeybdCtrl *ctrl)
{
GETDMXLOCALFROMPDEVICE;
int i, j;
if (dmxLocal->sendsCore) { /* Do for all core devices */
for (i = 0; i < dmxNumInputs; i++) {
DMXInputInfo *dmxInput = &dmxInputs[i];
if (dmxInput->detached) continue;
for (j = 0; j < dmxInput->numDevs; j++)
if (dmxInput->devs[j]->sendsCore)
_dmxKeyboardKbdCtrlProc(dmxInput->devs[j], ctrl);
}
} else { /* Do for this device only */
_dmxKeyboardKbdCtrlProc(dmxLocal, ctrl);
}
}
static void _dmxKeyboardBellProc(DMXLocalInputInfoPtr dmxLocal, int percent)
{
if (dmxLocal->kBell) dmxLocal->kBell(&dmxLocal->pDevice->public,
percent,
dmxLocal->kctrl.bell,
dmxLocal->kctrl.bell_pitch,
dmxLocal->kctrl.bell_duration);
}
/** Sound the bell on the device. If the device send core events, then
* sound the bell on all of the devices that send core events. */
void dmxKeyboardBellProc(int percent, DeviceIntPtr pDevice,
pointer ctrl, int unknown)
{
GETDMXLOCALFROMPDEVICE;
int i, j;
if (dmxLocal->sendsCore) { /* Do for all core devices */
for (i = 0; i < dmxNumInputs; i++) {
DMXInputInfo *dmxInput = &dmxInputs[i];
if (dmxInput->detached) continue;
for (j = 0; j < dmxInput->numDevs; j++)
if (dmxInput->devs[j]->sendsCore)
_dmxKeyboardBellProc(dmxInput->devs[j], percent);
}
} else { /* Do for this device only */
_dmxKeyboardBellProc(dmxLocal, percent);
}
}
static void dmxKeyboardFreeNames(XkbComponentNamesPtr names)
{
if (names->keycodes) XFree(names->keycodes);
if (names->types) XFree(names->types);
if (names->compat) XFree(names->compat);
if (names->symbols) XFree(names->symbols);
if (names->geometry) XFree(names->geometry);
}
static int dmxKeyboardOn(DeviceIntPtr pDevice, DMXLocalInitInfo *info)
{
GETDMXINPUTFROMPDEVICE;
XkbRMLVOSet rmlvo;
rmlvo.rules = dmxConfigGetXkbRules();
rmlvo.model = dmxConfigGetXkbModel();
rmlvo.layout = dmxConfigGetXkbLayout();
rmlvo.variant = dmxConfigGetXkbVariant();
rmlvo.options = dmxConfigGetXkbOptions();
XkbSetRulesDflts(&rmlvo);
if (!info->force && (dmxInput->keycodes
|| dmxInput->symbols
|| dmxInput->geometry)) {
if (info->freenames) dmxKeyboardFreeNames(&info->names);
info->freenames = 0;
info->names.keycodes = dmxInput->keycodes;
info->names.types = NULL;
info->names.compat = NULL;
info->names.symbols = dmxInput->symbols;
info->names.geometry = dmxInput->geometry;
dmxLogInput(dmxInput, "XKEYBOARD: From command line: %s",
info->names.keycodes);
if (info->names.symbols && *info->names.symbols)
dmxLogInputCont(dmxInput, " %s", info->names.symbols);
if (info->names.geometry && *info->names.geometry)
dmxLogInputCont(dmxInput, " %s", info->names.geometry);
dmxLogInputCont(dmxInput, "\n");
} else if (info->names.keycodes) {
dmxLogInput(dmxInput, "XKEYBOARD: From device: %s",
info->names.keycodes);
if (info->names.symbols && *info->names.symbols)
dmxLogInputCont(dmxInput, " %s", info->names.symbols);
if (info->names.geometry && *info->names.geometry)
dmxLogInputCont(dmxInput, " %s", info->names.geometry);
dmxLogInputCont(dmxInput, "\n");
} else {
dmxLogInput(dmxInput, "XKEYBOARD: Defaults: %s %s %s %s %s\n",
dmxConfigGetXkbRules(),
dmxConfigGetXkbLayout(),
dmxConfigGetXkbModel(),
dmxConfigGetXkbVariant()
? dmxConfigGetXkbVariant() : "",
dmxConfigGetXkbOptions()
? dmxConfigGetXkbOptions() : "");
}
InitKeyboardDeviceStruct(pDevice, &rmlvo,
dmxKeyboardBellProc,
dmxKeyboardKbdCtrlProc);
if (info->freenames) dmxKeyboardFreeNames(&info->names);
return Success;
}
static int dmxDeviceOnOff(DeviceIntPtr pDevice, int what)
{
GETDMXINPUTFROMPDEVICE;
int fd;
DMXLocalInitInfo info;
int i;
Atom btn_labels[MAX_BUTTONS] = {0}; /* FIXME */
Atom axis_labels[MAX_VALUATORS] = {0}; /* FIXME */
if (dmxInput->detached) return Success;
memset(&info, 0, sizeof(info));
switch (what) {
case DEVICE_INIT:
if (dmxLocal->init)
dmxLocal->init(pDev);
if (dmxLocal->get_info)
dmxLocal->get_info(pDev, &info);
if (info.keyboard) { /* XKEYBOARD makes this a special case */
dmxKeyboardOn(pDevice, &info);
break;
}
if (info.keyClass) {
XkbRMLVOSet rmlvo;
rmlvo.rules = dmxConfigGetXkbRules();
rmlvo.model = dmxConfigGetXkbModel();
rmlvo.layout = dmxConfigGetXkbLayout();
rmlvo.variant = dmxConfigGetXkbVariant();
rmlvo.options = dmxConfigGetXkbOptions();
InitKeyboardDeviceStruct(pDevice,
&rmlvo,
dmxBell, dmxKbdCtrl);
}
if (info.buttonClass) {
InitButtonClassDeviceStruct(pDevice, info.numButtons,
btn_labels, info.map);
}
if (info.valuatorClass) {
if (info.numRelAxes && dmxLocal->sendsCore) {
InitValuatorClassDeviceStruct(pDevice, info.numRelAxes,
axis_labels,
GetMaximumEventsNum(),
Relative);
for (i = 0; i < info.numRelAxes; i++)
InitValuatorAxisStruct(pDevice, i, axis_labels[i],
info.minval[i], info.maxval[i],
info.res[i],
info.minres[i], info.maxres[i],
Relative);
} else if (info.numRelAxes) {
InitValuatorClassDeviceStruct(pDevice, info.numRelAxes,
axis_labels,
dmxPointerGetMotionBufferSize(),
Relative);
for (i = 0; i < info.numRelAxes; i++)
InitValuatorAxisStruct(pDevice, i, axis_labels[i],
info.minval[i],
info.maxval[i], info.res[i],
info.minres[i], info.maxres[i],
Relative);
} else if (info.numAbsAxes) {
InitValuatorClassDeviceStruct(pDevice, info.numAbsAxes,
axis_labels,
dmxPointerGetMotionBufferSize(),
Absolute);
for (i = 0; i < info.numAbsAxes; i++)
InitValuatorAxisStruct(pDevice, i,
axis_labels[i],
info.minval[i], info.maxval[i],
info.res[i], info.minres[i],
info.maxres[i], Absolute);
}
}
if (info.focusClass) InitFocusClassDeviceStruct(pDevice);
if (info.proximityClass) InitProximityClassDeviceStruct(pDevice);
if (info.ptrFeedbackClass)
InitPtrFeedbackClassDeviceStruct(pDevice, dmxChangePointerControl);
if (info.intFeedbackClass || info.strFeedbackClass)
dmxLog(dmxWarning,
"Integer and string feedback not supported for %s\n",
pDevice->name);
if (!info.keyboard && (info.ledFeedbackClass || info.belFeedbackClass))
dmxLog(dmxWarning,
"Led and bel feedback not supported for non-keyboard %s\n",
pDevice->name);
break;
case DEVICE_ON:
if (!pDev->on) {
if (dmxLocal->on && (fd = dmxLocal->on(pDev)) >= 0)
dmxSigioRegister(dmxInput, fd);
pDev->on = TRUE;
}
break;
case DEVICE_OFF:
case DEVICE_CLOSE:
/* This can get called twice consecutively: once for a
* detached screen (DEVICE_OFF), and then again at server
* generation time (DEVICE_CLOSE). */
if (pDev->on) {
dmxSigioUnregister(dmxInput);
if (dmxLocal->off) dmxLocal->off(pDev);
pDev->on = FALSE;
}
break;
}
if (info.keySyms.map && info.freemap) {
XFree(info.keySyms.map);
info.keySyms.map = NULL;
}
if (info.xkb) XkbFreeKeyboard(info.xkb, 0, True);
return Success;
}
static void dmxProcessInputEvents(DMXInputInfo *dmxInput)
{
int i;
mieqProcessInputEvents();
#if 00 /*BP*/
miPointerUpdate();
#endif
if (dmxInput->detached)
return;
for (i = 0; i < dmxInput->numDevs; i += dmxInput->devs[i]->binding)
if (dmxInput->devs[i]->process_input) {
#if 11 /*BP*/
miPointerUpdateSprite(dmxInput->devs[i]->pDevice);
#endif
dmxInput->devs[i]->process_input(dmxInput->devs[i]->private);
}
#if 11 /*BP*/
mieqProcessInputEvents();
#endif
}
static void dmxUpdateWindowInformation(DMXInputInfo *dmxInput,
DMXUpdateType type,
WindowPtr pWindow)
{
int i;
#ifdef PANORAMIX
if (!noPanoramiXExtension && pWindow && pWindow->parent != screenInfo.screens[0]->root)
return;
#endif
#if DMX_WINDOW_DEBUG
{
const char *name = "Unknown";
switch (type) {
case DMX_UPDATE_REALIZE: name = "Realize"; break;
case DMX_UPDATE_UNREALIZE: name = "Unrealize"; break;
case DMX_UPDATE_RESTACK: name = "Restack"; break;
case DMX_UPDATE_COPY: name = "Copy"; break;
case DMX_UPDATE_RESIZE: name = "Resize"; break;
case DMX_UPDATE_REPARENT: name = "Repaint"; break;
}
dmxLog(dmxDebug, "Window %p changed: %s\n", pWindow, name);
}
#endif
if (dmxInput->detached)
return;
for (i = 0; i < dmxInput->numDevs; i += dmxInput->devs[i]->binding)
if (dmxInput->devs[i]->update_info)
dmxInput->devs[i]->update_info(dmxInput->devs[i]->private,
type, pWindow);
}
static void dmxCollectAll(DMXInputInfo *dmxInput)
{
int i;
if (dmxInput->detached)
return;
for (i = 0; i < dmxInput->numDevs; i += dmxInput->devs[i]->binding)
if (dmxInput->devs[i]->collect_events)
dmxInput->devs[i]->collect_events(&dmxInput->devs[i]->pDevice->public,
dmxMotion,
dmxEnqueue,
dmxCheckSpecialKeys, DMX_BLOCK);
}
static void dmxBlockHandler(pointer blockData, OSTimePtr pTimeout,
pointer pReadMask)
{
DMXInputInfo *dmxInput = &dmxInputs[(int)blockData];
static unsigned long generation = 0;
if (generation != serverGeneration) {
generation = serverGeneration;
dmxCollectAll(dmxInput);
}
}
static void dmxSwitchReturn(pointer p)
{
DMXInputInfo *dmxInput = p;
int i;
dmxLog(dmxInfo, "Returning from VT %d\n", dmxInput->vt_switched);
if (!dmxInput->vt_switched)
dmxLog(dmxFatal, "dmxSwitchReturn called, but not switched\n");
dmxSigioEnableInput();
for (i = 0; i < dmxInput->numDevs; i++)
if (dmxInput->devs[i]->vt_post_switch)
dmxInput->devs[i]->vt_post_switch(dmxInput->devs[i]->private);
dmxInput->vt_switched = 0;
}
static void dmxWakeupHandler(pointer blockData, int result, pointer pReadMask)
{
DMXInputInfo *dmxInput = &dmxInputs[(int)blockData];
int i;
if (dmxInput->vt_switch_pending) {
dmxLog(dmxInfo, "Switching to VT %d\n", dmxInput->vt_switch_pending);
for (i = 0; i < dmxInput->numDevs; i++)
if (dmxInput->devs[i]->vt_pre_switch)
dmxInput->devs[i]->vt_pre_switch(dmxInput->devs[i]->private);
dmxInput->vt_switched = dmxInput->vt_switch_pending;
dmxInput->vt_switch_pending = 0;
for (i = 0; i < dmxInput->numDevs; i++) {
if (dmxInput->devs[i]->vt_switch) {
dmxSigioDisableInput();
if (!dmxInput->devs[i]->vt_switch(dmxInput->devs[i]->private,
dmxInput->vt_switched,
dmxSwitchReturn,
dmxInput))
dmxSwitchReturn(dmxInput);
break; /* Only call one vt_switch routine */
}
}
}
dmxCollectAll(dmxInput);
}
static char *dmxMakeUniqueDeviceName(DMXLocalInputInfoPtr dmxLocal)
{
static int k = 0;
static int m = 0;
static int o = 0;
static unsigned long dmxGeneration = 0;
#define LEN 32
char * buf = malloc(LEN);
if (dmxGeneration != serverGeneration) {
k = m = o = 0;
dmxGeneration = serverGeneration;
}
switch (dmxLocal->type) {
case DMX_LOCAL_KEYBOARD: XmuSnprintf(buf, LEN, "Keyboard%d", k++); break;
case DMX_LOCAL_MOUSE: XmuSnprintf(buf, LEN, "Mouse%d", m++); break;
default: XmuSnprintf(buf, LEN, "Other%d", o++); break;
}
return buf;
}
static DeviceIntPtr dmxAddDevice(DMXLocalInputInfoPtr dmxLocal)
{
DeviceIntPtr pDevice;
Atom atom;
const char *name = NULL;
char *devname;
DMXInputInfo *dmxInput;
if (!dmxLocal)
return NULL;
dmxInput = &dmxInputs[dmxLocal->inputIdx];
if (dmxLocal->sendsCore) {
if (dmxLocal->type == DMX_LOCAL_KEYBOARD && !dmxLocalCoreKeyboard) {
dmxLocal->isCore = 1;
dmxLocalCoreKeyboard = dmxLocal;
name = "keyboard";
}
if (dmxLocal->type == DMX_LOCAL_MOUSE && !dmxLocalCorePointer) {
dmxLocal->isCore = 1;
dmxLocalCorePointer = dmxLocal;
name = "pointer";
}
}
if (!name) {
name = "extension";
}
if (!name)
dmxLog(dmxFatal, "Cannot add device %s\n", dmxLocal->name);
pDevice = AddInputDevice(serverClient, dmxDeviceOnOff, TRUE);
if (!pDevice) {
dmxLog(dmxError, "Too many devices -- cannot add device %s\n",
dmxLocal->name);
return NULL;
}
pDevice->public.devicePrivate = dmxLocal;
dmxLocal->pDevice = pDevice;
devname = dmxMakeUniqueDeviceName(dmxLocal);
atom = MakeAtom((char *)devname, strlen(devname), TRUE);
pDevice->type = atom;
pDevice->name = devname;
if (dmxLocal->isCore && dmxLocal->type == DMX_LOCAL_MOUSE) {
#if 00 /*BP*/
miRegisterPointerDevice(screenInfo.screens[0], pDevice);
#else
/* Nothing? dmxDeviceOnOff() should get called to init, right? */
#endif
}
if (dmxLocal->create_private)
dmxLocal->private = dmxLocal->create_private(pDevice);
dmxLogInput(dmxInput, "Added %s as %s device called %s%s\n",
dmxLocal->name, name, devname,
dmxLocal->isCore
? " [core]"
: (dmxLocal->sendsCore
? " [sends core events]"
: ""));
return pDevice;
}
static DMXLocalInputInfoPtr dmxLookupLocal(const char *name)
{
DMXLocalInputInfoPtr pt;
for (pt = &DMXLocalDevices[0]; pt->name; ++pt)
if (!strcmp(pt->name, name)) return pt; /* search for device name */
return NULL;
}
/** Copy the local input information from \a s into a new \a devs slot
* in \a dmxInput. */
DMXLocalInputInfoPtr dmxInputCopyLocal(DMXInputInfo *dmxInput,
DMXLocalInputInfoPtr s)
{
DMXLocalInputInfoPtr dmxLocal = malloc(sizeof(*dmxLocal));
if (!dmxLocal)
dmxLog(dmxFatal, "DMXLocalInputInfoPtr: out of memory\n");
memcpy(dmxLocal, s, sizeof(*dmxLocal));
dmxLocal->inputIdx = dmxInput->inputIdx;
dmxLocal->sendsCore = dmxInput->core;
dmxLocal->savedSendsCore = dmxInput->core;
dmxLocal->deviceId = -1;
++dmxInput->numDevs;
dmxInput->devs = realloc(dmxInput->devs,
dmxInput->numDevs * sizeof(*dmxInput->devs));
dmxInput->devs[dmxInput->numDevs-1] = dmxLocal;
return dmxLocal;
}
static void dmxPopulateLocal(DMXInputInfo *dmxInput, dmxArg a)
{
int i;
int help = 0;
DMXLocalInputInfoRec *pt;
for (i = 1; i < dmxArgC(a); i++) {
const char *name = dmxArgV(a, i);
if ((pt = dmxLookupLocal(name))) {
dmxInputCopyLocal(dmxInput, pt);
} else {
if (strlen(name))
dmxLog(dmxWarning,
"Could not find a driver called %s\n", name);
++help;
}
}
if (help) {
dmxLog(dmxInfo, "Available local device drivers:\n");
for (pt = &DMXLocalDevices[0]; pt->name; ++pt) {
const char *type;
switch (pt->type) {
case DMX_LOCAL_KEYBOARD: type = "keyboard"; break;
case DMX_LOCAL_MOUSE: type = "pointer"; break;
default: type = "unknown"; break;
}
dmxLog(dmxInfo, " %s (%s)\n", pt->name, type);
}
dmxLog(dmxFatal, "Must have valid local device driver\n");
}
}
int dmxInputExtensionErrorHandler(Display *dsp, _Xconst char *name, _Xconst char *reason)
{
return 0;
}
static void dmxInputScanForExtensions(DMXInputInfo *dmxInput, int doXI)
{
XExtensionVersion *ext;
XDeviceInfo *devices;
Display *display;
int num;
int i, j;
XextErrorHandler handler;
if (!(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) {
dmxLogInput(dmxInput, "%s is not available\n", INAME);
} else {
dmxLogInput(dmxInput, "Locating devices on %s (%s version %d.%d)\n",
dmxInput->name, INAME,
ext->major_version, ext->minor_version);
devices = XListInputDevices(display, &num);
XFree(ext);
ext = NULL;
/* Print a list of all devices */
for (i = 0; i < num; i++) {
const char *use = "Unknown";
switch (devices[i].use) {
case IsXPointer: use = "XPointer"; break;
case IsXKeyboard: use = "XKeyboard"; break;
case IsXExtensionDevice: use = "XExtensionDevice"; break;
case IsXExtensionPointer: use = "XExtensionPointer"; break;
case IsXExtensionKeyboard: use = "XExtensionKeyboard"; break;
}
dmxLogInput(dmxInput, " %2d %-10.10s %-16.16s\n",
devices[i].id,
devices[i].name ? devices[i].name : "",
use);
}
/* Search for extensions */
for (i = 0; i < num; i++) {
switch (devices[i].use) {
case IsXKeyboard:
for (j = 0; j < dmxInput->numDevs; j++) {
DMXLocalInputInfoPtr dmxL = dmxInput->devs[j];
if (dmxL->type == DMX_LOCAL_KEYBOARD
&& dmxL->deviceId < 0) {
dmxL->deviceId = devices[i].id;
dmxL->deviceName = (devices[i].name
? strdup(devices[i].name)
: NULL);
}
}
break;
case IsXPointer:
for (j = 0; j < dmxInput->numDevs; j++) {
DMXLocalInputInfoPtr dmxL = dmxInput->devs[j];
if (dmxL->type == DMX_LOCAL_MOUSE && dmxL->deviceId < 0) {
dmxL->deviceId = devices[i].id;
dmxL->deviceName = (devices[i].name
? xstrdup(devices[i].name)
: NULL);
}
}
break;
#if 0
case IsXExtensionDevice:
case IsXExtensionKeyboard:
case IsXExtensionPointer:
if (doXI) {
if (!dmxInput->numDevs) {
dmxLog(dmxWarning,
"Cannot use remote (%s) XInput devices if"
" not also using core devices\n",
dmxInput->name);
} else {
dmxLocal = dmxInputCopyLocal(dmxInput,
&DMXCommonOth);
dmxLocal->isCore = FALSE;
dmxLocal->sendsCore = FALSE;
dmxLocal->deviceId = devices[i].id;
dmxLocal->deviceName = (devices[i].name
? strdup(devices[i].name)
: NULL);
}
}
break;
#endif
}
}
XFreeDeviceList(devices);
}
XCloseDisplay(display);
}
/** Re-initialize all the devices described in \a dmxInput. Called from
#dmxAdjustCursorBoundaries before the cursor is redisplayed. */
void dmxInputReInit(DMXInputInfo *dmxInput)
{
int i;
for (i = 0; i < dmxInput->numDevs; i++) {
DMXLocalInputInfoPtr dmxLocal = dmxInput->devs[i];
if (dmxLocal->reinit)
dmxLocal->reinit(&dmxLocal->pDevice->public);
}
}
/** Re-initialize all the devices described in \a dmxInput. Called from
#dmxAdjustCursorBoundaries after the cursor is redisplayed. */
void dmxInputLateReInit(DMXInputInfo *dmxInput)
{
int i;
for (i = 0; i < dmxInput->numDevs; i++) {
DMXLocalInputInfoPtr dmxLocal = dmxInput->devs[i];
if (dmxLocal->latereinit)
dmxLocal->latereinit(&dmxLocal->pDevice->public);
}
}
/** Initialize all of the devices described in \a dmxInput. */
void dmxInputInit(DMXInputInfo *dmxInput)
{
DeviceIntPtr pPointer = NULL, pKeyboard = NULL;
dmxArg a;
const char *name;
int i;
int doXI = 1; /* Include by default */
int forceConsole = 0;
int doWindows = 1; /* On by default */
int hasXkb = 0;
a = dmxArgParse(dmxInput->name);
for (i = 1; i < dmxArgC(a); i++) {
switch (hasXkb) {
case 1:
dmxInput->keycodes = xstrdup(dmxArgV(a, i));
++hasXkb;
break;
case 2:
dmxInput->symbols = xstrdup(dmxArgV(a, i));
++hasXkb;
break;
case 3:
dmxInput->geometry = xstrdup(dmxArgV(a, i));
hasXkb = 0;
break;
case 0:
if (!strcmp(dmxArgV(a, i), "noxi")) doXI = 0;
else if (!strcmp(dmxArgV(a, i), "xi")) doXI = 1;
else if (!strcmp(dmxArgV(a, i), "console")) forceConsole = 1;
else if (!strcmp(dmxArgV(a, i), "noconsole")) forceConsole = 0;
else if (!strcmp(dmxArgV(a, i), "windows")) doWindows = 1;
else if (!strcmp(dmxArgV(a, i), "nowindows")) doWindows = 0;
else if (!strcmp(dmxArgV(a, i), "xkb")) hasXkb = 1;
else {
dmxLog(dmxFatal,
"Unknown input argument: %s\n", dmxArgV(a, i));
}
}
}
name = dmxArgV(a, 0);
if (!strcmp(name, "local")) {
dmxPopulateLocal(dmxInput, a);
} else if (!strcmp(name, "dummy")) {
dmxInputCopyLocal(dmxInput, &DMXDummyMou);
dmxInputCopyLocal(dmxInput, &DMXDummyKbd);
dmxLogInput(dmxInput, "Using dummy input\n");
} else {
int found;
for (found = 0, i = 0; i < dmxNumScreens; i++) {
if (dmxPropertySameDisplay(&dmxScreens[i], name)) {
if (dmxScreens[i].shared)
dmxLog(dmxFatal,
"Cannot take input from shared backend (%s)\n",
name);
if (!dmxInput->core) {
dmxLog(dmxWarning,
"Cannot use core devices on a backend (%s)"
" as XInput devices\n", name);
} else {
char *pt;
for (pt = (char *)dmxInput->name; pt && *pt; pt++)
if (*pt == ',') *pt = '\0';
dmxInputCopyLocal(dmxInput, &DMXBackendMou);
dmxInputCopyLocal(dmxInput, &DMXBackendKbd);
dmxInput->scrnIdx = i;
dmxLogInput(dmxInput,
"Using backend input from %s\n", name);
}
++found;
break;
}
}
if (!found || forceConsole) {
char *pt;
if (found) dmxInput->console = TRUE;
for (pt = (char *)dmxInput->name; pt && *pt; pt++)
if (*pt == ',') *pt = '\0';
dmxInputCopyLocal(dmxInput, &DMXConsoleMou);
dmxInputCopyLocal(dmxInput, &DMXConsoleKbd);
if (doWindows) {
dmxInput->windows = TRUE;
dmxInput->updateWindowInfo = dmxUpdateWindowInformation;
}
dmxLogInput(dmxInput,
"Using console input from %s (%s windows)\n",
name, doWindows ? "with" : "without");
}
}
dmxArgFree(a);
/* Locate extensions we may be interested in */
dmxInputScanForExtensions(dmxInput, doXI);
for (i = 0; i < dmxInput->numDevs; i++) {
DMXLocalInputInfoPtr dmxLocal = dmxInput->devs[i];
dmxLocal->pDevice = dmxAddDevice(dmxLocal);
if (dmxLocal->isCore) {
if (dmxLocal->type == DMX_LOCAL_MOUSE)
pPointer = dmxLocal->pDevice;
if (dmxLocal->type == DMX_LOCAL_KEYBOARD)
pKeyboard = dmxLocal->pDevice;
}
}
dmxInput->processInputEvents = dmxProcessInputEvents;
dmxInput->detached = False;
RegisterBlockAndWakeupHandlers(dmxBlockHandler,
dmxWakeupHandler,
(void *)dmxInput->inputIdx);
}
static void dmxInputFreeLocal(DMXLocalInputInfoRec *local)
{
if (!local) return;
if (local->isCore && local->type == DMX_LOCAL_MOUSE)
dmxLocalCorePointer = NULL;
if (local->isCore && local->type == DMX_LOCAL_KEYBOARD)
dmxLocalCoreKeyboard = NULL;
if (local->destroy_private) local->destroy_private(local->private);
free(local->history);
free(local->valuators);
free(local->deviceName);
local->private = NULL;
local->history = NULL;
local->deviceName = NULL;
free(local);
}
/** Free all of the memory associated with \a dmxInput */
void dmxInputFree(DMXInputInfo *dmxInput)
{
int i;
if (!dmxInput) return;
free(dmxInput->keycodes);
free(dmxInput->symbols);
free(dmxInput->geometry);
for (i = 0; i < dmxInput->numDevs; i++) {
dmxInputFreeLocal(dmxInput->devs[i]);
dmxInput->devs[i] = NULL;
}
free(dmxInput->devs);
dmxInput->devs = NULL;
dmxInput->numDevs = 0;
if (dmxInput->freename) free(dmxInput->name);
dmxInput->name = NULL;
}
/** Log information about all of the known devices using #dmxLog(). */
void dmxInputLogDevices(void)
{
int i, j;
dmxLog(dmxInfo, "%d devices:\n", dmxGetInputCount());
dmxLog(dmxInfo, " Id Name Classes\n");
for (j = 0; j < dmxNumInputs; j++) {
DMXInputInfo *dmxInput = &dmxInputs[j];
const char *pt = strchr(dmxInput->name, ',');
int len = (pt
? (size_t)(pt-dmxInput->name)
: strlen(dmxInput->name));
for (i = 0; i < dmxInput->numDevs; i++) {
DeviceIntPtr pDevice = dmxInput->devs[i]->pDevice;
if (pDevice) {
dmxLog(dmxInfo, " %2d%c %-20.20s",
pDevice->id,
dmxInput->detached ? 'D' : ' ',
pDevice->name);
if (pDevice->key) dmxLogCont(dmxInfo, " key");
if (pDevice->valuator) dmxLogCont(dmxInfo, " val");
if (pDevice->button) dmxLogCont(dmxInfo, " btn");
if (pDevice->focus) dmxLogCont(dmxInfo, " foc");
if (pDevice->kbdfeed) dmxLogCont(dmxInfo, " fb/kbd");
if (pDevice->ptrfeed) dmxLogCont(dmxInfo, " fb/ptr");
if (pDevice->intfeed) dmxLogCont(dmxInfo, " fb/int");
if (pDevice->stringfeed) dmxLogCont(dmxInfo, " fb/str");
if (pDevice->bell) dmxLogCont(dmxInfo, " fb/bel");
if (pDevice->leds) dmxLogCont(dmxInfo, " fb/led");
if (!pDevice->key && !pDevice->valuator && !pDevice->button
&& !pDevice->focus && !pDevice->kbdfeed
&& !pDevice->ptrfeed && !pDevice->intfeed
&& !pDevice->stringfeed && !pDevice->bell
&& !pDevice->leds) dmxLogCont(dmxInfo, " (none)");
dmxLogCont(dmxInfo, "\t[i%d/%*.*s",
dmxInput->inputIdx, len, len, dmxInput->name);
if (dmxInput->devs[i]->deviceId >= 0)
dmxLogCont(dmxInfo, "/id%d", dmxInput->devs[i]->deviceId);
if (dmxInput->devs[i]->deviceName)
dmxLogCont(dmxInfo, "=%s", dmxInput->devs[i]->deviceName);
dmxLogCont(dmxInfo, "] %s\n",
dmxInput->devs[i]->isCore
? "core"
: (dmxInput->devs[i]->sendsCore
? "extension (sends core events)"
: "extension"));
}
}
}
}
/** Detach an input */
int dmxInputDetach(DMXInputInfo *dmxInput)
{
int i;
if (dmxInput->detached) return BadAccess;
for (i = 0; i < dmxInput->numDevs; i++) {
DMXLocalInputInfoPtr dmxLocal = dmxInput->devs[i];
dmxLogInput(dmxInput, "Detaching device id %d: %s%s\n",
dmxLocal->pDevice->id,
dmxLocal->pDevice->name,
dmxLocal->isCore
? " [core]"
: (dmxLocal->sendsCore
? " [sends core events]"
: ""));
DisableDevice(dmxLocal->pDevice, TRUE);
}
dmxInput->detached = True;
dmxInputLogDevices();
return 0;
}
/** Search for input associated with \a dmxScreen, and detach. */
void dmxInputDetachAll(DMXScreenInfo *dmxScreen)
{
int i;
for (i = 0; i < dmxNumInputs; i++) {
DMXInputInfo *dmxInput = &dmxInputs[i];
if (dmxInput->scrnIdx == dmxScreen->index) dmxInputDetach(dmxInput);
}
}
/** Search for input associated with \a deviceId, and detach. */
int dmxInputDetachId(int id)
{
DMXInputInfo *dmxInput = dmxInputLocateId(id);
if (!dmxInput) return BadValue;
return dmxInputDetach(dmxInput);
}
DMXInputInfo *dmxInputLocateId(int id)
{
int i, j;
for (i = 0; i < dmxNumInputs; i++) {
DMXInputInfo *dmxInput = &dmxInputs[i];
for (j = 0; j < dmxInput->numDevs; j++) {
DMXLocalInputInfoPtr dmxLocal = dmxInput->devs[j];
if (dmxLocal->pDevice->id == id) return dmxInput;
}
}
return NULL;
}
static int dmxInputAttachNew(DMXInputInfo *dmxInput, int *id)
{
dmxInputInit(dmxInput);
InitAndStartDevices();
if (id && dmxInput->devs) *id = dmxInput->devs[0]->pDevice->id;
dmxInputLogDevices();
return 0;
}
static int dmxInputAttachOld(DMXInputInfo *dmxInput, int *id)
{
int i;
dmxInput->detached = False;
for (i = 0; i < dmxInput->numDevs; i++) {
DMXLocalInputInfoPtr dmxLocal = dmxInput->devs[i];
if (id) *id = dmxLocal->pDevice->id;
dmxLogInput(dmxInput,
"Attaching device id %d: %s%s\n",
dmxLocal->pDevice->id,
dmxLocal->pDevice->name,
dmxLocal->isCore
? " [core]"
: (dmxLocal->sendsCore
? " [sends core events]"
: ""));
EnableDevice(dmxLocal->pDevice, TRUE);
}
dmxInputLogDevices();
return 0;
}
int dmxInputAttachConsole(const char *name, int isCore, int *id)
{
DMXInputInfo *dmxInput;
int i;
for (i = 0; i < dmxNumInputs; i++) {
dmxInput = &dmxInputs[i];
if (dmxInput->scrnIdx == -1
&& dmxInput->detached
&& !strcmp(dmxInput->name, name)) {
/* Found match */
dmxLogInput(dmxInput, "Reattaching detached console input\n");
return dmxInputAttachOld(dmxInput, id);
}
}
/* No match found */
dmxInput = dmxConfigAddInput(xstrdup(name), isCore);
dmxInput->freename = TRUE;
dmxLogInput(dmxInput, "Attaching new console input\n");
return dmxInputAttachNew(dmxInput, id);
}
int dmxInputAttachBackend(int physicalScreen, int isCore, int *id)
{
DMXInputInfo *dmxInput;
DMXScreenInfo *dmxScreen;
int i;
if (physicalScreen < 0 || physicalScreen >= dmxNumScreens) return BadValue;
for (i = 0; i < dmxNumInputs; i++) {
dmxInput = &dmxInputs[i];
if (dmxInput->scrnIdx != -1 && dmxInput->scrnIdx == physicalScreen) {
/* Found match */
if (!dmxInput->detached) return BadAccess; /* Already attached */
dmxScreen = &dmxScreens[physicalScreen];
if (!dmxScreen->beDisplay) return BadAccess; /* Screen detached */
dmxLogInput(dmxInput, "Reattaching detached backend input\n");
return dmxInputAttachOld(dmxInput, id);
}
}
/* No match found */
dmxScreen = &dmxScreens[physicalScreen];
if (!dmxScreen->beDisplay) return BadAccess; /* Screen detached */
dmxInput = dmxConfigAddInput(dmxScreen->name, isCore);
dmxLogInput(dmxInput, "Attaching new backend input\n");
return dmxInputAttachNew(dmxInput, id);
}