mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-11-22 22:05:38 +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[])
|
||||
{
|
||||
// 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<SDLHostInterface> host_interface =
|
||||
SDLHostInterface::Create(filename, exp1_filename, state_filename.IsEmpty() ? nullptr : state_filename.GetCharArray());
|
||||
std::unique_ptr<SDLHostInterface> 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
|
||||
|
|
|
@ -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> 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<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(
|
||||
"##controller_type", &controller_type,
|
||||
[](void*, int index, const char** out_text) {
|
||||
|
@ -1542,6 +1592,8 @@ void SDLHostInterface::Run()
|
|||
}
|
||||
}
|
||||
|
||||
UpdateControllerRumble();
|
||||
|
||||
// rendering
|
||||
{
|
||||
DrawImGui();
|
||||
|
|
|
@ -55,6 +55,14 @@ private:
|
|||
|
||||
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 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<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_BUTTON_MAX> m_controller_button_mapping;
|
||||
|
||||
|
|
Loading…
Reference in a new issue