Merge branch 'mesa_7_5_branch' into mesa_7_6_branch
[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 #if !defined(_WIN32)
299 static void
300 addDeviceEventParser(void)
301 {
302 static Bool been_here = False;
303
304 if (been_here)
305 return;
306 been_here = True;
307 __glutRegisterEventParser(&eventParser);
308 }
309 #endif
310
311 static int
312 probeDevices(void)
313 {
314 static Bool been_here = False;
315 static int support;
316 #if !defined(_WIN32)
317 XExtensionVersion *version;
318 XDeviceInfoPtr device_info, device;
319 XAnyClassPtr any;
320 XButtonInfoPtr b;
321 XValuatorInfoPtr v;
322 XAxisInfoPtr a;
323 int num_dev = 0, btns = 0, dials = 0;
324 int i, j, k;
325 #endif /* !_WIN32 */
326
327 if (been_here) {
328 return support;
329 }
330 been_here = True;
331
332 #if !defined(_WIN32)
333 version = XGetExtensionVersion(__glutDisplay, "XInputExtension");
334 /* Ugh. XInput extension API forces annoying cast of a pointer
335 to a long so it can be compared with the NoSuchExtension
336 value (#defined to 1). */
337 if (version == NULL || ((long) version) == NoSuchExtension) {
338 support = 0;
339 return support;
340 }
341 XFree(version);
342 device_info = XListInputDevices(__glutDisplay, &num_dev);
343 if (device_info) {
344 for (i = 0; i < num_dev; i++) {
345 /* XXX These are SGI names for these devices;
346 unfortunately, no good standard exists for standard
347 types of X input extension devices. */
348
349 device = &device_info[i];
350 any = (XAnyClassPtr) device->inputclassinfo;
351
352 if (!__glutSpaceball && !strcmp(device->name, "spaceball")) {
353 v = NULL;
354 b = NULL;
355 for (j = 0; j < device->num_classes; j++) {
356 #if defined(__cplusplus) || defined(c_plusplus)
357 switch (any->c_class) {
358 #else
359 switch (any->class) {
360 #endif
361 case ButtonClass:
362 b = (XButtonInfoPtr) any;
363 btns = b->num_buttons;
364 break;
365 case ValuatorClass:
366 v = (XValuatorInfoPtr) any;
367 /* Sanity check: at least 6 valuators? */
368 if (v->num_axes < NUM_SPACEBALL_AXIS)
369 goto skip_device;
370 a = (XAxisInfoPtr) ((char *) v + sizeof(XValuatorInfo));
371 for (k = 0; k < NUM_SPACEBALL_AXIS; k++, a++) {
372 __glutSpaceballRange[k].min = a->min_value;
373 __glutSpaceballRange[k].range = a->max_value - a->min_value;
374 }
375 break;
376 }
377 any = (XAnyClassPtr) ((char *) any + any->length);
378 }
379 if (v) {
380 __glutSpaceball = XOpenDevice(__glutDisplay, device->id);
381 if (__glutSpaceball) {
382 __glutNumSpaceballButtons = btns;
383 addDeviceEventParser();
384 }
385 }
386 } else if (!__glutDials && !strcmp(device->name, "dial+buttons")) {
387 v = NULL;
388 b = NULL;
389 for (j = 0; j < device->num_classes; j++) {
390 #if defined(__cplusplus) || defined(c_plusplus)
391 switch (any->c_class) {
392 #else
393 switch (any->class) {
394 #endif
395 case ButtonClass:
396 b = (XButtonInfoPtr) any;
397 btns = b->num_buttons;
398 break;
399 case ValuatorClass:
400 v = (XValuatorInfoPtr) any;
401 /* Sanity check: at least 8 valuators? */
402 if (v->num_axes < NUM_DIALS_AXIS)
403 goto skip_device;
404 dials = v->num_axes;
405 __glutDialsResolution = (int *) malloc(sizeof(int) * dials);
406 a = (XAxisInfoPtr) ((char *) v + sizeof(XValuatorInfo));
407 for (k = 0; k < dials; k++, a++) {
408 __glutDialsResolution[k] = a->resolution;
409 }
410 break;
411 }
412 any = (XAnyClassPtr) ((char *) any + any->length);
413 }
414 if (v) {
415 __glutDials = XOpenDevice(__glutDisplay, device->id);
416 if (__glutDials) {
417 __glutNumButtonBoxButtons = btns;
418 __glutNumDials = dials;
419 addDeviceEventParser();
420 }
421 }
422 } else if (!__glutTablet && !strcmp(device->name, "tablet")) {
423 v = NULL;
424 b = NULL;
425 for (j = 0; j < device->num_classes; j++) {
426 #if defined(__cplusplus) || defined(c_plusplus)
427 switch (any->c_class) {
428 #else
429 switch (any->class) {
430 #endif
431 case ButtonClass:
432 b = (XButtonInfoPtr) any;
433 btns = b->num_buttons;
434 break;
435 case ValuatorClass:
436 v = (XValuatorInfoPtr) any;
437 /* Sanity check: exactly 2 valuators? */
438 if (v->num_axes != NUM_TABLET_AXIS)
439 goto skip_device;
440 a = (XAxisInfoPtr) ((char *) v + sizeof(XValuatorInfo));
441 for (k = 0; k < NUM_TABLET_AXIS; k++, a++) {
442 __glutTabletRange[k].min = a->min_value;
443 __glutTabletRange[k].range = a->max_value - a->min_value;
444 }
445 break;
446 }
447 any = (XAnyClassPtr) ((char *) any + any->length);
448 }
449 if (v) {
450 __glutTablet = XOpenDevice(__glutDisplay, device->id);
451 if (__glutTablet) {
452 __glutNumTabletButtons = btns;
453 addDeviceEventParser();
454 }
455 }
456 } else if (!strcmp(device->name, "mouse")) {
457 for (j = 0; j < device->num_classes; j++) {
458 #if defined(__cplusplus) || defined(c_plusplus)
459 if (any->c_class == ButtonClass) {
460 #else
461 if (any->class == ButtonClass) {
462 #endif
463 b = (XButtonInfoPtr) any;
464 __glutNumMouseButtons = b->num_buttons;
465 }
466 any = (XAnyClassPtr) ((char *) any + any->length);
467 }
468 }
469 skip_device:;
470 }
471 XFreeDeviceList(device_info);
472 }
473 #else /* _WIN32 */
474 __glutNumMouseButtons = GetSystemMetrics(SM_CMOUSEBUTTONS);
475 #endif /* !_WIN32 */
476 /* X Input extension might be supported, but only if there is
477 a tablet, dials, or spaceball do we claim devices are
478 supported. */
479 support = __glutTablet || __glutDials || __glutSpaceball;
480 return support;
481 }
482
483 void
484 __glutUpdateInputDeviceMask(GLUTwindow * window)
485 {
486 #if !defined(_WIN32)
487 /* 5 (dial and buttons) + 5 (tablet locator and buttons) + 5
488 (Spaceball buttons and axis) = 15 */
489 XEventClass eventList[15];
490 int rc, numEvents;
491
492 rc = probeDevices();
493 if (rc) {
494 numEvents = 0;
495 if (__glutTablet) {
496 if (window->tabletMotion) {
497 DeviceMotionNotify(__glutTablet, __glutDeviceMotionNotify,
498 eventList[numEvents]);
499 numEvents++;
500 }
501 if (window->tabletButton) {
502 DeviceButtonPress(__glutTablet, __glutDeviceButtonPress,
503 eventList[numEvents]);
504 numEvents++;
505 DeviceButtonPressGrab(__glutTablet, __glutDeviceButtonPressGrab,
506 eventList[numEvents]);
507 numEvents++;
508 DeviceButtonRelease(__glutTablet, __glutDeviceButtonRelease,
509 eventList[numEvents]);
510 numEvents++;
511 }
512 if (window->tabletMotion || window->tabletButton) {
513 DeviceStateNotify(__glutTablet, __glutDeviceStateNotify,
514 eventList[numEvents]);
515 numEvents++;
516 }
517 }
518 if (__glutDials) {
519 if (window->dials) {
520 DeviceMotionNotify(__glutDials, __glutDeviceMotionNotify,
521 eventList[numEvents]);
522 numEvents++;
523 }
524 if (window->buttonBox) {
525 DeviceButtonPress(__glutDials, __glutDeviceButtonPress,
526 eventList[numEvents]);
527 numEvents++;
528 DeviceButtonPressGrab(__glutDials, __glutDeviceButtonPressGrab,
529 eventList[numEvents]);
530 numEvents++;
531 DeviceButtonRelease(__glutDials, __glutDeviceButtonRelease,
532 eventList[numEvents]);
533 numEvents++;
534 }
535 if (window->dials || window->buttonBox) {
536 DeviceStateNotify(__glutDials, __glutDeviceStateNotify,
537 eventList[numEvents]);
538 numEvents++;
539 }
540 }
541 if (__glutSpaceball) {
542 if (window->spaceMotion || window->spaceRotate) {
543 DeviceMotionNotify(__glutSpaceball, __glutDeviceMotionNotify,
544 eventList[numEvents]);
545 numEvents++;
546 }
547 if (window->spaceButton) {
548 DeviceButtonPress(__glutSpaceball, __glutDeviceButtonPress,
549 eventList[numEvents]);
550 numEvents++;
551 DeviceButtonPressGrab(__glutSpaceball, __glutDeviceButtonPressGrab,
552 eventList[numEvents]);
553 numEvents++;
554 DeviceButtonRelease(__glutSpaceball, __glutDeviceButtonRelease,
555 eventList[numEvents]);
556 numEvents++;
557 }
558 if (window->spaceMotion || window->spaceRotate || window->spaceButton) {
559 DeviceStateNotify(__glutSpaceball, __glutDeviceStateNotify,
560 eventList[numEvents]);
561 numEvents++;
562 }
563 }
564 #if 0
565 if (window->children) {
566 GLUTwindow *child = window->children;
567
568 do {
569 XChangeDeviceDontPropagateList(__glutDisplay, child->win,
570 numEvents, eventList, AddToList);
571 child = child->siblings;
572 } while (child);
573 }
574 #endif
575 XSelectExtensionEvent(__glutDisplay, window->win,
576 eventList, numEvents);
577 if (window->overlay) {
578 XSelectExtensionEvent(__glutDisplay, window->overlay->win,
579 eventList, numEvents);
580 }
581 } else {
582 /* X Input extension not supported; no chance for exotic
583 input devices. */
584 }
585 #endif /* !_WIN32 */
586 }
587
588 /* CENTRY */
589 int GLUTAPIENTRY
590 glutDeviceGet(GLenum param)
591 {
592 probeDevices();
593 switch (param) {
594 case GLUT_HAS_KEYBOARD:
595 case GLUT_HAS_MOUSE:
596 /* Assume window system always has mouse and keyboard. */
597 return 1;
598 case GLUT_HAS_SPACEBALL:
599 return __glutSpaceball != NULL;
600 case GLUT_HAS_DIAL_AND_BUTTON_BOX:
601 return __glutDials != NULL;
602 case GLUT_HAS_TABLET:
603 return __glutTablet != NULL;
604 case GLUT_NUM_MOUSE_BUTTONS:
605 return __glutNumMouseButtons;
606 case GLUT_NUM_SPACEBALL_BUTTONS:
607 return __glutNumSpaceballButtons;
608 case GLUT_NUM_BUTTON_BOX_BUTTONS:
609 return __glutNumButtonBoxButtons;
610 case GLUT_NUM_DIALS:
611 return __glutNumDials;
612 case GLUT_NUM_TABLET_BUTTONS:
613 return __glutNumTabletButtons;
614 case GLUT_DEVICE_IGNORE_KEY_REPEAT:
615 return __glutCurrentWindow->ignoreKeyRepeat;
616 #ifndef _WIN32
617 case GLUT_DEVICE_KEY_REPEAT:
618 {
619 XKeyboardState state;
620
621 XGetKeyboardControl(__glutDisplay, &state);
622 return state.global_auto_repeat;
623 }
624 case GLUT_JOYSTICK_POLL_RATE:
625 return 0;
626 #else
627 case GLUT_DEVICE_KEY_REPEAT:
628 /* Win32 cannot globally disable key repeat. */
629 return GLUT_KEY_REPEAT_ON;
630 case GLUT_JOYSTICK_POLL_RATE:
631 return __glutCurrentWindow->joyPollInterval;
632 #endif
633 case GLUT_HAS_JOYSTICK:
634 return __glutHasJoystick;
635 case GLUT_JOYSTICK_BUTTONS:
636 return __glutNumJoystickButtons;
637 case GLUT_JOYSTICK_AXES:
638 return __glutNumJoystickAxes;
639 default:
640 __glutWarning("invalid glutDeviceGet parameter: %d", param);
641 return -1;
642 }
643 }
644 /* ENDCENTRY */