From 6f7154fbefdde8921bcc7d0d57be7018f641c5ac Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Sun, 15 Dec 2019 23:24:34 +1000 Subject: [PATCH] Frontend: Rumble support --- src/duckstation/main.cpp | 10 +- src/duckstation/sdl_host_interface.cpp | 138 +++++++++++++++++-------- src/duckstation/sdl_host_interface.h | 17 ++- 3 files changed, 113 insertions(+), 52 deletions(-) diff --git a/src/duckstation/main.cpp b/src/duckstation/main.cpp index a1daf9d8f..dde7e60e1 100644 --- a/src/duckstation/main.cpp +++ b/src/duckstation/main.cpp @@ -24,7 +24,7 @@ static int NoGUITest() static int Run(int argc, char* argv[]) { // init sdl - if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_GAMECONTROLLER) < 0) + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC) < 0) { Panic("SDL initialization failed"); return -1; @@ -51,8 +51,8 @@ static int Run(int argc, char* argv[]) } // create display and host interface - std::unique_ptr host_interface = - SDLHostInterface::Create(filename, exp1_filename, state_filename.IsEmpty() ? nullptr : state_filename.GetCharArray()); + std::unique_ptr host_interface = SDLHostInterface::Create( + filename, exp1_filename, state_filename.IsEmpty() ? nullptr : state_filename.GetCharArray()); if (!host_interface) { Panic("Failed to create host interface"); @@ -83,8 +83,8 @@ int main(int argc, char* argv[]) #else g_pLog->SetConsoleOutputParams(true, nullptr, LOGLEVEL_DEBUG); // g_pLog->SetConsoleOutputParams(true, "GPU GPU_HW_OpenGL SPU Pad DigitalController", LOGLEVEL_DEBUG); - // g_pLog->SetConsoleOutputParams(true, "GPU GPU_HW_OpenGL Pad DigitalController MemoryCard InterruptController SPU MDEC", LOGLEVEL_DEBUG); - // g_pLog->SetFilterLevel(LOGLEVEL_TRACE); + // g_pLog->SetConsoleOutputParams(true, "GPU GPU_HW_OpenGL Pad DigitalController MemoryCard InterruptController SPU + // MDEC", LOGLEVEL_DEBUG); g_pLog->SetFilterLevel(LOGLEVEL_TRACE); g_pLog->SetFilterLevel(LOGLEVEL_DEBUG); // g_pLog->SetFilterLevel(LOGLEVEL_DEV); #endif diff --git a/src/duckstation/sdl_host_interface.cpp b/src/duckstation/sdl_host_interface.cpp index a19fc3739..f73f552f4 100644 --- a/src/duckstation/sdl_host_interface.cpp +++ b/src/duckstation/sdl_host_interface.cpp @@ -130,30 +130,6 @@ bool SDLHostInterface::CreateAudioStream() return m_audio_stream->Reconfigure(44100, 2); } -void SDLHostInterface::OpenGameControllers() -{ - for (int i = 0; i < SDL_NumJoysticks(); i++) - { - SDL_GameController* gcontroller = SDL_GameControllerOpen(i); - if (gcontroller) - { - Log_InfoPrintf("Opened controller %d: %s", i, SDL_GameControllerName(gcontroller)); - m_sdl_controllers.emplace(i, gcontroller); - } - else - { - Log_WarningPrintf("Failed to open controller %d", i); - } - } -} - -void SDLHostInterface::CloseGameControllers() -{ - for (auto& it : m_sdl_controllers) - SDL_GameControllerClose(it.second); - m_sdl_controllers.clear(); -} - void SDLHostInterface::SaveSettings() { m_settings.Save(m_settings_filename.c_str()); @@ -252,7 +228,6 @@ std::unique_ptr SDLHostInterface::Create(const char* filename ImGui::NewFrame(); intf->UpdateSpeedLimiterState(); - intf->OpenGameControllers(); const bool boot = (filename != nullptr || exp1_filename != nullptr || save_state_filename != nullptr); if (boot) @@ -313,28 +288,15 @@ void SDLHostInterface::HandleSDLEvent(const SDL_Event* event) case SDL_CONTROLLERDEVICEADDED: { - auto iter = m_sdl_controllers.find(event->cdevice.which); - if (iter == m_sdl_controllers.end()) - { - SDL_GameController* gcontroller = SDL_GameControllerOpen(event->cdevice.which); - if (gcontroller) - { - Log_InfoPrintf("Controller %s inserted", SDL_GameControllerName(gcontroller)); - m_sdl_controllers.emplace(event->cdevice.which, gcontroller); - } - } + Log_InfoPrintf("Controller %d inserted", event->cdevice.which); + OpenGameController(event->cdevice.which); } break; case SDL_CONTROLLERDEVICEREMOVED: { - auto iter = m_sdl_controllers.find(event->cdevice.which); - if (iter != m_sdl_controllers.end()) - { - Log_InfoPrintf("Controller %s removed", SDL_GameControllerName(iter->second)); - SDL_GameControllerClose(iter->second); - m_sdl_controllers.erase(iter); - } + Log_InfoPrintf("Controller %d removed", event->cdevice.which); + CloseGameController(event->cdevice.which); } break; @@ -562,6 +524,61 @@ bool SDLHostInterface::HandleSDLKeyEventForController(const SDL_Event* event) return false; } +bool SDLHostInterface::OpenGameController(int index) +{ + if (m_sdl_controllers.find(index) != m_sdl_controllers.end()) + CloseGameController(index); + + SDL_GameController* gcontroller = SDL_GameControllerOpen(index); + if (!gcontroller) + { + Log_WarningPrintf("Failed to open controller %d", index); + return false; + } + + Log_InfoPrintf("Opened controller %d: %s", index, SDL_GameControllerName(gcontroller)); + + ControllerData cd = {}; + cd.controller = gcontroller; + + SDL_Joystick* joystick = SDL_GameControllerGetJoystick(gcontroller); + if (joystick) + { + SDL_Haptic* haptic = SDL_HapticOpenFromJoystick(joystick); + if (SDL_HapticRumbleSupported(haptic) && SDL_HapticRumbleInit(haptic) == 0) + cd.haptic = haptic; + else + SDL_HapticClose(haptic); + } + + if (cd.haptic) + Log_InfoPrintf("Rumble is supported on '%s'", SDL_GameControllerName(gcontroller)); + else + Log_WarningPrintf("Rumble is not supported on '%s'", SDL_GameControllerName(gcontroller)); + + m_sdl_controllers.emplace(index, cd); + return true; +} + +void SDLHostInterface::CloseGameControllers() +{ + while (!m_sdl_controllers.empty()) + CloseGameController(m_sdl_controllers.begin()->first); +} + +bool SDLHostInterface::CloseGameController(int index) +{ + auto it = m_sdl_controllers.find(index); + if (it == m_sdl_controllers.end()) + return false; + + if (it->second.haptic) + SDL_HapticClose(it->second.haptic); + + SDL_GameControllerClose(it->second.controller); + return true; +} + void SDLHostInterface::UpdateControllerControllerMapping() { m_controller_axis_mapping.fill(-1); @@ -667,6 +684,38 @@ void SDLHostInterface::HandleSDLControllerButtonEventForController(const SDL_Eve controller->SetButtonState(m_controller_button_mapping[ev->cbutton.button], ev->cbutton.state == SDL_PRESSED); } +void SDLHostInterface::UpdateControllerRumble() +{ + for (auto& it : m_sdl_controllers) + { + ControllerData& cd = it.second; + if (!cd.haptic) + continue; + + float new_strength = 0.0f; + if (m_system) + { + Controller* controller = m_system->GetController(cd.controller_index); + if (controller) + { + const u32 motor_count = controller->GetVibrationMotorCount(); + for (u32 i = 0; i < motor_count; i++) + new_strength = std::max(new_strength, controller->GetVibrationMotorStrength(i)); + } + } + + if (cd.last_rumble_strength == new_strength) + continue; + + if (new_strength > 0.01f) + SDL_HapticRumblePlay(cd.haptic, new_strength, 100000); + else + SDL_HapticRumbleStop(cd.haptic); + + cd.last_rumble_strength = new_strength; + } +} + void SDLHostInterface::DrawImGui() { DrawMainMenuBar(); @@ -1118,7 +1167,8 @@ void SDLHostInterface::DrawSettingsWindow() ImGui::Text("Controller:"); ImGui::SameLine(indent); - int controller_type = static_cast((i == 0) ? m_settings.controller_1_type : m_settings.controller_2_type); + int controller_type = + static_cast((i == 0) ? m_settings.controller_1_type : m_settings.controller_2_type); if (ImGui::Combo( "##controller_type", &controller_type, [](void*, int index, const char** out_text) { @@ -1542,6 +1592,8 @@ void SDLHostInterface::Run() } } + UpdateControllerRumble(); + // rendering { DrawImGui(); diff --git a/src/duckstation/sdl_host_interface.h b/src/duckstation/sdl_host_interface.h index 611cc6a7a..01b8cdb18 100644 --- a/src/duckstation/sdl_host_interface.h +++ b/src/duckstation/sdl_host_interface.h @@ -55,6 +55,14 @@ private: using KeyboardControllerActionMap = std::array(KeyboardControllerAction::Count)>; + struct ControllerData + { + SDL_GameController* controller; + SDL_Haptic* haptic; + u32 controller_index; + float last_rumble_strength; + }; + static constexpr u32 NUM_QUICK_SAVE_STATES = 10; static constexpr char RESUME_SAVESTATE_FILENAME[] = "savestate_resume.bin"; @@ -73,9 +81,6 @@ private: void CreateImGuiContext(); bool CreateAudioStream(); - void OpenGameControllers(); - void CloseGameControllers(); - void SaveSettings(); void QueueSwitchGPURenderer(); @@ -104,9 +109,13 @@ private: void UpdateKeyboardControllerMapping(); bool HandleSDLKeyEventForController(const SDL_Event* event); + bool OpenGameController(int index); + bool CloseGameController(int index); + void CloseGameControllers(); void UpdateControllerControllerMapping(); void HandleSDLControllerAxisEventForController(const SDL_Event* event); void HandleSDLControllerButtonEventForController(const SDL_Event* event); + void UpdateControllerRumble(); void DrawMainMenuBar(); void DrawQuickSettingsMenu(); @@ -124,7 +133,7 @@ private: KeyboardControllerActionMap m_keyboard_button_mapping; - std::map m_sdl_controllers; + std::map m_sdl_controllers; std::array m_controller_axis_mapping; std::array m_controller_button_mapping;