2 * Native Linux joystick for FreeSpace 2.
4 * Author: Karl Robillard <wickedsmoke@users.sf.net>
13 #include "osregistry.h"
14 #include "controlsconfig.h"
17 #include <linux/joystick.h>
23 #define DEFAULT_JOYSTICK 0
26 int Joy_sensitivity = 9;
27 int Dead_zone_size = 10; // percentage of range that is dead zone
31 static int _joyfd = -1;
32 static int _joy_axis_value[JOY_NUM_AXES];
35 typedef struct joy_button_info {
36 int state; // 1 when the button is down.
38 uint down_time; // Accumulated milliseconds down.
39 uint last_down_check; // Timestamp in milliseconds of last check.
40 uint down_post; // Time of last check or down transition.
43 static joy_button_info _joy_buttons[JOY_TOTAL_BUTTONS];
46 #define VALID_BTN(b) (_joyfd > -1) && (b >= 0) && (b < JOY_TOTAL_BUTTONS)
49 // --------------------------------------------------------------
52 // Return the state of button number 'btn'
54 // input: btn => button number to check
56 // returns: 0 => not pressed
63 return _joy_buttons[btn].state;
69 // --------------------------------------------------------------
72 // Return the number of times the joystick button has gone down since
73 // joy_down_count() was last called
75 // input: btn => button number to check
76 // reset_count => (default 1): if true reset down_count
78 // returns: number of times button 'btn' has gone down since last call
80 int joy_down_count(int btn, int reset_count)
84 int tmp = _joy_buttons[btn].down_count;
86 _joy_buttons[btn].down_count = 0;
93 // --------------------------------------------------------------
96 // Return a number between 0 and 1. This number represents the percentage
97 // time that the joystick button has been down since it was last checked
99 // input: btn => button number to check
101 // returns: value between 0 and 1
103 float joy_down_time(int btn)
111 now = timer_get_milliseconds();
113 bi = &_joy_buttons[btn];
117 bi->down_time += now - bi->down_post;
121 if (now > bi->last_down_check)
122 rval = i2fl(bi->down_time) / (now - bi->last_down_check);
127 bi->last_down_check = now;
140 // --------------------------------------------------------------
143 // Clear the state of the joystick.
151 uint now = timer_get_milliseconds();
153 for ( i = 0; i < JOY_TOTAL_BUTTONS; i++)
155 bi = &_joy_buttons[i];
160 bi->last_down_check = now;
166 int joy_get_unscaled_reading(int raw, int axn)
170 // Make sure it's calibrated properly.
171 if (joystick.axis_center[axn] - joystick.axis_min[axn] < 5)
174 if (joystick.axis_max[axn] - joystick.axis_center[axn] < 5)
177 rng = joystick.axis_max[axn] - joystick.axis_min[axn];
178 raw -= joystick.axis_min[axn]; // adjust for linear range starting at 0
186 return (int) ((unsigned int) raw * (unsigned int) F1_0 / (unsigned int) rng); // convert to 0 - F1_0 range.
190 // --------------------------------------------------------------
191 // joy_get_scaled_reading()
193 // input: raw => the raw value for an axis position
194 // axn => axis number, numbered starting at 0
196 // return: joy_get_scaled_reading will return a value that represents
197 // the joystick pos from -1 to +1 for the specified axis number
198 // 'axn', and the raw value 'raw'
200 int joy_get_scaled_reading(int raw, int axn)
202 int x, d, dead_zone, rng;
203 float percent, sensitivity_percent, non_sensitivity_percent;
205 // Make sure it's calibrated properly.
206 if (joystick.axis_center[axn] - joystick.axis_min[axn] < 5)
209 if (joystick.axis_max[axn] - joystick.axis_center[axn] < 5)
212 raw -= joystick.axis_center[axn];
214 dead_zone = (joystick.axis_max[axn] - joystick.axis_min[axn]) * Dead_zone_size / 100;
216 if (raw < -dead_zone) {
217 rng = joystick.axis_center[axn] - joystick.axis_min[axn] - dead_zone;
218 d = -raw - dead_zone;
220 } else if (raw > dead_zone) {
221 rng = joystick.axis_max[axn] - joystick.axis_center[axn] - dead_zone;
230 Assert(Joy_sensitivity >= 0 && Joy_sensitivity <= 9);
232 // compute percentages as a range between 0 and 1
233 sensitivity_percent = (float) Joy_sensitivity / 9.0f;
234 non_sensitivity_percent = (float) (9 - Joy_sensitivity) / 9.0f;
236 // find percent of max axis is at
237 percent = (float) d / (float) rng;
239 // work sensitivity on axis value
240 percent = (percent * sensitivity_percent + percent * percent * percent * percent * percent * non_sensitivity_percent);
242 x = (int) ((float) F1_0 * percent);
244 //nprintf(("AI", "d=%6i, sens=%3i, percent=%6.3f, val=%6i, ratio=%6.3f\n", d, Joy_sensitivity, percent, (raw<0) ? -x : x, (float) d/x));
253 // --------------------------------------------------------------
256 // input: x => OUTPUT PARAMETER: x-axis position of stick (-1 to 1)
257 // y => OUTPUT PARAMETER: y-axis position of stick (-1 to 1)
258 // z => OUTPUT PARAMETER: z-axis (throttle) position of stick (-1 to 1)
259 // r => OUTPUT PARAMETER: rudder position of stick (-1 to 1)
261 // return: success => 1
264 int joy_get_pos(int *x, int *y, int *z, int *rx)
274 // joy_get_scaled_reading will return a value represents the joystick pos from -1 to +1
275 if (x && joystick.axis_valid[0])
276 *x = joy_get_scaled_reading(_joy_axis_value[0], 0);
277 if (y && joystick.axis_valid[1])
278 *y = joy_get_scaled_reading(_joy_axis_value[1], 1);
279 if (z && joystick.axis_valid[2])
280 *z = joy_get_unscaled_reading(_joy_axis_value[2], 2);
281 if (rx && joystick.axis_valid[3])
282 *rx = joy_get_scaled_reading(_joy_axis_value[3], 3);
289 Convert Linux axis value to FreeSpace button.
291 static void convertAxisToHat( int value, int btnA, int btnB )
295 _joy_buttons[ btnA ].state = 1;
296 _joy_buttons[ btnB ].state = 0;
298 else if( value > 20000 )
300 _joy_buttons[ btnA ].state = 0;
301 _joy_buttons[ btnB ].state = 1;
305 _joy_buttons[ btnA ].state = 0;
306 _joy_buttons[ btnB ].state = 0;
318 while( read(_joyfd, &je, sizeof(struct js_event)) > 0 )
320 if( je.type & JS_EVENT_BUTTON )
322 // je.value will be 0 or 1.
324 if( je.number < JOY_NUM_AXES )
326 joy_button_info* bi = &_joy_buttons[ je.number ];
327 uint now = timer_get_milliseconds();
329 bi->state = je.value;
333 // Went from up to down.
339 // Went from down to up.
342 bi->down_time += now - bi->down_post;
348 else if( je.type & JS_EVENT_AXIS )
350 // je.value will be -32767 to +32767.
352 if( je.number < JOY_NUM_AXES )
354 _joy_axis_value[ je.number ] = je.value + 32768;
356 // Joystick hat is typically on axis 4/5
359 convertAxisToHat( je.value, JOY_HATLEFT, JOY_HATRIGHT );
361 else if( je.number == 5 )
363 convertAxisToHat( je.value, JOY_HATFORWARD, JOY_HATBACK );
369 if( errno != EAGAIN )
370 perror( "joy_read" );
374 static char _joy_device[] = "/dev/js0";
376 static bool openJoyDevice( int n )
378 _joy_device[7] = '0' + n;
379 _joyfd = open( _joy_device, O_RDONLY | O_NONBLOCK );
380 return (_joyfd > -1) ? true : false;
384 // --------------------------------------------------------------
387 // Initialize the joystick system. This is called once at game startup.
399 current = os_config_read_uint (NULL, "CurrentJoystick", DEFAULT_JOYSTICK);
401 if( ! openJoyDevice( current ) )
403 if( current != DEFAULT_JOYSTICK )
404 mprintf(("Unable to open joystick %s\n", _joy_device ));
406 for( i = 0; i < 4; ++i )
410 if( openJoyDevice( i ) )
419 mprintf(("No joysticks found\n"));
424 ioctl( _joyfd, JSIOCGAXES, &axes );
425 ioctl( _joyfd, JSIOCGBUTTONS, &buttons );
426 ioctl( _joyfd, JSIOCGNAME(80), &joyname );
428 nprintf (("JOYSTICK", "Joystick #%d: %s\n", current, joyname));
430 for (i=0; i < JOY_NUM_AXES; i++)
432 joystick.axis_valid[i] = (i < axes) ? 1 : 0;
433 _joy_axis_value[i] = 32768; // Center
438 // Fake a calibration
439 for (i=0; i<4; i++) {
440 joystick.axis_center[i] = 32768;
441 joystick.axis_min[i] = 0;
442 joystick.axis_max[i] = 65536;
446 // Map throttle by default if joystick has at least 3 axes.
449 extern int Axis_map_to_defaults[5];
450 Axis_map_to_defaults[3] = JOY_Z_AXIS;
457 // --------------------------------------------------------------
460 // Close the joystick system. Should be called at game exit.
474 joystick_read_raw_axis( 2, joystick.axis_center );
477 int joystick_read_raw_axis(int num_axes, int *axis)
484 for (i = 0; i < num_axes; i++)
485 axis[i] = _joy_axis_value[i];
490 void joy_ff_adjust_handling(int speed)
495 void joy_ff_afterburn_off()
500 void joy_ff_afterburn_on()
505 void joy_ff_deathroll()
515 void joy_ff_explode()
520 void joy_ff_fly_by(int mag)
525 void joy_ff_mission_init(vector v)
530 void joy_ff_play_dir_effect(float x, float y)
535 void joy_ff_play_primary_shoot(int gain)
540 void joy_ff_play_reload_effect()
545 void joy_ff_play_secondary_shoot(int gain)
550 void joy_ff_play_vector_effect(vector *v, float scaler)
555 void joy_ff_stop_effects()
557 joy_ff_afterburn_off();