2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
4 * All source code herein is the property of Volition, Inc. You may not sell
5 * or otherwise commercially exploit the source or things you created based on
14 #include "osregistry.h"
19 static int Joy_inited = 0;
20 int Dead_zone_size = 10;
21 int Joy_sensitivity = 9;
23 static int Joy_last_x_reading = 0;
24 static int Joy_last_y_reading = 0;
26 typedef struct Joy_info {
29 int axis_min[JOY_NUM_AXES];
30 int axis_center[JOY_NUM_AXES];
31 int axis_max[JOY_NUM_AXES];
32 int axis_current[JOY_NUM_AXES];
35 static Joy_info joystick;
37 typedef struct joy_button_info {
38 int actual_state; // Set if the button is physically down
39 int state; // Set when the button goes from up to down, cleared on down to up. Different than actual_state after a flush.
43 uint last_down_check; // timestamp in milliseconds of last
46 joy_button_info joy_buttons[JOY_TOTAL_BUTTONS];
48 static SDL_JoystickID JoystickID = -1;
57 bool joystick_is_controller()
59 return (joystick.is_controller == 1);
71 if ( joystick_is_controller() ) {
72 SDL_GameController *sdlcon = SDL_GameControllerFromInstanceID(JoystickID);
74 if ( SDL_GameControllerGetAttached(sdlcon) ) {
75 SDL_GameControllerClose(sdlcon);
78 SDL_Joystick *sdljoy = SDL_JoystickFromInstanceID(JoystickID);
80 if ( SDL_JoystickGetAttached(sdljoy) ) {
81 SDL_JoystickClose(sdljoy);
87 SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
98 if ( (btn < 0) || (btn >= JOY_TOTAL_BUTTONS)) {
102 tmp = joy_buttons[btn].state;
107 int joy_down_count(int btn, int reset_count)
115 if ( (btn < 0) || (btn >= JOY_TOTAL_BUTTONS)) {
119 tmp = joy_buttons[btn].down_count;
121 joy_buttons[btn].down_count = 0;
127 float joy_down_time(int btn)
130 unsigned int now, delta;
137 if ( (btn < 0) || (btn >= JOY_TOTAL_BUTTONS)) {
141 bi = &joy_buttons[btn];
143 now = timer_get_milliseconds();
144 delta = now - bi->last_down_check;
146 if ( (now - bi->last_down_check) > 0)
147 rval = i2fl((now - bi->down_time)) / delta;
152 bi->last_down_check = now;
162 void joy_mark_button(int btn, int state)
171 if ( (btn < 0) || (btn >= JOY_TOTAL_BUTTONS)) {
175 bi = &joy_buttons[btn];
182 bi->down_time = timer_get_milliseconds();
184 // toggle off other positions if hat
185 if (btn >= JOY_HATBACK) {
186 for (i = JOY_HATBACK; i < (JOY_HATBACK+JOY_NUM_HAT_POS); i++) {
188 joy_buttons[i].state = 0;
198 // special hat handling - make sure all hat pos are off
199 if (btn == JOY_HATBACK) {
200 for (i = JOY_HATBACK; i < (JOY_HATBACK+JOY_NUM_HAT_POS); i++) {
201 joy_buttons[i].state = 0;
216 for ( i = 0; i < JOY_TOTAL_BUTTONS; i++) {
217 bi = &joy_buttons[i];
222 bi->last_down_check = timer_get_milliseconds();
226 int joy_get_unscaled_reading(int axn)
234 if (axn >= joystick.num_axes) {
238 int raw = joystick.axis_current[axn];
240 rng = joystick.axis_max[axn] - joystick.axis_min[axn];
241 raw -= joystick.axis_min[axn]; // adjust for linear range starting at 0
249 return (int) ((unsigned int) raw * (unsigned int) F1_0 / (unsigned int) rng); // convert to 0 - F1_0 range.
252 // --------------------------------------------------------------
253 // joy_get_scaled_reading()
255 // input: raw => the raw value for an axis position
256 // axn => axis number, numbered starting at 0
258 // return: joy_get_scaled_reading will return a value that represents
259 // the joystick pos from -1 to +1 for the specified axis number 'axn', and
260 // the raw value 'raw'
262 int joy_get_scaled_reading(int axn)
264 int x, d, dead_zone, rng, raw;
265 float percent, sensitivity_percent, non_sensitivity_percent;
271 if (axn >= joystick.num_axes) {
275 raw = joystick.axis_current[axn] - joystick.axis_center[axn];
277 dead_zone = (joystick.axis_max[axn] - joystick.axis_min[axn]) * Dead_zone_size / 100;
279 if (raw < -dead_zone) {
280 rng = joystick.axis_center[axn] - joystick.axis_min[axn] - dead_zone;
281 d = -raw - dead_zone;
283 } else if (raw > dead_zone) {
284 rng = joystick.axis_max[axn] - joystick.axis_center[axn] - dead_zone;
294 SDL_assert(Joy_sensitivity >= 0 && Joy_sensitivity <= 9);
296 // compute percentages as a range between 0 and 1
297 sensitivity_percent = (float) Joy_sensitivity / 9.0f;
298 non_sensitivity_percent = (float) (9 - Joy_sensitivity) / 9.0f;
300 // find percent of max axis is at
301 percent = (float) d / (float) rng;
303 // work sensitivity on axis value
304 percent = (percent * sensitivity_percent + percent * percent * percent * percent * percent * non_sensitivity_percent);
306 x = (int) ((float) F1_0 * percent);
308 //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));
317 // --------------------------------------------------------------
320 // input: x => OUTPUT PARAMETER: x-axis position of stick (-1 to 1)
321 // y => OUTPUT PARAMETER: y-axis position of stick (-1 to 1)
322 // z => OUTPUT PARAMETER: z-axis (throttle) position of stick (-1 to 1)
323 // r => OUTPUT PARAMETER: rudder position of stick (-1 to 1)
325 // return: success => 1
328 int joy_get_pos(int *x, int *y, int *z, int *rx)
339 // joy_get_scaled_reading will return a value represents the joystick
341 if (x && joystick.num_axes > 0) {
342 *x = joy_get_scaled_reading(0);
343 Joy_last_x_reading = *x;
346 if (y && joystick.num_axes > 1) {
347 *y = joy_get_scaled_reading(1);
348 Joy_last_y_reading = *y;
351 if (z && joystick.num_axes > 2) {
352 *z = joy_get_unscaled_reading(2);
355 if (rx && joystick.num_axes > 3) {
356 *rx = joy_get_scaled_reading(3);
362 static int joy_init_internal(int with_index)
365 const char *ptr = nullptr;
367 SDL_Joystick *sdljoy = nullptr;
368 const char *joy_name = nullptr;
369 SDL_JoystickGUID guid;
374 num_sticks = SDL_NumJoysticks();
376 if (num_sticks < 1) {
377 mprintf((" No joysticks found\n\n"));
381 if ( (with_index >= 0) && (with_index < num_sticks) ) {
382 Cur_joystick = with_index;
386 ptr = os_config_read_string("Controls", "CurrentJoystick", nullptr);
388 if ( ptr && SDL_strlen(ptr) ) {
389 for (i = 0; i < num_sticks; i++) {
390 const char *jname = nullptr;
392 if ( SDL_IsGameController(i) ) {
393 jname = SDL_GameControllerNameForIndex(i);
395 jname = SDL_JoystickNameForIndex(i);
398 if ( jname && !SDL_strcasecmp(ptr, jname) ) {
406 if ( SDL_IsGameController(Cur_joystick) ) {
407 joystick.is_controller = 1;
409 SDL_GameController *sdlcon = SDL_GameControllerOpen(Cur_joystick);
411 if (sdlcon == nullptr) {
412 mprintf((" Unable to init game controller %d (%s)\n\n", Cur_joystick, SDL_GameControllerNameForIndex(Cur_joystick)));
416 joy_name = SDL_GameControllerName(sdlcon);
418 sdljoy = SDL_GameControllerGetJoystick(sdlcon);
420 joystick.is_controller = 0;
422 sdljoy = SDL_JoystickOpen(Cur_joystick);
424 if (sdljoy == nullptr) {
425 mprintf((" Unable to init joystick %d (%s)\n\n", Cur_joystick, SDL_JoystickNameForIndex(Cur_joystick)));
429 joy_name = SDL_JoystickName(sdljoy);
432 JoystickID = SDL_JoystickInstanceID(sdljoy);
434 guid = SDL_JoystickGetGUID(sdljoy);
437 SDL_JoystickGetGUIDString(guid, guid_str, SDL_arraysize(guid_str));
439 mprintf((" Name : %s\n", joy_name ? joy_name : "<unknown>"));
440 mprintf((" GUID : %s\n", guid_str));
441 mprintf((" Controller : %s\n", SDL_IsGameController(Cur_joystick) ? "Yes" : "No"));
442 mprintf((" Axes : %d\n", SDL_JoystickNumAxes(sdljoy)));
443 mprintf((" Buttons : %d\n", SDL_JoystickNumButtons(sdljoy)));
444 mprintf((" Hats : %d\n", SDL_JoystickNumHats(sdljoy)));
445 mprintf((" Haptic : %s\n", SDL_JoystickIsHaptic(sdljoy) ? "Yes" : "No"));
451 joystick.num_axes = SDL_JoystickNumAxes(sdljoy);
455 for (i = 0; i < JOY_NUM_AXES; i++) {
456 joystick.axis_min[i] = SDL_JOYSTICK_AXIS_MIN;
457 joystick.axis_max[i] = SDL_JOYSTICK_AXIS_MAX;
460 if (joystick.is_controller == 1) {
461 // the last two axes should be triggers, so set values manually
462 for (i = JOY_NUM_AXES-2; i < JOY_NUM_AXES; i++) {
463 joystick.axis_min[i] = 0;
464 joystick.axis_max[i] = SDL_JOYSTICK_AXIS_MAX;
477 mprintf(("Initializing Joystick...\n"));
479 if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) < 0) {
480 mprintf((" Could not initialize joystick subsystem\n\n"));
484 int num_sticks = joy_init_internal(-1);
486 if (num_sticks > 0) {
493 void joy_reinit(int with_index)
500 // close out what we have already opened...
503 if (joystick.is_controller == 1) {
504 SDL_GameController *sdlcon = SDL_GameControllerFromInstanceID(JoystickID);
506 if ( SDL_GameControllerGetAttached(sdlcon) ) {
507 SDL_GameControllerClose(sdlcon);
510 SDL_Joystick *sdljoy = SDL_JoystickFromInstanceID(JoystickID);
512 if ( SDL_JoystickGetAttached(sdljoy) ) {
513 SDL_JoystickClose(sdljoy);
519 // attempt to get a new joystick to use...
520 mprintf(("Re-Initializing Joystick...\n"));
522 joy_init_internal(with_index);
527 SDL_GameController *sdlcon = nullptr;
528 SDL_Joystick *sdljoy = nullptr;
534 if ( joystick_is_controller() ) {
535 sdlcon = SDL_GameControllerFromInstanceID(JoystickID);
537 sdljoy = SDL_JoystickFromInstanceID(JoystickID);
540 for (int i = 0; i < JOY_NUM_AXES; i++) {
541 if (i < joystick.num_axes) {
542 if ( joystick_is_controller() ) {
543 joystick.axis_center[i] = SDL_GameControllerGetAxis(sdlcon, (SDL_GameControllerAxis)i);
545 joystick.axis_center[i] = SDL_JoystickGetAxis(sdljoy, i);
548 joystick.axis_center[i] = 0;
554 int joystick_read_raw_axis(int num_axes, int *axis)
562 for (i = 0; i < num_axes; i++) {
563 if (i < joystick.num_axes) {
564 axis[i] = joystick.axis_current[i];
573 bool joy_axis_valid(int axis)
575 return (axis < joystick.num_axes);
578 void joystick_update_axis(int axis, int value)
580 if (axis < JOY_NUM_AXES) {
581 joystick.axis_current[axis] = value;;