/* $XFree86$ */ /* * 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 * */ /** \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 #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" #ifdef XINPUT #include #include #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 */ }; 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) { InitKeyClassDeviceStruct(pDevice, &info.keySyms, info.modMap); } if (info.buttonClass) { InitButtonClassDeviceStruct(pDevice, info.numButtons, info.map); } if (info.valuatorClass) { if (info.numRelAxes && dmxLocal->sendsCore) { InitValuatorClassDeviceStruct(pDevice, info.numRelAxes, miPointerGetMotionEvents, miPointerGetMotionBufferSize(), 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(); miPointerUpdate(); if (dmxInput->detached) return; for (i = 0; i < dmxInput->numDevs; i += dmxInput->devs[i]->binding) if (dmxInput->devs[i]->process_input) dmxInput->devs[i]->process_input(dmxInput->devs[i]->private); } 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) miRegisterPointerDevice(screenInfo.screens[0], pDevice); 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); }