| 1 | /* |
| 2 | * fg_joystick_x11.c |
| 3 | * |
| 4 | * Joystick handling code |
| 5 | * |
| 6 | * Copyright (c) 1999-2000 Pawel W. Olszta. All Rights Reserved. |
| 7 | * Written by Steve Baker, <sjbaker1@airmail.net> |
| 8 | * Copied for Platform code by Evan Felix <karcaw at gmail.com> |
| 9 | * Creation date: Thur Feb 2 2012 |
| 10 | * |
| 11 | * Permission is hereby granted, free of charge, to any person obtaining a |
| 12 | * copy of this software and associated documentation files (the "Software"), |
| 13 | * to deal in the Software without restriction, including without limitation |
| 14 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| 15 | * and/or sell copies of the Software, and to permit persons to whom the |
| 16 | * Software is furnished to do so, subject to the following conditions: |
| 17 | * |
| 18 | * The above copyright notice and this permission notice shall be included |
| 19 | * in all copies or substantial portions of the Software. |
| 20 | * |
| 21 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
| 22 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 23 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| 24 | * PAWEL W. OLSZTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER |
| 25 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
| 26 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| 27 | */ |
| 28 | |
| 29 | /* |
| 30 | * FreeBSD port by Stephen Montgomery-Smith <stephen@math.missouri.edu> |
| 31 | * |
| 32 | * Redone by John Fay 2/4/04 with another look from the PLIB "js" library. |
| 33 | * Many thanks for Steve Baker for permission to pull from that library. |
| 34 | */ |
| 35 | |
| 36 | #include <GL/freeglut.h> |
| 37 | #include "../fg_internal.h" |
| 38 | #ifdef HAVE_SYS_PARAM_H |
| 39 | # include <sys/param.h> |
| 40 | #endif |
| 41 | |
| 42 | #include <fcntl.h> |
| 43 | |
| 44 | |
| 45 | /*this should be defined in a header file */ |
| 46 | #define MAX_NUM_JOYSTICKS 2 |
| 47 | extern SFG_Joystick *fgJoystick [ MAX_NUM_JOYSTICKS ]; |
| 48 | |
| 49 | void fgPlatformJoystickRawRead( SFG_Joystick* joy, int* buttons, float* axes ) |
| 50 | { |
| 51 | int status; |
| 52 | |
| 53 | #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) |
| 54 | int len; |
| 55 | |
| 56 | if ( joy->pJoystick.os->is_analog ) |
| 57 | { |
| 58 | int status = read ( joy->pJoystick.os->fd, &joy->pJoystick.os->ajs, sizeof(joy->pJoystick.os->ajs) ); |
| 59 | if ( status != sizeof(joy->pJoystick.os->ajs) ) { |
| 60 | perror ( joy->pJoystick.os->fname ); |
| 61 | joy->error = GL_TRUE; |
| 62 | return; |
| 63 | } |
| 64 | if ( buttons != NULL ) |
| 65 | *buttons = ( joy->pJoystick.os->ajs.b1 ? 1 : 0 ) | ( joy->pJoystick.os->ajs.b2 ? 2 : 0 ); |
| 66 | |
| 67 | if ( axes != NULL ) |
| 68 | { |
| 69 | axes[0] = (float) joy->pJoystick.os->ajs.x; |
| 70 | axes[1] = (float) joy->pJoystick.os->ajs.y; |
| 71 | } |
| 72 | |
| 73 | return; |
| 74 | } |
| 75 | |
| 76 | # ifdef HAVE_USB_JS |
| 77 | while ( ( len = read ( joy->pJoystick.os->fd, joy->pJoystick.os->hid_data_buf, joy->pJoystick.os->hid_dlen ) ) == joy->pJoystick.os->hid_dlen ) |
| 78 | { |
| 79 | struct hid_item *h; |
| 80 | |
| 81 | for ( h = joy->pJoystick.os->hids; h; h = h->next ) |
| 82 | { |
| 83 | int d = hid_get_data ( joy->pJoystick.os->hid_data_buf, h ); |
| 84 | |
| 85 | int page = HID_PAGE ( h->usage ); |
| 86 | int usage = HID_USAGE ( h->usage ); |
| 87 | |
| 88 | if ( page == HUP_GENERIC_DESKTOP ) |
| 89 | { |
| 90 | int i; |
| 91 | for ( i = 0; i < joy->num_axes; i++ ) |
| 92 | if (joy->pJoystick.os->axes_usage[i] == usage) |
| 93 | { |
| 94 | if (usage == HUG_HAT_SWITCH) |
| 95 | { |
| 96 | if (d < 0 || d > 8) |
| 97 | d = 0; /* safety */ |
| 98 | joy->pJoystick.os->cache_axes[i] = (float)hatmap_x[d]; |
| 99 | joy->pJoystick.os->cache_axes[i + 1] = (float)hatmap_y[d]; |
| 100 | } |
| 101 | else |
| 102 | { |
| 103 | joy->pJoystick.os->cache_axes[i] = (float)d; |
| 104 | } |
| 105 | break; |
| 106 | } |
| 107 | } |
| 108 | else if (page == HUP_BUTTON) |
| 109 | { |
| 110 | if (usage > 0 && usage < _JS_MAX_BUTTONS + 1) |
| 111 | { |
| 112 | if (d) |
| 113 | joy->pJoystick.os->cache_buttons |= (1 << ( usage - 1 )); |
| 114 | else |
| 115 | joy->pJoystick.os->cache_buttons &= ~(1 << ( usage - 1 )); |
| 116 | } |
| 117 | } |
| 118 | } |
| 119 | } |
| 120 | if ( len < 0 && errno != EAGAIN ) |
| 121 | { |
| 122 | perror( joy->pJoystick.os->fname ); |
| 123 | joy->error = 1; |
| 124 | } |
| 125 | if ( buttons != NULL ) *buttons = joy->pJoystick.os->cache_buttons; |
| 126 | if ( axes != NULL ) |
| 127 | memcpy ( axes, joy->pJoystick.os->cache_axes, sizeof(float) * joy->num_axes ); |
| 128 | # endif |
| 129 | #endif |
| 130 | |
| 131 | #ifdef JS_NEW |
| 132 | |
| 133 | while ( 1 ) |
| 134 | { |
| 135 | status = read ( joy->pJoystick.fd, &joy->pJoystick.js, sizeof(struct js_event) ); |
| 136 | |
| 137 | if ( status != sizeof( struct js_event ) ) |
| 138 | { |
| 139 | if ( errno == EAGAIN ) |
| 140 | { |
| 141 | /* Use the old values */ |
| 142 | if ( buttons ) |
| 143 | *buttons = joy->pJoystick.tmp_buttons; |
| 144 | if ( axes ) |
| 145 | memcpy( axes, joy->pJoystick.tmp_axes, |
| 146 | sizeof( float ) * joy->num_axes ); |
| 147 | return; |
| 148 | } |
| 149 | |
| 150 | fgWarning ( "%s" , joy->pJoystick.fname ); |
| 151 | joy->error = GL_TRUE; |
| 152 | return; |
| 153 | } |
| 154 | |
| 155 | switch ( joy->pJoystick.js.type & ~JS_EVENT_INIT ) |
| 156 | { |
| 157 | case JS_EVENT_BUTTON: |
| 158 | if( joy->pJoystick.js.value == 0 ) /* clear the flag */ |
| 159 | joy->pJoystick.tmp_buttons &= ~( 1 << joy->pJoystick.js.number ); |
| 160 | else |
| 161 | joy->pJoystick.tmp_buttons |= ( 1 << joy->pJoystick.js.number ); |
| 162 | break; |
| 163 | |
| 164 | case JS_EVENT_AXIS: |
| 165 | if ( joy->pJoystick.js.number < joy->num_axes ) |
| 166 | { |
| 167 | joy->pJoystick.tmp_axes[ joy->pJoystick.js.number ] = ( float )joy->pJoystick.js.value; |
| 168 | |
| 169 | if( axes ) |
| 170 | memcpy( axes, joy->pJoystick.tmp_axes, sizeof(float) * joy->num_axes ); |
| 171 | } |
| 172 | break; |
| 173 | |
| 174 | default: |
| 175 | fgWarning ( "PLIB_JS: Unrecognised /dev/js return!?!" ); |
| 176 | |
| 177 | /* use the old values */ |
| 178 | |
| 179 | if ( buttons != NULL ) *buttons = joy->pJoystick.tmp_buttons; |
| 180 | if ( axes != NULL ) |
| 181 | memcpy ( axes, joy->pJoystick.tmp_axes, sizeof(float) * joy->num_axes ); |
| 182 | |
| 183 | return; |
| 184 | } |
| 185 | |
| 186 | if( buttons ) |
| 187 | *buttons = joy->pJoystick.tmp_buttons; |
| 188 | } |
| 189 | #else |
| 190 | |
| 191 | status = read( joy->pJoystick.fd, &joy->pJoystick.js, JS_RETURN ); |
| 192 | |
| 193 | if ( status != JS_RETURN ) |
| 194 | { |
| 195 | fgWarning( "%s" , joy->pJoystick.fname ); |
| 196 | joy->error = GL_TRUE; |
| 197 | return; |
| 198 | } |
| 199 | |
| 200 | if ( buttons ) |
| 201 | # if defined( __FreeBSD__ ) || defined(__FreeBSD_kernel__) || defined( __NetBSD__ ) |
| 202 | *buttons = ( joy->pJoystick.js.b1 ? 1 : 0 ) | ( joy->pJoystick.js.b2 ? 2 : 0 ); /* XXX Should not be here -- BSD is handled earlier */ |
| 203 | # else |
| 204 | *buttons = joy->pJoystick.js.buttons; |
| 205 | # endif |
| 206 | |
| 207 | if ( axes ) |
| 208 | { |
| 209 | axes[ 0 ] = (float) joy->pJoystick.js.x; |
| 210 | axes[ 1 ] = (float) joy->pJoystick.js.y; |
| 211 | } |
| 212 | #endif |
| 213 | } |
| 214 | |
| 215 | |
| 216 | void fgPlatformJoystickOpen( SFG_Joystick* joy ) |
| 217 | { |
| 218 | #if defined( __FreeBSD__ ) || defined(__FreeBSD_kernel__) || defined( __NetBSD__ ) |
| 219 | int i = 0; |
| 220 | char *cp; |
| 221 | #endif |
| 222 | #ifdef JS_NEW |
| 223 | unsigned char u; |
| 224 | int i=0; |
| 225 | #else |
| 226 | # if defined( __linux__ ) || TARGET_HOST_SOLARIS |
| 227 | int i = 0; |
| 228 | int counter = 0; |
| 229 | # endif |
| 230 | #endif |
| 231 | |
| 232 | #if defined( __FreeBSD__ ) || defined(__FreeBSD_kernel__) || defined( __NetBSD__ ) |
| 233 | for( i = 0; i < _JS_MAX_AXES; i++ ) |
| 234 | joy->pJoystick.os->cache_axes[ i ] = 0.0f; |
| 235 | |
| 236 | joy->pJoystick.os->cache_buttons = 0; |
| 237 | |
| 238 | joy->pJoystick.os->fd = open( joy->pJoystick.os->fname, O_RDONLY | O_NONBLOCK); |
| 239 | |
| 240 | if( joy->pJoystick.os->fd < 0 && errno == EACCES ) |
| 241 | fgWarning ( "%s exists but is not readable by you" , joy->pJoystick.os->fname ); |
| 242 | |
| 243 | joy->error =( joy->pJoystick.os->fd < 0 ); |
| 244 | |
| 245 | if( joy->error ) |
| 246 | return; |
| 247 | |
| 248 | joy->num_axes = 0; |
| 249 | joy->num_buttons = 0; |
| 250 | if( joy->pJoystick.os->is_analog ) |
| 251 | { |
| 252 | FILE *joyfile; |
| 253 | char joyfname[ 1024 ]; |
| 254 | int noargs, in_no_axes; |
| 255 | |
| 256 | float axes [ _JS_MAX_AXES ]; |
| 257 | int buttons[ _JS_MAX_AXES ]; |
| 258 | |
| 259 | joy->num_axes = 2; |
| 260 | joy->num_buttons = 32; |
| 261 | |
| 262 | fghJoystickRawRead( joy, buttons, axes ); |
| 263 | joy->error = axes[ 0 ] < -1000000000.0f; |
| 264 | if( joy->error ) |
| 265 | return; |
| 266 | |
| 267 | snprintf( joyfname, sizeof(joyfname), "%s/.joy%drc" , getenv( "HOME" ), joy->id ); |
| 268 | |
| 269 | joyfile = fopen( joyfname, "r" ); |
| 270 | joy->error =( joyfile == NULL ); |
| 271 | if( joy->error ) |
| 272 | return; |
| 273 | |
| 274 | noargs = fscanf( joyfile, "%d%f%f%f%f%f%f" , &in_no_axes, |
| 275 | &joy->min[ 0 ], &joy->center[ 0 ], &joy->max[ 0 ], |
| 276 | &joy->min[ 1 ], &joy->center[ 1 ], &joy->max[ 1 ] ); |
| 277 | joy->error = noargs != 7 || in_no_axes != _JS_MAX_AXES; |
| 278 | fclose( joyfile ); |
| 279 | if( joy->error ) |
| 280 | return; |
| 281 | |
| 282 | for( i = 0; i < _JS_MAX_AXES; i++ ) |
| 283 | { |
| 284 | joy->dead_band[ i ] = 0.0f; |
| 285 | joy->saturate [ i ] = 1.0f; |
| 286 | } |
| 287 | |
| 288 | return; /* End of analog code */ |
| 289 | } |
| 290 | |
| 291 | # ifdef HAVE_USB_JS |
| 292 | if( ! fghJoystickInitializeHID( joy->pJoystick.os, &joy->num_axes, |
| 293 | &joy->num_buttons ) ) |
| 294 | { |
| 295 | close( joy->pJoystick.os->fd ); |
| 296 | joy->error = GL_TRUE; |
| 297 | return; |
| 298 | } |
| 299 | |
| 300 | cp = strrchr( joy->pJoystick.os->fname, '/' ); |
| 301 | if( cp ) |
| 302 | { |
| 303 | if( fghJoystickFindUSBdev( &cp[1], joy->name, sizeof( joy->name ) ) == |
| 304 | 0 ) |
| 305 | strcpy( joy->name, &cp[1] ); |
| 306 | } |
| 307 | |
| 308 | if( joy->num_axes > _JS_MAX_AXES ) |
| 309 | joy->num_axes = _JS_MAX_AXES; |
| 310 | |
| 311 | for( i = 0; i < _JS_MAX_AXES; i++ ) |
| 312 | { |
| 313 | /* We really should get this from the HID, but that data seems |
| 314 | * to be quite unreliable for analog-to-USB converters. Punt for |
| 315 | * now. |
| 316 | */ |
| 317 | if( joy->pJoystick.os->axes_usage[ i ] == HUG_HAT_SWITCH ) |
| 318 | { |
| 319 | joy->max [ i ] = 1.0f; |
| 320 | joy->center[ i ] = 0.0f; |
| 321 | joy->min [ i ] = -1.0f; |
| 322 | } |
| 323 | else |
| 324 | { |
| 325 | joy->max [ i ] = 255.0f; |
| 326 | joy->center[ i ] = 127.0f; |
| 327 | joy->min [ i ] = 0.0f; |
| 328 | } |
| 329 | |
| 330 | joy->dead_band[ i ] = 0.0f; |
| 331 | joy->saturate[ i ] = 1.0f; |
| 332 | } |
| 333 | # endif |
| 334 | #endif |
| 335 | |
| 336 | #if defined( __linux__ ) || TARGET_HOST_SOLARIS |
| 337 | /* Default for older Linux systems. */ |
| 338 | joy->num_axes = 2; |
| 339 | joy->num_buttons = 32; |
| 340 | |
| 341 | # ifdef JS_NEW |
| 342 | for( i = 0; i < _JS_MAX_AXES; i++ ) |
| 343 | joy->pJoystick.tmp_axes[ i ] = 0.0f; |
| 344 | |
| 345 | joy->pJoystick.tmp_buttons = 0; |
| 346 | # endif |
| 347 | |
| 348 | joy->pJoystick.fd = open( joy->pJoystick.fname, O_RDONLY ); |
| 349 | |
| 350 | joy->error =( joy->pJoystick.fd < 0 ); |
| 351 | |
| 352 | if( joy->error ) |
| 353 | return; |
| 354 | |
| 355 | /* Set the correct number of axes for the linux driver */ |
| 356 | # ifdef JS_NEW |
| 357 | /* Melchior Franz's fixes for big-endian Linuxes since writing |
| 358 | * to the upper byte of an uninitialized word doesn't work. |
| 359 | * 9 April 2003 |
| 360 | */ |
| 361 | ioctl( joy->pJoystick.fd, JSIOCGAXES, &u ); |
| 362 | joy->num_axes = u; |
| 363 | ioctl( joy->pJoystick.fd, JSIOCGBUTTONS, &u ); |
| 364 | joy->num_buttons = u; |
| 365 | ioctl( joy->pJoystick.fd, JSIOCGNAME( sizeof( joy->name ) ), joy->name ); |
| 366 | fcntl( joy->pJoystick.fd, F_SETFL, O_NONBLOCK ); |
| 367 | # endif |
| 368 | |
| 369 | /* |
| 370 | * The Linux driver seems to return 512 for all axes |
| 371 | * when no stick is present - but there is a chance |
| 372 | * that could happen by accident - so it's gotta happen |
| 373 | * on both axes for at least 100 attempts. |
| 374 | * |
| 375 | * PWO: shouldn't be that done somehow wiser on the kernel level? |
| 376 | */ |
| 377 | # ifndef JS_NEW |
| 378 | counter = 0; |
| 379 | |
| 380 | do |
| 381 | { |
| 382 | fghJoystickRawRead( joy, NULL, joy->center ); |
| 383 | counter++; |
| 384 | } while( !joy->error && |
| 385 | counter < 100 && |
| 386 | joy->center[ 0 ] == 512.0f && |
| 387 | joy->center[ 1 ] == 512.0f ); |
| 388 | |
| 389 | if ( counter >= 100 ) |
| 390 | joy->error = GL_TRUE; |
| 391 | # endif |
| 392 | |
| 393 | for( i = 0; i < _JS_MAX_AXES; i++ ) |
| 394 | { |
| 395 | # ifdef JS_NEW |
| 396 | joy->max [ i ] = 32767.0f; |
| 397 | joy->center[ i ] = 0.0f; |
| 398 | joy->min [ i ] = -32767.0f; |
| 399 | # else |
| 400 | joy->max[ i ] = joy->center[ i ] * 2.0f; |
| 401 | joy->min[ i ] = 0.0f; |
| 402 | # endif |
| 403 | joy->dead_band[ i ] = 0.0f; |
| 404 | joy->saturate [ i ] = 1.0f; |
| 405 | } |
| 406 | #endif |
| 407 | } |
| 408 | |
| 409 | |
| 410 | void fgPlatformJoystickInit( SFG_Joystick *fgJoystick[], int ident ) |
| 411 | { |
| 412 | #if defined( __FreeBSD__ ) || defined(__FreeBSD_kernel__) || defined( __NetBSD__ ) |
| 413 | fgJoystick[ ident ]->id = ident; |
| 414 | fgJoystick[ ident ]->error = GL_FALSE; |
| 415 | |
| 416 | fgJoystick[ ident ]->pJoystick.os = calloc( 1, sizeof( struct os_specific_s ) ); |
| 417 | memset( fgJoystick[ ident ]->pJoystick.os, 0, sizeof( struct os_specific_s ) ); |
| 418 | if( ident < USB_IDENT_OFFSET ) |
| 419 | fgJoystick[ ident ]->pJoystick.os->is_analog = 1; |
| 420 | if( fgJoystick[ ident ]->pJoystick.os->is_analog ) |
| 421 | snprintf( fgJoystick[ ident ]->pJoystick.os->fname, sizeof(fgJoystick[ ident ]->pJoystick.os->fname), "%s%d" , AJSDEV, ident ); |
| 422 | else |
| 423 | snprintf( fgJoystick[ ident ]->pJoystick.os->fname, sizeof(fgJoystick[ ident ]->pJoystick.os->fname), "%s%d" , UHIDDEV, |
| 424 | ident - USB_IDENT_OFFSET ); |
| 425 | #elif defined( __linux__ ) |
| 426 | fgJoystick[ ident ]->id = ident; |
| 427 | fgJoystick[ ident ]->error = GL_FALSE; |
| 428 | |
| 429 | snprintf( fgJoystick[ident]->pJoystick.fname, sizeof(fgJoystick[ident]->pJoystick.fname), "/dev/input/js%d" , ident ); |
| 430 | |
| 431 | if( access( fgJoystick[ ident ]->pJoystick.fname, F_OK ) != 0 ) |
| 432 | snprintf( fgJoystick[ ident ]->pJoystick.fname, sizeof(fgJoystick[ ident ]->pJoystick.fname), "/dev/js%d" , ident ); |
| 433 | #endif |
| 434 | } |
| 435 | |
| 436 | |
| 437 | void fgPlatformJoystickClose ( int ident ) |
| 438 | { |
| 439 | #if defined( __FreeBSD__ ) || defined(__FreeBSD_kernel__) || defined( __NetBSD__ ) |
| 440 | if( fgJoystick[ident]->pJoystick.os ) |
| 441 | { |
| 442 | if( ! fgJoystick[ ident ]->error ) |
| 443 | close( fgJoystick[ ident ]->pJoystick.os->fd ); |
| 444 | #ifdef HAVE_USB_JS |
| 445 | if( fgJoystick[ ident ]->pJoystick.os->hids ) |
| 446 | free (fgJoystick[ ident ]->pJoystick.os->hids); |
| 447 | if( fgJoystick[ ident ]->pJoystick.os->hid_data_buf ) |
| 448 | free( fgJoystick[ ident ]->pJoystick.os->hid_data_buf ); |
| 449 | #endif |
| 450 | free( fgJoystick[ident]->pJoystick.os ); |
| 451 | } |
| 452 | #endif |
| 453 | |
| 454 | if( ! fgJoystick[ident]->error ) |
| 455 | close( fgJoystick[ ident ]->pJoystick.fd ); |
| 456 | } |
| 457 | |
| 458 | |