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