Fix rumble and add PS4/PS5 controller rumble.

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.
This commit is contained in:
Gibson Pilconis 2025-12-20 18:15:07 -05:00
parent 983932214a
commit 78d3ab86d6
16 changed files with 32 additions and 2 deletions

View file

@ -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"

View file

@ -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"

View file

@ -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"

View file

@ -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"

View file

@ -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"

View file

@ -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"

View file

@ -100,6 +100,7 @@ CONTROLS = "CONTROLS"
N64_BINDS = "ニンテンドウ64の入力"
EXTRA_BINDS = "追加の入力"
BACKGROUND_GAMEPAD = "バックグラウンドでのコントローラー認識"
EXTENDED_REPORTS = "拡張レポート"
DISABLE_GAMEPADS = "コントローラーを無効化"
GAMEPAD = "コントローラー"
DEADZONE = "デッドゾーン"

View file

@ -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"

View file

@ -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"

View file

@ -98,6 +98,7 @@ CONTROLS = "CONTROLS"
N64_BINDS = "Кнопки N64"
EXTRA_BINDS = "Дополнительные кнопки"
BACKGROUND_GAMEPAD = "Фоновый ввод"
EXTENDED_REPORTS = "Расширенные отчеты"
DISABLE_GAMEPADS = "Отключить геймпады"
GAMEPAD = "Геймпад"
DEADZONE = "Mёртвая зона"

View file

@ -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"

View file

@ -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();

View file

@ -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

View file

@ -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;

View file

@ -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");
@ -198,6 +206,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");

View file

@ -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; }