diff options
-rw-r--r-- | src/setup/joystick.c | 217 |
1 files changed, 212 insertions, 5 deletions
diff --git a/src/setup/joystick.c b/src/setup/joystick.c index 709ad34c..f0c65780 100644 --- a/src/setup/joystick.c +++ b/src/setup/joystick.c @@ -45,6 +45,19 @@ typedef enum CALIBRATE_DOWN, } calibration_stage_t; +typedef struct +{ + char *name; // Config file name + int value; +} joystick_config_t; + +typedef struct +{ + char *name; + int axes, buttons, hats; + const joystick_config_t *configs; +} known_joystick_t; + // SDL joystick successfully initialized? static int joystick_initted = 0; @@ -97,7 +110,7 @@ int joystick_physical_buttons[NUM_VIRTUAL_BUTTONS] = { static txt_button_t *joystick_button; // -// Calibration +// Calibration // static txt_window_t *calibration_window; @@ -105,6 +118,188 @@ static txt_label_t *calibration_label; static calibration_stage_t calibrate_stage; static SDL_Joystick **all_joysticks = NULL; +// Known controllers. +// There are lots of game controllers on the market. Try to configure +// them in a consistent way: +// +// * Use the D-pad rather than an analog stick. left/right turns the +// player, up/down moves forward/backward - ie. a "traditional" +// layout like Vanilla Doom rather than something more elaborate. +// * No strafe axis. +// * Fire and run keys together, on the main right-side buttons, +// ideally arranged so both can be controlled/covered simultaneously +// with the thumb. +// * Jump/use keys in the same cluster if possible. +// * Strafe left/right configured to map to shoulder buttons if they +// are present. No "strafe on" key unless shoulder buttons not present. +// * If a second set of shoulder buttons are also present, these map +// to prev weapon/next weapon. +// * Menu button mapped to start button. +// +// With the common right-side button arrangement that looks like this, +// which is similar to the Vanilla default configuration when using +// a Gravis Gamepad: +// +// B A = Fire +// A D B = Jump +// C C = Speed +// D = Use + +// Always loaded before others, to get a known starting configuration. +static const joystick_config_t empty_defaults[] = +{ + {"joystick_x_axis", -1}, + {"joystick_x_invert", 0}, + {"joystick_y_axis", -1}, + {"joystick_y_invert", 0}, + {"joystick_strafe_axis", -1}, + {"joystick_strafe_invert", 0}, + {"joyb_fire", -1}, + {"joyb_use", -1}, + {"joyb_strafe", -1}, + {"joyb_speed", -1}, + {"joyb_strafeleft", -1}, + {"joyb_straferight", -1}, + {"joyb_prevweapon", -1}, + {"joyb_nextweapon", -1}, + {"joyb_menu_activate", -1}, + {NULL, 0}, +}; + +static const joystick_config_t ps3_controller[] = +{ + {"joystick_x_axis", CREATE_BUTTON_AXIS(7, 5)}, + {"joystick_y_axis", CREATE_BUTTON_AXIS(4, 6)}, + {"joyb_fire", 15}, // Square + {"joyb_speed", 14}, // X + {"joyb_use", 13}, // Circle + {"joyb_jump", 12}, // Triangle + {"joyb_strafeleft", 8}, // Bottom shoulder buttons + {"joyb_straferight", 9}, + {"joyb_prevweapon", 10}, // Top shoulder buttons + {"joyb_nextweapon", 11}, + {"joyb_menu_activate", 3}, // Start + {NULL, 0}, +}; + +static const joystick_config_t airflo_controller[] = +{ + {"joystick_x_axis", CREATE_HAT_AXIS(0, HAT_AXIS_HORIZONTAL)}, + {"joystick_y_axis", CREATE_HAT_AXIS(0, HAT_AXIS_VERTICAL)}, + {"joyb_fire", 2}, // "3" + {"joyb_speed", 0}, // "1" + {"joyb_jump", 3}, // "4" + {"joyb_use", 1}, // "2" + {"joyb_strafeleft", 6}, // Bottom shoulder buttons + {"joyb_straferight", 7}, + {"joyb_prevweapon", 4}, // Top shoulder buttons + {"joyb_nextweapon", 5}, + {"joyb_menu_activate", 9}, // "10", where "Start" usually is. + {NULL, 0}, +}; + +static const known_joystick_t known_joysticks[] = +{ + { + "PLAYSTATION(R)3 Controller", + 4, 19, 0, + ps3_controller, + }, + + { + "AIRFLO ", + 4, 13, 1, + airflo_controller, + }, +}; + +static const known_joystick_t *GetJoystickType(int index) +{ + SDL_Joystick *joystick; + const char *name; + int axes, buttons, hats; + int i; + + joystick = all_joysticks[index]; + name = SDL_JoystickName(index); + axes = SDL_JoystickNumAxes(joystick); + buttons = SDL_JoystickNumButtons(joystick); + hats = SDL_JoystickNumHats(joystick); + + for (i = 0; i < arrlen(known_joysticks); ++i) + { + if (!strcmp(known_joysticks[i].name, name) + && known_joysticks[i].axes == axes + && known_joysticks[i].buttons == buttons + && known_joysticks[i].hats == hats) + { + return &known_joysticks[i]; + } + } + + printf("Unknown joystick '%s' with %i axes, %i buttons, %i hats\n", + name, axes, buttons, hats); + + return NULL; +} + +// Query if the joystick at the given index is a known joystick type. +static boolean IsKnownJoystick(int index) +{ + return GetJoystickType(index) != NULL; +} + +// Load a configuration set. +static void LoadConfigurationSet(const joystick_config_t *configs) +{ + const joystick_config_t *config; + char buf[10]; + int button; + int i; + + button = 0; + + for (i = 0; configs[i].name != NULL; ++i) + { + config = &configs[i]; + + // Don't overwrite autorun if it is set. + if (!strcmp(config->name, "joyb_speed") && joybspeed >= 20) + { + continue; + } + + // For buttons, set the virtual button mapping as well. + if (M_StringStartsWith(config->name, "joyb_")) + { + joystick_physical_buttons[button] = config->value; + M_snprintf(buf, sizeof(buf), "%i", button); + M_SetVariable(config->name, buf); + ++button; + } + else + { + M_snprintf(buf, sizeof(buf), "%i", config->value); + M_SetVariable(config->name, buf); + } + } +} + +// Load configuration for joystick_index based on known types. +static void LoadKnownConfiguration(void) +{ + const known_joystick_t *jstype; + + jstype = GetJoystickType(joystick_index); + if (jstype == NULL) + { + return; + } + + LoadConfigurationSet(empty_defaults); + LoadConfigurationSet(jstype->configs); +} + // Set the label showing the name of the currently selected joystick static void SetJoystickButtonLabel(void) @@ -113,7 +308,7 @@ static void SetJoystickButtonLabel(void) name = "None set"; - if (joystick_initted + if (joystick_initted && joystick_index >= 0 && joystick_index < SDL_NumJoysticks()) { name = (char *) SDL_JoystickName(joystick_index); @@ -463,9 +658,21 @@ static int CalibrationEventCallback(SDL_Event *event, void *user_data) calibrate_button = event->jbutton.button; IdentifyBadAxes(); - // Advance to next stage. - calibrate_stage = CALIBRATE_LEFT; - SetCalibrationLabel(); + // If the joystick is a known one, auto-load default + // config for it. + if (IsKnownJoystick(joystick_index)) + { + LoadKnownConfiguration(); + usejoystick = 1; + TXT_CloseWindow(calibration_window); + } + else + { + // Advance to next stage. + calibrate_stage = CALIBRATE_LEFT; + SetCalibrationLabel(); + } + return 1; } |