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@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
81DMXLocalInputInfoPtr dmxLocalCorePointer, dmxLocalCoreKeyboard;
82
83static DMXLocalInputInfoRec DMXDummyMou = {
84    "dummy-mou", DMX_LOCAL_MOUSE, DMX_LOCAL_TYPE_LOCAL, 1,
85    NULL, NULL, NULL, NULL, NULL, dmxDummyMouGetInfo
86};
87
88static DMXLocalInputInfoRec DMXDummyKbd = {
89    "dummy-kbd", DMX_LOCAL_KEYBOARD, DMX_LOCAL_TYPE_LOCAL, 1,
90    NULL, NULL, NULL, NULL, NULL, dmxDummyKbdGetInfo
91};
92
93static 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
103static 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
114static 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
124static 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
135static 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*/
208void
209DDXRingBell(int volume, int pitch, int duration)
210{
211   /* NO-OP */
212}
213
214/* taken from kdrive/src/kinput.c: */
215static void
216dmxKbdCtrl (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: */
236static void
237dmxBell(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
257static 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. */
268void 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
286static 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. */
306void 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
324static 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. */
335void 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
354static 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
364static 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
422static 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
537static 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
560static 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
593static 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
607static 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
619static 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
635static 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
662static 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
685static 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
753static 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. */
764DMXLocalInputInfoPtr 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
786static 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
818int dmxInputExtensionErrorHandler(Display *dsp, _Xconst char *name, _Xconst char *reason)
819{
820    return 0;
821}
822
823static 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. */
901void 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. */
914void 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. */
926void 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
1043static 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 */
1061void 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(). */
1083void 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 */
1137int 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. */
1161void 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. */
1172int dmxInputDetachId(int id)
1173{
1174    DMXInputInfo *dmxInput = dmxInputLocateId(id);
1175
1176    if (!dmxInput) return BadValue;
1177
1178    return dmxInputDetach(dmxInput);
1179}
1180
1181DMXInputInfo *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
1195static 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
1204static 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
1227int 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
1250int 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