mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-11-26 15:45:42 +00:00
Frontend: Rumble support
This commit is contained in:
parent
cbcb9b02b0
commit
6f7154fbef
|
@ -24,7 +24,7 @@ static int NoGUITest()
|
||||||
static int Run(int argc, char* argv[])
|
static int Run(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
// init sdl
|
// 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");
|
Panic("SDL initialization failed");
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -51,8 +51,8 @@ static int Run(int argc, char* argv[])
|
||||||
}
|
}
|
||||||
|
|
||||||
// create display and host interface
|
// create display and host interface
|
||||||
std::unique_ptr<SDLHostInterface> host_interface =
|
std::unique_ptr<SDLHostInterface> host_interface = SDLHostInterface::Create(
|
||||||
SDLHostInterface::Create(filename, exp1_filename, state_filename.IsEmpty() ? nullptr : state_filename.GetCharArray());
|
filename, exp1_filename, state_filename.IsEmpty() ? nullptr : state_filename.GetCharArray());
|
||||||
if (!host_interface)
|
if (!host_interface)
|
||||||
{
|
{
|
||||||
Panic("Failed to create host interface");
|
Panic("Failed to create host interface");
|
||||||
|
@ -83,8 +83,8 @@ int main(int argc, char* argv[])
|
||||||
#else
|
#else
|
||||||
g_pLog->SetConsoleOutputParams(true, nullptr, LOGLEVEL_DEBUG);
|
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 SPU Pad DigitalController", LOGLEVEL_DEBUG);
|
||||||
// g_pLog->SetConsoleOutputParams(true, "GPU GPU_HW_OpenGL Pad DigitalController MemoryCard InterruptController SPU MDEC", LOGLEVEL_DEBUG);
|
// g_pLog->SetConsoleOutputParams(true, "GPU GPU_HW_OpenGL Pad DigitalController MemoryCard InterruptController SPU
|
||||||
// g_pLog->SetFilterLevel(LOGLEVEL_TRACE);
|
// MDEC", LOGLEVEL_DEBUG); g_pLog->SetFilterLevel(LOGLEVEL_TRACE);
|
||||||
g_pLog->SetFilterLevel(LOGLEVEL_DEBUG);
|
g_pLog->SetFilterLevel(LOGLEVEL_DEBUG);
|
||||||
// g_pLog->SetFilterLevel(LOGLEVEL_DEV);
|
// g_pLog->SetFilterLevel(LOGLEVEL_DEV);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -130,30 +130,6 @@ bool SDLHostInterface::CreateAudioStream()
|
||||||
return m_audio_stream->Reconfigure(44100, 2);
|
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()
|
void SDLHostInterface::SaveSettings()
|
||||||
{
|
{
|
||||||
m_settings.Save(m_settings_filename.c_str());
|
m_settings.Save(m_settings_filename.c_str());
|
||||||
|
@ -252,7 +228,6 @@ std::unique_ptr<SDLHostInterface> SDLHostInterface::Create(const char* filename
|
||||||
ImGui::NewFrame();
|
ImGui::NewFrame();
|
||||||
|
|
||||||
intf->UpdateSpeedLimiterState();
|
intf->UpdateSpeedLimiterState();
|
||||||
intf->OpenGameControllers();
|
|
||||||
|
|
||||||
const bool boot = (filename != nullptr || exp1_filename != nullptr || save_state_filename != nullptr);
|
const bool boot = (filename != nullptr || exp1_filename != nullptr || save_state_filename != nullptr);
|
||||||
if (boot)
|
if (boot)
|
||||||
|
@ -313,28 +288,15 @@ void SDLHostInterface::HandleSDLEvent(const SDL_Event* event)
|
||||||
|
|
||||||
case SDL_CONTROLLERDEVICEADDED:
|
case SDL_CONTROLLERDEVICEADDED:
|
||||||
{
|
{
|
||||||
auto iter = m_sdl_controllers.find(event->cdevice.which);
|
Log_InfoPrintf("Controller %d inserted", event->cdevice.which);
|
||||||
if (iter == m_sdl_controllers.end())
|
OpenGameController(event->cdevice.which);
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SDL_CONTROLLERDEVICEREMOVED:
|
case SDL_CONTROLLERDEVICEREMOVED:
|
||||||
{
|
{
|
||||||
auto iter = m_sdl_controllers.find(event->cdevice.which);
|
Log_InfoPrintf("Controller %d removed", event->cdevice.which);
|
||||||
if (iter != m_sdl_controllers.end())
|
CloseGameController(event->cdevice.which);
|
||||||
{
|
|
||||||
Log_InfoPrintf("Controller %s removed", SDL_GameControllerName(iter->second));
|
|
||||||
SDL_GameControllerClose(iter->second);
|
|
||||||
m_sdl_controllers.erase(iter);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -562,6 +524,61 @@ bool SDLHostInterface::HandleSDLKeyEventForController(const SDL_Event* event)
|
||||||
return false;
|
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()
|
void SDLHostInterface::UpdateControllerControllerMapping()
|
||||||
{
|
{
|
||||||
m_controller_axis_mapping.fill(-1);
|
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);
|
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()
|
void SDLHostInterface::DrawImGui()
|
||||||
{
|
{
|
||||||
DrawMainMenuBar();
|
DrawMainMenuBar();
|
||||||
|
@ -1118,7 +1167,8 @@ void SDLHostInterface::DrawSettingsWindow()
|
||||||
ImGui::Text("Controller:");
|
ImGui::Text("Controller:");
|
||||||
ImGui::SameLine(indent);
|
ImGui::SameLine(indent);
|
||||||
|
|
||||||
int controller_type = static_cast<int>((i == 0) ? m_settings.controller_1_type : m_settings.controller_2_type);
|
int controller_type =
|
||||||
|
static_cast<int>((i == 0) ? m_settings.controller_1_type : m_settings.controller_2_type);
|
||||||
if (ImGui::Combo(
|
if (ImGui::Combo(
|
||||||
"##controller_type", &controller_type,
|
"##controller_type", &controller_type,
|
||||||
[](void*, int index, const char** out_text) {
|
[](void*, int index, const char** out_text) {
|
||||||
|
@ -1542,6 +1592,8 @@ void SDLHostInterface::Run()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UpdateControllerRumble();
|
||||||
|
|
||||||
// rendering
|
// rendering
|
||||||
{
|
{
|
||||||
DrawImGui();
|
DrawImGui();
|
||||||
|
|
|
@ -55,6 +55,14 @@ private:
|
||||||
|
|
||||||
using KeyboardControllerActionMap = std::array<s32, static_cast<int>(KeyboardControllerAction::Count)>;
|
using KeyboardControllerActionMap = std::array<s32, static_cast<int>(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 u32 NUM_QUICK_SAVE_STATES = 10;
|
||||||
static constexpr char RESUME_SAVESTATE_FILENAME[] = "savestate_resume.bin";
|
static constexpr char RESUME_SAVESTATE_FILENAME[] = "savestate_resume.bin";
|
||||||
|
|
||||||
|
@ -73,9 +81,6 @@ private:
|
||||||
void CreateImGuiContext();
|
void CreateImGuiContext();
|
||||||
bool CreateAudioStream();
|
bool CreateAudioStream();
|
||||||
|
|
||||||
void OpenGameControllers();
|
|
||||||
void CloseGameControllers();
|
|
||||||
|
|
||||||
void SaveSettings();
|
void SaveSettings();
|
||||||
|
|
||||||
void QueueSwitchGPURenderer();
|
void QueueSwitchGPURenderer();
|
||||||
|
@ -104,9 +109,13 @@ private:
|
||||||
void UpdateKeyboardControllerMapping();
|
void UpdateKeyboardControllerMapping();
|
||||||
bool HandleSDLKeyEventForController(const SDL_Event* event);
|
bool HandleSDLKeyEventForController(const SDL_Event* event);
|
||||||
|
|
||||||
|
bool OpenGameController(int index);
|
||||||
|
bool CloseGameController(int index);
|
||||||
|
void CloseGameControllers();
|
||||||
void UpdateControllerControllerMapping();
|
void UpdateControllerControllerMapping();
|
||||||
void HandleSDLControllerAxisEventForController(const SDL_Event* event);
|
void HandleSDLControllerAxisEventForController(const SDL_Event* event);
|
||||||
void HandleSDLControllerButtonEventForController(const SDL_Event* event);
|
void HandleSDLControllerButtonEventForController(const SDL_Event* event);
|
||||||
|
void UpdateControllerRumble();
|
||||||
|
|
||||||
void DrawMainMenuBar();
|
void DrawMainMenuBar();
|
||||||
void DrawQuickSettingsMenu();
|
void DrawQuickSettingsMenu();
|
||||||
|
@ -124,7 +133,7 @@ private:
|
||||||
|
|
||||||
KeyboardControllerActionMap m_keyboard_button_mapping;
|
KeyboardControllerActionMap m_keyboard_button_mapping;
|
||||||
|
|
||||||
std::map<int, SDL_GameController*> m_sdl_controllers;
|
std::map<int, ControllerData> m_sdl_controllers;
|
||||||
std::array<s32, SDL_CONTROLLER_AXIS_MAX> m_controller_axis_mapping;
|
std::array<s32, SDL_CONTROLLER_AXIS_MAX> m_controller_axis_mapping;
|
||||||
std::array<s32, SDL_CONTROLLER_BUTTON_MAX> m_controller_button_mapping;
|
std::array<s32, SDL_CONTROLLER_BUTTON_MAX> m_controller_button_mapping;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue