/*
* 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 <[email protected]>
*
*/
/** \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 "dmxeq.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"
#ifdef XINPUT
#include <X11/extensions/XI.h>
#include <X11/extensions/XIproto.h>
#include "exevents.h"
#define EXTENSION_PROC_ARGS void *
#include "extinit.h"
#endif
/* From XI.h */
#ifndef Relative
#define Relative 0
#endif
#ifndef Absolute
#define Absolute 1
#endif
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);
#ifdef XKB
if (!noXkbExtension && dmxLocal->pDevice->kbdfeed) {
XkbEventCauseRec cause;
XkbSetCauseUnknown(&cause);
/* Generate XKB events, as necessary */
XkbUpdateIndicators(dmxLocal->pDevice, XkbAllIndicatorsMask, False,
NULL, &cause);
}
#endif
}
}
/** 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);
}
}
#ifdef XKB
static void dmxKeyboardFreeNames(XkbComponentNamesPtr names)
{
if (names->keymap) XFree(names->keymap);
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);
}
#endif
static int dmxKeyboardOn(DeviceIntPtr pDevice, DMXLocalInitInfo *info)
{
#ifdef XKB
GETDMXINPUTFROMPDEVICE;
#else
DevicePtr pDev = &pDevice->public;
#endif
#ifdef XKB
if (noXkbExtension) {
#endif
if (!InitKeyboardDeviceStruct(pDev, &info->keySyms, info->modMap,
dmxKeyboardBellProc,
dmxKeyboardKbdCtrlProc))
return BadImplementation;
#ifdef XKB
} else {
XkbSetRulesDflts(dmxConfigGetXkbRules(),
dmxConfigGetXkbModel(),
dmxConfigGetXkbLayout(),
dmxConfigGetXkbVariant(),
dmxConfigGetXkbOptions());
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() : "");
}
XkbInitKeyboardDeviceStruct(pDevice,
&info->names,
&info->keySyms,
info->modMap,
dmxKeyboardBellProc,
dmxKeyboardKbdCtrlProc);
}
if (info->freenames) dmxKeyboardFreeNames(&info->names);
#endif
return Success;
}
static int dmxDeviceOnOff(DeviceIntPtr pDevice, int what)
{
GETDMXINPUTFROMPDEVICE;
int fd;
DMXLocalInitInfo info;
#ifdef XINPUT
int i;
#endif
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) {
#if 00 /*BP*/
InitKeyClassDeviceStruct(pDevice, &info.keySyms, info.modMap);
#else
DevicePtr pDev = (DevicePtr) pDevice;
InitKeyboardDeviceStruct(pDev,
&info.keySyms,
info.modMap,
dmxBell, dmxKbdCtrl);
#endif
}
if (info.buttonClass) {
InitButtonClassDeviceStruct(pDevice, info.numButtons, info.map);
}
if (info.valuatorClass) {
if (info.numRelAxes && dmxLocal->sendsCore) {
InitValuatorClassDeviceStruct(pDevice, info.numRelAxes,
#if 00 /*BP*/
miPointerGetMotionEvents,
miPointerGetMotionBufferSize(),
#else
GetMotionHistory,
GetMaximumEventsNum(),
#endif
Relative);
#ifdef XINPUT
for (i = 0; i < info.numRelAxes; i++)
InitValuatorAxisStruct(pDevice, i, info.minval[0],
info.maxval[0], info.res[0],
info.minres[0], info.maxres[0]);
#endif
} else if (info.numRelAxes) {
InitValuatorClassDeviceStruct(pDevice, info.numRelAxes,
dmxPointerGetMotionEvents,
dmxPointerGetMotionBufferSize(),
Relative);
#ifdef XINPUT
for (i = 0; i < info.numRelAxes; i++)
InitValuatorAxisStruct(pDevice, i, info.minval[0],
info.maxval[0], info.res[0],
info.minres[0], info.maxres[0]);
#endif
} else if (info.numAbsAxes) {
InitValuatorClassDeviceStruct(pDevice, info.numAbsAxes,
dmxPointerGetMotionEvents,
dmxPointerGetMotionBufferSize(),
Absolute);
#ifdef XINPUT
for (i = 0; i < info.numAbsAxes; i++)
InitValuatorAxisStruct(pDevice, i+info.numRelAxes,
info.minval[i+1], info.maxval[i+1],
info.res[i+1], info.minres[i+1],
info.maxres[i+1]);
#endif
}
}
if (info.focusClass) InitFocusClassDeviceStruct(pDevice);
#ifdef XINPUT
if (info.proximityClass) InitProximityClassDeviceStruct(pDevice);
#endif
if (info.ptrFeedbackClass)
InitPtrFeedbackClassDeviceStruct(pDevice, dmxChangePointerControl);
if (info.kbdFeedbackClass)
InitKbdFeedbackClassDeviceStruct(pDevice, dmxKeyboardBellProc,
dmxKeyboardKbdCtrlProc);
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;
}
#ifdef XKB
if (info.xkb) XkbFreeKeyboard(info.xkb, 0, True);
#endif
return Success;
}
static void dmxProcessInputEvents(DMXInputInfo *dmxInput)
{
int i;
dmxeqProcessInputEvents();
#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 != WindowTable[0])
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 = xalloc(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;
void (*registerProcPtr)(DeviceIntPtr) = 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";
registerProcPtr = RegisterKeyboardDevice;
}
if (dmxLocal->type == DMX_LOCAL_MOUSE && !dmxLocalCorePointer) {
dmxLocal->isCore = 1;
dmxLocalCorePointer = dmxLocal;
name = "pointer";
registerProcPtr = RegisterPointerDevice;
}
}
#ifdef XINPUT
if (!name) {
name = "extension";
registerProcPtr = RegisterOtherDevice;
}
#else
if (!name)
dmxLog(dmxFatal,
"Server not build with XINPUT support (cannot add %s)\n",
dmxLocal->name);
#endif
if (!name || !registerProcPtr)
dmxLog(dmxFatal, "Cannot add device %s\n", dmxLocal->name);
pDevice = AddInputDevice(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;
registerProcPtr(pDevice);
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 = xalloc(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 = xrealloc(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, char *name, char *reason)
{
return 0;
}
static void dmxInputScanForExtensions(DMXInputInfo *dmxInput, int doXI)
{
XExtensionVersion *ext;
XDeviceInfo *devices;
Display *display;
int num;
int i, j;
DMXLocalInputInfoPtr dmxLocal;
int (*handler)(Display *, char *, char *);
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;
}
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
? xstrdup(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;
case IsXExtensionDevice:
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
? xstrdup(devices[i].name)
: NULL);
}
}
break;
}
}
XFreeDeviceList(devices);
}
XCloseDisplay(display);
}
/** Re-initialize all the devices described in \a dmxInput. Called from
#dmxReconfig 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
#dmxReconfig 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];
#ifndef XINPUT
if (!dmxLocal->isCore)
dmxLog(dmxFatal,
"This server was not compiled to support the XInput"
" extension, but %s is not a core device.\n",
dmxLocal->name);
#endif
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;
}
}
if (pPointer && pKeyboard) {
if (dmxeqInit(&pKeyboard->public, &pPointer->public))
dmxLogInput(dmxInput, "Using %s and %s as true core devices\n",
pKeyboard->name, pPointer->name);
}
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);
if (local->history) xfree(local->history);
if (local->valuators) xfree(local->valuators);
if (local->deviceName) xfree(local->deviceName);
local->private = NULL;
local->history = NULL;
local->deviceName = NULL;
xfree(local);
}
/** Free all of the memory associated with \a dmxInput */
void dmxInputFree(DMXInputInfo *dmxInput)
{
int i;
if (!dmxInput) return;
if (dmxInput->keycodes) xfree(dmxInput->keycodes);
if (dmxInput->symbols) xfree(dmxInput->symbols);
if (dmxInput->geometry) xfree(dmxInput->geometry);
for (i = 0; i < dmxInput->numDevs; i++) {
dmxInputFreeLocal(dmxInput->devs[i]);
dmxInput->devs[i] = NULL;
}
xfree(dmxInput->devs);
dmxInput->devs = NULL;
dmxInput->numDevs = 0;
if (dmxInput->freename) xfree(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);
}
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);
}
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);
}
|