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