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  * 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 
     64 static int  dmxGlobalX, dmxGlobalY; /* Global cursor position */
     65 static 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 
     88 static 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 
     99 static 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 
    159 DMXScreenInfo *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  */
    175 static 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 
    196 void
    197 dmxCoreMotion(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 */
    284 static 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 
    388 static 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 
    525 static 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. */
    539 void dmxGetGlobalPosition(int *x, int *y)
    540 {
    541     *x = dmxGlobalX;
    542     *y = dmxGlobalY;
    543 }
    544 
    545 /** Invalidate the global position for #dmxCoreMotion. */
    546 void 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(). */
    561 void 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 
    585 static 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 
    605 out:
    606     return keysym;
    607 }
    608 
    609 static 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 
    637 static 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 
    664 void 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. */
    748 int 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