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