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