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