Home | History | Annotate | Line # | Download | only in input
      1 /*
      2  * Copyright 2002-2003 Red Hat Inc., Durham, North Carolina.
      3  *
      4  * All Rights Reserved.
      5  *
      6  * Permission is hereby granted, free of charge, to any person obtaining
      7  * a copy of this software and associated documentation files (the
      8  * "Software"), to deal in the Software without restriction, including
      9  * without limitation on the rights to use, copy, modify, merge,
     10  * publish, distribute, sublicense, and/or sell copies of the Software,
     11  * and to permit persons to whom the Software is furnished to do so,
     12  * subject to the following conditions:
     13  *
     14  * The above copyright notice and this permission notice (including the
     15  * next paragraph) shall be included in all copies or substantial
     16  * portions of the Software.
     17  *
     18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     19  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     20  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
     21  * NON-INFRINGEMENT.  IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS
     22  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
     23  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
     24  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     25  * SOFTWARE.
     26  */
     27 
     28 /*
     29  * Authors:
     30  *   Rickard E. (Rik) Faith <faith (at) redhat.com>
     31  *
     32  */
     33 
     34 /** \file
     35  * This file provides generic input support.  Functions here set up
     36  * input and lead to the calling of low-level device drivers for
     37  * input. */
     38 
     39 #ifdef HAVE_DMX_CONFIG_H
     40 #include <dmx-config.h>
     41 #endif
     42 
     43 #define DMX_WINDOW_DEBUG 0
     44 
     45 #include "dmxinputinit.h"
     46 #include "dmxextension.h"       /* For dmxInputCount */
     47 
     48 #include "dmxdummy.h"
     49 #include "dmxbackend.h"
     50 #include "dmxconsole.h"
     51 #include "dmxcommon.h"
     52 #include "dmxevents.h"
     53 #include "dmxmotion.h"
     54 #include "dmxprop.h"
     55 #include "config/dmxconfig.h"
     56 #include "dmxcursor.h"
     57 
     58 #include "lnx-keyboard.h"
     59 #include "lnx-ms.h"
     60 #include "lnx-ps2.h"
     61 #include "usb-keyboard.h"
     62 #include "usb-mouse.h"
     63 #include "usb-other.h"
     64 #include "usb-common.h"
     65 
     66 #include "dmxsigio.h"
     67 #include "dmxarg.h"
     68 
     69 #include "inputstr.h"
     70 #include "input.h"
     71 #include "mipointer.h"
     72 #include "windowstr.h"
     73 #include "mi.h"
     74 #include "xkbsrv.h"
     75 
     76 #include <X11/extensions/XI.h>
     77 #include <X11/extensions/XIproto.h>
     78 #include "exevents.h"
     79 #include "extinit.h"
     80 
     81 DMXLocalInputInfoPtr dmxLocalCorePointer, dmxLocalCoreKeyboard;
     82 
     83 static DMXLocalInputInfoRec DMXDummyMou = {
     84     "dummy-mou", DMX_LOCAL_MOUSE, DMX_LOCAL_TYPE_LOCAL, 1,
     85     NULL, NULL, NULL, NULL, NULL, dmxDummyMouGetInfo
     86 };
     87 
     88 static DMXLocalInputInfoRec DMXDummyKbd = {
     89     "dummy-kbd", DMX_LOCAL_KEYBOARD, DMX_LOCAL_TYPE_LOCAL, 1,
     90     NULL, NULL, NULL, NULL, NULL, dmxDummyKbdGetInfo
     91 };
     92 
     93 static DMXLocalInputInfoRec DMXBackendMou = {
     94     "backend-mou", DMX_LOCAL_MOUSE, DMX_LOCAL_TYPE_BACKEND, 2,
     95     dmxBackendCreatePrivate, dmxBackendDestroyPrivate,
     96     dmxBackendInit, NULL, dmxBackendLateReInit, dmxBackendMouGetInfo,
     97     dmxCommonMouOn, dmxCommonMouOff, dmxBackendUpdatePosition,
     98     NULL, NULL, NULL,
     99     dmxBackendCollectEvents, dmxBackendProcessInput, dmxBackendFunctions, NULL,
    100     dmxCommonMouCtrl
    101 };
    102 
    103 static DMXLocalInputInfoRec DMXBackendKbd = {
    104     "backend-kbd", DMX_LOCAL_KEYBOARD, DMX_LOCAL_TYPE_BACKEND,
    105     1, /* With backend-mou or console-mou */
    106     dmxCommonCopyPrivate, NULL,
    107     dmxBackendInit, NULL, NULL, dmxBackendKbdGetInfo,
    108     dmxCommonKbdOn, dmxCommonKbdOff, NULL,
    109     NULL, NULL, NULL,
    110     NULL, NULL, NULL, NULL,
    111     NULL, dmxCommonKbdCtrl, dmxCommonKbdBell
    112 };
    113 
    114 static DMXLocalInputInfoRec DMXConsoleMou = {
    115     "console-mou", DMX_LOCAL_MOUSE, DMX_LOCAL_TYPE_CONSOLE, 2,
    116     dmxConsoleCreatePrivate, dmxConsoleDestroyPrivate,
    117     dmxConsoleInit, dmxConsoleReInit, NULL, dmxConsoleMouGetInfo,
    118     dmxCommonMouOn, dmxCommonMouOff, dmxConsoleUpdatePosition,
    119     NULL, NULL, NULL,
    120     dmxConsoleCollectEvents, NULL, dmxConsoleFunctions, dmxConsoleUpdateInfo,
    121     dmxCommonMouCtrl
    122 };
    123 
    124 static DMXLocalInputInfoRec DMXConsoleKbd = {
    125     "console-kbd", DMX_LOCAL_KEYBOARD, DMX_LOCAL_TYPE_CONSOLE,
    126     1, /* With backend-mou or console-mou */
    127     dmxCommonCopyPrivate, NULL,
    128     dmxConsoleInit, dmxConsoleReInit, NULL, dmxConsoleKbdGetInfo,
    129     dmxCommonKbdOn, dmxCommonKbdOff, NULL,
    130     NULL, NULL, NULL,
    131     NULL, NULL, NULL, NULL,
    132     NULL, dmxCommonKbdCtrl, dmxCommonKbdBell
    133 };
    134 
    135 static DMXLocalInputInfoRec DMXLocalDevices[] = {
    136                                 /* Dummy drivers that can compile on any OS */
    137 #ifdef __linux__
    138                                 /* Linux-specific drivers */
    139     {
    140         "kbd", DMX_LOCAL_KEYBOARD, DMX_LOCAL_TYPE_LOCAL, 1,
    141         kbdLinuxCreatePrivate, kbdLinuxDestroyPrivate,
    142         kbdLinuxInit, NULL, NULL, kbdLinuxGetInfo,
    143         kbdLinuxOn, kbdLinuxOff, NULL,
    144         kbdLinuxVTPreSwitch, kbdLinuxVTPostSwitch, kbdLinuxVTSwitch,
    145         kbdLinuxRead, NULL, NULL, NULL,
    146         NULL, kbdLinuxCtrl, kbdLinuxBell
    147     },
    148     {
    149         "ms", DMX_LOCAL_MOUSE, DMX_LOCAL_TYPE_LOCAL, 1,
    150         msLinuxCreatePrivate, msLinuxDestroyPrivate,
    151         msLinuxInit, NULL, NULL, msLinuxGetInfo,
    152         msLinuxOn, msLinuxOff, NULL,
    153         msLinuxVTPreSwitch, msLinuxVTPostSwitch, NULL,
    154         msLinuxRead
    155     },
    156     {
    157         "ps2", DMX_LOCAL_MOUSE, DMX_LOCAL_TYPE_LOCAL, 1,
    158         ps2LinuxCreatePrivate, ps2LinuxDestroyPrivate,
    159         ps2LinuxInit, NULL, NULL, ps2LinuxGetInfo,
    160         ps2LinuxOn, ps2LinuxOff, NULL,
    161         ps2LinuxVTPreSwitch, ps2LinuxVTPostSwitch, NULL,
    162         ps2LinuxRead
    163     },
    164 #endif
    165 #ifdef __linux__
    166                                 /* USB drivers, currently only for
    167                                    Linux, but relatively easy to port to
    168                                    other OSs */
    169     {
    170         "usb-kbd", DMX_LOCAL_KEYBOARD, DMX_LOCAL_TYPE_LOCAL, 1,
    171         usbCreatePrivate, usbDestroyPrivate,
    172         kbdUSBInit, NULL, NULL, kbdUSBGetInfo,
    173         kbdUSBOn, usbOff, NULL,
    174         NULL, NULL, NULL,
    175         kbdUSBRead, NULL, NULL, NULL,
    176         NULL, kbdUSBCtrl
    177     },
    178     {
    179         "usb-mou", DMX_LOCAL_MOUSE, DMX_LOCAL_TYPE_LOCAL, 1,
    180         usbCreatePrivate, usbDestroyPrivate,
    181         mouUSBInit, NULL, NULL, mouUSBGetInfo,
    182         mouUSBOn, usbOff, NULL,
    183         NULL, NULL, NULL,
    184         mouUSBRead
    185     },
    186     {
    187         "usb-oth", DMX_LOCAL_OTHER, DMX_LOCAL_TYPE_LOCAL, 1,
    188         usbCreatePrivate, usbDestroyPrivate,
    189         othUSBInit, NULL, NULL, othUSBGetInfo,
    190         othUSBOn, usbOff, NULL,
    191         NULL, NULL, NULL,
    192         othUSBRead
    193     },
    194 #endif
    195     {
    196         "dummy-mou", DMX_LOCAL_MOUSE, DMX_LOCAL_TYPE_LOCAL, 1,
    197         NULL, NULL, NULL, NULL, NULL, dmxDummyMouGetInfo
    198     },
    199     {
    200         "dummy-kbd", DMX_LOCAL_KEYBOARD, DMX_LOCAL_TYPE_LOCAL, 1,
    201         NULL, NULL, NULL, NULL, NULL, dmxDummyKbdGetInfo
    202     },
    203     { NULL }                    /* Must be last */
    204 };
    205 
    206 
    207 #if 11 /*BP*/
    208 void
    209 DDXRingBell(int volume, int pitch, int duration)
    210 {
    211    /* NO-OP */
    212 }
    213 
    214 /* taken from kdrive/src/kinput.c: */
    215 static void
    216 dmxKbdCtrl (DeviceIntPtr pDevice, KeybdCtrl *ctrl)
    217 {
    218 #if 0
    219     KdKeyboardInfo *ki;
    220 
    221     for (ki = kdKeyboards; ki; ki = ki->next) {
    222         if (ki->dixdev && ki->dixdev->id == pDevice->id)
    223             break;
    224     }
    225 
    226     if (!ki || !ki->dixdev || ki->dixdev->id != pDevice->id || !ki->driver)
    227         return;
    228 
    229     KdSetLeds(ki, ctrl->leds);
    230     ki->bellPitch = ctrl->bell_pitch;
    231     ki->bellDuration = ctrl->bell_duration;
    232 #endif
    233 }
    234 
    235 /* taken from kdrive/src/kinput.c: */
    236 static void
    237 dmxBell(int volume, DeviceIntPtr pDev, pointer arg, int something)
    238 {
    239 #if 0
    240     KeybdCtrl *ctrl = arg;
    241     KdKeyboardInfo *ki = NULL;
    242 
    243     for (ki = kdKeyboards; ki; ki = ki->next) {
    244         if (ki->dixdev && ki->dixdev->id == pDev->id)
    245             break;
    246     }
    247 
    248     if (!ki || !ki->dixdev || ki->dixdev->id != pDev->id || !ki->driver)
    249         return;
    250 
    251     KdRingBell(ki, volume, ctrl->bell_pitch, ctrl->bell_duration);
    252 #endif
    253 }
    254 
    255 #endif /*BP*/
    256 
    257 static void _dmxChangePointerControl(DMXLocalInputInfoPtr dmxLocal,
    258                                      PtrCtrl *ctrl)
    259 {
    260     if (!dmxLocal) return;
    261     dmxLocal->mctrl = *ctrl;
    262     if (dmxLocal->mCtrl) dmxLocal->mCtrl(&dmxLocal->pDevice->public, ctrl);
    263 }
    264 
    265 /** Change the pointer control information for the \a pDevice.  If the
    266  * device sends core events, then also change the control information
    267  * for all of the pointer devices that send core events. */
    268 void dmxChangePointerControl(DeviceIntPtr pDevice, PtrCtrl *ctrl)
    269 {
    270     GETDMXLOCALFROMPDEVICE;
    271     int i, j;
    272 
    273     if (dmxLocal->sendsCore) {       /* Do for all core devices */
    274         for (i = 0; i < dmxNumInputs; i++) {
    275             DMXInputInfo *dmxInput = &dmxInputs[i];
    276             if (dmxInput->detached) continue;
    277             for (j = 0; j < dmxInput->numDevs; j++)
    278                 if (dmxInput->devs[j]->sendsCore)
    279                     _dmxChangePointerControl(dmxInput->devs[j], ctrl);
    280         }
    281     } else {                    /* Do for this device only */
    282         _dmxChangePointerControl(dmxLocal, ctrl);
    283     }
    284 }
    285 
    286 static void _dmxKeyboardKbdCtrlProc(DMXLocalInputInfoPtr dmxLocal,
    287                                     KeybdCtrl *ctrl)
    288 {
    289     dmxLocal->kctrl = *ctrl;
    290     if (dmxLocal->kCtrl) {
    291         dmxLocal->kCtrl(&dmxLocal->pDevice->public, ctrl);
    292         if (dmxLocal->pDevice->kbdfeed) {
    293             XkbEventCauseRec cause;
    294             XkbSetCauseUnknown(&cause);
    295             /* Generate XKB events, as necessary */
    296             XkbUpdateIndicators(dmxLocal->pDevice, XkbAllIndicatorsMask, False,
    297                                 NULL, &cause);
    298         }
    299     }
    300 }
    301 
    302 
    303 /** Change the keyboard control information for the \a pDevice.  If the
    304  * device sends core events, then also change the control information
    305  * for all of the keyboard devices that send core events. */
    306 void dmxKeyboardKbdCtrlProc(DeviceIntPtr pDevice, KeybdCtrl *ctrl)
    307 {
    308     GETDMXLOCALFROMPDEVICE;
    309     int i, j;
    310 
    311     if (dmxLocal->sendsCore) {       /* Do for all core devices */
    312         for (i = 0; i < dmxNumInputs; i++) {
    313             DMXInputInfo *dmxInput = &dmxInputs[i];
    314             if (dmxInput->detached) continue;
    315             for (j = 0; j < dmxInput->numDevs; j++)
    316                 if (dmxInput->devs[j]->sendsCore)
    317                     _dmxKeyboardKbdCtrlProc(dmxInput->devs[j], ctrl);
    318         }
    319     } else {                    /* Do for this device only */
    320         _dmxKeyboardKbdCtrlProc(dmxLocal, ctrl);
    321     }
    322 }
    323 
    324 static void _dmxKeyboardBellProc(DMXLocalInputInfoPtr dmxLocal, int percent)
    325 {
    326     if (dmxLocal->kBell) dmxLocal->kBell(&dmxLocal->pDevice->public,
    327                                          percent,
    328                                          dmxLocal->kctrl.bell,
    329                                          dmxLocal->kctrl.bell_pitch,
    330                                          dmxLocal->kctrl.bell_duration);
    331 }
    332 
    333 /** Sound the bell on the device.  If the device send core events, then
    334  * sound the bell on all of the devices that send core events. */
    335 void dmxKeyboardBellProc(int percent, DeviceIntPtr pDevice,
    336                          pointer ctrl, int unknown)
    337 {
    338     GETDMXLOCALFROMPDEVICE;
    339     int i, j;
    340 
    341     if (dmxLocal->sendsCore) {       /* Do for all core devices */
    342         for (i = 0; i < dmxNumInputs; i++) {
    343             DMXInputInfo *dmxInput = &dmxInputs[i];
    344             if (dmxInput->detached) continue;
    345             for (j = 0; j < dmxInput->numDevs; j++)
    346                 if (dmxInput->devs[j]->sendsCore)
    347                     _dmxKeyboardBellProc(dmxInput->devs[j], percent);
    348         }
    349     } else {                    /* Do for this device only */
    350         _dmxKeyboardBellProc(dmxLocal, percent);
    351     }
    352 }
    353 
    354 static void dmxKeyboardFreeNames(XkbComponentNamesPtr names)
    355 {
    356     if (names->keycodes) XFree(names->keycodes);
    357     if (names->types)    XFree(names->types);
    358     if (names->compat)   XFree(names->compat);
    359     if (names->symbols)  XFree(names->symbols);
    360     if (names->geometry) XFree(names->geometry);
    361 }
    362 
    363 
    364 static int dmxKeyboardOn(DeviceIntPtr pDevice, DMXLocalInitInfo *info)
    365 {
    366     GETDMXINPUTFROMPDEVICE;
    367     XkbRMLVOSet rmlvo;
    368 
    369     rmlvo.rules = dmxConfigGetXkbRules();
    370     rmlvo.model = dmxConfigGetXkbModel();
    371     rmlvo.layout = dmxConfigGetXkbLayout();
    372     rmlvo.variant = dmxConfigGetXkbVariant();
    373     rmlvo.options = dmxConfigGetXkbOptions();
    374 
    375     XkbSetRulesDflts(&rmlvo);
    376     if (!info->force && (dmxInput->keycodes
    377                          || dmxInput->symbols
    378                          || dmxInput->geometry)) {
    379         if (info->freenames) dmxKeyboardFreeNames(&info->names);
    380         info->freenames      = 0;
    381         info->names.keycodes = dmxInput->keycodes;
    382         info->names.types    = NULL;
    383         info->names.compat   = NULL;
    384         info->names.symbols  = dmxInput->symbols;
    385         info->names.geometry = dmxInput->geometry;
    386 
    387         dmxLogInput(dmxInput, "XKEYBOARD: From command line: %s",
    388                     info->names.keycodes);
    389         if (info->names.symbols && *info->names.symbols)
    390             dmxLogInputCont(dmxInput, " %s", info->names.symbols);
    391         if (info->names.geometry && *info->names.geometry)
    392             dmxLogInputCont(dmxInput, " %s", info->names.geometry);
    393         dmxLogInputCont(dmxInput, "\n");
    394     } else if (info->names.keycodes) {
    395         dmxLogInput(dmxInput, "XKEYBOARD: From device: %s",
    396                     info->names.keycodes);
    397         if (info->names.symbols && *info->names.symbols)
    398             dmxLogInputCont(dmxInput, " %s", info->names.symbols);
    399         if (info->names.geometry && *info->names.geometry)
    400             dmxLogInputCont(dmxInput, " %s", info->names.geometry);
    401         dmxLogInputCont(dmxInput, "\n");
    402     } else {
    403         dmxLogInput(dmxInput, "XKEYBOARD: Defaults: %s %s %s %s %s\n",
    404                     dmxConfigGetXkbRules(),
    405                     dmxConfigGetXkbLayout(),
    406                     dmxConfigGetXkbModel(),
    407                     dmxConfigGetXkbVariant()
    408                     ? dmxConfigGetXkbVariant() : "",
    409                     dmxConfigGetXkbOptions()
    410                     ? dmxConfigGetXkbOptions() : "");
    411     }
    412     InitKeyboardDeviceStruct(pDevice, &rmlvo,
    413                                 dmxKeyboardBellProc,
    414                                 dmxKeyboardKbdCtrlProc);
    415 
    416     if (info->freenames) dmxKeyboardFreeNames(&info->names);
    417 
    418     return Success;
    419 }
    420 
    421 
    422 static int dmxDeviceOnOff(DeviceIntPtr pDevice, int what)
    423 {
    424     GETDMXINPUTFROMPDEVICE;
    425     int              fd;
    426     DMXLocalInitInfo info;
    427     int              i;
    428     Atom             btn_labels[MAX_BUTTONS] = {0}; /* FIXME */
    429     Atom             axis_labels[MAX_VALUATORS] = {0}; /* FIXME */
    430 
    431     if (dmxInput->detached) return Success;
    432 
    433     memset(&info, 0, sizeof(info));
    434     switch (what) {
    435     case DEVICE_INIT:
    436         if (dmxLocal->init)
    437             dmxLocal->init(pDev);
    438         if (dmxLocal->get_info)
    439             dmxLocal->get_info(pDev, &info);
    440         if (info.keyboard) {    /* XKEYBOARD makes this a special case */
    441             dmxKeyboardOn(pDevice, &info);
    442             break;
    443         }
    444         if (info.keyClass) {
    445             XkbRMLVOSet rmlvo;
    446 
    447             rmlvo.rules = dmxConfigGetXkbRules();
    448             rmlvo.model = dmxConfigGetXkbModel();
    449             rmlvo.layout = dmxConfigGetXkbLayout();
    450             rmlvo.variant = dmxConfigGetXkbVariant();
    451             rmlvo.options = dmxConfigGetXkbOptions();
    452 
    453             InitKeyboardDeviceStruct(pDevice,
    454                                      &rmlvo,
    455                                      dmxBell, dmxKbdCtrl);
    456         }
    457         if (info.buttonClass) {
    458             InitButtonClassDeviceStruct(pDevice, info.numButtons,
    459                                         btn_labels, info.map);
    460         }
    461         if (info.valuatorClass) {
    462             if (info.numRelAxes && dmxLocal->sendsCore) {
    463                 InitValuatorClassDeviceStruct(pDevice, info.numRelAxes,
    464                                               axis_labels,
    465                                               GetMaximumEventsNum(),
    466                                               Relative);
    467                 for (i = 0; i < info.numRelAxes; i++)
    468                     InitValuatorAxisStruct(pDevice, i, axis_labels[i],
    469                                            info.minval[i], info.maxval[i],
    470                                            info.res[i],
    471                                            info.minres[i], info.maxres[i],
    472                                            Relative);
    473             } else if (info.numRelAxes) {
    474                 InitValuatorClassDeviceStruct(pDevice, info.numRelAxes,
    475                                               axis_labels,
    476                                               dmxPointerGetMotionBufferSize(),
    477                                               Relative);
    478                 for (i = 0; i < info.numRelAxes; i++)
    479                     InitValuatorAxisStruct(pDevice, i, axis_labels[i],
    480                                            info.minval[i],
    481                                            info.maxval[i], info.res[i],
    482                                            info.minres[i], info.maxres[i],
    483                                            Relative);
    484             } else if (info.numAbsAxes) {
    485                 InitValuatorClassDeviceStruct(pDevice, info.numAbsAxes,
    486                                               axis_labels,
    487                                               dmxPointerGetMotionBufferSize(),
    488                                               Absolute);
    489                 for (i = 0; i < info.numAbsAxes; i++)
    490                     InitValuatorAxisStruct(pDevice, i,
    491                                            axis_labels[i],
    492                                            info.minval[i], info.maxval[i],
    493                                            info.res[i], info.minres[i],
    494                                            info.maxres[i], Absolute);
    495             }
    496         }
    497         if (info.focusClass)       InitFocusClassDeviceStruct(pDevice);
    498         if (info.proximityClass)   InitProximityClassDeviceStruct(pDevice);
    499         if (info.ptrFeedbackClass)
    500             InitPtrFeedbackClassDeviceStruct(pDevice, dmxChangePointerControl);
    501         if (info.intFeedbackClass || info.strFeedbackClass)
    502             dmxLog(dmxWarning,
    503                    "Integer and string feedback not supported for %s\n",
    504                    pDevice->name);
    505         if (!info.keyboard && (info.ledFeedbackClass || info.belFeedbackClass))
    506             dmxLog(dmxWarning,
    507                    "Led and bel feedback not supported for non-keyboard %s\n",
    508                    pDevice->name);
    509         break;
    510     case DEVICE_ON:
    511         if (!pDev->on) {
    512             if (dmxLocal->on && (fd = dmxLocal->on(pDev)) >= 0)
    513                 dmxSigioRegister(dmxInput, fd);
    514             pDev->on = TRUE;
    515         }
    516         break;
    517     case DEVICE_OFF:
    518     case DEVICE_CLOSE:
    519             /* This can get called twice consecutively: once for a
    520              * detached screen (DEVICE_OFF), and then again at server
    521              * generation time (DEVICE_CLOSE). */
    522         if (pDev->on) {
    523             dmxSigioUnregister(dmxInput);
    524             if (dmxLocal->off) dmxLocal->off(pDev);
    525             pDev->on = FALSE;
    526         }
    527         break;
    528     }
    529     if (info.keySyms.map && info.freemap) {
    530         XFree(info.keySyms.map);
    531         info.keySyms.map = NULL;
    532     }
    533     if (info.xkb) XkbFreeKeyboard(info.xkb, 0, True);
    534     return Success;
    535 }
    536 
    537 static void dmxProcessInputEvents(DMXInputInfo *dmxInput)
    538 {
    539     int i;
    540 
    541     mieqProcessInputEvents();
    542 #if 00 /*BP*/
    543     miPointerUpdate();
    544 #endif
    545     if (dmxInput->detached)
    546         return;
    547     for (i = 0; i < dmxInput->numDevs; i += dmxInput->devs[i]->binding)
    548         if (dmxInput->devs[i]->process_input) {
    549 #if 11 /*BP*/
    550             miPointerUpdateSprite(dmxInput->devs[i]->pDevice);
    551 #endif
    552             dmxInput->devs[i]->process_input(dmxInput->devs[i]->private);
    553         }
    554 
    555 #if 11 /*BP*/
    556     mieqProcessInputEvents();
    557 #endif
    558 }
    559 
    560 static void dmxUpdateWindowInformation(DMXInputInfo *dmxInput,
    561                                        DMXUpdateType type,
    562                                        WindowPtr pWindow)
    563 {
    564     int i;
    565 
    566 #ifdef PANORAMIX
    567     if (!noPanoramiXExtension && pWindow && pWindow->parent != screenInfo.screens[0]->root)
    568         return;
    569 #endif
    570 #if DMX_WINDOW_DEBUG
    571     {
    572         const char *name = "Unknown";
    573         switch (type) {
    574         case DMX_UPDATE_REALIZE:            name = "Realize";         break;
    575         case DMX_UPDATE_UNREALIZE:          name = "Unrealize";       break;
    576         case DMX_UPDATE_RESTACK:            name = "Restack";         break;
    577         case DMX_UPDATE_COPY:               name = "Copy";            break;
    578         case DMX_UPDATE_RESIZE:             name = "Resize";          break;
    579         case DMX_UPDATE_REPARENT:           name = "Repaint";         break;
    580         }
    581         dmxLog(dmxDebug, "Window %p changed: %s\n", pWindow, name);
    582     }
    583 #endif
    584 
    585     if (dmxInput->detached)
    586         return;
    587     for (i = 0; i < dmxInput->numDevs; i += dmxInput->devs[i]->binding)
    588         if (dmxInput->devs[i]->update_info)
    589             dmxInput->devs[i]->update_info(dmxInput->devs[i]->private,
    590                                            type, pWindow);
    591 }
    592 
    593 static void dmxCollectAll(DMXInputInfo *dmxInput)
    594 {
    595     int i;
    596 
    597     if (dmxInput->detached)
    598         return;
    599     for (i = 0; i < dmxInput->numDevs; i += dmxInput->devs[i]->binding)
    600         if (dmxInput->devs[i]->collect_events)
    601             dmxInput->devs[i]->collect_events(&dmxInput->devs[i]->pDevice->public,
    602                                               dmxMotion,
    603                                               dmxEnqueue,
    604                                               dmxCheckSpecialKeys, DMX_BLOCK);
    605 }
    606 
    607 static void dmxBlockHandler(pointer blockData, OSTimePtr pTimeout,
    608                             pointer pReadMask)
    609 {
    610     DMXInputInfo    *dmxInput = &dmxInputs[(uintptr_t)blockData];
    611     static unsigned long generation = 0;
    612 
    613     if (generation != serverGeneration) {
    614         generation = serverGeneration;
    615         dmxCollectAll(dmxInput);
    616     }
    617 }
    618 
    619 static void dmxSwitchReturn(pointer p)
    620 {
    621     DMXInputInfo *dmxInput = p;
    622     int          i;
    623 
    624     dmxLog(dmxInfo, "Returning from VT %d\n", dmxInput->vt_switched);
    625 
    626     if (!dmxInput->vt_switched)
    627         dmxLog(dmxFatal, "dmxSwitchReturn called, but not switched\n");
    628     dmxSigioEnableInput();
    629     for (i = 0; i < dmxInput->numDevs; i++)
    630         if (dmxInput->devs[i]->vt_post_switch)
    631             dmxInput->devs[i]->vt_post_switch(dmxInput->devs[i]->private);
    632     dmxInput->vt_switched = 0;
    633 }
    634 
    635 static void dmxWakeupHandler(pointer blockData, int result, pointer pReadMask)
    636 {
    637     DMXInputInfo *dmxInput = &dmxInputs[(uintptr_t)blockData];
    638     int          i;
    639 
    640     if (dmxInput->vt_switch_pending) {
    641         dmxLog(dmxInfo, "Switching to VT %d\n", dmxInput->vt_switch_pending);
    642         for (i = 0; i < dmxInput->numDevs; i++)
    643             if (dmxInput->devs[i]->vt_pre_switch)
    644                 dmxInput->devs[i]->vt_pre_switch(dmxInput->devs[i]->private);
    645         dmxInput->vt_switched       = dmxInput->vt_switch_pending;
    646         dmxInput->vt_switch_pending = 0;
    647         for (i = 0; i < dmxInput->numDevs; i++) {
    648             if (dmxInput->devs[i]->vt_switch) {
    649                 dmxSigioDisableInput();
    650                 if (!dmxInput->devs[i]->vt_switch(dmxInput->devs[i]->private,
    651                                                   dmxInput->vt_switched,
    652                                                   dmxSwitchReturn,
    653                                                   dmxInput))
    654                     dmxSwitchReturn(dmxInput);
    655                 break;          /* Only call one vt_switch routine */
    656             }
    657         }
    658     }
    659     dmxCollectAll(dmxInput);
    660 }
    661 
    662 static char *dmxMakeUniqueDeviceName(DMXLocalInputInfoPtr dmxLocal)
    663 {
    664     static int           k = 0;
    665     static int           m = 0;
    666     static int           o = 0;
    667     static unsigned long dmxGeneration = 0;
    668 #define LEN  32
    669     char *               buf = malloc(LEN);
    670 
    671     if (dmxGeneration != serverGeneration) {
    672         k = m = o     = 0;
    673         dmxGeneration = serverGeneration;
    674     }
    675 
    676     switch (dmxLocal->type) {
    677     case DMX_LOCAL_KEYBOARD: XmuSnprintf(buf, LEN, "Keyboard%d", k++); break;
    678     case DMX_LOCAL_MOUSE:    XmuSnprintf(buf, LEN, "Mouse%d", m++);    break;
    679     default:                 XmuSnprintf(buf, LEN, "Other%d", o++);    break;
    680     }
    681 
    682     return buf;
    683 }
    684 
    685 static DeviceIntPtr dmxAddDevice(DMXLocalInputInfoPtr dmxLocal)
    686 {
    687     DeviceIntPtr pDevice;
    688     Atom         atom;
    689     const char   *name = NULL;
    690     char         *devname;
    691     DMXInputInfo *dmxInput;
    692 
    693     if (!dmxLocal)
    694         return NULL;
    695     dmxInput = &dmxInputs[dmxLocal->inputIdx];
    696 
    697     if (dmxLocal->sendsCore) {
    698         if (dmxLocal->type == DMX_LOCAL_KEYBOARD && !dmxLocalCoreKeyboard) {
    699             dmxLocal->isCore     = 1;
    700             dmxLocalCoreKeyboard = dmxLocal;
    701             name                 = "keyboard";
    702         }
    703         if (dmxLocal->type == DMX_LOCAL_MOUSE && !dmxLocalCorePointer) {
    704             dmxLocal->isCore     = 1;
    705             dmxLocalCorePointer  = dmxLocal;
    706             name                 = "pointer";
    707         }
    708     }
    709 
    710     if (!name) {
    711         name            = "extension";
    712     }
    713 
    714     if (!name)
    715         dmxLog(dmxFatal, "Cannot add device %s\n", dmxLocal->name);
    716 
    717     pDevice                       = AddInputDevice(serverClient, dmxDeviceOnOff, TRUE);
    718     if (!pDevice) {
    719         dmxLog(dmxError, "Too many devices -- cannot add device %s\n",
    720                dmxLocal->name);
    721         return NULL;
    722     }
    723     pDevice->public.devicePrivate = dmxLocal;
    724     dmxLocal->pDevice             = pDevice;
    725 
    726     devname       = dmxMakeUniqueDeviceName(dmxLocal);
    727     atom          = MakeAtom((char *)devname, strlen(devname), TRUE);
    728     pDevice->type = atom;
    729     pDevice->name = devname;
    730 
    731     if (dmxLocal->isCore && dmxLocal->type == DMX_LOCAL_MOUSE) {
    732 #if 00 /*BP*/
    733         miRegisterPointerDevice(screenInfo.screens[0], pDevice);
    734 #else
    735         /* Nothing? dmxDeviceOnOff() should get called to init, right? */
    736 #endif
    737     }
    738 
    739     if (dmxLocal->create_private)
    740         dmxLocal->private = dmxLocal->create_private(pDevice);
    741 
    742     dmxLogInput(dmxInput, "Added %s as %s device called %s%s\n",
    743                 dmxLocal->name, name, devname,
    744                 dmxLocal->isCore
    745                 ? " [core]"
    746                 : (dmxLocal->sendsCore
    747                    ? " [sends core events]"
    748                    : ""));
    749 
    750     return pDevice;
    751 }
    752 
    753 static DMXLocalInputInfoPtr dmxLookupLocal(const char *name)
    754 {
    755     DMXLocalInputInfoPtr pt;
    756 
    757     for (pt = &DMXLocalDevices[0]; pt->name; ++pt)
    758         if (!strcmp(pt->name, name)) return pt; /* search for device name */
    759     return NULL;
    760 }
    761 
    762 /** Copy the local input information from \a s into a new \a devs slot
    763  * in \a dmxInput. */
    764 DMXLocalInputInfoPtr dmxInputCopyLocal(DMXInputInfo *dmxInput,
    765                                        DMXLocalInputInfoPtr s)
    766 {
    767     DMXLocalInputInfoPtr dmxLocal = malloc(sizeof(*dmxLocal));
    768 
    769     if (!dmxLocal)
    770         dmxLog(dmxFatal, "DMXLocalInputInfoPtr: out of memory\n");
    771 
    772     memcpy(dmxLocal, s, sizeof(*dmxLocal));
    773     dmxLocal->inputIdx       = dmxInput->inputIdx;
    774     dmxLocal->sendsCore      = dmxInput->core;
    775     dmxLocal->savedSendsCore = dmxInput->core;
    776     dmxLocal->deviceId       = -1;
    777 
    778     ++dmxInput->numDevs;
    779     dmxInput->devs = realloc(dmxInput->devs,
    780                               dmxInput->numDevs * sizeof(*dmxInput->devs));
    781     dmxInput->devs[dmxInput->numDevs-1] = dmxLocal;
    782 
    783     return dmxLocal;
    784 }
    785 
    786 static void dmxPopulateLocal(DMXInputInfo *dmxInput, dmxArg a)
    787 {
    788     int                  i;
    789     int                  help = 0;
    790     DMXLocalInputInfoRec *pt;
    791 
    792     for (i = 1; i < dmxArgC(a); i++) {
    793         const char *name = dmxArgV(a, i);
    794         if ((pt = dmxLookupLocal(name))) {
    795             dmxInputCopyLocal(dmxInput, pt);
    796         } else {
    797             if (strlen(name))
    798                 dmxLog(dmxWarning,
    799                        "Could not find a driver called %s\n", name);
    800             ++help;
    801         }
    802     }
    803     if (help) {
    804         dmxLog(dmxInfo, "Available local device drivers:\n");
    805         for (pt = &DMXLocalDevices[0]; pt->name; ++pt) {
    806             const char *type;
    807             switch (pt->type) {
    808             case DMX_LOCAL_KEYBOARD: type = "keyboard"; break;
    809             case DMX_LOCAL_MOUSE:    type = "pointer";  break;
    810             default:                 type = "unknown";  break;
    811             }
    812             dmxLog(dmxInfo, "   %s (%s)\n", pt->name, type);
    813         }
    814         dmxLog(dmxFatal, "Must have valid local device driver\n");
    815     }
    816 }
    817 
    818 int dmxInputExtensionErrorHandler(Display *dsp, _Xconst char *name, _Xconst char *reason)
    819 {
    820     return 0;
    821 }
    822 
    823 static void dmxInputScanForExtensions(DMXInputInfo *dmxInput, int doXI)
    824 {
    825     XExtensionVersion    *ext;
    826     XDeviceInfo          *devices;
    827     Display              *display;
    828     int                  num;
    829     int                  i, j;
    830     XextErrorHandler     handler;
    831 
    832     if (!(display = XOpenDisplay(dmxInput->name))) return;
    833 
    834     /* Print out information about the XInput Extension. */
    835     handler = XSetExtensionErrorHandler(dmxInputExtensionErrorHandler);
    836     ext     = XGetExtensionVersion(display, INAME);
    837     XSetExtensionErrorHandler(handler);
    838 
    839     if (!ext || ext == (XExtensionVersion *)NoSuchExtension) {
    840         dmxLogInput(dmxInput, "%s is not available\n", INAME);
    841     } else {
    842         dmxLogInput(dmxInput, "Locating devices on %s (%s version %d.%d)\n",
    843                     dmxInput->name, INAME,
    844                     ext->major_version, ext->minor_version);
    845         devices = XListInputDevices(display, &num);
    846 
    847         XFree(ext);
    848         ext = NULL;
    849 
    850                                 /* Print a list of all devices */
    851         for (i = 0; i < num; i++) {
    852             const char *use = "Unknown";
    853             switch (devices[i].use) {
    854             case IsXPointer:           use = "XPointer";         break;
    855             case IsXKeyboard:          use = "XKeyboard";        break;
    856             case IsXExtensionDevice:   use = "XExtensionDevice"; break;
    857             case IsXExtensionPointer:  use = "XExtensionPointer"; break;
    858             case IsXExtensionKeyboard: use = "XExtensionKeyboard"; break;
    859             }
    860             dmxLogInput(dmxInput, "  %2d %-10.10s %-16.16s\n",
    861                         devices[i].id,
    862                         devices[i].name ? devices[i].name : "",
    863                         use);
    864         }
    865 
    866                                 /* Search for extensions */
    867         for (i = 0; i < num; i++) {
    868             switch (devices[i].use) {
    869             case IsXKeyboard:
    870                 for (j = 0; j < dmxInput->numDevs; j++) {
    871                     DMXLocalInputInfoPtr dmxL = dmxInput->devs[j];
    872                     if (dmxL->type == DMX_LOCAL_KEYBOARD
    873                         && dmxL->deviceId < 0) {
    874                         dmxL->deviceId   = devices[i].id;
    875                         dmxL->deviceName = (devices[i].name
    876                                             ? strdup(devices[i].name)
    877                                             : NULL);
    878                     }
    879                 }
    880                 break;
    881             case IsXPointer:
    882                 for (j = 0; j < dmxInput->numDevs; j++) {
    883                     DMXLocalInputInfoPtr dmxL = dmxInput->devs[j];
    884                     if (dmxL->type == DMX_LOCAL_MOUSE && dmxL->deviceId < 0) {
    885                         dmxL->deviceId   = devices[i].id;
    886                         dmxL->deviceName = (devices[i].name
    887                                             ? xstrdup(devices[i].name)
    888                                             : NULL);
    889                     }
    890                 }
    891                 break;
    892             }
    893         }
    894         XFreeDeviceList(devices);
    895     }
    896     XCloseDisplay(display);
    897 }
    898 
    899 /** Re-initialize all the devices described in \a dmxInput.  Called from
    900     #dmxAdjustCursorBoundaries before the cursor is redisplayed. */
    901 void dmxInputReInit(DMXInputInfo *dmxInput)
    902 {
    903     int i;
    904 
    905     for (i = 0; i < dmxInput->numDevs; i++) {
    906         DMXLocalInputInfoPtr dmxLocal = dmxInput->devs[i];
    907         if (dmxLocal->reinit)
    908             dmxLocal->reinit(&dmxLocal->pDevice->public);
    909     }
    910 }
    911 
    912 /** Re-initialize all the devices described in \a dmxInput.  Called from
    913     #dmxAdjustCursorBoundaries after the cursor is redisplayed. */
    914 void dmxInputLateReInit(DMXInputInfo *dmxInput)
    915 {
    916     int i;
    917 
    918     for (i = 0; i < dmxInput->numDevs; i++) {
    919         DMXLocalInputInfoPtr dmxLocal = dmxInput->devs[i];
    920         if (dmxLocal->latereinit)
    921             dmxLocal->latereinit(&dmxLocal->pDevice->public);
    922     }
    923 }
    924 
    925 /** Initialize all of the devices described in \a dmxInput. */
    926 void dmxInputInit(DMXInputInfo *dmxInput)
    927 {
    928     DeviceIntPtr         pPointer = NULL, pKeyboard = NULL;
    929     dmxArg               a;
    930     const char           *name;
    931     int                  i;
    932     int                  doXI               = 1; /* Include by default */
    933     int                  forceConsole       = 0;
    934     int                  doWindows          = 1; /* On by default */
    935     int                  hasXkb             = 0;
    936 
    937     a = dmxArgParse(dmxInput->name);
    938 
    939     for (i = 1; i < dmxArgC(a); i++) {
    940         switch (hasXkb) {
    941         case 1:
    942             dmxInput->keycodes = xstrdup(dmxArgV(a, i));
    943             ++hasXkb;
    944             break;
    945         case 2:
    946             dmxInput->symbols  = xstrdup(dmxArgV(a, i));
    947             ++hasXkb;
    948             break;
    949         case 3:
    950             dmxInput->geometry = xstrdup(dmxArgV(a, i));
    951             hasXkb = 0;
    952             break;
    953         case 0:
    954             if      (!strcmp(dmxArgV(a, i), "noxi"))      doXI         = 0;
    955             else if (!strcmp(dmxArgV(a, i), "xi"))        doXI         = 1;
    956             else if (!strcmp(dmxArgV(a, i), "console"))   forceConsole = 1;
    957             else if (!strcmp(dmxArgV(a, i), "noconsole")) forceConsole = 0;
    958             else if (!strcmp(dmxArgV(a, i), "windows"))   doWindows    = 1;
    959             else if (!strcmp(dmxArgV(a, i), "nowindows")) doWindows    = 0;
    960             else if (!strcmp(dmxArgV(a, i), "xkb"))       hasXkb       = 1;
    961             else {
    962                 dmxLog(dmxFatal,
    963                        "Unknown input argument: %s\n", dmxArgV(a, i));
    964             }
    965         }
    966     }
    967 
    968     name = dmxArgV(a, 0);
    969 
    970     if (!strcmp(name, "local")) {
    971         dmxPopulateLocal(dmxInput, a);
    972     } else if (!strcmp(name, "dummy")) {
    973         dmxInputCopyLocal(dmxInput, &DMXDummyMou);
    974         dmxInputCopyLocal(dmxInput, &DMXDummyKbd);
    975         dmxLogInput(dmxInput, "Using dummy input\n");
    976     } else {
    977         int found;
    978 
    979         for (found = 0, i = 0; i < dmxNumScreens; i++) {
    980             if (dmxPropertySameDisplay(&dmxScreens[i], name)) {
    981                 if (dmxScreens[i].shared)
    982                     dmxLog(dmxFatal,
    983                            "Cannot take input from shared backend (%s)\n",
    984                            name);
    985                 if (!dmxInput->core) {
    986                     dmxLog(dmxWarning,
    987                            "Cannot use core devices on a backend (%s)"
    988                            " as XInput devices\n", name);
    989                 } else {
    990                     char *pt;
    991                     for (pt = (char *)dmxInput->name; pt && *pt; pt++)
    992                         if (*pt == ',') *pt = '\0';
    993                     dmxInputCopyLocal(dmxInput, &DMXBackendMou);
    994                     dmxInputCopyLocal(dmxInput, &DMXBackendKbd);
    995                     dmxInput->scrnIdx = i;
    996                     dmxLogInput(dmxInput,
    997                                 "Using backend input from %s\n", name);
    998                 }
    999                 ++found;
   1000                 break;
   1001             }
   1002         }
   1003         if (!found || forceConsole) {
   1004             char *pt;
   1005             if (found) dmxInput->console = TRUE;
   1006             for (pt = (char *)dmxInput->name; pt && *pt; pt++)
   1007                 if (*pt == ',') *pt = '\0';
   1008             dmxInputCopyLocal(dmxInput, &DMXConsoleMou);
   1009             dmxInputCopyLocal(dmxInput, &DMXConsoleKbd);
   1010             if (doWindows) {
   1011                 dmxInput->windows          = TRUE;
   1012                 dmxInput->updateWindowInfo = dmxUpdateWindowInformation;
   1013             }
   1014             dmxLogInput(dmxInput,
   1015                         "Using console input from %s (%s windows)\n",
   1016                         name, doWindows ? "with" : "without");
   1017         }
   1018     }
   1019 
   1020     dmxArgFree(a);
   1021 
   1022                                 /* Locate extensions we may be interested in */
   1023     dmxInputScanForExtensions(dmxInput, doXI);
   1024 
   1025     for (i = 0; i < dmxInput->numDevs; i++) {
   1026         DMXLocalInputInfoPtr dmxLocal = dmxInput->devs[i];
   1027         dmxLocal->pDevice = dmxAddDevice(dmxLocal);
   1028         if (dmxLocal->isCore) {
   1029             if (dmxLocal->type == DMX_LOCAL_MOUSE)
   1030                 pPointer  = dmxLocal->pDevice;
   1031             if (dmxLocal->type == DMX_LOCAL_KEYBOARD)
   1032                 pKeyboard = dmxLocal->pDevice;
   1033         }
   1034     }
   1035 
   1036     dmxInput->processInputEvents    = dmxProcessInputEvents;
   1037     dmxInput->detached              = False;
   1038 
   1039     RegisterBlockAndWakeupHandlers(dmxBlockHandler, dmxWakeupHandler,
   1040                                    (void *)(uintptr_t)dmxInput->inputIdx);
   1041 }
   1042 
   1043 static void dmxInputFreeLocal(DMXLocalInputInfoRec *local)
   1044 {
   1045     if (!local) return;
   1046     if (local->isCore && local->type == DMX_LOCAL_MOUSE)
   1047         dmxLocalCorePointer  = NULL;
   1048     if (local->isCore && local->type == DMX_LOCAL_KEYBOARD)
   1049         dmxLocalCoreKeyboard = NULL;
   1050     if (local->destroy_private) local->destroy_private(local->private);
   1051     free(local->history);
   1052     free(local->valuators);
   1053     free(local->deviceName);
   1054     local->private    = NULL;
   1055     local->history    = NULL;
   1056     local->deviceName = NULL;
   1057     free(local);
   1058 }
   1059 
   1060 /** Free all of the memory associated with \a dmxInput */
   1061 void dmxInputFree(DMXInputInfo *dmxInput)
   1062 {
   1063     int i;
   1064 
   1065     if (!dmxInput) return;
   1066 
   1067     free(dmxInput->keycodes);
   1068     free(dmxInput->symbols);
   1069     free(dmxInput->geometry);
   1070 
   1071     for (i = 0; i < dmxInput->numDevs; i++) {
   1072         dmxInputFreeLocal(dmxInput->devs[i]);
   1073         dmxInput->devs[i] = NULL;
   1074     }
   1075     free(dmxInput->devs);
   1076     dmxInput->devs    = NULL;
   1077     dmxInput->numDevs = 0;
   1078     if (dmxInput->freename) free(dmxInput->name);
   1079     dmxInput->name    = NULL;
   1080 }
   1081 
   1082 /** Log information about all of the known devices using #dmxLog(). */
   1083 void dmxInputLogDevices(void)
   1084 {
   1085     int i, j;
   1086 
   1087     dmxLog(dmxInfo, "%d devices:\n", dmxGetInputCount());
   1088     dmxLog(dmxInfo, "  Id  Name                 Classes\n");
   1089     for (j = 0; j < dmxNumInputs; j++) {
   1090         DMXInputInfo *dmxInput = &dmxInputs[j];
   1091         const char   *pt = strchr(dmxInput->name, ',');
   1092         int          len = (pt
   1093                             ? (size_t)(pt-dmxInput->name)
   1094                             : strlen(dmxInput->name));
   1095 
   1096         for (i = 0; i < dmxInput->numDevs; i++) {
   1097             DeviceIntPtr pDevice = dmxInput->devs[i]->pDevice;
   1098             if (pDevice) {
   1099                 dmxLog(dmxInfo, "  %2d%c %-20.20s",
   1100                        pDevice->id,
   1101                        dmxInput->detached ? 'D' : ' ',
   1102                        pDevice->name);
   1103                 if (pDevice->key)        dmxLogCont(dmxInfo, " key");
   1104                 if (pDevice->valuator)   dmxLogCont(dmxInfo, " val");
   1105                 if (pDevice->button)     dmxLogCont(dmxInfo, " btn");
   1106                 if (pDevice->focus)      dmxLogCont(dmxInfo, " foc");
   1107                 if (pDevice->kbdfeed)    dmxLogCont(dmxInfo, " fb/kbd");
   1108                 if (pDevice->ptrfeed)    dmxLogCont(dmxInfo, " fb/ptr");
   1109                 if (pDevice->intfeed)    dmxLogCont(dmxInfo, " fb/int");
   1110                 if (pDevice->stringfeed) dmxLogCont(dmxInfo, " fb/str");
   1111                 if (pDevice->bell)       dmxLogCont(dmxInfo, " fb/bel");
   1112                 if (pDevice->leds)       dmxLogCont(dmxInfo, " fb/led");
   1113                 if (!pDevice->key && !pDevice->valuator && !pDevice->button
   1114                     && !pDevice->focus && !pDevice->kbdfeed
   1115                     && !pDevice->ptrfeed && !pDevice->intfeed
   1116                     && !pDevice->stringfeed && !pDevice->bell
   1117                     && !pDevice->leds)   dmxLogCont(dmxInfo, " (none)");
   1118 
   1119                 dmxLogCont(dmxInfo, "\t[i%d/%*.*s",
   1120                            dmxInput->inputIdx, len, len, dmxInput->name);
   1121                 if (dmxInput->devs[i]->deviceId >= 0)
   1122                     dmxLogCont(dmxInfo, "/id%d", dmxInput->devs[i]->deviceId);
   1123                 if (dmxInput->devs[i]->deviceName)
   1124                     dmxLogCont(dmxInfo, "=%s", dmxInput->devs[i]->deviceName);
   1125                 dmxLogCont(dmxInfo, "] %s\n",
   1126                            dmxInput->devs[i]->isCore
   1127                            ? "core"
   1128                            : (dmxInput->devs[i]->sendsCore
   1129                               ? "extension (sends core events)"
   1130                               : "extension"));
   1131             }
   1132         }
   1133     }
   1134 }
   1135 
   1136 /** Detach an input */
   1137 int dmxInputDetach(DMXInputInfo *dmxInput)
   1138 {
   1139     int i;
   1140 
   1141     if (dmxInput->detached) return BadAccess;
   1142 
   1143     for (i = 0; i < dmxInput->numDevs; i++) {
   1144         DMXLocalInputInfoPtr dmxLocal = dmxInput->devs[i];
   1145         dmxLogInput(dmxInput, "Detaching device id %d: %s%s\n",
   1146                     dmxLocal->pDevice->id,
   1147                     dmxLocal->pDevice->name,
   1148                     dmxLocal->isCore
   1149                     ? " [core]"
   1150                     : (dmxLocal->sendsCore
   1151                        ? " [sends core events]"
   1152                        : ""));
   1153         DisableDevice(dmxLocal->pDevice, TRUE);
   1154     }
   1155     dmxInput->detached = True;
   1156     dmxInputLogDevices();
   1157     return 0;
   1158 }
   1159 
   1160 /** Search for input associated with \a dmxScreen, and detach. */
   1161 void dmxInputDetachAll(DMXScreenInfo *dmxScreen)
   1162 {
   1163     int i;
   1164 
   1165     for (i = 0; i < dmxNumInputs; i++) {
   1166         DMXInputInfo *dmxInput = &dmxInputs[i];
   1167         if (dmxInput->scrnIdx == dmxScreen->index) dmxInputDetach(dmxInput);
   1168     }
   1169 }
   1170 
   1171 /** Search for input associated with \a deviceId, and detach. */
   1172 int dmxInputDetachId(int id)
   1173 {
   1174     DMXInputInfo *dmxInput = dmxInputLocateId(id);
   1175 
   1176     if (!dmxInput) return BadValue;
   1177 
   1178     return dmxInputDetach(dmxInput);
   1179 }
   1180 
   1181 DMXInputInfo *dmxInputLocateId(int id)
   1182 {
   1183     int i, j;
   1184 
   1185     for (i = 0; i < dmxNumInputs; i++) {
   1186         DMXInputInfo *dmxInput = &dmxInputs[i];
   1187         for (j = 0; j < dmxInput->numDevs; j++) {
   1188             DMXLocalInputInfoPtr dmxLocal = dmxInput->devs[j];
   1189             if (dmxLocal->pDevice->id == id) return dmxInput;
   1190         }
   1191     }
   1192     return NULL;
   1193 }
   1194 
   1195 static int dmxInputAttachNew(DMXInputInfo *dmxInput, int *id)
   1196 {
   1197     dmxInputInit(dmxInput);
   1198     InitAndStartDevices();
   1199     if (id && dmxInput->devs) *id = dmxInput->devs[0]->pDevice->id;
   1200     dmxInputLogDevices();
   1201     return 0;
   1202 }
   1203 
   1204 static int dmxInputAttachOld(DMXInputInfo *dmxInput, int *id)
   1205 {
   1206     int i;
   1207 
   1208     dmxInput->detached = False;
   1209     for (i = 0; i < dmxInput->numDevs; i++) {
   1210         DMXLocalInputInfoPtr dmxLocal = dmxInput->devs[i];
   1211         if (id) *id = dmxLocal->pDevice->id;
   1212         dmxLogInput(dmxInput,
   1213                     "Attaching device id %d: %s%s\n",
   1214                     dmxLocal->pDevice->id,
   1215                     dmxLocal->pDevice->name,
   1216                     dmxLocal->isCore
   1217                     ? " [core]"
   1218                     : (dmxLocal->sendsCore
   1219                        ? " [sends core events]"
   1220                        : ""));
   1221         EnableDevice(dmxLocal->pDevice, TRUE);
   1222     }
   1223     dmxInputLogDevices();
   1224     return 0;
   1225 }
   1226 
   1227 int dmxInputAttachConsole(const char *name, int isCore, int *id)
   1228 {
   1229     DMXInputInfo  *dmxInput;
   1230     int           i;
   1231 
   1232     for (i = 0; i < dmxNumInputs; i++) {
   1233         dmxInput = &dmxInputs[i];
   1234         if (dmxInput->scrnIdx == -1
   1235             && dmxInput->detached
   1236             && !strcmp(dmxInput->name, name)) {
   1237                                 /* Found match */
   1238             dmxLogInput(dmxInput, "Reattaching detached console input\n");
   1239             return dmxInputAttachOld(dmxInput, id);
   1240         }
   1241     }
   1242 
   1243                                 /* No match found */
   1244     dmxInput = dmxConfigAddInput(xstrdup(name), isCore);
   1245     dmxInput->freename = TRUE;
   1246     dmxLogInput(dmxInput, "Attaching new console input\n");
   1247     return dmxInputAttachNew(dmxInput, id);
   1248 }
   1249 
   1250 int dmxInputAttachBackend(int physicalScreen, int isCore, int *id)
   1251 {
   1252     DMXInputInfo  *dmxInput;
   1253     DMXScreenInfo *dmxScreen;
   1254     int           i;
   1255 
   1256     if (physicalScreen < 0 || physicalScreen >= dmxNumScreens) return BadValue;
   1257     for (i = 0; i < dmxNumInputs; i++) {
   1258         dmxInput = &dmxInputs[i];
   1259         if (dmxInput->scrnIdx != -1 && dmxInput->scrnIdx == physicalScreen) {
   1260                                 /* Found match */
   1261             if (!dmxInput->detached) return BadAccess; /* Already attached */
   1262             dmxScreen = &dmxScreens[physicalScreen];
   1263             if (!dmxScreen->beDisplay) return BadAccess; /* Screen detached */
   1264             dmxLogInput(dmxInput, "Reattaching detached backend input\n");
   1265             return dmxInputAttachOld(dmxInput, id);
   1266         }
   1267     }
   1268                                 /* No match found */
   1269     dmxScreen = &dmxScreens[physicalScreen];
   1270     if (!dmxScreen->beDisplay) return BadAccess; /* Screen detached */
   1271     dmxInput = dmxConfigAddInput(dmxScreen->name, isCore);
   1272     dmxLogInput(dmxInput, "Attaching new backend input\n");
   1273     return dmxInputAttachNew(dmxInput, id);
   1274 }
   1275