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 * Provide support and helper functions for enqueing events received by
36 * the low-level input drivers. */
37
38#ifdef HAVE_DMX_CONFIG_H
39#include <dmx-config.h>
40#endif
41
42#define DMX_EVENTS_DEBUG 0
43
44#include "dmxinputinit.h"
45#include "dmxevents.h"
46#include "dmxcb.h"
47#include "dmxcommon.h"
48#include "dmxcursor.h"
49#include "dmxmotion.h"
50#include "dmxsigio.h"
51#include "dmxmap.h"
52
53#include <X11/keysym.h>
54#include "opaque.h"
55#include "inputstr.h"
56#include "inpututils.h"
57#include "mipointer.h"
58#include "mi.h"
59#include "exglobals.h"
60
61#include "xkbsrv.h"
62#include "XIstubs.h"
63
64static int  dmxGlobalX, dmxGlobalY; /* Global cursor position */
65static int  dmxGlobalInvalid;       /* Flag indicating dmxCoreMotion
66                                     * should move the mouse anyway. */
67
68#if DMX_EVENTS_DEBUG
69#define DMXDBG0(f)               dmxLog(dmxDebug,f)
70#define DMXDBG1(f,a)             dmxLog(dmxDebug,f,a)
71#define DMXDBG2(f,a,b)           dmxLog(dmxDebug,f,a,b)
72#define DMXDBG3(f,a,b,c)         dmxLog(dmxDebug,f,a,b,c)
73#define DMXDBG4(f,a,b,c,d)       dmxLog(dmxDebug,f,a,b,c,d)
74#define DMXDBG5(f,a,b,c,d,e)     dmxLog(dmxDebug,f,a,b,c,d,e)
75#define DMXDBG6(f,a,b,c,d,e,g)   dmxLog(dmxDebug,f,a,b,c,d,e,g)
76#define DMXDBG7(f,a,b,c,d,e,g,h) dmxLog(dmxDebug,f,a,b,c,d,e,g,h)
77#else
78#define DMXDBG0(f)
79#define DMXDBG1(f,a)
80#define DMXDBG2(f,a,b)
81#define DMXDBG3(f,a,b,c)
82#define DMXDBG4(f,a,b,c,d)
83#define DMXDBG5(f,a,b,c,d,e)
84#define DMXDBG6(f,a,b,c,d,e,g)
85#define DMXDBG7(f,a,b,c,d,e,g,h)
86#endif
87
88static int dmxApplyFunctions(DMXInputInfo *dmxInput, DMXFunctionType f)
89{
90    int i;
91    int rc = 0;
92
93    for (i = 0; i < dmxInput->numDevs; i+= dmxInput->devs[i]->binding)
94        if (dmxInput->devs[i]->functions)
95            rc += dmxInput->devs[i]->functions(dmxInput->devs[i]->private, f);
96    return rc;
97}
98
99static int dmxCheckFunctionKeys(DMXLocalInputInfoPtr dmxLocal,
100                                int type,
101                                KeySym keySym)
102{
103    DMXInputInfo   *dmxInput = &dmxInputs[dmxLocal->inputIdx];
104
105#if 1 /* hack to detect ctrl-alt-q, etc */
106    static int ctrl = 0, alt = 0;
107    /* keep track of ctrl/alt key status */
108    if (type == KeyPress && keySym == 0xffe3) {
109        ctrl = 1;
110    }
111    else if (type == KeyRelease && keySym == 0xffe3) {
112        ctrl = 0;
113    }
114    else if (type == KeyPress && keySym == 0xffe9) {
115        alt = 1;
116    }
117    else if (type == KeyRelease && keySym == 0xffe9) {
118        alt = 0;
119    }
120    if (!ctrl || !alt)
121        return 0;
122#else
123    unsigned short state = 0;
124
125    if (dmxLocal->sendsCore)
126        state = dmxLocalCoreKeyboard->pDevice->key->state;
127    else if (dmxLocal->pDevice->key)
128        state = dmxLocal->pDevice->key->state;
129
130    DMXDBG3("dmxCheckFunctionKeys: keySym=0x%04x %s state=0x%04x\n",
131            keySym, type == KeyPress ? "press" : "release", state);
132
133    if ((state & (ControlMask|Mod1Mask)) != (ControlMask|Mod1Mask))
134        return 0;
135#endif
136
137    switch (keySym) {
138    case XK_g:
139        if (type == KeyPress)
140            dmxApplyFunctions(dmxInput, DMX_FUNCTION_GRAB);
141        return 1;
142    case XK_f:
143        if (type == KeyPress)
144            dmxApplyFunctions(dmxInput, DMX_FUNCTION_FINE);
145        return 1;
146    case XK_q:
147        if (type == KeyPress && dmxLocal->sendsCore)
148            if (dmxApplyFunctions(dmxInput, DMX_FUNCTION_TERMINATE)) {
149                dmxLog(dmxInfo, "User request for termination\n");
150                dispatchException |= DE_TERMINATE;
151            }
152        return 1;
153    }
154
155    return 0;
156}
157
158
159DMXScreenInfo *dmxFindFirstScreen(int x, int y)
160{
161    int i;
162
163    for (i = 0; i < dmxNumScreens; i++) {
164        DMXScreenInfo *dmxScreen = &dmxScreens[i];
165        if (dmxOnScreen(x, y, dmxScreen))
166            return dmxScreen;
167    }
168    return NULL;
169}
170
171
172/**
173 * Enqueue a motion event.
174 */
175static void enqueueMotion(DevicePtr pDev, int x, int y)
176{
177    GETDMXLOCALFROMPDEV;
178    DeviceIntPtr p = dmxLocal->pDevice;
179    int i, nevents, valuators[3];
180    EventListPtr events;
181    int detail = 0;  /* XXX should this be mask of pressed buttons? */
182    ValuatorMask mask;
183    valuators[0] = x;
184    valuators[1] = y;
185
186    valuator_mask_set_range(&mask, 0, 2, valuators);
187    GetEventList(&events);
188    nevents = GetPointerEvents(events, p, MotionNotify, detail,
189                               POINTER_ABSOLUTE | POINTER_SCREEN, &mask);
190    for (i = 0; i < nevents; i++)
191       mieqEnqueue(p, (InternalEvent*)(events + i)->event);
192    return;
193}
194
195
196void
197dmxCoreMotion(DevicePtr pDev, int x, int y, int delta, DMXBlockType block)
198{
199    DMXScreenInfo *dmxScreen;
200    DMXInputInfo  *dmxInput;
201    ScreenPtr     pScreen;
202    int           localX;
203    int           localY;
204    int           i;
205
206    if (!dmxGlobalInvalid && dmxGlobalX == x && dmxGlobalY == y)
207        return;
208
209    DMXDBG5("dmxCoreMotion(%d,%d,%d) dmxGlobalX=%d dmxGlobalY=%d\n",
210            x, y, delta, dmxGlobalX, dmxGlobalY);
211
212    dmxGlobalInvalid = 0;
213    dmxGlobalX       = x;
214    dmxGlobalY       = y;
215
216    if (dmxGlobalX < 0)
217        dmxGlobalX = 0;
218    if (dmxGlobalY < 0)
219        dmxGlobalY = 0;
220    if (dmxGlobalX >= dmxGlobalWidth)
221        dmxGlobalX = dmxGlobalWidth  + delta -1;
222    if (dmxGlobalY >= dmxGlobalHeight)
223        dmxGlobalY = dmxGlobalHeight + delta -1;
224
225    if ((dmxScreen = dmxFindFirstScreen(dmxGlobalX, dmxGlobalY))) {
226        localX = dmxGlobalX - dmxScreen->rootXOrigin;
227        localY = dmxGlobalY - dmxScreen->rootYOrigin;
228        if ((pScreen = miPointerGetScreen(inputInfo.pointer))
229            && pScreen->myNum == dmxScreen->index) {
230                                /* Screen is old screen */
231            if (block)
232                dmxSigioBlock();
233            if (pDev)
234               enqueueMotion(pDev, localX, localY);
235            if (block)
236                dmxSigioUnblock();
237        } else {
238                                /* Screen is new */
239            DMXDBG4("   New screen: old=%d new=%d localX=%d localY=%d\n",
240                    pScreen->myNum, dmxScreen->index, localX, localY);
241            if (block)
242                dmxSigioBlock();
243            mieqProcessInputEvents();
244            miPointerSetScreen(inputInfo.pointer, dmxScreen->index,
245                               localX, localY);
246            if (pDev)
247               enqueueMotion(pDev, localX, localY);
248            if (block)
249                dmxSigioUnblock();
250        }
251#if 00
252        miPointerGetPosition(inputInfo.pointer, &localX, &localY);
253
254        if ((pScreen = miPointerGetScreen(inputInfo.pointer))) {
255            dmxGlobalX = localX + dmxScreens[pScreen->myNum].rootXOrigin;
256            dmxGlobalY = localY + dmxScreens[pScreen->myNum].rootYOrigin;
257            ErrorF("Global is now %d, %d  %d, %d\n", dmxGlobalX, dmxGlobalY,
258                   localX, localY);
259            DMXDBG6("   Moved to dmxGlobalX=%d dmxGlobalY=%d"
260                    " on screen index=%d/%d localX=%d localY=%d\n",
261                    dmxGlobalX, dmxGlobalY,
262                    dmxScreen ? dmxScreen->index : -1, pScreen->myNum,
263                    localX, localY);
264        }
265#endif
266    }
267                                /* Send updates down to all core input
268                                 * drivers */
269    for (i = 0, dmxInput = &dmxInputs[0]; i < dmxNumInputs; i++, dmxInput++) {
270        int j;
271        for (j = 0; j < dmxInput->numDevs; j += dmxInput->devs[j]->binding)
272            if (!dmxInput->detached
273                && dmxInput->devs[j]->sendsCore
274                && dmxInput->devs[j]->update_position)
275                dmxInput->devs[j]->update_position(dmxInput->devs[j]->private,
276                                                   dmxGlobalX, dmxGlobalY);
277    }
278    if (!dmxScreen) ProcessInputEvents();
279}
280
281
282
283#define DMX_MAX_AXES 32         /* Max axes reported by this routine */
284static void dmxExtMotion(DMXLocalInputInfoPtr dmxLocal,
285                         int *v, int firstAxis, int axesCount,
286                         DMXMotionType type, DMXBlockType block)
287{
288    DeviceIntPtr           pDevice = dmxLocal->pDevice;
289    xEvent                 xE[2 * DMX_MAX_AXES/6];
290    deviceKeyButtonPointer *xev    = (deviceKeyButtonPointer *)xE;
291    deviceValuator         *xv     = (deviceValuator *)xev+1;
292    int                    thisX   = 0;
293    int                    thisY   = 0;
294    int                    i;
295    int                    count;
296    EventListPtr           events;
297    int                    nevents;
298    ValuatorMask           mask;
299
300    memset(xE, 0, sizeof(xE));
301
302    if (axesCount > DMX_MAX_AXES) axesCount = DMX_MAX_AXES;
303
304    if ((valuator_get_mode(pDevice,0) == Relative) && axesCount == 2) {
305                                /* The dmx console is a relative mode
306                                 * device that sometimes reports
307                                 * absolute motion.  It only has two
308                                 * axes. */
309        if (type == DMX_RELATIVE) {
310            thisX = -v[0];
311            thisY = -v[1];
312            dmxLocal->lastX += thisX;
313            dmxLocal->lastY += thisY;
314            if (dmxLocal->update_position)
315                dmxLocal->update_position(dmxLocal->private,
316                                          dmxLocal->lastX, dmxLocal->lastY);
317        } else {                    /* Convert to relative */
318            if (dmxLocal->lastX || dmxLocal->lastY) {
319                thisX = v[0] - dmxLocal->lastX;
320                thisY = v[1] - dmxLocal->lastY;
321            }
322            dmxLocal->lastX = v[0];
323            dmxLocal->lastY = v[1];
324        }
325        v[0] = thisX;
326        v[1] = thisY;
327    }
328
329    if (axesCount <= 6) {
330                                /* Optimize for the common case when
331                                 * only 1 or 2 axes change. */
332            xev->time          = GetTimeInMillis();
333            xev->type          = DeviceMotionNotify;
334            xev->detail        = 0;
335            xev->deviceid      = pDevice->id | MORE_EVENTS;
336
337            xv->type           = DeviceValuator;
338            xv->deviceid       = pDevice->id;
339            xv->num_valuators  = axesCount;
340            xv->first_valuator = firstAxis;
341            switch (xv->num_valuators) {
342            case 6: xv->valuator5 = v[5];
343            case 5: xv->valuator4 = v[4];
344            case 4: xv->valuator3 = v[3];
345            case 3: xv->valuator2 = v[2];
346            case 2: xv->valuator1 = v[1];
347            case 1: xv->valuator0 = v[0];
348            }
349            count              = 2;
350    } else {
351        for (i = 0, count = 0; i < axesCount; i += 6) {
352            xev->time          = GetTimeInMillis();
353            xev->type          = DeviceMotionNotify;
354            xev->detail        = 0;
355            xev->deviceid      = pDevice->id | MORE_EVENTS;
356            xev               += 2;
357
358            xv->type           = DeviceValuator;
359            xv->deviceid       = pDevice->id;
360            xv->num_valuators  = (i+6 >= axesCount ? axesCount - i : 6);
361            xv->first_valuator = firstAxis + i;
362            switch (xv->num_valuators) {
363            case 6: xv->valuator5 = v[i+5];
364            case 5: xv->valuator4 = v[i+4];
365            case 4: xv->valuator3 = v[i+3];
366            case 3: xv->valuator2 = v[i+2];
367            case 2: xv->valuator1 = v[i+1];
368            case 1: xv->valuator0 = v[i+0];
369            }
370            xv                += 2;
371            count             += 2;
372        }
373    }
374
375    if (block)
376        dmxSigioBlock();
377    valuator_mask_set_range(&mask, firstAxis, axesCount, v);
378    GetEventList(&events);
379    nevents = GetPointerEvents(events, pDevice, MotionNotify, 0,
380                               POINTER_ABSOLUTE, &mask);
381    for (i = 0; i < nevents; i++)
382        mieqEnqueue(pDevice, (InternalEvent*)(events + i)->event);
383
384    if (block)
385        dmxSigioUnblock();
386}
387
388static int dmxTranslateAndEnqueueExtEvent(DMXLocalInputInfoPtr dmxLocal,
389                                          XEvent *e, DMXBlockType block)
390{
391    int                    type;
392    int                    event   = -1;
393    XDeviceKeyEvent        *ke     = (XDeviceKeyEvent *)e;
394    XDeviceMotionEvent     *me     = (XDeviceMotionEvent *)e;
395    DeviceIntPtr           pDevice = dmxLocal->pDevice;
396    int                    valuators[MAX_VALUATORS];
397    EventListPtr           events;
398    int                    nevents, i;
399    ValuatorMask           mask;
400
401    if (!e)
402        return -1;          /* No extended event passed, cannot handle */
403
404    if ((XID)dmxLocal->deviceId != ke->deviceid) {
405                                /* Search for the correct dmxLocal,
406                                 * since backend and console events are
407                                 * picked up for the first device on
408                                 * that X server. */
409        int i;
410        DMXInputInfo *dmxInput = &dmxInputs[dmxLocal->inputIdx];
411        for (i = 0; i < dmxInput->numDevs; i++) {
412            dmxLocal = dmxInput->devs[i];
413            if ((XID)dmxLocal->deviceId == ke->deviceid)
414                break;
415        }
416    }
417
418    if ((XID)dmxLocal->deviceId != ke->deviceid
419        || (type = dmxMapLookup(dmxLocal, e->type)) < 0)
420        return -1;    /* No mapping, so this event is unhandled */
421
422    switch (type) {
423    case XI_DeviceValuator:          event = DeviceValuator;          break;
424    case XI_DeviceKeyPress:          event = KeyPress;                break;
425    case XI_DeviceKeyRelease:        event = KeyRelease;              break;
426    case XI_DeviceButtonPress:       event = ButtonPress;             break;
427    case XI_DeviceButtonRelease:     event = ButtonRelease;           break;
428    case XI_DeviceMotionNotify:      event = MotionNotify;            break;
429    case XI_DeviceFocusIn:           event = DeviceFocusIn;           break;
430    case XI_DeviceFocusOut:          event = DeviceFocusOut;          break;
431    case XI_ProximityIn:             event = ProximityIn;             break;
432    case XI_ProximityOut:            event = ProximityOut;            break;
433    case XI_DeviceStateNotify:       event = DeviceStateNotify;       break;
434    case XI_DeviceMappingNotify:     event = DeviceMappingNotify;     break;
435    case XI_ChangeDeviceNotify:      event = ChangeDeviceNotify;      break;
436    case XI_DeviceKeystateNotify:    event = DeviceStateNotify;       break;
437    case XI_DeviceButtonstateNotify: event = DeviceStateNotify;       break;
438    }
439
440#define EXTRACT_VALUATORS(ke, valuators) \
441        valuators[0] = ke->axis_data[0]; \
442        valuators[1] = ke->axis_data[1]; \
443        valuators[2] = ke->axis_data[2]; \
444        valuators[3] = ke->axis_data[3]; \
445        valuators[4] = ke->axis_data[4]; \
446        valuators[5] = ke->axis_data[5]; \
447
448    switch (type) {
449    case XI_DeviceKeyPress:
450    case XI_DeviceKeyRelease:
451        EXTRACT_VALUATORS(ke, valuators);
452        valuator_mask_set_range(&mask, ke->first_axis, ke->axes_count, valuators);
453        if (block)
454            dmxSigioBlock();
455        GetEventList(&events);
456        nevents = GetKeyboardValuatorEvents(events, pDevice, event,
457                                            ke->keycode, &mask);
458        for (i = 0; i < nevents; i++)
459            mieqEnqueue(pDevice, (InternalEvent*)(events + i)->event);
460
461        if (block)
462            dmxSigioUnblock();
463        break;
464    case XI_DeviceButtonPress:
465    case XI_DeviceButtonRelease:
466        EXTRACT_VALUATORS(ke, valuators);
467        valuator_mask_set_range(&mask, ke->first_axis, ke->axes_count, valuators);
468        if (block)
469            dmxSigioBlock();
470        GetEventList(&events);
471        nevents = GetPointerEvents(events, pDevice, event, ke->keycode,
472                                   POINTER_ABSOLUTE, &mask);
473        for (i = 0; i < nevents; i++)
474            mieqEnqueue(pDevice, (InternalEvent*)(events + i)->event);
475
476        if (block)
477            dmxSigioUnblock();
478        break;
479    case XI_ProximityIn:
480    case XI_ProximityOut:
481        EXTRACT_VALUATORS(ke, valuators);
482        valuator_mask_set_range(&mask, ke->first_axis, ke->axes_count, valuators);
483        if (block)
484            dmxSigioBlock();
485        GetEventList(&events);
486        nevents = GetProximityEvents(events, pDevice, event, &mask);
487        for (i = 0; i < nevents; i++)
488            mieqEnqueue(pDevice, (InternalEvent*)(events + i)->event);
489
490        if (block)
491            dmxSigioUnblock();
492        break;
493
494        break;
495
496    case XI_DeviceMotionNotify:
497        dmxExtMotion(dmxLocal, me->axis_data, me->first_axis, me->axes_count,
498                     DMX_ABSOLUTE, block);
499        break;
500    case XI_DeviceFocusIn:
501    case XI_DeviceFocusOut:
502    case XI_DeviceStateNotify:
503    case XI_DeviceMappingNotify:
504    case XI_ChangeDeviceNotify:
505    case XI_DeviceKeystateNotify:
506    case XI_DeviceButtonstateNotify:
507                                /* These are ignored, since DMX will
508                                 * generate its own events of these
509                                 * types, as necessary.
510
511                                 * Perhaps ChangeDeviceNotify should
512                                 * generate an error, because it is
513                                 * unexpected? */
514        break;
515    case XI_DeviceValuator:
516    default:
517        dmxLog(dmxWarning,
518               "XInput extension event (remote=%d -> zero-based=%d)"
519               " not supported yet\n", e->type, type);
520        return -1;
521    }
522    return 0;
523}
524
525static int dmxGetButtonMapping(DMXLocalInputInfoPtr dmxLocal, int button)
526{
527    ButtonClassPtr b = dmxLocal->pDevice->button;
528
529    if (button > b->numButtons) { /* This shouldn't happen. */
530        dmxLog(dmxWarning, "Button %d pressed, but only %d buttons?!?\n",
531               button, b->numButtons);
532        return button;
533    }
534    return b->map[button];
535}
536
537/** Return DMX's notion of the pointer position in the global coordinate
538 * space. */
539void dmxGetGlobalPosition(int *x, int *y)
540{
541    *x = dmxGlobalX;
542    *y = dmxGlobalY;
543}
544
545/** Invalidate the global position for #dmxCoreMotion. */
546void dmxInvalidateGlobalPosition(void)
547{
548    dmxGlobalInvalid = 1;
549}
550
551/** Enqueue a motion event for \a pDev.  The \a v vector has length \a
552 * axesCount, and contains values for each of the axes, starting at \a
553 * firstAxes.
554 *
555 * The \a type of the motion may be \a DMX_RELATIVE, \a DMX_ABSOLUTE, or
556 * \a DMX_ABSOLUTE_CONFINED (in the latter case, the pointer will not be
557 * allowed to move outside the global boundaires).
558 *
559 * If \a block is set to \a DMX_BLOCK, then the SIGIO handler will be
560 * blocked around calls to \a enqueueMotion(). */
561void dmxMotion(DevicePtr pDev, int *v, int firstAxes, int axesCount,
562               DMXMotionType type, DMXBlockType block)
563{
564    GETDMXLOCALFROMPDEV;
565
566    if (!dmxLocal->sendsCore) {
567        dmxExtMotion(dmxLocal, v, firstAxes, axesCount, type, block);
568        return;
569    }
570    if (axesCount == 2) {
571        switch (type) {
572        case DMX_RELATIVE:
573            dmxCoreMotion(pDev, dmxGlobalX - v[0], dmxGlobalY - v[1], 0, block);
574            break;
575        case DMX_ABSOLUTE:
576            dmxCoreMotion(pDev, v[0], v[1], 0, block);
577            break;
578        case DMX_ABSOLUTE_CONFINED:
579            dmxCoreMotion(pDev, v[0], v[1], -1, block);
580            break;
581        }
582    }
583}
584
585static KeySym dmxKeyCodeToKeySym(DMXLocalInputInfoPtr dmxLocal,
586                                 KeyCode keyCode)
587{
588    KeySym keysym = NoSymbol;
589    int effectiveGroup;
590    XkbSrvInfoPtr xkbi;
591
592    if (!dmxLocal || !dmxLocal->pDevice || !dmxLocal->pDevice->key)
593        goto out;
594
595    xkbi = dmxLocal->pDevice->key->xkbInfo;
596    effectiveGroup = XkbGetEffectiveGroup(xkbi, &xkbi->state, keyCode);
597
598    if (effectiveGroup == -1)
599        goto out;
600
601    keysym = XkbKeySym(xkbi->desc, keyCode, effectiveGroup);
602    DMXDBG2("dmxKeyCodeToKeySym: Translated keyCode=%d to keySym=0x%04x\n",
603            keyCode, keysym);
604
605out:
606    return keysym;
607}
608
609static KeyCode dmxKeySymToKeyCode(DMXLocalInputInfoPtr dmxLocal, KeySym keySym,
610                                  int tryFirst)
611{
612    /* FIXME: this is quite ineffective, converting to a core map first and
613     * then extracting the info from there. It'd be better to run the actual
614     * xkb map */
615    XkbSrvInfoPtr xkbi = dmxLocal->pDevice->key->xkbInfo;
616    KeySymsPtr pKeySyms = XkbGetCoreMap(dmxLocal->pDevice);
617    int        i;
618
619                                /* Optimize for similar maps */
620    if (XkbKeycodeInRange(xkbi->desc, tryFirst)
621        && pKeySyms->map[(tryFirst - xkbi->desc->min_key_code)
622                         * pKeySyms->mapWidth] == keySym)
623        return tryFirst;
624
625    for (i = pKeySyms->minKeyCode; i <= pKeySyms->maxKeyCode; i++) {
626        if (pKeySyms->map[(i - pKeySyms->minKeyCode)
627                          * pKeySyms->mapWidth] == keySym) {
628            DMXDBG3("dmxKeySymToKeyCode: Translated keySym=0x%04x to"
629                    " keyCode=%d (reverses to core keySym=0x%04x)\n",
630                    keySym, i, dmxKeyCodeToKeySym(dmxLocalCoreKeyboard,i));
631            return i;
632        }
633    }
634    return 0;
635}
636
637static int dmxFixup(DevicePtr pDev, int detail, KeySym keySym)
638{
639    GETDMXLOCALFROMPDEV;
640    int keyCode;
641
642    if (!dmxLocal->pDevice->key) {
643        dmxLog(dmxWarning, "dmxFixup: not a keyboard device (%s)\n",
644               dmxLocal->pDevice->name);
645        return NoSymbol;
646    }
647    if (!keySym)
648        keySym = dmxKeyCodeToKeySym(dmxLocal, detail);
649    if (keySym == NoSymbol)
650        return detail;
651    keyCode = dmxKeySymToKeyCode(dmxLocalCoreKeyboard, keySym, detail);
652
653    return keyCode ? keyCode : detail;
654}
655
656/** Enqueue an event from the \a pDev device with the
657 * specified \a type and \a detail.  If the event is a KeyPress or
658 * KeyRelease event, then the \a keySym is also specified.
659 *
660 * FIXME: make the code do what the comment says, or remove this comment.
661 * If \a block is set to \a DMX_BLOCK, then the SIGIO handler will be
662 * blocked around calls to dmxeqEnqueue(). */
663
664void dmxEnqueue(DevicePtr pDev, int type, int detail, KeySym keySym,
665                XEvent *e, DMXBlockType block)
666{
667    GETDMXINPUTFROMPDEV;
668    xEvent xE;
669    DeviceIntPtr p = dmxLocal->pDevice;
670    int i, nevents, valuators[3];
671    EventListPtr events;
672    ValuatorMask mask;
673
674    DMXDBG2("dmxEnqueue: Enqueuing type=%d detail=0x%0x\n", type, detail);
675
676    switch (type) {
677    case KeyPress:
678    case KeyRelease:
679        if (!keySym)
680            keySym = dmxKeyCodeToKeySym(dmxLocal, detail);
681        if (dmxCheckFunctionKeys(dmxLocal, type, keySym))
682            return;
683        if (dmxLocal->sendsCore && dmxLocal != dmxLocalCoreKeyboard)
684            xE.u.u.detail = dmxFixup(pDev, detail, keySym);
685
686        GetEventList(&events);
687        /*ErrorF("KEY %d  sym %d\n", detail, (int) keySym);*/
688        nevents = GetKeyboardEvents(events, p, type, detail);
689        for (i = 0; i < nevents; i++)
690            mieqEnqueue(p, (InternalEvent*)(events + i)->event);
691        return;
692
693    case ButtonPress:
694    case ButtonRelease:
695        detail = dmxGetButtonMapping(dmxLocal, detail);
696        valuator_mask_zero(&mask);
697        GetEventList(&events);
698        nevents = GetPointerEvents(events, p, type, detail,
699                                   POINTER_ABSOLUTE | POINTER_SCREEN, &mask);
700        for (i = 0; i < nevents; i++)
701            mieqEnqueue(p, (InternalEvent*)(events + i)->event);
702        return;
703
704    case MotionNotify:
705        GetEventList(&events);
706        valuators[0] = e->xmotion.x;
707        valuators[1] = e->xmotion.y;
708        valuators[2] = e->xmotion.state; /* FIXME: WTF?? */
709        valuator_mask_set_range(&mask, 0, 3, valuators);
710        nevents = GetPointerEvents(events, p, type, detail,
711                                   POINTER_ABSOLUTE | POINTER_SCREEN, &mask);
712        for (i = 0; i < nevents; i++)
713            mieqEnqueue(p, (InternalEvent*)(events + i)->event);
714        return;
715
716    case EnterNotify:
717    case LeaveNotify:
718    case KeymapNotify:
719    case MappingNotify:         /* This is sent because we change the
720                                 * modifier map on the backend/console
721                                 * input device so that we have complete
722                                 * control of the input device LEDs. */
723        return;
724    default:
725        if (type == ProximityIn || type == ProximityOut) {
726            if (dmxLocal->sendsCore)
727                return; /* Not a core event */
728            break;
729        }
730        if (type >= LASTEvent) {
731            if (dmxTranslateAndEnqueueExtEvent(dmxLocal, e, block))
732                dmxLogInput(dmxInput, "Unhandled extension event: %d\n", type);
733        } else {
734            dmxLogInput(dmxInput, "Unhandled event: %d (%s)\n",
735                        type, dmxEventName(type));
736        }
737        return;
738    }
739
740}
741
742/** A pointer to this routine is passed to low-level input drivers so
743 * that all special keychecking is unified to this file.  This function
744 * returns 0 if no special keys have been pressed.  If the user has
745 * requested termination of the DMX server, -1 is returned.  If the user
746 * has requested a switch to a VT, then the (1-based) number of that VT
747 * is returned. */
748int dmxCheckSpecialKeys(DevicePtr pDev, KeySym keySym)
749{
750    GETDMXINPUTFROMPDEV;
751    int            vt    = 0;
752    unsigned short state = 0;
753
754    if (dmxLocal->sendsCore)
755        state = XkbStateFieldFromRec(&dmxLocalCoreKeyboard->pDevice->key->xkbInfo->state);
756    else if (dmxLocal->pDevice->key)
757        state = XkbStateFieldFromRec(&dmxLocal->pDevice->key->xkbInfo->state);
758
759    if (!dmxLocal->sendsCore) return 0; /* Only for core devices */
760
761    DMXDBG2("dmxCheckSpecialKeys: keySym=0x%04x state=0x%04x\n", keySym,state);
762
763    if ((state & (ControlMask|Mod1Mask)) != (ControlMask|Mod1Mask)) return 0;
764
765    switch (keySym) {
766    case XK_F1:
767    case XK_F2:
768    case XK_F3:
769    case XK_F4:
770    case XK_F5:
771    case XK_F6:
772    case XK_F7:
773    case XK_F8:
774    case XK_F9:
775    case XK_F10:
776        vt = keySym - XK_F1 + 1;
777        break;
778
779    case XK_F11:
780    case XK_F12:
781        vt = keySym - XK_F11 + 11;
782        break;
783
784    case XK_q:                  /* To avoid confusion  */
785    case XK_BackSpace:
786    case XK_Delete:
787    case XK_KP_Delete:
788        dmxLog(dmxInfo, "User request for termination\n");
789        dispatchException |= DE_TERMINATE;
790        return -1;              /* Terminate */
791    }
792
793    if (vt) {
794        dmxLog(dmxInfo, "Request to switch to VT %d\n", vt);
795        dmxInput->vt_switch_pending = vt;
796        return vt;
797    }
798
799    return 0;                   /* Do nothing */
800}
801