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