From b17c29bac50d81c252352092b1fe78d2b442b758 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Fri, 28 Nov 2025 09:43:20 -0800
Subject: [PATCH] Improved joystick control
The player can be controlled using either thumbstick or D-pad, and fire and shield with the triggers.
The start button can be used to pause the game and the back button can be used to quit.
---
game/controls.cpp | 191 ++++++++++++++++++++++++----------------------
game/init.cpp | 6 +-
2 files changed, 101 insertions(+), 96 deletions(-)
diff --git a/game/controls.cpp b/game/controls.cpp
index 516353f3..21e22de0 100644
--- a/game/controls.cpp
+++ b/game/controls.cpp
@@ -241,14 +241,14 @@ static Uint8 joystickMasks[MAX_JOYSTICKS] = {
CONTROL_JOYSTICK2,
CONTROL_JOYSTICK3
};
-static SDL_Joystick *joysticks[MAX_JOYSTICKS];
+static SDL_Gamepad *joysticks[MAX_JOYSTICKS];
static SDL_JoystickID joystickIDs[MAX_JOYSTICKS];
static void OpenJoystick(SDL_JoystickID id)
{
for (int i = 0; i < MAX_JOYSTICKS; ++i) {
if (joysticks[i] == NULL) {
- joysticks[i] = SDL_OpenJoystick(id);
+ joysticks[i] = SDL_OpenGamepad(id);
if (joysticks[i]) {
joystickIDs[i] = id;
}
@@ -261,7 +261,7 @@ static void CloseJoystick(SDL_JoystickID id)
{
for (int i = 0; i < MAX_JOYSTICKS; ++i) {
if (joystickIDs[i] == id) {
- SDL_CloseJoystick(joysticks[i]);
+ SDL_CloseGamepad(joysticks[i]);
joysticks[i] = NULL;
joystickIDs[i] = 0;
break;
@@ -279,6 +279,77 @@ static Player *GetJoystickPlayer(SDL_JoystickID id)
return NULL;
}
+static void UpdateControl(Player *player)
+{
+ bool keys[FIRE_KEY+1];
+
+ SDL_zeroa(keys);
+ for (int i = 0; i < MAX_JOYSTICKS; ++i) {
+ SDL_Gamepad *gamepad = joysticks[i];
+ if (!gamepad) {
+ continue;
+ }
+
+ if (!(player->GetControlType() & joystickMasks[i])) {
+ continue;
+ }
+
+ if (SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_SOUTH) ||
+ SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER) >= 8000) {
+ keys[FIRE_KEY] = true;
+ }
+
+ if (SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_EAST) ||
+ SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFT_TRIGGER) >= 8000) {
+ keys[SHIELD_KEY] = true;
+ }
+
+ if (SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_LEFT) ||
+ SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTX) <= -16000 ||
+ SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX) <= -16000) {
+ keys[LEFT_KEY] = true;
+ }
+
+ if (SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_RIGHT) ||
+ SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTX) >= 16000 ||
+ SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX) >= 16000) {
+ keys[RIGHT_KEY] = true;
+ }
+
+ if (SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_UP) ||
+ SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTY) <= -16000 ||
+ SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) <= -16000) {
+ keys[THRUST_KEY] = true;
+ }
+ }
+
+ if (player->GetControlType() & CONTROL_KEYBOARD) {
+ const bool *keystate = SDL_GetKeyboardState(nullptr);
+
+ if (keystate[SDL_GetScancodeFromKey(controls.gFireControl, SDL_KMOD_NONE)]) {
+ keys[FIRE_KEY] = true;
+ }
+ if (keystate[SDL_GetScancodeFromKey(controls.gTurnRControl, SDL_KMOD_NONE)]) {
+ keys[RIGHT_KEY] = true;
+ }
+ if (keystate[SDL_GetScancodeFromKey(controls.gTurnLControl, SDL_KMOD_NONE)]) {
+ keys[LEFT_KEY] = true;
+ }
+ if (keystate[SDL_GetScancodeFromKey(controls.gShieldControl, SDL_KMOD_NONE)]) {
+ keys[SHIELD_KEY] = true;
+ }
+ if (keystate[SDL_GetScancodeFromKey(controls.gThrustControl, SDL_KMOD_NONE)]) {
+ keys[THRUST_KEY] = true;
+ }
+ }
+
+ player->SetControl(FIRE_KEY, keys[FIRE_KEY]);
+ player->SetControl(SHIELD_KEY, keys[SHIELD_KEY]);
+ player->SetControl(LEFT_KEY, keys[LEFT_KEY]);
+ player->SetControl(RIGHT_KEY, keys[RIGHT_KEY]);
+ player->SetControl(THRUST_KEY, keys[THRUST_KEY]);
+}
+
static void HandleEvent(SDL_Event *event)
{
Player *player;
@@ -290,89 +361,45 @@ static void HandleEvent(SDL_Event *event)
switch (event->type) {
/* -- Handle joystick added */
- case SDL_EVENT_JOYSTICK_ADDED:
- OpenJoystick(event->jdevice.which);
+ case SDL_EVENT_GAMEPAD_ADDED:
+ OpenJoystick(event->gdevice.which);
break;
/* -- Handle joystick removed */
- case SDL_EVENT_JOYSTICK_REMOVED:
- CloseJoystick(event->jdevice.which);
- break;
-
- /* -- Handle joystick axis motion */
- case SDL_EVENT_JOYSTICK_AXIS_MOTION:
- player = GetJoystickPlayer(event->jaxis.which);
- if (!player) {
- break;
- }
- /* X-Axis - rotate right/left */
- if ( event->jaxis.axis == 0 ) {
- if ( event->jaxis.value < -16000 ) {
- player->SetControl(LEFT_KEY, 1);
- player->SetControl(RIGHT_KEY, 0);
- } else
- if ( event->jaxis.value > 16000 ) {
- player->SetControl(RIGHT_KEY, 1);
- player->SetControl(LEFT_KEY, 0);
- } else {
- player->SetControl(LEFT_KEY, 0);
- player->SetControl(RIGHT_KEY, 0);
- }
- } else
- /* Y-Axis - accelerate */
- if ( event->jaxis.axis == 1 ) {
- if ( event->jaxis.value < -20000 ) {
- player->SetControl(THRUST_KEY, 1);
- } else {
- player->SetControl(THRUST_KEY, 0);
- }
- }
+ case SDL_EVENT_GAMEPAD_REMOVED:
+ CloseJoystick(event->gdevice.which);
break;
/* -- Handle joystick axis motion */
- case SDL_EVENT_JOYSTICK_HAT_MOTION:
- player = GetJoystickPlayer(event->jhat.which);
+ case SDL_EVENT_GAMEPAD_AXIS_MOTION:
+ player = GetJoystickPlayer(event->gaxis.which);
if (!player) {
break;
}
- if ( event->jhat.value & SDL_HAT_LEFT ) {
- player->SetControl(LEFT_KEY, 1);
- } else {
- player->SetControl(LEFT_KEY, 0);
- }
- if ( event->jhat.value & SDL_HAT_RIGHT ) {
- player->SetControl(RIGHT_KEY, 1);
- } else {
- player->SetControl(RIGHT_KEY, 0);
- }
- if ( event->jhat.value & SDL_HAT_UP ) {
- player->SetControl(THRUST_KEY, 1);
- } else {
- player->SetControl(THRUST_KEY, 0);
- }
+ UpdateControl(player);
break;
/* -- Handle joystick button presses/releases */
- case SDL_EVENT_JOYSTICK_BUTTON_DOWN:
- case SDL_EVENT_JOYSTICK_BUTTON_UP:
- player = GetJoystickPlayer(event->jbutton.which);
+ case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
+ case SDL_EVENT_GAMEPAD_BUTTON_UP:
+ player = GetJoystickPlayer(event->gbutton.which);
if (!player) {
break;
}
- if ( event->jbutton.down ) {
- if ( event->jbutton.button == 0 ) {
- player->SetControl(FIRE_KEY, 1);
- } else
- if ( event->jbutton.button == 1 ) {
- player->SetControl(SHIELD_KEY, 1);
+ switch (event->gbutton.button) {
+ case SDL_GAMEPAD_BUTTON_START:
+ if (!event->gbutton.down) {
+ gGameInfo.ToggleLocalState(STATE_PAUSE);
}
- } else {
- if ( event->jbutton.button == 0 ) {
- player->SetControl(FIRE_KEY, 0);
- } else
- if ( event->jbutton.button == 1 ) {
- player->SetControl(SHIELD_KEY, 0);
+ break;
+ case SDL_GAMEPAD_BUTTON_BACK:
+ if (!event->gbutton.down) {
+ gGameInfo.SetLocalState(STATE_ABORT, true);
}
+ break;
+ default:
+ UpdateControl(player);
+ break;
}
break;
@@ -385,17 +412,8 @@ static void HandleEvent(SDL_Event *event)
break;
}
- /* Check for various control keys */
- if ( key == controls.gFireControl )
- player->SetControl(FIRE_KEY, 1);
- else if ( key == controls.gTurnRControl )
- player->SetControl(RIGHT_KEY, 1);
- else if ( key == controls.gTurnLControl )
- player->SetControl(LEFT_KEY, 1);
- else if ( key == controls.gShieldControl )
- player->SetControl(SHIELD_KEY, 1);
- else if ( key == controls.gThrustControl )
- player->SetControl(THRUST_KEY, 1);
+ /* Update control key status */
+ UpdateControl(player);
break;
case SDL_EVENT_KEY_UP:
@@ -436,16 +454,7 @@ static void HandleEvent(SDL_Event *event)
}
/* Update control key status */
- if ( key == controls.gFireControl )
- player->SetControl(FIRE_KEY, 0);
- else if ( key == controls.gTurnRControl )
- player->SetControl(RIGHT_KEY, 0);
- else if ( key == controls.gTurnLControl )
- player->SetControl(LEFT_KEY, 0);
- else if ( key == controls.gShieldControl )
- player->SetControl(SHIELD_KEY, 0);
- else if ( key == controls.gThrustControl )
- player->SetControl(THRUST_KEY, 0);
+ UpdateControl(player);
break;
case SDL_EVENT_WINDOW_MINIMIZED:
@@ -477,7 +486,7 @@ void QuitPlayerControls(void)
{
for (int i = 0; i < MAX_JOYSTICKS; ++i) {
if (joysticks[i]) {
- SDL_CloseJoystick(joysticks[i]);
+ SDL_CloseGamepad(joysticks[i]);
joysticks[i] = NULL;
}
}
diff --git a/game/init.cpp b/game/init.cpp
index baed5d72..2f32cf9f 100644
--- a/game/init.cpp
+++ b/game/init.cpp
@@ -763,11 +763,7 @@ int DoInitializations(Uint32 window_flags)
// -- Load our controls
LoadControls();
- Uint32 init_flags = (SDL_INIT_VIDEO|SDL_INIT_AUDIO);
-#ifdef SDL_INIT_JOYSTICK
- init_flags |= SDL_INIT_JOYSTICK;
-#endif
- if ( !SDL_Init(init_flags) ) {
+ if ( !SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO|SDL_INIT_GAMEPAD) ) {
error("Couldn't initialize SDL: %s\n", SDL_GetError());
return(-1);
}