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