From 21e6c5814c1e6c30157e015a3db8131c66fe005f Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Mon, 10 Mar 2025 17:25:01 -0700
Subject: [PATCH] Fixed Nintendo controller face button layout under Steam
(cherry picked from commit 2ce3dfdf1df248dbd3a694bfe3a4d4a869db3476)
---
src/joystick/SDL_joystick.c | 43 ++++++++++++++++++++++++++++++++++
src/joystick/SDL_sysjoystick.h | 1 +
2 files changed, 44 insertions(+)
diff --git a/src/joystick/SDL_joystick.c b/src/joystick/SDL_joystick.c
index 7574adc6c0771..a4d86688c7302 100644
--- a/src/joystick/SDL_joystick.c
+++ b/src/joystick/SDL_joystick.c
@@ -1043,6 +1043,27 @@ static void CleanupSensorFusion(SDL_Joystick *joystick)
}
}
+static bool ShouldSwapFaceButtons(const SDL_SteamVirtualGamepadInfo *info)
+{
+ // When "Use Nintendo Button Layout" is enabled under Steam (the default)
+ // it will send button 0 for the A (east) button and button 1 for the
+ // B (south) button. This is done so that games that interpret the
+ // buttons as Xbox input will get button 0 for "A" as they expect.
+ //
+ // However, SDL reports positional buttons, so we need to swap
+ // the buttons so they show up in the correct position. This provides
+ // consistent behavior regardless of whether we're running under Steam,
+ // under the default settings.
+ if (info &&
+ (info->type == SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO ||
+ info->type == SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_LEFT ||
+ info->type == SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT ||
+ info->type == SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_PAIR)) {
+ return true;
+ }
+ return false;
+}
+
/*
* Open a joystick for use - the index passed as an argument refers to
* the N'th joystick on the system. This index is the value which will
@@ -1148,6 +1169,7 @@ SDL_Joystick *SDL_OpenJoystick(SDL_JoystickID instance_id)
info = SDL_GetJoystickVirtualGamepadInfoForID(instance_id);
if (info) {
joystick->steam_handle = info->handle;
+ joystick->swap_face_buttons = ShouldSwapFaceButtons(info);
}
// Use system gyro and accelerometer if the gamepad doesn't have built-in sensors
@@ -2305,6 +2327,25 @@ void SDL_SendJoystickButton(Uint64 timestamp, SDL_Joystick *joystick, Uint8 butt
event.type = SDL_EVENT_JOYSTICK_BUTTON_UP;
}
+ if (joystick->swap_face_buttons) {
+ switch (button) {
+ case 0:
+ button = 1;
+ break;
+ case 1:
+ button = 0;
+ break;
+ case 2:
+ button = 3;
+ break;
+ case 3:
+ button = 2;
+ break;
+ default:
+ break;
+ }
+ }
+
// Make sure we're not getting garbage or duplicate events
if (button >= joystick->nbuttons) {
return;
@@ -2353,11 +2394,13 @@ static void SendSteamHandleUpdateEvents(void)
if (info) {
if (joystick->steam_handle != info->handle) {
joystick->steam_handle = info->handle;
+ joystick->swap_face_buttons = ShouldSwapFaceButtons(info);
changed = true;
}
} else {
if (joystick->steam_handle != 0) {
joystick->steam_handle = 0;
+ joystick->swap_face_buttons = false;
changed = true;
}
}
diff --git a/src/joystick/SDL_sysjoystick.h b/src/joystick/SDL_sysjoystick.h
index 343d2061e7178..97f145de9a28e 100644
--- a/src/joystick/SDL_sysjoystick.h
+++ b/src/joystick/SDL_sysjoystick.h
@@ -83,6 +83,7 @@ struct SDL_Joystick
SDL_GUID guid _guarded; // Joystick guid
Uint16 firmware_version _guarded; // Firmware version, if available
Uint64 steam_handle _guarded; // Steam controller API handle
+ bool swap_face_buttons _guarded; // Whether we should swap face buttons
int naxes _guarded; // Number of axis controls on the joystick
SDL_JoystickAxisInfo *axes _guarded;