4 * Joystick handling code
6 * Copyright (c) 1999-2000 Pawel W. Olszta. All Rights Reserved.
7 * Written by Steve Baker, <sjbaker1@airmail.net>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 * Permission is hereby granted, free of charge, to any person obtaining a
14 * copy of this software and associated documentation files (the "Software"),
15 * to deal in the Software without restriction, including without limitation
16 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
17 * and/or sell copies of the Software, and to permit persons to whom the
18 * Software is furnished to do so, subject to the following conditions:
20 * The above copyright notice and this permission notice shall be included
21 * in all copies or substantial portions of the Software.
23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26 * PAWEL W. OLSZTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
27 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
28 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 * PWO: this is not exactly what Steve Baker has done for PLIB, as I had to convert
33 * it from C++ to C. And I've also reformatted it a bit (that's my little
34 * personal deviation :]) I don't really know if it is still portable...
35 * Steve: could you please add some comments to the code? :)
37 * FreeBSD port - courtesy of Stephen Montgomery-Smith <stephen@math.missouri.edu>
44 #define G_LOG_DOMAIN "freeglut-joystick"
46 #include "../include/GL/freeglut.h"
47 #include "../include/GL/freeglut_internal.h"
50 * PWO: I don't like it at all. It's a mess. Could it be cleared?
54 # if defined( __CYGWIN32__ ) || defined( __CYGWIN__ )
58 # include <mmsystem.h>
64 # include <machine/joystick.h>
65 # define JS_DATA_TYPE joystick
66 # define JS_RETURN (sizeof(struct JS_DATA_TYPE))
67 # elif defined(__linux__)
68 # include <sys/ioctl.h>
69 # include <linux/joystick.h>
73 * Check the joystick driver version
76 # if JS_VERSION >= 0x010000
84 * Not Windoze and no joystick driver...
86 * Well - we'll put these values in and that should
87 * allow the code to at least compile. The JS open
88 * routine should error out and shut off all the code
98 # define JS_RETURN (sizeof(struct JS_DATA_TYPE))
104 # define _JS_MAX_AXES 6
107 # define _JS_MAX_AXES 2
109 # define _JS_MAX_AXES 6
113 typedef struct tagSFG_Joystick SFG_Joystick;
114 struct tagSFG_Joystick
127 float tmp_axes[ _JS_MAX_AXES ];
140 float dead_band[ _JS_MAX_AXES ];
141 float saturate [ _JS_MAX_AXES ];
142 float center [ _JS_MAX_AXES ];
143 float max [ _JS_MAX_AXES ];
144 float min [ _JS_MAX_AXES ];
148 * The static joystick structure pointer
150 static SFG_Joystick* fgJoystick = NULL;
153 * Read the raw joystick data
155 static void fghJoystickRawRead ( SFG_Joystick* joy, gint* buttons, float* axes )
171 for( i=0; i<joy->num_axes; i++ )
178 status = joyGetPosEx( joy->js_id, &joy->js );
180 if( status != JOYERR_NOERROR )
187 *buttons = (int) joy->js.dwButtons;
192 * WARNING - Fall through case clauses!!
194 switch( joy->num_axes )
196 case 6: axes[5] = (float) joy->js.dwVpos;
197 case 5: axes[4] = (float) joy->js.dwUpos;
198 case 4: axes[3] = (float) joy->js.dwRpos;
199 case 3: axes[2] = (float) joy->js.dwZpos;
200 case 2: axes[1] = (float) joy->js.dwYpos;
201 case 1: axes[0] = (float) joy->js.dwXpos;
209 gint status = read( joy->fd, &joy->js, sizeof(struct js_event) );
211 if( status != sizeof(struct js_event) )
213 if( errno == EAGAIN )
218 if( buttons ) *buttons = joy->tmp_buttons;
219 if( axes ) memcpy( axes, joy->tmp_axes, sizeof(float) * joy->num_axes );
223 g_warning( joy->fname );
228 switch( joy->js.type & ~JS_EVENT_INIT )
230 case JS_EVENT_BUTTON:
231 if ( joy->js.value == 0 ) /* clear the flag */
232 joy->tmp_buttons &= ~(1 << joy->js.number);
234 joy->tmp_buttons |= (1 << joy->js.number);
238 joy->tmp_axes[ joy->js.number ] = (float) joy->js.value;
241 memcpy( axes, joy->tmp_axes, sizeof(float) * joy->num_axes );
246 *buttons = joy->tmp_buttons;
250 status = read( joy->fd, &joy->js, JS_RETURN );
252 if( status != JS_RETURN )
261 *buttons = (joy->js.b1 ? 1 : 0) | (joy->js.b2 ? 2 : 0);
263 *buttons = joy->js.buttons;
268 axes[ 0 ] = (float) joy->js.x;
269 axes[ 1 ] = (float) joy->js.y;
276 * Correct the joystick axis data
278 static float fghJoystickFudgeAxis( SFG_Joystick* joy, float value, gint axis )
280 if( value < joy->center[ axis ] )
282 float xx = (value - joy->center[ axis ]) / (joy->center[ axis ] - joy->min[ axis ]);
284 if( xx < -joy->saturate[ axis ] )
287 if( xx > -joy->dead_band [ axis ] )
290 xx = (xx + joy->dead_band[ axis ]) / (joy->saturate[ axis ] - joy->dead_band[ axis ]);
292 return( ( xx < -1.0f ) ? -1.0f : xx );
296 float xx = (value - joy->center [ axis ]) / (joy->max[ axis ] - joy->center[ axis ]);
298 if( xx > joy->saturate[ axis ] )
301 if( xx < joy->dead_band[ axis ] )
304 xx = (xx - joy->dead_band[ axis ]) / (joy->saturate[ axis ] - joy->dead_band[ axis ]);
306 return( ( xx > 1.0f ) ? 1.0f : xx );
311 * Read the corrected joystick data
313 static void fghJoystickRead( SFG_Joystick* joy, gint* buttons, float* axes )
315 float raw_axes[ _JS_MAX_AXES ];
324 for ( i=0; i<joy->num_axes ; i++ )
328 fghJoystickRawRead( joy, buttons, raw_axes );
331 for( i=0 ; i<joy->num_axes ; i++ )
332 axes[ i ] = fghJoystickFudgeAxis( joy, raw_axes[ i ], i );
336 * Happy happy happy joy joy joy (happy new year toudi :D)
338 static void fghJoystickOpen( SFG_Joystick* joy )
344 joy->js.dwFlags = JOY_RETURNALL;
345 joy->js.dwSize = sizeof( joy->js );
347 memset( &jsCaps, 0, sizeof(jsCaps) );
349 joy->error = (joyGetDevCaps( joy->js_id, &jsCaps, sizeof(jsCaps) ) != JOYERR_NOERROR);
350 joy->num_axes = (jsCaps.wNumAxes < _JS_MAX_AXES ) ? jsCaps.wNumAxes : _JS_MAX_AXES;
353 * WARNING - Fall through case clauses!!
355 switch( joy->num_axes )
357 case 6 : joy->min[ 5 ] = (float) jsCaps.wVmin; joy->max[ 5 ] = (float) jsCaps.wVmax;
358 case 5 : joy->min[ 4 ] = (float) jsCaps.wUmin; joy->max[ 4 ] = (float) jsCaps.wUmax;
359 case 4 : joy->min[ 3 ] = (float) jsCaps.wRmin; joy->max[ 3 ] = (float) jsCaps.wRmax;
360 case 3 : joy->min[ 2 ] = (float) jsCaps.wZmin; joy->max[ 2 ] = (float) jsCaps.wZmax;
361 case 2 : joy->min[ 1 ] = (float) jsCaps.wYmin; joy->max[ 1 ] = (float) jsCaps.wYmax;
362 case 1 : joy->min[ 0 ] = (float) jsCaps.wXmin; joy->max[ 0 ] = (float) jsCaps.wXmax; break;
365 * I guess we have no axes at all
367 default: joy->error = TRUE; break;
371 * Guess all the rest judging on the axes extremals
373 for( i=0 ; i<joy->num_axes ; i++ )
375 joy->center [ i ] = (joy->max[i] + joy->min[i]) * 0.5f;
376 joy->dead_band[ i ] = 0.0f;
377 joy->saturate [ i ] = 1.0f;
382 gint buttons[ _JS_MAX_AXES ];
383 float axes[ _JS_MAX_AXES ];
384 gint noargs, in_no_axes;
385 gchar joyfname[ 1024 ];
392 * Default for older Linux systems.
395 joy->num_buttons = 32;
398 for( i=0 ; i<_JS_MAX_AXES ; i++ )
399 joy->tmp_axes[ i ] = 0.0f ;
401 joy->tmp_buttons = 0 ;
404 joy->fd = open( joy->fname, O_RDONLY );
406 joy->error = (joy->fd < 0);
412 fghJoystickRawRead( buttons, axes );
413 joy->error = axes[ 0 ] < -1000000000.0f;
417 sprintf( joyfname, "%s/.joy%drc", g_getenv( "HOME" ), id );
419 joyfile = fopen( joyfname, "r" );
420 joy->error = (joyfile == NULL);
425 joyfile, "%d%f%f%f%f%f%f",
427 &joy->min[ 0 ], &joy->center[ 0 ], &joy->max[ 0 ],
428 &joy->min[ 1 ], &joy->center[ 1 ], &joy->max[ 1 ]
431 joy->error = (noargs != 7) || (in_no_axes != _JS_MAX_AXES);
436 for( i=0 ; i<_JS_MAX_AXES ; i++ )
438 dead_band[ i ] = 0.0f;
439 saturate [ i ] = 1.0f;
444 * Set the correct number of axes for the linux driver
447 ioctl( joy->fd, JSIOCGAXES , &joy->num_axes );
448 ioctl( joy->fd, JSIOCGBUTTONS, &joy->num_buttons );
449 fcntl( joy->fd, F_SETFL, O_NONBLOCK );
454 * The Linux driver seems to return 512 for all axes
455 * when no stick is present - but there is a chance
456 * that could happen by accident - so it's gotta happen
457 * on both axes for at least 100 attempts.
459 * PWO: shouldn't be that done somehow wiser on the kernel level?
466 fghJoystickRawRead( joy, NULL, center );
468 } while( !joy->error && counter < 100 && center[ 0 ] == 512.0f && center[ 1 ] == 512.0f );
474 for( i=0 ; i<_JS_MAX_AXES ; i++ )
477 joy->max [ i ] = 32767.0f;
478 joy->center[ i ] = 0.0f;
479 joy->min [ i ] = -32767.0f;
481 joy->max[ i ] = center[ i ] * 2.0f;
482 joy->min[ i ] = 0.0f;
484 joy->dead_band[ i ] = 0.0f ;
485 joy->saturate [ i ] = 1.0f ;
494 void fgJoystickInit( gint ident )
497 * Make sure we don't get reinitialized
499 if( fgJoystick != NULL )
500 g_error( "illegal attemp to initialize joystick device" );
503 * Have the global joystick structure created
505 fgJoystick = g_new0( SFG_Joystick, 1 );
510 case 0: fgJoystick->js_id = JOYSTICKID1; fghJoystickOpen( fgJoystick ); break;
511 case 1: fgJoystick->js_id = JOYSTICKID2; fghJoystickOpen( fgJoystick ); break;
512 default: fgJoystick->num_axes = 0; fgJoystick->error = TRUE; break;
517 fgJoystick->id = ident;
518 sprintf( fgJoystick->fname, "/dev/joy%d", ident );
520 sprintf( fgJoystick->fname, "/dev/js%d", ident );
524 * Let's try opening the joystick device now:
526 fghJoystickOpen( fgJoystick );
533 void fgJoystickClose( void )
535 if( fgJoystick == NULL )
536 g_error( "illegal attempt to deinitialize joystick device" );
539 if( fgJoystick->error != TRUE )
540 close( fgJoystick->fd );
545 * Polls the joystick and executes the joystick callback hooked to the
546 * window specified in the function's parameter:
548 void fgJoystickPollWindow( SFG_Window* window )
550 float axes[ _JS_MAX_AXES ];
554 * Make sure the joystick device is initialized, the window seems valid
555 * and that there is a joystick callback hooked to it:
557 freeglut_return_if_fail( fgJoystick == NULL || window == NULL );
558 freeglut_return_if_fail( window->Callbacks.Joystick == NULL );
561 * Poll the joystick now:
563 fghJoystickRead( fgJoystick, &buttons, axes );
566 * Execute the freeglut joystick callback now
568 window->Callbacks.Joystick(
570 (gint) (axes[ 0 ] * 1000.0f),
571 (gint) (axes[ 1 ] * 1000.0f),
572 (gint) (axes[ 2 ] * 1000.0f)
577 * PWO: These jsJoystick class methods have not been implemented.
578 * We might consider adding such functions to freeglut-2.0.
581 int getNumAxes () { return num_axes ; }
582 int notWorking () { return error ; }
584 float getDeadBand ( int axis ) { return dead_band [ axis ] ; }
585 void setDeadBand ( int axis, float db ) { dead_band [ axis ] = db ; }
587 float getSaturation ( int axis ) { return saturate [ axis ] ; }
588 void setSaturation ( int axis, float st ) { saturate [ axis ] = st ; }
590 void setMinRange ( float *axes ) { memcpy ( min , axes, num_axes * sizeof(float) ) ; }
591 void setMaxRange ( float *axes ) { memcpy ( max , axes, num_axes * sizeof(float) ) ; }
592 void setCenter ( float *axes ) { memcpy ( center, axes, num_axes * sizeof(float) ) ; }
594 void getMinRange ( float *axes ) { memcpy ( axes, min , num_axes * sizeof(float) ) ; }
595 void getMaxRange ( float *axes ) { memcpy ( axes, max , num_axes * sizeof(float) ) ; }
596 void getCenter ( float *axes ) { memcpy ( axes, center, num_axes * sizeof(float) ) ; }
599 /*** END OF FILE ***/