source: trunk/src/opengl/glut/glut_input.c

Last change on this file was 2689, checked in by jeroen, 26 years ago

* empty log message *

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