aboutsummaryrefslogtreecommitdiff
path: root/xorg-server/hw/dmx/input/dmxevents.c
diff options
context:
space:
mode:
Diffstat (limited to 'xorg-server/hw/dmx/input/dmxevents.c')
-rw-r--r--xorg-server/hw/dmx/input/dmxevents.c834
1 files changed, 834 insertions, 0 deletions
diff --git a/xorg-server/hw/dmx/input/dmxevents.c b/xorg-server/hw/dmx/input/dmxevents.c
new file mode 100644
index 000000000..26dc067dc
--- /dev/null
+++ b/xorg-server/hw/dmx/input/dmxevents.c
@@ -0,0 +1,834 @@
+/*
+ * Copyright 2002-2003 Red Hat Inc., Durham, North Carolina.
+ *
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation on the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT. IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/*
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@redhat.com>
+ *
+ */
+
+/** \file
+ * Provide support and helper functions for enqueing events received by
+ * the low-level input drivers. */
+
+#ifdef HAVE_DMX_CONFIG_H
+#include <dmx-config.h>
+#endif
+
+#define DMX_EVENTS_DEBUG 0
+
+#include "dmxinputinit.h"
+#include "dmxevents.h"
+#include "dmxcb.h"
+#include "dmxcommon.h"
+#include "dmxcursor.h"
+#include "dmxmotion.h"
+#include "dmxeq.h"
+#include "dmxsigio.h"
+#include "dmxmap.h"
+
+#include <X11/keysym.h>
+#include "opaque.h"
+#include "inputstr.h"
+#include "mipointer.h"
+#include "mi.h"
+
+#ifdef XINPUT
+#include "XIstubs.h"
+#endif
+
+static int dmxGlobalX, dmxGlobalY; /* Global cursor position */
+static int dmxGlobalInvalid; /* Flag indicating dmxCoreMotion
+ * should move the mouse anyway. */
+
+#if DMX_EVENTS_DEBUG
+#define DMXDBG0(f) dmxLog(dmxDebug,f)
+#define DMXDBG1(f,a) dmxLog(dmxDebug,f,a)
+#define DMXDBG2(f,a,b) dmxLog(dmxDebug,f,a,b)
+#define DMXDBG3(f,a,b,c) dmxLog(dmxDebug,f,a,b,c)
+#define DMXDBG4(f,a,b,c,d) dmxLog(dmxDebug,f,a,b,c,d)
+#define DMXDBG5(f,a,b,c,d,e) dmxLog(dmxDebug,f,a,b,c,d,e)
+#define DMXDBG6(f,a,b,c,d,e,g) dmxLog(dmxDebug,f,a,b,c,d,e,g)
+#define DMXDBG7(f,a,b,c,d,e,g,h) dmxLog(dmxDebug,f,a,b,c,d,e,g,h)
+#else
+#define DMXDBG0(f)
+#define DMXDBG1(f,a)
+#define DMXDBG2(f,a,b)
+#define DMXDBG3(f,a,b,c)
+#define DMXDBG4(f,a,b,c,d)
+#define DMXDBG5(f,a,b,c,d,e)
+#define DMXDBG6(f,a,b,c,d,e,g)
+#define DMXDBG7(f,a,b,c,d,e,g,h)
+#endif
+
+static int dmxApplyFunctions(DMXInputInfo *dmxInput, DMXFunctionType f)
+{
+ int i;
+ int rc = 0;
+
+ for (i = 0; i < dmxInput->numDevs; i+= dmxInput->devs[i]->binding)
+ if (dmxInput->devs[i]->functions)
+ rc += dmxInput->devs[i]->functions(dmxInput->devs[i]->private, f);
+ return rc;
+}
+
+static int dmxCheckFunctionKeys(DMXLocalInputInfoPtr dmxLocal,
+ int type,
+ KeySym keySym)
+{
+ DMXInputInfo *dmxInput = &dmxInputs[dmxLocal->inputIdx];
+ unsigned short state = 0;
+
+#if 1 /* hack to detect ctrl-alt-q, etc */
+ static int ctrl = 0, alt = 0;
+ /* keep track of ctrl/alt key status */
+ if (type == KeyPress && keySym == 0xffe3) {
+ ctrl = 1;
+ }
+ else if (type == KeyRelease && keySym == 0xffe3) {
+ ctrl = 0;
+ }
+ else if (type == KeyPress && keySym == 0xffe9) {
+ alt = 1;
+ }
+ else if (type == KeyRelease && keySym == 0xffe9) {
+ alt = 0;
+ }
+ if (!ctrl || !alt)
+ return 0;
+#else
+ if (dmxLocal->sendsCore)
+ state = dmxLocalCoreKeyboard->pDevice->key->state;
+ else if (dmxLocal->pDevice->key)
+ state = dmxLocal->pDevice->key->state;
+
+ DMXDBG3("dmxCheckFunctionKeys: keySym=0x%04x %s state=0x%04x\n",
+ keySym, type == KeyPress ? "press" : "release", state);
+
+ if ((state & (ControlMask|Mod1Mask)) != (ControlMask|Mod1Mask))
+ return 0;
+#endif
+
+ switch (keySym) {
+ case XK_g:
+ if (type == KeyPress)
+ dmxApplyFunctions(dmxInput, DMX_FUNCTION_GRAB);
+ return 1;
+ case XK_f:
+ if (type == KeyPress)
+ dmxApplyFunctions(dmxInput, DMX_FUNCTION_FINE);
+ return 1;
+ case XK_q:
+ if (type == KeyPress && dmxLocal->sendsCore)
+ if (dmxApplyFunctions(dmxInput, DMX_FUNCTION_TERMINATE)) {
+ dmxLog(dmxInfo, "User request for termination\n");
+ dispatchException |= DE_TERMINATE;
+ }
+ return 1;
+ }
+
+ return 0;
+}
+
+#ifdef XINPUT
+static void dmxEnqueueExtEvent(DMXLocalInputInfoPtr dmxLocal, xEvent *e,
+ DMXBlockType block)
+{
+ xEvent xE[2];
+ deviceKeyButtonPointer *xev = (deviceKeyButtonPointer *)xE;
+ deviceValuator *xv = (deviceValuator *)xev+1;
+ DeviceIntPtr pDevice = dmxLocal->pDevice;
+ DMXInputInfo *dmxInput = &dmxInputs[dmxLocal->inputIdx];
+ int type = e->u.u.type;
+
+ switch (e->u.u.type) {
+ case KeyPress:
+ type = DeviceKeyPress;
+ break;
+ case KeyRelease:
+ type = DeviceKeyRelease;
+ break;
+ case ButtonPress:
+ type = DeviceButtonPress;
+ break;
+ case ButtonRelease:
+ type = DeviceButtonRelease;
+ break;
+ case MotionNotify:
+ dmxLog(dmxError,
+ "dmxEnqueueExtEvent: MotionNotify not allowed here\n");
+ return;
+ default:
+ if (e->u.u.type == ProximityIn || e->u.u.type == ProximityOut)
+ break;
+ dmxLogInput(dmxInput,
+ "dmxEnqueueExtEvent: Unhandled %s event (%d)\n",
+ e->u.u.type >= LASTEvent ? "extension" : "non-extension",
+ e->u.u.type);
+ return;
+ }
+
+ xev->type = type;
+ xev->detail = e->u.u.detail;
+ xev->deviceid = pDevice->id | MORE_EVENTS;
+ xev->time = e->u.keyButtonPointer.time;
+
+ xv->type = DeviceValuator;
+ xv->deviceid = pDevice->id;
+ xv->num_valuators = 0;
+ xv->first_valuator = 0;
+
+ if (block)
+ dmxSigioBlock();
+ dmxeqEnqueue(xE);
+ if (block)
+ dmxSigioUnblock();
+}
+#endif
+
+DMXScreenInfo *dmxFindFirstScreen(int x, int y)
+{
+ int i;
+
+ for (i = 0; i < dmxNumScreens; i++) {
+ DMXScreenInfo *dmxScreen = &dmxScreens[i];
+ if (dmxOnScreen(x, y, dmxScreen))
+ return dmxScreen;
+ }
+ return NULL;
+}
+
+
+/**
+ * Enqueue a motion event.
+ */
+static void enqueueMotion(DevicePtr pDev, int x, int y)
+{
+ GETDMXLOCALFROMPDEV;
+ DeviceIntPtr p = dmxLocal->pDevice;
+ int i, nevents, valuators[3];
+ xEvent *events = Xcalloc(sizeof(xEvent), GetMaximumEventsNum());
+ int detail = 0; /* XXX should this be mask of pressed buttons? */
+ valuators[0] = x;
+ valuators[1] = y;
+ nevents = GetPointerEvents(events, p, MotionNotify, detail,
+ POINTER_ABSOLUTE, 0, 2, valuators);
+ for (i = 0; i < nevents; i++)
+ mieqEnqueue(p, events + i);
+ xfree(events);
+ return;
+}
+
+
+void
+dmxCoreMotion(DevicePtr pDev, int x, int y, int delta, DMXBlockType block)
+{
+ DMXScreenInfo *dmxScreen;
+ DMXInputInfo *dmxInput;
+ ScreenPtr pScreen;
+ int localX;
+ int localY;
+ int i;
+
+ if (!dmxGlobalInvalid && dmxGlobalX == x && dmxGlobalY == y)
+ return;
+
+ DMXDBG5("dmxCoreMotion(%d,%d,%d) dmxGlobalX=%d dmxGlobalY=%d\n",
+ x, y, delta, dmxGlobalX, dmxGlobalY);
+
+ dmxGlobalInvalid = 0;
+ dmxGlobalX = x;
+ dmxGlobalY = y;
+
+ if (dmxGlobalX < 0)
+ dmxGlobalX = 0;
+ if (dmxGlobalY < 0)
+ dmxGlobalY = 0;
+ if (dmxGlobalX >= dmxGlobalWidth)
+ dmxGlobalX = dmxGlobalWidth + delta -1;
+ if (dmxGlobalY >= dmxGlobalHeight)
+ dmxGlobalY = dmxGlobalHeight + delta -1;
+
+ if ((dmxScreen = dmxFindFirstScreen(dmxGlobalX, dmxGlobalY))) {
+ localX = dmxGlobalX - dmxScreen->rootXOrigin;
+ localY = dmxGlobalY - dmxScreen->rootYOrigin;
+ if ((pScreen = miPointerGetScreen(inputInfo.pointer))
+ && pScreen->myNum == dmxScreen->index) {
+ /* Screen is old screen */
+ if (block)
+ dmxSigioBlock();
+ if (pDev)
+ enqueueMotion(pDev, localX, localY);
+ if (block)
+ dmxSigioUnblock();
+ } else {
+ /* Screen is new */
+ DMXDBG4(" New screen: old=%d new=%d localX=%d localY=%d\n",
+ pScreen->myNum, dmxScreen->index, localX, localY);
+ if (block)
+ dmxSigioBlock();
+ dmxeqProcessInputEvents();
+ miPointerSetScreen(inputInfo.pointer, dmxScreen->index,
+ localX, localY);
+ if (pDev)
+ enqueueMotion(pDev, localX, localY);
+ if (block)
+ dmxSigioUnblock();
+ }
+#if 00
+ miPointerGetPosition(inputInfo.pointer, &localX, &localY);
+
+ if ((pScreen = miPointerGetScreen(inputInfo.pointer))) {
+ dmxGlobalX = localX + dmxScreens[pScreen->myNum].rootXOrigin;
+ dmxGlobalY = localY + dmxScreens[pScreen->myNum].rootYOrigin;
+ ErrorF("Global is now %d, %d %d, %d\n", dmxGlobalX, dmxGlobalY,
+ localX, localY);
+ DMXDBG6(" Moved to dmxGlobalX=%d dmxGlobalY=%d"
+ " on screen index=%d/%d localX=%d localY=%d\n",
+ dmxGlobalX, dmxGlobalY,
+ dmxScreen ? dmxScreen->index : -1, pScreen->myNum,
+ localX, localY);
+ }
+#endif
+ }
+ /* Send updates down to all core input
+ * drivers */
+ for (i = 0, dmxInput = &dmxInputs[0]; i < dmxNumInputs; i++, dmxInput++) {
+ int j;
+ for (j = 0; j < dmxInput->numDevs; j += dmxInput->devs[j]->binding)
+ if (!dmxInput->detached
+ && dmxInput->devs[j]->sendsCore
+ && dmxInput->devs[j]->update_position)
+ dmxInput->devs[j]->update_position(dmxInput->devs[j]->private,
+ dmxGlobalX, dmxGlobalY);
+ }
+ if (!dmxScreen) ProcessInputEvents();
+}
+
+
+
+#ifdef XINPUT
+#define DMX_MAX_AXES 32 /* Max axes reported by this routine */
+static void dmxExtMotion(DMXLocalInputInfoPtr dmxLocal,
+ int *v, int firstAxis, int axesCount,
+ DMXMotionType type, DMXBlockType block)
+{
+ DeviceIntPtr pDevice = dmxLocal->pDevice;
+ xEvent xE[2 * DMX_MAX_AXES/6];
+ deviceKeyButtonPointer *xev = (deviceKeyButtonPointer *)xE;
+ deviceValuator *xv = (deviceValuator *)xev+1;
+ int thisX = 0;
+ int thisY = 0;
+ int i;
+ int count;
+
+ memset(xE, 0, sizeof(xE));
+
+ if (axesCount > DMX_MAX_AXES) axesCount = DMX_MAX_AXES;
+
+ if (!pDevice->valuator->mode && axesCount == 2) {
+ /* The dmx console is a relative mode
+ * device that sometimes reports
+ * absolute motion. It only has two
+ * axes. */
+ if (type == DMX_RELATIVE) {
+ thisX = -v[0];
+ thisY = -v[1];
+ dmxLocal->lastX += thisX;
+ dmxLocal->lastY += thisY;
+ if (dmxLocal->update_position)
+ dmxLocal->update_position(dmxLocal->private,
+ dmxLocal->lastX, dmxLocal->lastY);
+ } else { /* Convert to relative */
+ if (dmxLocal->lastX || dmxLocal->lastY) {
+ thisX = v[0] - dmxLocal->lastX;
+ thisY = v[1] - dmxLocal->lastY;
+ }
+ dmxLocal->lastX = v[0];
+ dmxLocal->lastY = v[1];
+ }
+ v[0] = thisX;
+ v[1] = thisY;
+ }
+
+ if (axesCount <= 6) {
+ /* Optimize for the common case when
+ * only 1 or 2 axes change. */
+ xev->time = GetTimeInMillis();
+ xev->type = DeviceMotionNotify;
+ xev->detail = 0;
+ xev->deviceid = pDevice->id | MORE_EVENTS;
+
+ xv->type = DeviceValuator;
+ xv->deviceid = pDevice->id;
+ xv->num_valuators = axesCount;
+ xv->first_valuator = firstAxis;
+ switch (xv->num_valuators) {
+ case 6: xv->valuator5 = v[5];
+ case 5: xv->valuator4 = v[4];
+ case 4: xv->valuator3 = v[3];
+ case 3: xv->valuator2 = v[2];
+ case 2: xv->valuator1 = v[1];
+ case 1: xv->valuator0 = v[0];
+ }
+ count = 2;
+ } else {
+ for (i = 0, count = 0; i < axesCount; i += 6) {
+ xev->time = GetTimeInMillis();
+ xev->type = DeviceMotionNotify;
+ xev->detail = 0;
+ xev->deviceid = pDevice->id | MORE_EVENTS;
+ xev += 2;
+
+ xv->type = DeviceValuator;
+ xv->deviceid = pDevice->id;
+ xv->num_valuators = (i+6 >= axesCount ? axesCount - i : 6);
+ xv->first_valuator = firstAxis + i;
+ switch (xv->num_valuators) {
+ case 6: xv->valuator5 = v[i+5];
+ case 5: xv->valuator4 = v[i+4];
+ case 4: xv->valuator3 = v[i+3];
+ case 3: xv->valuator2 = v[i+2];
+ case 2: xv->valuator1 = v[i+1];
+ case 1: xv->valuator0 = v[i+0];
+ }
+ xv += 2;
+ count += 2;
+ }
+ }
+
+ if (block)
+ dmxSigioBlock();
+ dmxPointerPutMotionEvent(pDevice, firstAxis, axesCount, v, xev->time);
+ dmxeqEnqueue(xE);
+ if (block)
+ dmxSigioUnblock();
+}
+
+static int dmxTranslateAndEnqueueExtEvent(DMXLocalInputInfoPtr dmxLocal,
+ XEvent *e, DMXBlockType block)
+{
+ xEvent xE[2];
+ deviceKeyButtonPointer *xev = (deviceKeyButtonPointer *)xE;
+ deviceValuator *xv = (deviceValuator *)xev+1;
+ int type;
+ int event = -1;
+ XDeviceKeyEvent *ke = (XDeviceKeyEvent *)e;
+ XDeviceMotionEvent *me = (XDeviceMotionEvent *)e;
+
+ if (!e)
+ return -1; /* No extended event passed, cannot handle */
+
+ if ((XID)dmxLocal->deviceId != ke->deviceid) {
+ /* Search for the correct dmxLocal,
+ * since backend and console events are
+ * picked up for the first device on
+ * that X server. */
+ int i;
+ DMXInputInfo *dmxInput = &dmxInputs[dmxLocal->inputIdx];
+ for (i = 0; i < dmxInput->numDevs; i++) {
+ dmxLocal = dmxInput->devs[i];
+ if ((XID)dmxLocal->deviceId == ke->deviceid)
+ break;
+ }
+ }
+
+ if ((XID)dmxLocal->deviceId != ke->deviceid
+ || (type = dmxMapLookup(dmxLocal, e->type)) < 0)
+ return -1; /* No mapping, so this event is unhandled */
+
+ switch (type) {
+ case XI_DeviceValuator: event = DeviceValuator; break;
+ case XI_DeviceKeyPress: event = DeviceKeyPress; break;
+ case XI_DeviceKeyRelease: event = DeviceKeyRelease; break;
+ case XI_DeviceButtonPress: event = DeviceButtonPress; break;
+ case XI_DeviceButtonRelease: event = DeviceButtonRelease; break;
+ case XI_DeviceMotionNotify: event = DeviceMotionNotify; break;
+ case XI_DeviceFocusIn: event = DeviceFocusIn; break;
+ case XI_DeviceFocusOut: event = DeviceFocusOut; break;
+ case XI_ProximityIn: event = ProximityIn; break;
+ case XI_ProximityOut: event = ProximityOut; break;
+ case XI_DeviceStateNotify: event = DeviceStateNotify; break;
+ case XI_DeviceMappingNotify: event = DeviceMappingNotify; break;
+ case XI_ChangeDeviceNotify: event = ChangeDeviceNotify; break;
+ case XI_DeviceKeystateNotify: event = DeviceStateNotify; break;
+ case XI_DeviceButtonstateNotify: event = DeviceStateNotify; break;
+ }
+
+ switch (type) {
+ case XI_DeviceKeyPress:
+ case XI_DeviceKeyRelease:
+ case XI_DeviceButtonPress:
+ case XI_DeviceButtonRelease:
+ case XI_ProximityIn:
+ case XI_ProximityOut:
+ xev->type = event;
+ xev->detail = ke->keycode; /* same as ->button */
+ xev->deviceid = dmxLocal->pDevice->id | MORE_EVENTS;
+ xev->time = GetTimeInMillis();
+
+ xv->type = DeviceValuator;
+ xv->deviceid = dmxLocal->pDevice->id;
+ xv->num_valuators = ke->axes_count;
+ xv->first_valuator = ke->first_axis;
+ xv->valuator0 = ke->axis_data[0];
+ xv->valuator1 = ke->axis_data[1];
+ xv->valuator2 = ke->axis_data[2];
+ xv->valuator3 = ke->axis_data[3];
+ xv->valuator4 = ke->axis_data[4];
+ xv->valuator5 = ke->axis_data[5];
+
+ if (block)
+ dmxSigioBlock();
+ dmxeqEnqueue(xE);
+ if (block)
+ dmxSigioUnblock();
+ break;
+
+ case XI_DeviceMotionNotify:
+ dmxExtMotion(dmxLocal, me->axis_data, me->first_axis, me->axes_count,
+ DMX_ABSOLUTE, block);
+ break;
+ case XI_DeviceFocusIn:
+ case XI_DeviceFocusOut:
+ case XI_DeviceStateNotify:
+ case XI_DeviceMappingNotify:
+ case XI_ChangeDeviceNotify:
+ case XI_DeviceKeystateNotify:
+ case XI_DeviceButtonstateNotify:
+ /* These are ignored, since DMX will
+ * generate its own events of these
+ * types, as necessary.
+
+ * Perhaps ChangeDeviceNotify should
+ * generate an error, because it is
+ * unexpected? */
+ break;
+ case XI_DeviceValuator:
+ default:
+ dmxLog(dmxWarning,
+ "XInput extension event (remote=%d -> zero-based=%d)"
+ " not supported yet\n", e->type, type);
+ return -1;
+ }
+ return 0;
+}
+#endif
+
+static int dmxGetButtonMapping(DMXLocalInputInfoPtr dmxLocal, int button)
+{
+ ButtonClassPtr b = dmxLocal->pDevice->button;
+
+ if (button > b->numButtons) { /* This shouldn't happen. */
+ dmxLog(dmxWarning, "Button %d pressed, but only %d buttons?!?\n",
+ button, b->numButtons);
+ return button;
+ }
+ return b->map[button];
+}
+
+/** Return DMX's notion of the pointer position in the global coordinate
+ * space. */
+void dmxGetGlobalPosition(int *x, int *y)
+{
+ *x = dmxGlobalX;
+ *y = dmxGlobalY;
+}
+
+/** Invalidate the global position for #dmxCoreMotion. */
+void dmxInvalidateGlobalPosition(void)
+{
+ dmxGlobalInvalid = 1;
+}
+
+/** Enqueue a motion event for \a pDev. The \a v vector has length \a
+ * axesCount, and contains values for each of the axes, starting at \a
+ * firstAxes.
+ *
+ * The \a type of the motion may be \a DMX_RELATIVE, \a DMX_ABSOLUTE, or
+ * \a DMX_ABSOLUTE_CONFINED (in the latter case, the pointer will not be
+ * allowed to move outside the global boundaires).
+ *
+ * If \a block is set to \a DMX_BLOCK, then the SIGIO handler will be
+ * blocked around calls to #dmxeqEnqueue(). */
+void dmxMotion(DevicePtr pDev, int *v, int firstAxes, int axesCount,
+ DMXMotionType type, DMXBlockType block)
+{
+#ifdef XINPUT
+ GETDMXLOCALFROMPDEV;
+
+ if (!dmxLocal->sendsCore) {
+ dmxExtMotion(dmxLocal, v, firstAxes, axesCount, type, block);
+ return;
+ }
+#endif
+ if (axesCount == 2) {
+ switch (type) {
+ case DMX_RELATIVE:
+ dmxCoreMotion(pDev, dmxGlobalX - v[0], dmxGlobalY - v[1], 0, block);
+ break;
+ case DMX_ABSOLUTE:
+ dmxCoreMotion(pDev, v[0], v[1], 0, block);
+ break;
+ case DMX_ABSOLUTE_CONFINED:
+ dmxCoreMotion(pDev, v[0], v[1], -1, block);
+ break;
+ }
+ }
+}
+
+static KeySym dmxKeyCodeToKeySym(DMXLocalInputInfoPtr dmxLocal,
+ KeyCode keyCode)
+{
+ KeySymsPtr pKeySyms = NULL;
+
+ if (!dmxLocal || !dmxLocal->pDevice || !dmxLocal->pDevice->key)
+ return NoSymbol;
+ pKeySyms = &dmxLocal->pDevice->key->curKeySyms;
+ if (!pKeySyms)
+ return NoSymbol;
+
+ if (keyCode > pKeySyms->minKeyCode && keyCode <= pKeySyms->maxKeyCode) {
+ DMXDBG2("dmxKeyCodeToKeySym: Translated keyCode=%d to keySym=0x%04x\n",
+ keyCode,
+ pKeySyms->map[(keyCode - pKeySyms->minKeyCode)
+ * pKeySyms->mapWidth]);
+
+ return pKeySyms->map[(keyCode - pKeySyms->minKeyCode)
+ * pKeySyms->mapWidth];
+ }
+ return NoSymbol;
+}
+
+static KeyCode dmxKeySymToKeyCode(DMXLocalInputInfoPtr dmxLocal, KeySym keySym,
+ int tryFirst)
+{
+ KeySymsPtr pKeySyms = &dmxLocal->pDevice->key->curKeySyms;
+ int i;
+
+ /* Optimize for similar maps */
+ if (tryFirst >= pKeySyms->minKeyCode
+ && tryFirst <= pKeySyms->maxKeyCode
+ && pKeySyms->map[(tryFirst - pKeySyms->minKeyCode)
+ * pKeySyms->mapWidth] == keySym)
+ return tryFirst;
+
+ for (i = pKeySyms->minKeyCode; i <= pKeySyms->maxKeyCode; i++) {
+ if (pKeySyms->map[(i - pKeySyms->minKeyCode)
+ * pKeySyms->mapWidth] == keySym) {
+ DMXDBG3("dmxKeySymToKeyCode: Translated keySym=0x%04x to"
+ " keyCode=%d (reverses to core keySym=0x%04x)\n",
+ keySym, i, dmxKeyCodeToKeySym(dmxLocalCoreKeyboard,i));
+ return i;
+ }
+ }
+ return 0;
+}
+
+static int dmxFixup(DevicePtr pDev, int detail, KeySym keySym)
+{
+ GETDMXLOCALFROMPDEV;
+ int keyCode;
+
+ if (!dmxLocal->pDevice->key) {
+ dmxLog(dmxWarning, "dmxFixup: not a keyboard device (%s)\n",
+ dmxLocal->pDevice->name);
+ return NoSymbol;
+ }
+ if (!keySym)
+ keySym = dmxKeyCodeToKeySym(dmxLocal, detail);
+ if (keySym == NoSymbol)
+ return detail;
+ keyCode = dmxKeySymToKeyCode(dmxLocalCoreKeyboard, keySym, detail);
+
+ return keyCode ? keyCode : detail;
+}
+
+/** Enqueue an event from the \a pDev device with the
+ * specified \a type and \a detail. If the event is a KeyPress or
+ * KeyRelease event, then the \a keySym is also specified.
+ *
+ * If \a block is set to \a DMX_BLOCK, then the SIGIO handler will be
+ * blocked around calls to #dmxeqEnqueue(). */
+
+void dmxEnqueue(DevicePtr pDev, int type, int detail, KeySym keySym,
+ XEvent *e, DMXBlockType block)
+{
+ GETDMXINPUTFROMPDEV;
+ xEvent xE;
+ DeviceIntPtr p = dmxLocal->pDevice;
+ int i, nevents, valuators[3];
+ xEvent *events;
+
+ DMXDBG2("dmxEnqueue: Enqueuing type=%d detail=0x%0x\n", type, detail);
+
+ switch (type) {
+ case KeyPress:
+ case KeyRelease:
+ if (!keySym)
+ keySym = dmxKeyCodeToKeySym(dmxLocal, detail);
+ if (dmxCheckFunctionKeys(dmxLocal, type, keySym))
+ return;
+ if (dmxLocal->sendsCore && dmxLocal != dmxLocalCoreKeyboard)
+ xE.u.u.detail = dmxFixup(pDev, detail, keySym);
+
+ events = Xcalloc(sizeof(xEvent), GetMaximumEventsNum());
+ /*ErrorF("KEY %d sym %d\n", detail, (int) keySym);*/
+ nevents = GetKeyboardEvents(events, p, type, detail);
+ for (i = 0; i < nevents; i++)
+ mieqEnqueue(p, events + i);
+ xfree(events);
+ return;
+
+ case ButtonPress:
+ case ButtonRelease:
+ detail = dmxGetButtonMapping(dmxLocal, detail);
+ events = Xcalloc(sizeof(xEvent), GetMaximumEventsNum());
+ nevents = GetPointerEvents(events, p, type, detail,
+ POINTER_ABSOLUTE,
+ 0, /* first_valuator = 0 */
+ 0, /* num_valuators = 0 */
+ valuators);
+ for (i = 0; i < nevents; i++)
+ mieqEnqueue(p, events + i);
+ xfree(events);
+ return;
+
+ case MotionNotify:
+ events = Xcalloc(sizeof(xEvent), GetMaximumEventsNum());
+ valuators[0] = e->xmotion.x;
+ valuators[1] = e->xmotion.y;
+ valuators[2] = e->xmotion.state;
+ nevents = GetPointerEvents(events, p, type, detail,
+ POINTER_ABSOLUTE, 0, 3, valuators);
+ for (i = 0; i < nevents; i++)
+ mieqEnqueue(p, events + i);
+ xfree(events);
+ return;
+
+ case EnterNotify:
+ case LeaveNotify:
+ case KeymapNotify:
+ case MappingNotify: /* This is sent because we change the
+ * modifier map on the backend/console
+ * input device so that we have complete
+ * control of the input device LEDs. */
+ return;
+ default:
+#ifdef XINPUT
+ if (type == ProximityIn || type == ProximityOut) {
+ if (dmxLocal->sendsCore)
+ return; /* Not a core event */
+ break;
+ }
+#endif
+ if (type >= LASTEvent) {
+#ifdef XINPUT
+ if (dmxTranslateAndEnqueueExtEvent(dmxLocal, e, block))
+#endif
+ dmxLogInput(dmxInput, "Unhandled extension event: %d\n", type);
+ } else {
+ dmxLogInput(dmxInput, "Unhandled event: %d (%s)\n",
+ type, dmxEventName(type));
+ }
+ return;
+ }
+
+#if 00 /* dead code? */
+ memset(&xE, 0, sizeof(xE));
+ xE.u.u.type = type;
+ xE.u.u.detail = detail;
+ xE.u.keyButtonPointer.time = GetTimeInMillis();
+
+#ifdef XINPUT
+ if (!dmxLocal->sendsCore)
+ dmxEnqueueExtEvent(dmxLocal, &xE, block);
+ else
+#endif
+ dmxeqEnqueue(&xE);
+#endif /*00*/
+}
+
+/** A pointer to this routine is passed to low-level input drivers so
+ * that all special keychecking is unified to this file. This function
+ * returns 0 if no special keys have been pressed. If the user has
+ * requested termination of the DMX server, -1 is returned. If the user
+ * has requested a switch to a VT, then the (1-based) number of that VT
+ * is returned. */
+int dmxCheckSpecialKeys(DevicePtr pDev, KeySym keySym)
+{
+ GETDMXINPUTFROMPDEV;
+ int vt = 0;
+ unsigned short state = 0;
+
+ if (dmxLocal->sendsCore)
+ state = dmxLocalCoreKeyboard->pDevice->key->state;
+ else if (dmxLocal->pDevice->key)
+ state = dmxLocal->pDevice->key->state;
+
+ if (!dmxLocal->sendsCore) return 0; /* Only for core devices */
+
+ DMXDBG2("dmxCheckSpecialKeys: keySym=0x%04x state=0x%04x\n", keySym,state);
+
+ if ((state & (ControlMask|Mod1Mask)) != (ControlMask|Mod1Mask)) return 0;
+
+ switch (keySym) {
+ case XK_F1:
+ case XK_F2:
+ case XK_F3:
+ case XK_F4:
+ case XK_F5:
+ case XK_F6:
+ case XK_F7:
+ case XK_F8:
+ case XK_F9:
+ case XK_F10:
+ vt = keySym - XK_F1 + 1;
+ break;
+
+ case XK_F11:
+ case XK_F12:
+ vt = keySym - XK_F11 + 11;
+ break;
+
+ case XK_q: /* To avoid confusion */
+ case XK_BackSpace:
+ case XK_Delete:
+ case XK_KP_Delete:
+ dmxLog(dmxInfo, "User request for termination\n");
+ dispatchException |= DE_TERMINATE;
+ return -1; /* Terminate */
+ }
+
+ if (vt) {
+ dmxLog(dmxInfo, "Request to switch to VT %d\n", vt);
+ dmxInput->vt_switch_pending = vt;
+ return vt;
+ }
+
+ return 0; /* Do nothing */
+}