Merge branch 'master' of git+ssh://michal@git.freedesktop.org/git/mesa/mesa into...
[mesa.git] / src / glut / glx / glut_input.c
1
2 /* Copyright (c) Mark J. Kilgard, 1994, 1997, 1998. */
3
4 /* This program is freely distributable without licensing fees
5 and is provided without guarantee or warrantee expressed or
6 implied. This program is -not- in the public domain. */
7
8 #ifdef __VMS
9 #include <GL/vms_x_fix.h>
10 #endif
11
12 #include <assert.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16
17 #if !defined(_WIN32)
18 #include <X11/Xlib.h>
19 #if defined(__vms)
20 #include <X11/XInput.h>
21 #else
22 #include <X11/extensions/XInput.h>
23 #endif
24 #include <X11/Xutil.h>
25 #else
26 #ifdef __MINGW32__
27 #include <GL/gl.h>
28 #endif
29 #include <windows.h>
30 #ifndef __CYGWIN32__
31 #include <mmsystem.h> /* Win32 Multimedia API header. */
32 #endif
33 #endif /* !_WIN32 */
34
35 #include "glutint.h"
36
37 int __glutNumDials = 0;
38 int __glutNumSpaceballButtons = 0;
39 int __glutNumButtonBoxButtons = 0;
40 int __glutNumTabletButtons = 0;
41 int __glutNumMouseButtons = 3; /* Good guess. */
42 XDevice *__glutTablet = NULL;
43 XDevice *__glutDials = NULL;
44 XDevice *__glutSpaceball = NULL;
45
46 int __glutHasJoystick = 0;
47 int __glutNumJoystickButtons = 0;
48 int __glutNumJoystickAxes = 0;
49
50 #if !defined(_WIN32)
51 typedef struct _Range {
52 int min;
53 int range;
54 } Range;
55
56 #define NUM_SPACEBALL_AXIS 6
57 #define NUM_TABLET_AXIS 2
58 #define NUM_DIALS_AXIS 8
59
60 Range __glutSpaceballRange[NUM_SPACEBALL_AXIS];
61 Range __glutTabletRange[NUM_TABLET_AXIS];
62 int *__glutDialsResolution;
63
64 /* Safely assumes 0 is an illegal event type for X Input
65 extension events. */
66 int __glutDeviceMotionNotify = 0;
67 int __glutDeviceButtonPress = 0;
68 int __glutDeviceButtonPressGrab = 0;
69 int __glutDeviceButtonRelease = 0;
70 int __glutDeviceStateNotify = 0;
71
72 static int
73 normalizeTabletPos(int axis, int rawValue)
74 {
75 assert(rawValue >= __glutTabletRange[axis].min);
76 assert(rawValue <= __glutTabletRange[axis].min
77 + __glutTabletRange[axis].range);
78 /* Normalize rawValue to between 0 and 4000. */
79 return ((rawValue - __glutTabletRange[axis].min) * 4000) /
80 __glutTabletRange[axis].range;
81 }
82
83 static int
84 normalizeDialAngle(int axis, int rawValue)
85 {
86 /* XXX Assumption made that the resolution of the device is
87 number of clicks for one complete dial revolution. This
88 is true for SGI's dial & button box. */
89 return (rawValue * 360.0) / __glutDialsResolution[axis];
90 }
91
92 static int
93 normalizeSpaceballAngle(int axis, int rawValue)
94 {
95 assert(rawValue >= __glutSpaceballRange[axis].min);
96 assert(rawValue <= __glutSpaceballRange[axis].min +
97 __glutSpaceballRange[axis].range);
98 /* Normalize rawValue to between -1800 and 1800. */
99 return ((rawValue - __glutSpaceballRange[axis].min) * 3600) /
100 __glutSpaceballRange[axis].range - 1800;
101 }
102
103 static int
104 normalizeSpaceballDelta(int axis, int rawValue)
105 {
106 assert(rawValue >= __glutSpaceballRange[axis].min);
107 assert(rawValue <= __glutSpaceballRange[axis].min +
108 __glutSpaceballRange[axis].range);
109 /* Normalize rawValue to between -1000 and 1000. */
110 return ((rawValue - __glutSpaceballRange[axis].min) * 2000) /
111 __glutSpaceballRange[axis].range - 1000;
112 }
113
114 static void
115 queryTabletPos(GLUTwindow * window)
116 {
117 XDeviceState *state;
118 XInputClass *any;
119 XValuatorState *v;
120 int i;
121
122 state = XQueryDeviceState(__glutDisplay, __glutTablet);
123 any = state->data;
124 for (i = 0; i < state->num_classes; i++) {
125 #if defined(__cplusplus) || defined(c_plusplus)
126 switch (any->c_class) {
127 #else
128 switch (any->class) {
129 #endif
130 case ValuatorClass:
131 v = (XValuatorState *) any;
132 if (v->num_valuators < 2)
133 goto end;
134 if (window->tabletPos[0] == -1)
135 window->tabletPos[0] = normalizeTabletPos(0, v->valuators[0]);
136 if (window->tabletPos[1] == -1)
137 window->tabletPos[1] = normalizeTabletPos(1, v->valuators[1]);
138 }
139 any = (XInputClass *) ((char *) any + any->length);
140 }
141 end:
142 XFreeDeviceState(state);
143 }
144
145 static void
146 tabletPosChange(GLUTwindow * window, int first, int count, int *data)
147 {
148 int i, value, genEvent = 0;
149
150 for (i = first; i < first + count; i++) {
151 switch (i) {
152 case 0: /* X axis */
153 case 1: /* Y axis */
154 value = normalizeTabletPos(i, data[i - first]);
155 if (value != window->tabletPos[i]) {
156 window->tabletPos[i] = value;
157 genEvent = 1;
158 }
159 break;
160 }
161 }
162 if (window->tabletPos[0] == -1 || window->tabletPos[1] == -1)
163 queryTabletPos(window);
164 if (genEvent)
165 window->tabletMotion(window->tabletPos[0], window->tabletPos[1]);
166 }
167 #endif /* !_WIN32 */
168
169 static int
170 __glutProcessDeviceEvents(XEvent * event)
171 {
172 #if !defined(_WIN32)
173 GLUTwindow *window;
174
175 /* XXX Ugly code fan out. */
176
177 /* Can't use switch/case since X Input event types are
178 dynamic. */
179
180 if (__glutDeviceMotionNotify && event->type == __glutDeviceMotionNotify) {
181 XDeviceMotionEvent *devmot = (XDeviceMotionEvent *) event;
182
183 window = __glutGetWindow(devmot->window);
184 if (window) {
185 if (__glutTablet
186 && devmot->deviceid == __glutTablet->device_id
187 && window->tabletMotion) {
188 tabletPosChange(window, devmot->first_axis, devmot->axes_count,
189 devmot->axis_data);
190 } else if (__glutDials
191 && devmot->deviceid == __glutDials->device_id
192 && window->dials) {
193 int i, first = devmot->first_axis, count = devmot->axes_count;
194
195 for (i = first; i < first + count; i++)
196 window->dials(i + 1,
197 normalizeDialAngle(i, devmot->axis_data[i - first]));
198 } else if (__glutSpaceball
199 && devmot->deviceid == __glutSpaceball->device_id) {
200 /* XXX Assume that space ball motion events come in as
201 all the first 6 axes. Assume first 3 axes are XYZ
202 translations; second 3 axes are XYZ rotations. */
203 if (devmot->first_axis == 0 && devmot->axes_count == 6) {
204 if (window->spaceMotion)
205 window->spaceMotion(
206 normalizeSpaceballDelta(0, devmot->axis_data[0]),
207 normalizeSpaceballDelta(1, devmot->axis_data[1]),
208 normalizeSpaceballDelta(2, devmot->axis_data[2]));
209 if (window->spaceRotate)
210 window->spaceRotate(
211 normalizeSpaceballAngle(3, devmot->axis_data[3]),
212 normalizeSpaceballAngle(4, devmot->axis_data[4]),
213 normalizeSpaceballAngle(5, devmot->axis_data[5]));
214 }
215 }
216 return 1;
217 }
218 } else if (__glutDeviceButtonPress
219 && event->type == __glutDeviceButtonPress) {
220 XDeviceButtonEvent *devbtn = (XDeviceButtonEvent *) event;
221
222 window = __glutGetWindow(devbtn->window);
223 if (window) {
224 if (__glutTablet
225 && devbtn->deviceid == __glutTablet->device_id
226 && window->tabletButton
227 && devbtn->first_axis == 0
228 && devbtn->axes_count == 2) {
229 tabletPosChange(window, devbtn->first_axis, devbtn->axes_count,
230 devbtn->axis_data);
231 window->tabletButton(devbtn->button, GLUT_DOWN,
232 window->tabletPos[0], window->tabletPos[1]);
233 } else if (__glutDials
234 && devbtn->deviceid == __glutDials->device_id
235 && window->buttonBox) {
236 window->buttonBox(devbtn->button, GLUT_DOWN);
237 } else if (__glutSpaceball
238 && devbtn->deviceid == __glutSpaceball->device_id
239 && window->spaceButton) {
240 window->spaceButton(devbtn->button, GLUT_DOWN);
241 }
242 return 1;
243 }
244 } else if (__glutDeviceButtonRelease
245 && event->type == __glutDeviceButtonRelease) {
246 XDeviceButtonEvent *devbtn = (XDeviceButtonEvent *) event;
247
248 window = __glutGetWindow(devbtn->window);
249 if (window) {
250 if (__glutTablet
251 && devbtn->deviceid == __glutTablet->device_id
252 && window->tabletButton
253 && devbtn->first_axis == 0
254 && devbtn->axes_count == 2) {
255 tabletPosChange(window, devbtn->first_axis, devbtn->axes_count,
256 devbtn->axis_data);
257 window->tabletButton(devbtn->button, GLUT_UP,
258 window->tabletPos[0], window->tabletPos[1]);
259 } else if (__glutDials
260 && devbtn->deviceid == __glutDials->device_id
261 && window->buttonBox) {
262 window->buttonBox(devbtn->button, GLUT_UP);
263 } else if (__glutSpaceball
264 && devbtn->deviceid == __glutSpaceball->device_id
265 && window->spaceButton) {
266 window->spaceButton(devbtn->button, GLUT_UP);
267 }
268 return 1;
269 }
270 }
271 #else
272 {
273 JOYINFOEX info;
274 JOYCAPS joyCaps;
275
276 memset(&info, 0, sizeof(JOYINFOEX));
277 info.dwSize = sizeof(JOYINFOEX);
278 info.dwFlags = JOY_RETURNALL;
279
280 if (joyGetPosEx(JOYSTICKID1,&info) != JOYERR_NOERROR) {
281 __glutHasJoystick = 1;
282 joyGetDevCaps(JOYSTICKID1, &joyCaps, sizeof(joyCaps));
283 __glutNumJoystickButtons = joyCaps.wNumButtons;
284 __glutNumJoystickAxes = joyCaps.wNumAxes;
285 } else {
286 __glutHasJoystick = 0;
287 __glutNumJoystickButtons = 0;
288 __glutNumJoystickAxes = 0;
289 }
290 }
291 #endif /* !_WIN32 */
292 return 0;
293 }
294
295 static GLUTeventParser eventParser =
296 {__glutProcessDeviceEvents, NULL};
297
298 static void
299 addDeviceEventParser(void)
300 {
301 static Bool been_here = False;
302
303 if (been_here)
304 return;
305 been_here = True;
306 __glutRegisterEventParser(&eventParser);
307 }
308
309 static int
310 probeDevices(void)
311 {
312 static Bool been_here = False;
313 static int support;
314 #if !defined(_WIN32)
315 XExtensionVersion *version;
316 XDeviceInfoPtr device_info, device;
317 XAnyClassPtr any;
318 XButtonInfoPtr b;
319 XValuatorInfoPtr v;
320 XAxisInfoPtr a;
321 int num_dev = 0, btns = 0, dials = 0;
322 int i, j, k;
323 #endif /* !_WIN32 */
324
325 if (been_here) {
326 return support;
327 }
328 been_here = True;
329
330 #if !defined(_WIN32)
331 version = XGetExtensionVersion(__glutDisplay, "XInputExtension");
332 /* Ugh. XInput extension API forces annoying cast of a pointer
333 to a long so it can be compared with the NoSuchExtension
334 value (#defined to 1). */
335 if (version == NULL || ((long) version) == NoSuchExtension) {
336 support = 0;
337 return support;
338 }
339 XFree(version);
340 device_info = XListInputDevices(__glutDisplay, &num_dev);
341 if (device_info) {
342 for (i = 0; i < num_dev; i++) {
343 /* XXX These are SGI names for these devices;
344 unfortunately, no good standard exists for standard
345 types of X input extension devices. */
346
347 device = &device_info[i];
348 any = (XAnyClassPtr) device->inputclassinfo;
349
350 if (!__glutSpaceball && !strcmp(device->name, "spaceball")) {
351 v = NULL;
352 b = NULL;
353 for (j = 0; j < device->num_classes; j++) {
354 #if defined(__cplusplus) || defined(c_plusplus)
355 switch (any->c_class) {
356 #else
357 switch (any->class) {
358 #endif
359 case ButtonClass:
360 b = (XButtonInfoPtr) any;
361 btns = b->num_buttons;
362 break;
363 case ValuatorClass:
364 v = (XValuatorInfoPtr) any;
365 /* Sanity check: at least 6 valuators? */
366 if (v->num_axes < NUM_SPACEBALL_AXIS)
367 goto skip_device;
368 a = (XAxisInfoPtr) ((char *) v + sizeof(XValuatorInfo));
369 for (k = 0; k < NUM_SPACEBALL_AXIS; k++, a++) {
370 __glutSpaceballRange[k].min = a->min_value;
371 __glutSpaceballRange[k].range = a->max_value - a->min_value;
372 }
373 break;
374 }
375 any = (XAnyClassPtr) ((char *) any + any->length);
376 }
377 if (v) {
378 __glutSpaceball = XOpenDevice(__glutDisplay, device->id);
379 if (__glutSpaceball) {
380 __glutNumSpaceballButtons = btns;
381 addDeviceEventParser();
382 }
383 }
384 } else if (!__glutDials && !strcmp(device->name, "dial+buttons")) {
385 v = NULL;
386 b = NULL;
387 for (j = 0; j < device->num_classes; j++) {
388 #if defined(__cplusplus) || defined(c_plusplus)
389 switch (any->c_class) {
390 #else
391 switch (any->class) {
392 #endif
393 case ButtonClass:
394 b = (XButtonInfoPtr) any;
395 btns = b->num_buttons;
396 break;
397 case ValuatorClass:
398 v = (XValuatorInfoPtr) any;
399 /* Sanity check: at least 8 valuators? */
400 if (v->num_axes < NUM_DIALS_AXIS)
401 goto skip_device;
402 dials = v->num_axes;
403 __glutDialsResolution = (int *) malloc(sizeof(int) * dials);
404 a = (XAxisInfoPtr) ((char *) v + sizeof(XValuatorInfo));
405 for (k = 0; k < dials; k++, a++) {
406 __glutDialsResolution[k] = a->resolution;
407 }
408 break;
409 }
410 any = (XAnyClassPtr) ((char *) any + any->length);
411 }
412 if (v) {
413 __glutDials = XOpenDevice(__glutDisplay, device->id);
414 if (__glutDials) {
415 __glutNumButtonBoxButtons = btns;
416 __glutNumDials = dials;
417 addDeviceEventParser();
418 }
419 }
420 } else if (!__glutTablet && !strcmp(device->name, "tablet")) {
421 v = NULL;
422 b = NULL;
423 for (j = 0; j < device->num_classes; j++) {
424 #if defined(__cplusplus) || defined(c_plusplus)
425 switch (any->c_class) {
426 #else
427 switch (any->class) {
428 #endif
429 case ButtonClass:
430 b = (XButtonInfoPtr) any;
431 btns = b->num_buttons;
432 break;
433 case ValuatorClass:
434 v = (XValuatorInfoPtr) any;
435 /* Sanity check: exactly 2 valuators? */
436 if (v->num_axes != NUM_TABLET_AXIS)
437 goto skip_device;
438 a = (XAxisInfoPtr) ((char *) v + sizeof(XValuatorInfo));
439 for (k = 0; k < NUM_TABLET_AXIS; k++, a++) {
440 __glutTabletRange[k].min = a->min_value;
441 __glutTabletRange[k].range = a->max_value - a->min_value;
442 }
443 break;
444 }
445 any = (XAnyClassPtr) ((char *) any + any->length);
446 }
447 if (v) {
448 __glutTablet = XOpenDevice(__glutDisplay, device->id);
449 if (__glutTablet) {
450 __glutNumTabletButtons = btns;
451 addDeviceEventParser();
452 }
453 }
454 } else if (!strcmp(device->name, "mouse")) {
455 for (j = 0; j < device->num_classes; j++) {
456 #if defined(__cplusplus) || defined(c_plusplus)
457 if (any->c_class == ButtonClass) {
458 #else
459 if (any->class == ButtonClass) {
460 #endif
461 b = (XButtonInfoPtr) any;
462 __glutNumMouseButtons = b->num_buttons;
463 }
464 any = (XAnyClassPtr) ((char *) any + any->length);
465 }
466 }
467 skip_device:;
468 }
469 XFreeDeviceList(device_info);
470 }
471 #else /* _WIN32 */
472 __glutNumMouseButtons = GetSystemMetrics(SM_CMOUSEBUTTONS);
473 #endif /* !_WIN32 */
474 /* X Input extension might be supported, but only if there is
475 a tablet, dials, or spaceball do we claim devices are
476 supported. */
477 support = __glutTablet || __glutDials || __glutSpaceball;
478 return support;
479 }
480
481 void
482 __glutUpdateInputDeviceMask(GLUTwindow * window)
483 {
484 #if !defined(_WIN32)
485 /* 5 (dial and buttons) + 5 (tablet locator and buttons) + 5
486 (Spaceball buttons and axis) = 15 */
487 XEventClass eventList[15];
488 int rc, numEvents;
489
490 rc = probeDevices();
491 if (rc) {
492 numEvents = 0;
493 if (__glutTablet) {
494 if (window->tabletMotion) {
495 DeviceMotionNotify(__glutTablet, __glutDeviceMotionNotify,
496 eventList[numEvents]);
497 numEvents++;
498 }
499 if (window->tabletButton) {
500 DeviceButtonPress(__glutTablet, __glutDeviceButtonPress,
501 eventList[numEvents]);
502 numEvents++;
503 DeviceButtonPressGrab(__glutTablet, __glutDeviceButtonPressGrab,
504 eventList[numEvents]);
505 numEvents++;
506 DeviceButtonRelease(__glutTablet, __glutDeviceButtonRelease,
507 eventList[numEvents]);
508 numEvents++;
509 }
510 if (window->tabletMotion || window->tabletButton) {
511 DeviceStateNotify(__glutTablet, __glutDeviceStateNotify,
512 eventList[numEvents]);
513 numEvents++;
514 }
515 }
516 if (__glutDials) {
517 if (window->dials) {
518 DeviceMotionNotify(__glutDials, __glutDeviceMotionNotify,
519 eventList[numEvents]);
520 numEvents++;
521 }
522 if (window->buttonBox) {
523 DeviceButtonPress(__glutDials, __glutDeviceButtonPress,
524 eventList[numEvents]);
525 numEvents++;
526 DeviceButtonPressGrab(__glutDials, __glutDeviceButtonPressGrab,
527 eventList[numEvents]);
528 numEvents++;
529 DeviceButtonRelease(__glutDials, __glutDeviceButtonRelease,
530 eventList[numEvents]);
531 numEvents++;
532 }
533 if (window->dials || window->buttonBox) {
534 DeviceStateNotify(__glutDials, __glutDeviceStateNotify,
535 eventList[numEvents]);
536 numEvents++;
537 }
538 }
539 if (__glutSpaceball) {
540 if (window->spaceMotion || window->spaceRotate) {
541 DeviceMotionNotify(__glutSpaceball, __glutDeviceMotionNotify,
542 eventList[numEvents]);
543 numEvents++;
544 }
545 if (window->spaceButton) {
546 DeviceButtonPress(__glutSpaceball, __glutDeviceButtonPress,
547 eventList[numEvents]);
548 numEvents++;
549 DeviceButtonPressGrab(__glutSpaceball, __glutDeviceButtonPressGrab,
550 eventList[numEvents]);
551 numEvents++;
552 DeviceButtonRelease(__glutSpaceball, __glutDeviceButtonRelease,
553 eventList[numEvents]);
554 numEvents++;
555 }
556 if (window->spaceMotion || window->spaceRotate || window->spaceButton) {
557 DeviceStateNotify(__glutSpaceball, __glutDeviceStateNotify,
558 eventList[numEvents]);
559 numEvents++;
560 }
561 }
562 #if 0
563 if (window->children) {
564 GLUTwindow *child = window->children;
565
566 do {
567 XChangeDeviceDontPropagateList(__glutDisplay, child->win,
568 numEvents, eventList, AddToList);
569 child = child->siblings;
570 } while (child);
571 }
572 #endif
573 XSelectExtensionEvent(__glutDisplay, window->win,
574 eventList, numEvents);
575 if (window->overlay) {
576 XSelectExtensionEvent(__glutDisplay, window->overlay->win,
577 eventList, numEvents);
578 }
579 } else {
580 /* X Input extension not supported; no chance for exotic
581 input devices. */
582 }
583 #endif /* !_WIN32 */
584 }
585
586 /* CENTRY */
587 int GLUTAPIENTRY
588 glutDeviceGet(GLenum param)
589 {
590 probeDevices();
591 switch (param) {
592 case GLUT_HAS_KEYBOARD:
593 case GLUT_HAS_MOUSE:
594 /* Assume window system always has mouse and keyboard. */
595 return 1;
596 case GLUT_HAS_SPACEBALL:
597 return __glutSpaceball != NULL;
598 case GLUT_HAS_DIAL_AND_BUTTON_BOX:
599 return __glutDials != NULL;
600 case GLUT_HAS_TABLET:
601 return __glutTablet != NULL;
602 case GLUT_NUM_MOUSE_BUTTONS:
603 return __glutNumMouseButtons;
604 case GLUT_NUM_SPACEBALL_BUTTONS:
605 return __glutNumSpaceballButtons;
606 case GLUT_NUM_BUTTON_BOX_BUTTONS:
607 return __glutNumButtonBoxButtons;
608 case GLUT_NUM_DIALS:
609 return __glutNumDials;
610 case GLUT_NUM_TABLET_BUTTONS:
611 return __glutNumTabletButtons;
612 case GLUT_DEVICE_IGNORE_KEY_REPEAT:
613 return __glutCurrentWindow->ignoreKeyRepeat;
614 #ifndef _WIN32
615 case GLUT_DEVICE_KEY_REPEAT:
616 {
617 XKeyboardState state;
618
619 XGetKeyboardControl(__glutDisplay, &state);
620 return state.global_auto_repeat;
621 }
622 case GLUT_JOYSTICK_POLL_RATE:
623 return 0;
624 #else
625 case GLUT_DEVICE_KEY_REPEAT:
626 /* Win32 cannot globally disable key repeat. */
627 return GLUT_KEY_REPEAT_ON;
628 case GLUT_JOYSTICK_POLL_RATE:
629 return __glutCurrentWindow->joyPollInterval;
630 #endif
631 case GLUT_HAS_JOYSTICK:
632 return __glutHasJoystick;
633 case GLUT_JOYSTICK_BUTTONS:
634 return __glutNumJoystickButtons;
635 case GLUT_JOYSTICK_AXES:
636 return __glutNumJoystickAxes;
637 default:
638 __glutWarning("invalid glutDeviceGet parameter: %d", param);
639 return -1;
640 }
641 }
642 /* ENDCENTRY */