2 /* Copyright (c) Mark J. Kilgard, 1994, 1997, 1998. */
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. */
16 #include <X11/XInput.h>
18 #include <X11/extensions/XInput.h>
20 #include <X11/Xutil.h>
24 #include <mmsystem.h> /* Win32 Multimedia API header. */
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
;
39 int __glutHasJoystick
= 0;
40 int __glutNumJoystickButtons
= 0;
41 int __glutNumJoystickAxes
= 0;
44 typedef struct _Range
{
49 #define NUM_SPACEBALL_AXIS 6
50 #define NUM_TABLET_AXIS 2
51 #define NUM_DIALS_AXIS 8
53 Range __glutSpaceballRange
[NUM_SPACEBALL_AXIS
];
54 Range __glutTabletRange
[NUM_TABLET_AXIS
];
55 int *__glutDialsResolution
;
57 /* Safely assumes 0 is an illegal event type for X Input
59 int __glutDeviceMotionNotify
= 0;
60 int __glutDeviceButtonPress
= 0;
61 int __glutDeviceButtonPressGrab
= 0;
62 int __glutDeviceButtonRelease
= 0;
63 int __glutDeviceStateNotify
= 0;
66 normalizeTabletPos(int axis
, int rawValue
)
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
;
77 normalizeDialAngle(int axis
, int rawValue
)
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
];
86 normalizeSpaceballAngle(int axis
, int rawValue
)
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;
97 normalizeSpaceballDelta(int axis
, int rawValue
)
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;
108 queryTabletPos(GLUTwindow
* window
)
115 state
= XQueryDeviceState(__glutDisplay
, __glutTablet
);
117 for (i
= 0; i
< state
->num_classes
; i
++) {
118 #if defined(__cplusplus) || defined(c_plusplus)
119 switch (any
->c_class
) {
121 switch (any
->class) {
124 v
= (XValuatorState
*) any
;
125 if (v
->num_valuators
< 2)
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]);
132 any
= (XInputClass
*) ((char *) any
+ any
->length
);
135 XFreeDeviceState(state
);
139 tabletPosChange(GLUTwindow
* window
, int first
, int count
, int *data
)
141 int i
, value
, genEvent
= 0;
143 for (i
= first
; i
< first
+ count
; i
++) {
147 value
= normalizeTabletPos(i
, data
[i
- first
]);
148 if (value
!= window
->tabletPos
[i
]) {
149 window
->tabletPos
[i
] = value
;
155 if (window
->tabletPos
[0] == -1 || window
->tabletPos
[1] == -1)
156 queryTabletPos(window
);
158 window
->tabletMotion(window
->tabletPos
[0], window
->tabletPos
[1]);
163 __glutProcessDeviceEvents(XEvent
* event
)
168 /* XXX Ugly code fan out. */
170 /* Can't use switch/case since X Input event types are
173 if (__glutDeviceMotionNotify
&& event
->type
== __glutDeviceMotionNotify
) {
174 XDeviceMotionEvent
*devmot
= (XDeviceMotionEvent
*) event
;
176 window
= __glutGetWindow(devmot
->window
);
179 && devmot
->deviceid
== __glutTablet
->device_id
180 && window
->tabletMotion
) {
181 tabletPosChange(window
, devmot
->first_axis
, devmot
->axes_count
,
183 } else if (__glutDials
184 && devmot
->deviceid
== __glutDials
->device_id
186 int i
, first
= devmot
->first_axis
, count
= devmot
->axes_count
;
188 for (i
= first
; i
< first
+ count
; i
++)
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
)
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
)
204 normalizeSpaceballAngle(3, devmot
->axis_data
[3]),
205 normalizeSpaceballAngle(4, devmot
->axis_data
[4]),
206 normalizeSpaceballAngle(5, devmot
->axis_data
[5]));
211 } else if (__glutDeviceButtonPress
212 && event
->type
== __glutDeviceButtonPress
) {
213 XDeviceButtonEvent
*devbtn
= (XDeviceButtonEvent
*) event
;
215 window
= __glutGetWindow(devbtn
->window
);
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
,
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
);
237 } else if (__glutDeviceButtonRelease
238 && event
->type
== __glutDeviceButtonRelease
) {
239 XDeviceButtonEvent
*devbtn
= (XDeviceButtonEvent
*) event
;
241 window
= __glutGetWindow(devbtn
->window
);
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
,
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
);
269 memset(&info
, 0, sizeof(JOYINFOEX
));
270 info
.dwSize
= sizeof(JOYINFOEX
);
271 info
.dwFlags
= JOY_RETURNALL
;
273 if (joyGetPosEx(JOYSTICKID1
,&info
) != JOYERR_NOERROR
) {
274 __glutHasJoystick
= 1;
275 joyGetDevCaps(JOYSTICKID1
, &joyCaps
, sizeof(joyCaps
));
276 __glutNumJoystickButtons
= joyCaps
.wNumButtons
;
277 __glutNumJoystickAxes
= joyCaps
.wNumAxes
;
279 __glutHasJoystick
= 0;
280 __glutNumJoystickButtons
= 0;
281 __glutNumJoystickAxes
= 0;
288 static GLUTeventParser eventParser
=
289 {__glutProcessDeviceEvents
, NULL
};
292 addDeviceEventParser(void)
294 static Bool been_here
= False
;
299 __glutRegisterEventParser(&eventParser
);
305 static Bool been_here
= False
;
308 XExtensionVersion
*version
;
309 XDeviceInfoPtr device_info
, device
;
314 int num_dev
, btns
, dials
;
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
) {
333 device_info
= XListInputDevices(__glutDisplay
, &num_dev
);
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. */
340 device
= &device_info
[i
];
341 any
= (XAnyClassPtr
) device
->inputclassinfo
;
343 if (!__glutSpaceball
&& !strcmp(device
->name
, "spaceball")) {
346 for (j
= 0; j
< device
->num_classes
; j
++) {
347 #if defined(__cplusplus) || defined(c_plusplus)
348 switch (any
->c_class
) {
350 switch (any
->class) {
353 b
= (XButtonInfoPtr
) any
;
354 btns
= b
->num_buttons
;
357 v
= (XValuatorInfoPtr
) any
;
358 /* Sanity check: at least 6 valuators? */
359 if (v
->num_axes
< NUM_SPACEBALL_AXIS
)
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
;
368 any
= (XAnyClassPtr
) ((char *) any
+ any
->length
);
371 __glutSpaceball
= XOpenDevice(__glutDisplay
, device
->id
);
372 if (__glutSpaceball
) {
373 __glutNumSpaceballButtons
= btns
;
374 addDeviceEventParser();
377 } else if (!__glutDials
&& !strcmp(device
->name
, "dial+buttons")) {
380 for (j
= 0; j
< device
->num_classes
; j
++) {
381 #if defined(__cplusplus) || defined(c_plusplus)
382 switch (any
->c_class
) {
384 switch (any
->class) {
387 b
= (XButtonInfoPtr
) any
;
388 btns
= b
->num_buttons
;
391 v
= (XValuatorInfoPtr
) any
;
392 /* Sanity check: at least 8 valuators? */
393 if (v
->num_axes
< NUM_DIALS_AXIS
)
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
;
403 any
= (XAnyClassPtr
) ((char *) any
+ any
->length
);
406 __glutDials
= XOpenDevice(__glutDisplay
, device
->id
);
408 __glutNumButtonBoxButtons
= btns
;
409 __glutNumDials
= dials
;
410 addDeviceEventParser();
413 } else if (!__glutTablet
&& !strcmp(device
->name
, "tablet")) {
416 for (j
= 0; j
< device
->num_classes
; j
++) {
417 #if defined(__cplusplus) || defined(c_plusplus)
418 switch (any
->c_class
) {
420 switch (any
->class) {
423 b
= (XButtonInfoPtr
) any
;
424 btns
= b
->num_buttons
;
427 v
= (XValuatorInfoPtr
) any
;
428 /* Sanity check: exactly 2 valuators? */
429 if (v
->num_axes
!= NUM_TABLET_AXIS
)
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
;
438 any
= (XAnyClassPtr
) ((char *) any
+ any
->length
);
441 __glutTablet
= XOpenDevice(__glutDisplay
, device
->id
);
443 __glutNumTabletButtons
= btns
;
444 addDeviceEventParser();
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
) {
452 if (any
->class == ButtonClass
) {
454 b
= (XButtonInfoPtr
) any
;
455 __glutNumMouseButtons
= b
->num_buttons
;
457 any
= (XAnyClassPtr
) ((char *) any
+ any
->length
);
462 XFreeDeviceList(device_info
);
465 __glutNumMouseButtons
= GetSystemMetrics(SM_CMOUSEBUTTONS
);
467 /* X Input extension might be supported, but only if there is
468 a tablet, dials, or spaceball do we claim devices are
470 support
= __glutTablet
|| __glutDials
|| __glutSpaceball
;
475 __glutUpdateInputDeviceMask(GLUTwindow
* window
)
478 /* 5 (dial and buttons) + 5 (tablet locator and buttons) + 5
479 (Spaceball buttons and axis) = 15 */
480 XEventClass eventList
[15];
487 if (window
->tabletMotion
) {
488 DeviceMotionNotify(__glutTablet
, __glutDeviceMotionNotify
,
489 eventList
[numEvents
]);
492 if (window
->tabletButton
) {
493 DeviceButtonPress(__glutTablet
, __glutDeviceButtonPress
,
494 eventList
[numEvents
]);
496 DeviceButtonPressGrab(__glutTablet
, __glutDeviceButtonPressGrab
,
497 eventList
[numEvents
]);
499 DeviceButtonRelease(__glutTablet
, __glutDeviceButtonRelease
,
500 eventList
[numEvents
]);
503 if (window
->tabletMotion
|| window
->tabletButton
) {
504 DeviceStateNotify(__glutTablet
, __glutDeviceStateNotify
,
505 eventList
[numEvents
]);
511 DeviceMotionNotify(__glutDials
, __glutDeviceMotionNotify
,
512 eventList
[numEvents
]);
515 if (window
->buttonBox
) {
516 DeviceButtonPress(__glutDials
, __glutDeviceButtonPress
,
517 eventList
[numEvents
]);
519 DeviceButtonPressGrab(__glutDials
, __glutDeviceButtonPressGrab
,
520 eventList
[numEvents
]);
522 DeviceButtonRelease(__glutDials
, __glutDeviceButtonRelease
,
523 eventList
[numEvents
]);
526 if (window
->dials
|| window
->buttonBox
) {
527 DeviceStateNotify(__glutDials
, __glutDeviceStateNotify
,
528 eventList
[numEvents
]);
532 if (__glutSpaceball
) {
533 if (window
->spaceMotion
|| window
->spaceRotate
) {
534 DeviceMotionNotify(__glutSpaceball
, __glutDeviceMotionNotify
,
535 eventList
[numEvents
]);
538 if (window
->spaceButton
) {
539 DeviceButtonPress(__glutSpaceball
, __glutDeviceButtonPress
,
540 eventList
[numEvents
]);
542 DeviceButtonPressGrab(__glutSpaceball
, __glutDeviceButtonPressGrab
,
543 eventList
[numEvents
]);
545 DeviceButtonRelease(__glutSpaceball
, __glutDeviceButtonRelease
,
546 eventList
[numEvents
]);
549 if (window
->spaceMotion
|| window
->spaceRotate
|| window
->spaceButton
) {
550 DeviceStateNotify(__glutSpaceball
, __glutDeviceStateNotify
,
551 eventList
[numEvents
]);
556 if (window
->children
) {
557 GLUTwindow
*child
= window
->children
;
560 XChangeDeviceDontPropagateList(__glutDisplay
, child
->win
,
561 numEvents
, eventList
, AddToList
);
562 child
= child
->siblings
;
566 XSelectExtensionEvent(__glutDisplay
, window
->win
,
567 eventList
, numEvents
);
568 if (window
->overlay
) {
569 XSelectExtensionEvent(__glutDisplay
, window
->overlay
->win
,
570 eventList
, numEvents
);
573 /* X Input extension not supported; no chance for exotic
581 glutDeviceGet(GLenum param
)
585 case GLUT_HAS_KEYBOARD
:
587 /* Assume window system always has mouse and keyboard. */
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
;
602 return __glutNumDials
;
603 case GLUT_NUM_TABLET_BUTTONS
:
604 return __glutNumTabletButtons
;
605 case GLUT_DEVICE_IGNORE_KEY_REPEAT
:
606 return __glutCurrentWindow
->ignoreKeyRepeat
;
608 case GLUT_DEVICE_KEY_REPEAT
:
610 XKeyboardState state
;
612 XGetKeyboardControl(__glutDisplay
, &state
);
613 return state
.global_auto_repeat
;
615 case GLUT_JOYSTICK_POLL_RATE
:
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
;
624 case GLUT_HAS_JOYSTICK
:
625 return __glutHasJoystick
;
626 case GLUT_JOYSTICK_BUTTONS
:
627 return __glutNumJoystickButtons
;
628 case GLUT_JOYSTICK_AXES
:
629 return __glutNumJoystickAxes
;
631 __glutWarning("invalid glutDeviceGet parameter: %d", param
);