From b27fa1dd7a6f9775e927ab7b45872a9a4b65fad4 Mon Sep 17 00:00:00 2001 From: Gibson Pilconis Date: Thu, 26 Mar 2026 22:54:05 -0400 Subject: [PATCH] Fix rumble and add PS4/PS5 controller rumble. (#1007) Previously `thread6_rumble_loop()` was only being called once in `thread5_game_loop()`. This commit removes that call and adds one to `game_loop_one_iteration()`, creating the desired outcome of `thread6_runnable_loop()` running once per frame. Additionally, PS4 and PS5 rumble support is added. PS4 and PS5 rumble requires a feature called 'extended reporting', which can be enabled using two SDL hints. Since extended reported, once enabled, breaks compatibility with DirectInput until the controller is restarted, an option is also added to the configuration menu to toggle this functionality. --- lang/Czech.ini | 1 + lang/Dutch.ini | 1 + lang/English.ini | 1 + lang/French.ini | 1 + lang/German.ini | 1 + lang/Italian.ini | 1 + lang/Japanese.ini | 1 + lang/Polish.ini | 1 + lang/Portuguese.ini | 1 + lang/Russian.ini | 1 + lang/Spanish.ini | 1 + src/game/game_init.c | 3 +-- src/pc/configfile.c | 2 ++ src/pc/configfile.h | 1 + src/pc/controller/controller_sdl2.c | 15 +++++++++++++++ src/pc/djui/djui_panel_controls.c | 2 ++ 16 files changed, 32 insertions(+), 2 deletions(-) diff --git a/lang/Czech.ini b/lang/Czech.ini index ddf7ae547..81a2e89a3 100644 --- a/lang/Czech.ini +++ b/lang/Czech.ini @@ -100,6 +100,7 @@ N64_BINDS = "N64 Ovládání" EXTRA_BINDS = "Extra Ovládání" BACKGROUND_GAMEPAD = "Ovladač v pozadí" DISABLE_GAMEPADS = "Zakažte gamepady" +EXTENDED_REPORTS = "Rozšířené zprávy" GAMEPAD = "Použít ovladač" DEADZONE = "Deadzone" RUMBLE_STRENGTH = "Síla vibrace" diff --git a/lang/Dutch.ini b/lang/Dutch.ini index a06530f8b..c4e32f77c 100644 --- a/lang/Dutch.ini +++ b/lang/Dutch.ini @@ -100,6 +100,7 @@ N64_BINDS = "N64 Toetsen" EXTRA_BINDS = "Extra Toetsen" BACKGROUND_GAMEPAD = "Achtergrond Gamepad" DISABLE_GAMEPADS = "Gamepads uitschakelen" +EXTENDED_REPORTS = "Uitgebreide rapporten" GAMEPAD = "Gamepad" DEADZONE = "Doode-zone" RUMBLE_STRENGTH = "Rommel Kracht" diff --git a/lang/English.ini b/lang/English.ini index 14ec1d462..0560d9ac2 100644 --- a/lang/English.ini +++ b/lang/English.ini @@ -100,6 +100,7 @@ N64_BINDS = "N64 Binds" EXTRA_BINDS = "Extra Binds" BACKGROUND_GAMEPAD = "Background Gamepad" DISABLE_GAMEPADS = "Disable Gamepads" +EXTENDED_REPORTS = "Extended Reports" GAMEPAD = "Gamepad" DEADZONE = "Deadzone" RUMBLE_STRENGTH = "Rumble Strength" diff --git a/lang/French.ini b/lang/French.ini index b0dfe3be2..645ed0c2a 100644 --- a/lang/French.ini +++ b/lang/French.ini @@ -100,6 +100,7 @@ N64_BINDS = "Touches N64" EXTRA_BINDS = "Touches Supplémentaires" BACKGROUND_GAMEPAD = "Manette en arrière plan" DISABLE_GAMEPADS = "Désactiver les manettes de jeu" +EXTENDED_REPORTS = "Rapports détaillés" GAMEPAD = "Manette" DEADZONE = "Zone Morte" RUMBLE_STRENGTH = "Vibrations" diff --git a/lang/German.ini b/lang/German.ini index e44e5c718..1fb8031d7 100644 --- a/lang/German.ini +++ b/lang/German.ini @@ -99,6 +99,7 @@ CONTROLS = "STEUERUNG" N64_BINDS = "N64-Einstellungen" EXTRA_BINDS = "Zusätzliche Einstellungen" BACKGROUND_GAMEPAD = "Hintergrund-Gamepad" +EXTENDED_REPORTS = "Erweiterte Berichte" DISABLE_GAMEPADS = "Gamepads deaktivieren" GAMEPAD = "Gamepad" DEADZONE = "Tote Zone" diff --git a/lang/Italian.ini b/lang/Italian.ini index e0827a2f6..dfe13d3bb 100644 --- a/lang/Italian.ini +++ b/lang/Italian.ini @@ -98,6 +98,7 @@ CONTROLS = "CONTROLLI" N64_BINDS = "Comandi N64" EXTRA_BINDS = "Comandi Extra" BACKGROUND_GAMEPAD = "Attivi in Background" +EXTENDED_REPORTS = "Rapporti estesi" DISABLE_GAMEPADS = "Disabilita i Gamepad" GAMEPAD = "Controller" DEADZONE = "Zona Morta" diff --git a/lang/Japanese.ini b/lang/Japanese.ini index a15f74b3a..05b20a216 100644 --- a/lang/Japanese.ini +++ b/lang/Japanese.ini @@ -99,6 +99,7 @@ CONTROLS = "CONTROLS" N64_BINDS = "ニンテンドウ64のボタン割り当て" EXTRA_BINDS = "追加のボタン割り当て" BACKGROUND_GAMEPAD = "バックグラウンドでのコントローラー認識" +EXTENDED_REPORTS = "拡張レポート" DISABLE_GAMEPADS = "コントローラーを無効化" GAMEPAD = "コントローラー" DEADZONE = "デッドゾーン" diff --git a/lang/Polish.ini b/lang/Polish.ini index 9781c549e..cb983027d 100644 --- a/lang/Polish.ini +++ b/lang/Polish.ini @@ -99,6 +99,7 @@ CONTROLS = "STEROWANIE" N64_BINDS = "Przypisania N64" EXTRA_BINDS = "Dodatkowe Przypisania" BACKGROUND_GAMEPAD = "Gamepad w Tle" +EXTENDED_REPORTS = "Rozszerzone raporty" DISABLE_GAMEPADS = "Wyłącz Gamepady" GAMEPAD = "Gamepad" DEADZONE = "Martwa Strefa" diff --git a/lang/Portuguese.ini b/lang/Portuguese.ini index e935499a6..d3b9e0c6b 100644 --- a/lang/Portuguese.ini +++ b/lang/Portuguese.ini @@ -99,6 +99,7 @@ CONTROLS = "CONTROLES" N64_BINDS = "N64" EXTRA_BINDS = "Outros" BACKGROUND_GAMEPAD = "Controle de fundo" +EXTENDED_REPORTS = "Relatórios detalhados" DISABLE_GAMEPADS = "Desativar controles" GAMEPAD = "Controle" DEADZONE = "Zona morta" diff --git a/lang/Russian.ini b/lang/Russian.ini index 52861a4cf..85d39ae37 100644 --- a/lang/Russian.ini +++ b/lang/Russian.ini @@ -98,6 +98,7 @@ CONTROLS = "CONTROLS" N64_BINDS = "Кнопки N64" EXTRA_BINDS = "Дополнительные кнопки" BACKGROUND_GAMEPAD = "Фоновый ввод" +EXTENDED_REPORTS = "Расширенные отчеты" DISABLE_GAMEPADS = "Отключить геймпады" GAMEPAD = "Геймпад" DEADZONE = "Mёртвая зона" diff --git a/lang/Spanish.ini b/lang/Spanish.ini index f083c8ba2..50660a637 100644 --- a/lang/Spanish.ini +++ b/lang/Spanish.ini @@ -100,6 +100,7 @@ N64_BINDS = "Botones de N64" EXTRA_BINDS = "Botones Adicionales" BACKGROUND_GAMEPAD = "Mando en segundo plano" DISABLE_GAMEPADS = "Desactivar mandos" +EXTENDED_REPORTS = "Informes ampliados" GAMEPAD = "Mando" DEADZONE = "Zona muerta" RUMBLE_STRENGTH = "Intensidad de vibración" diff --git a/src/game/game_init.c b/src/game/game_init.c index 2e13c7e57..058cc9697 100644 --- a/src/game/game_init.c +++ b/src/game/game_init.c @@ -594,8 +594,6 @@ void thread5_game_loop(UNUSED void *arg) { play_music(SEQ_PLAYER_SFX, SEQUENCE_ARGS(0, SEQ_SOUND_PLAYER), 0); set_sound_mode(save_file_get_sound_mode()); - thread6_rumble_loop(NULL); - gGlobalTimer++; } @@ -609,6 +607,7 @@ void game_loop_one_iteration(void) { osContStartReadData(&gSIEventMesgQueue); } + thread6_rumble_loop(NULL); audio_game_loop_tick(); config_gfx_pool(); read_controller_inputs(); diff --git a/src/pc/configfile.c b/src/pc/configfile.c index f2267a7db..4e52b50b6 100644 --- a/src/pc/configfile.c +++ b/src/pc/configfile.c @@ -127,6 +127,7 @@ unsigned int configStickDeadzone = 16; unsigned int configRumbleStrength = 50; unsigned int configGamepadNumber = 0; bool configBackgroundGamepad = true; +bool configExtendedReports = false; bool configDisableGamepads = false; bool configUseStandardKeyBindingsChat = false; bool configSmoothScrolling = false; @@ -270,6 +271,7 @@ static const struct ConfigOption options[] = { {.name = "rumble_strength", .type = CONFIG_TYPE_UINT, .uintValue = &configRumbleStrength}, {.name = "gamepad_number", .type = CONFIG_TYPE_UINT, .uintValue = &configGamepadNumber}, {.name = "background_gamepad", .type = CONFIG_TYPE_UINT, .boolValue = &configBackgroundGamepad}, + {.name = "extended_reports", .type = CONFIG_TYPE_BOOL, .boolValue = &configExtendedReports}, #ifndef HANDHELD {.name = "disable_gamepads", .type = CONFIG_TYPE_BOOL, .boolValue = &configDisableGamepads}, #endif diff --git a/src/pc/configfile.h b/src/pc/configfile.h index d537970e4..11cdbd5bc 100644 --- a/src/pc/configfile.h +++ b/src/pc/configfile.h @@ -93,6 +93,7 @@ extern unsigned int configStickDeadzone; extern unsigned int configRumbleStrength; extern unsigned int configGamepadNumber; extern bool configBackgroundGamepad; +extern bool configExtendedReports; extern bool configDisableGamepads; extern bool configUseStandardKeyBindingsChat; extern bool configSmoothScrolling; diff --git a/src/pc/controller/controller_sdl2.c b/src/pc/controller/controller_sdl2.c index 251d8a85f..6d4fc30ed 100644 --- a/src/pc/controller/controller_sdl2.c +++ b/src/pc/controller/controller_sdl2.c @@ -40,6 +40,7 @@ static SDL_GameController *sdl_cntrl = NULL; static SDL_Joystick *sdl_joystick = NULL; static SDL_Haptic *sdl_haptic = NULL; +static bool sExtendedReports = false; static bool sBackgroundGamepad = false; static u32 num_joy_binds = 0; @@ -102,6 +103,13 @@ static void controller_sdl_bind(void) { } static void controller_sdl_init(void) { + // Allows extended reports on PS4 and PS5 controllers + if (configExtendedReports) { + SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, "1"); + SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1"); + } + sExtendedReports = configExtendedReports; + // Allows game to be controlled by gamepad when not in focus if (configBackgroundGamepad) { SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1"); @@ -200,6 +208,13 @@ static void controller_sdl_read(OSContPad *pad) { // remember buttons that changed from 0 to 1 last_mouse = (mouse_prev ^ mouse) & mouse; + if (configExtendedReports != sExtendedReports) { + sExtendedReports = configExtendedReports; + char* hint = sExtendedReports ? "1" : "0"; + SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, hint); + SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, hint); + } + if (configBackgroundGamepad != sBackgroundGamepad) { sBackgroundGamepad = configBackgroundGamepad; SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, sBackgroundGamepad ? "1" : "0"); diff --git a/src/pc/djui/djui_panel_controls.c b/src/pc/djui/djui_panel_controls.c index 8088580a9..e8b39396d 100644 --- a/src/pc/djui/djui_panel_controls.c +++ b/src/pc/djui/djui_panel_controls.c @@ -43,6 +43,8 @@ void djui_panel_controls_create(struct DjuiBase* caller) { djui_checkbox_create(body, DLANG(MISC, USE_STANDARD_KEY_BINDINGS_CHAT), &configUseStandardKeyBindingsChat, NULL); #ifdef HAVE_SDL2 + djui_checkbox_create(body, DLANG(CONTROLS, EXTENDED_REPORTS), &configExtendedReports, NULL); + int numJoys = SDL_NumJoysticks(); if (numJoys == 0) { numJoys = 1; }