summaryrefslogtreecommitdiff
path: root/src/setup/joystick.c
diff options
context:
space:
mode:
authorSimon Howard2014-05-03 22:51:09 -0400
committerSimon Howard2014-05-03 22:51:09 -0400
commit930eae1c242daabda3668532e0638f5d19c65ed8 (patch)
treec15f6af24ef770017e64bb6155f03e6432d86467 /src/setup/joystick.c
parent0cd8f2e5fd268f48d09b5d38407ee5226dfd7e1b (diff)
downloadchocolate-doom-930eae1c242daabda3668532e0638f5d19c65ed8.tar.gz
chocolate-doom-930eae1c242daabda3668532e0638f5d19c65ed8.tar.bz2
chocolate-doom-930eae1c242daabda3668532e0638f5d19c65ed8.zip
setup: Start a list of known gamepads.
Detect certain gamepads by name and automatically configure defaults for buttons and axes, to minimize setup time. Supported so far are the PS3 controller and AIRFLO gamepad.
Diffstat (limited to 'src/setup/joystick.c')
-rw-r--r--src/setup/joystick.c217
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;
}