Frontend: Dynamic button mapping based on controller type

This commit is contained in:
Connor McLaughlin 2019-12-15 00:17:43 +10:00
parent c65279f944
commit 32d8b4dc84
6 changed files with 271 additions and 167 deletions

View file

@ -43,10 +43,15 @@ std::optional<s32> Controller::GetButtonCodeByName(ControllerType type, std::str
switch (type) switch (type)
{ {
case ControllerType::DigitalController: case ControllerType::DigitalController:
return DigitalController::GetButtonCodeByName(button_name); return DigitalController::StaticGetButtonCodeByName(button_name);
case ControllerType::None: case ControllerType::None:
default: default:
return std::nullopt; return std::nullopt;
} }
} }
std::optional<s32> Controller::GetButtonCodeByName(std::string_view button_name) const
{
return std::nullopt;
}

View file

@ -12,6 +12,12 @@ public:
Controller(); Controller();
virtual ~Controller(); virtual ~Controller();
/// Returns the type of controller.
virtual ControllerType GetType() const = 0;
/// Gets the integer code for a button in the specified controller type.
virtual std::optional<s32> GetButtonCodeByName(std::string_view button_name) const;
virtual void Reset(); virtual void Reset();
virtual bool DoState(StateWrapper& sw); virtual bool DoState(StateWrapper& sw);

View file

@ -6,6 +6,16 @@ DigitalController::DigitalController() = default;
DigitalController::~DigitalController() = default; DigitalController::~DigitalController() = default;
ControllerType DigitalController::GetType() const
{
return ControllerType::DigitalController;
}
std::optional<s32> DigitalController::GetButtonCodeByName(std::string_view button_name) const
{
return StaticGetButtonCodeByName(button_name);
}
void DigitalController::SetButtonState(Button button, bool pressed) void DigitalController::SetButtonState(Button button, bool pressed)
{ {
if (pressed) if (pressed)
@ -81,7 +91,7 @@ std::unique_ptr<DigitalController> DigitalController::Create()
return std::make_unique<DigitalController>(); return std::make_unique<DigitalController>();
} }
std::optional<s32> DigitalController::GetButtonCodeByName(std::string_view button_name) std::optional<s32> DigitalController::StaticGetButtonCodeByName(std::string_view button_name)
{ {
#define BUTTON(name) \ #define BUTTON(name) \
if (button_name == #name) \ if (button_name == #name) \

View file

@ -32,7 +32,10 @@ public:
~DigitalController() override; ~DigitalController() override;
static std::unique_ptr<DigitalController> Create(); static std::unique_ptr<DigitalController> Create();
static std::optional<s32> GetButtonCodeByName(std::string_view button_name); static std::optional<s32> StaticGetButtonCodeByName(std::string_view button_name);
ControllerType GetType() const override;
std::optional<s32> GetButtonCodeByName(std::string_view button_name) const override;
void SetButtonState(Button button, bool pressed); void SetButtonState(Button button, bool pressed);
void SetButtonState(s32 button_code, bool pressed); void SetButtonState(s32 button_code, bool pressed);

View file

@ -4,7 +4,6 @@
#include "YBaseLib/Error.h" #include "YBaseLib/Error.h"
#include "YBaseLib/Log.h" #include "YBaseLib/Log.h"
#include "core/cdrom.h" #include "core/cdrom.h"
#include "core/digital_controller.h"
#include "core/dma.h" #include "core/dma.h"
#include "core/gpu.h" #include "core/gpu.h"
#include "core/host_display.h" #include "core/host_display.h"
@ -216,6 +215,12 @@ void SDLHostInterface::UpdateFullscreen()
m_settings.display_fullscreen ? 0 : static_cast<int>(20.0f * ImGui::GetIO().DisplayFramebufferScale.x)); m_settings.display_fullscreen ? 0 : static_cast<int>(20.0f * ImGui::GetIO().DisplayFramebufferScale.x));
} }
void SDLHostInterface::UpdateControllerMapping()
{
UpdateKeyboardControllerMapping();
UpdateControllerControllerMapping();
}
std::unique_ptr<SDLHostInterface> SDLHostInterface::Create(const char* filename /* = nullptr */, std::unique_ptr<SDLHostInterface> SDLHostInterface::Create(const char* filename /* = nullptr */,
const char* exp1_filename /* = nullptr */, const char* exp1_filename /* = nullptr */,
const char* save_state_filename /* = nullptr */) const char* save_state_filename /* = nullptr */)
@ -257,6 +262,8 @@ std::unique_ptr<SDLHostInterface> SDLHostInterface::Create(const char* filename
if (save_state_filename) if (save_state_filename)
intf->LoadState(save_state_filename); intf->LoadState(save_state_filename);
intf->UpdateControllerMapping();
} }
intf->UpdateFullscreen(); intf->UpdateFullscreen();
@ -279,156 +286,6 @@ void SDLHostInterface::ReportMessage(const char* message)
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION, "DuckStation Information", message, m_window); SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION, "DuckStation Information", message, m_window);
} }
static inline u32 SDLButtonToHostButton(u32 button)
{
// SDL left = 1, middle = 2, right = 3 :/
switch (button)
{
case 1:
return 0;
case 2:
return 2;
case 3:
return 1;
default:
return 0xFFFFFFFF;
}
}
static bool HandleSDLKeyEventForController(const SDL_Event* event, DigitalController* controller)
{
const bool pressed = (event->type == SDL_KEYDOWN);
switch (event->key.keysym.scancode)
{
case SDL_SCANCODE_KP_8:
case SDL_SCANCODE_I:
controller->SetButtonState(DigitalController::Button::Triangle, pressed);
return true;
case SDL_SCANCODE_KP_2:
case SDL_SCANCODE_K:
controller->SetButtonState(DigitalController::Button::Cross, pressed);
return true;
case SDL_SCANCODE_KP_4:
case SDL_SCANCODE_J:
controller->SetButtonState(DigitalController::Button::Square, pressed);
return true;
case SDL_SCANCODE_KP_6:
case SDL_SCANCODE_L:
controller->SetButtonState(DigitalController::Button::Circle, pressed);
return true;
case SDL_SCANCODE_W:
case SDL_SCANCODE_UP:
controller->SetButtonState(DigitalController::Button::Up, pressed);
return true;
case SDL_SCANCODE_S:
case SDL_SCANCODE_DOWN:
controller->SetButtonState(DigitalController::Button::Down, pressed);
return true;
case SDL_SCANCODE_A:
case SDL_SCANCODE_LEFT:
controller->SetButtonState(DigitalController::Button::Left, pressed);
return true;
case SDL_SCANCODE_D:
case SDL_SCANCODE_RIGHT:
controller->SetButtonState(DigitalController::Button::Right, pressed);
return true;
case SDL_SCANCODE_Q:
controller->SetButtonState(DigitalController::Button::L1, pressed);
return true;
case SDL_SCANCODE_E:
controller->SetButtonState(DigitalController::Button::R1, pressed);
return true;
case SDL_SCANCODE_1:
controller->SetButtonState(DigitalController::Button::L2, pressed);
return true;
case SDL_SCANCODE_3:
controller->SetButtonState(DigitalController::Button::R2, pressed);
return true;
case SDL_SCANCODE_RETURN:
controller->SetButtonState(DigitalController::Button::Start, pressed);
return true;
case SDL_SCANCODE_BACKSPACE:
controller->SetButtonState(DigitalController::Button::Select, pressed);
return true;
default:
break;
}
return false;
}
static void HandleSDLControllerAxisEventForController(const SDL_Event* ev, DigitalController* controller)
{
// Log_DevPrintf("axis %d %d", ev->caxis.axis, ev->caxis.value);
static constexpr int deadzone = 8192;
const bool negative = (ev->caxis.value < 0);
const bool active = (std::abs(ev->caxis.value) >= deadzone);
if (ev->caxis.axis == SDL_CONTROLLER_AXIS_TRIGGERLEFT)
{
controller->SetButtonState(DigitalController::Button::L2, active);
}
else if (ev->caxis.axis == SDL_CONTROLLER_AXIS_TRIGGERRIGHT)
{
controller->SetButtonState(DigitalController::Button::R2, active);
}
else
{
DigitalController::Button negative_button, positive_button;
if (ev->caxis.axis & 1)
{
negative_button = DigitalController::Button::Up;
positive_button = DigitalController::Button::Down;
}
else
{
negative_button = DigitalController::Button::Left;
positive_button = DigitalController::Button::Right;
}
controller->SetButtonState(negative_button, negative && active);
controller->SetButtonState(positive_button, !negative && active);
}
}
static void HandleSDLControllerButtonEventForController(const SDL_Event* ev, DigitalController* controller)
{
// Log_DevPrintf("button %d %s", ev->cbutton.button, ev->cbutton.state == SDL_PRESSED ? "pressed" : "released");
// For xbox one controller..
static constexpr std::pair<SDL_GameControllerButton, DigitalController::Button> button_mapping[] = {
{SDL_CONTROLLER_BUTTON_A, DigitalController::Button::Cross},
{SDL_CONTROLLER_BUTTON_B, DigitalController::Button::Circle},
{SDL_CONTROLLER_BUTTON_X, DigitalController::Button::Square},
{SDL_CONTROLLER_BUTTON_Y, DigitalController::Button::Triangle},
{SDL_CONTROLLER_BUTTON_BACK, DigitalController::Button::Select},
{SDL_CONTROLLER_BUTTON_START, DigitalController::Button::Start},
{SDL_CONTROLLER_BUTTON_GUIDE, DigitalController::Button::Start},
{SDL_CONTROLLER_BUTTON_LEFTSTICK, DigitalController::Button::L3},
{SDL_CONTROLLER_BUTTON_RIGHTSTICK, DigitalController::Button::R3},
{SDL_CONTROLLER_BUTTON_LEFTSHOULDER, DigitalController::Button::L1},
{SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, DigitalController::Button::R1},
{SDL_CONTROLLER_BUTTON_DPAD_UP, DigitalController::Button::Up},
{SDL_CONTROLLER_BUTTON_DPAD_DOWN, DigitalController::Button::Down},
{SDL_CONTROLLER_BUTTON_DPAD_LEFT, DigitalController::Button::Left},
{SDL_CONTROLLER_BUTTON_DPAD_RIGHT, DigitalController::Button::Right}};
for (const auto& bm : button_mapping)
{
if (bm.first == ev->cbutton.button)
{
controller->SetButtonState(bm.second, ev->cbutton.state == SDL_PRESSED);
break;
}
}
}
void SDLHostInterface::HandleSDLEvent(const SDL_Event* event) void SDLHostInterface::HandleSDLEvent(const SDL_Event* event)
{ {
ImGui_ImplSDL2_ProcessEvent(event); ImGui_ImplSDL2_ProcessEvent(event);
@ -482,11 +339,7 @@ void SDLHostInterface::HandleSDLEvent(const SDL_Event* event)
break; break;
case SDL_CONTROLLERAXISMOTION: case SDL_CONTROLLERAXISMOTION:
{ HandleSDLControllerAxisEventForController(event);
DigitalController* controller = static_cast<DigitalController*>(m_system->GetController(0));
if (controller)
HandleSDLControllerAxisEventForController(event, controller);
}
break; break;
case SDL_CONTROLLERBUTTONDOWN: case SDL_CONTROLLERBUTTONDOWN:
@ -498,9 +351,7 @@ void SDLHostInterface::HandleSDLEvent(const SDL_Event* event)
m_focus_main_menu_bar = true; m_focus_main_menu_bar = true;
} }
DigitalController* controller = static_cast<DigitalController*>(m_system->GetController(0)); HandleSDLControllerButtonEventForController(event);
if (controller)
HandleSDLControllerButtonEventForController(event, controller);
} }
break; break;
@ -516,8 +367,7 @@ void SDLHostInterface::HandleSDLEvent(const SDL_Event* event)
void SDLHostInterface::HandleSDLKeyEvent(const SDL_Event* event) void SDLHostInterface::HandleSDLKeyEvent(const SDL_Event* event)
{ {
const bool repeat = event->key.repeat != 0; const bool repeat = event->key.repeat != 0;
DigitalController* controller = static_cast<DigitalController*>(m_system->GetController(0)); if (!repeat && HandleSDLKeyEventForController(event))
if (!repeat && controller && HandleSDLKeyEventForController(event, controller))
return; return;
const bool pressed = (event->type == SDL_KEYDOWN); const bool pressed = (event->type == SDL_KEYDOWN);
@ -605,6 +455,198 @@ void SDLHostInterface::HandleSDLKeyEvent(const SDL_Event* event)
} }
} }
void SDLHostInterface::UpdateKeyboardControllerMapping()
{
m_keyboard_button_mapping.fill(-1);
const Controller* controller = m_system->GetController(0);
if (controller)
{
#define SET_BUTTON_MAP(action, name) \
m_keyboard_button_mapping[static_cast<int>(action)] = controller->GetButtonCodeByName(name).value_or(-1)
SET_BUTTON_MAP(KeyboardControllerAction::Up, "Up");
SET_BUTTON_MAP(KeyboardControllerAction::Down, "Down");
SET_BUTTON_MAP(KeyboardControllerAction::Left, "Left");
SET_BUTTON_MAP(KeyboardControllerAction::Right, "Right");
SET_BUTTON_MAP(KeyboardControllerAction::Triangle, "Triangle");
SET_BUTTON_MAP(KeyboardControllerAction::Cross, "Cross");
SET_BUTTON_MAP(KeyboardControllerAction::Square, "Square");
SET_BUTTON_MAP(KeyboardControllerAction::Circle, "Circle");
SET_BUTTON_MAP(KeyboardControllerAction::L1, "L1");
SET_BUTTON_MAP(KeyboardControllerAction::R1, "R1");
SET_BUTTON_MAP(KeyboardControllerAction::L2, "L2");
SET_BUTTON_MAP(KeyboardControllerAction::R2, "R2");
SET_BUTTON_MAP(KeyboardControllerAction::Start, "Start");
SET_BUTTON_MAP(KeyboardControllerAction::Select, "Select");
#undef SET_BUTTON_MAP
}
}
bool SDLHostInterface::HandleSDLKeyEventForController(const SDL_Event* event)
{
const bool pressed = (event->type == SDL_KEYDOWN);
Controller* controller;
#define DO_ACTION(action) \
if ((controller = m_system->GetController(0)) != nullptr && m_keyboard_button_mapping[static_cast<int>(action)]) \
{ \
controller->SetButtonState(m_keyboard_button_mapping[static_cast<int>(action)], pressed); \
}
switch (event->key.keysym.scancode)
{
case SDL_SCANCODE_KP_8:
case SDL_SCANCODE_I:
DO_ACTION(KeyboardControllerAction::Triangle);
return true;
case SDL_SCANCODE_KP_2:
case SDL_SCANCODE_K:
DO_ACTION(KeyboardControllerAction::Cross);
return true;
case SDL_SCANCODE_KP_4:
case SDL_SCANCODE_J:
DO_ACTION(KeyboardControllerAction::Square);
return true;
case SDL_SCANCODE_KP_6:
case SDL_SCANCODE_L:
DO_ACTION(KeyboardControllerAction::Circle);
return true;
case SDL_SCANCODE_W:
case SDL_SCANCODE_UP:
DO_ACTION(KeyboardControllerAction::Up);
return true;
case SDL_SCANCODE_S:
case SDL_SCANCODE_DOWN:
DO_ACTION(KeyboardControllerAction::Down);
return true;
case SDL_SCANCODE_A:
case SDL_SCANCODE_LEFT:
DO_ACTION(KeyboardControllerAction::Left);
return true;
case SDL_SCANCODE_D:
case SDL_SCANCODE_RIGHT:
DO_ACTION(KeyboardControllerAction::Right);
return true;
case SDL_SCANCODE_Q:
DO_ACTION(KeyboardControllerAction::L1);
return true;
case SDL_SCANCODE_E:
DO_ACTION(KeyboardControllerAction::R1);
return true;
case SDL_SCANCODE_1:
DO_ACTION(KeyboardControllerAction::L2);
return true;
case SDL_SCANCODE_3:
DO_ACTION(KeyboardControllerAction::R2);
return true;
case SDL_SCANCODE_RETURN:
DO_ACTION(KeyboardControllerAction::Start);
return true;
case SDL_SCANCODE_BACKSPACE:
DO_ACTION(KeyboardControllerAction::Select);
return true;
default:
break;
}
#undef DO_ACTION
return false;
}
void SDLHostInterface::UpdateControllerControllerMapping()
{
m_controller_button_mapping.fill(-1);
const Controller* controller = m_system->GetController(0);
if (controller)
{
#define SET_BUTTON_MAP(action, name) \
m_controller_button_mapping[static_cast<int>(action)] = controller->GetButtonCodeByName(name).value_or(-1)
SET_BUTTON_MAP(SDL_CONTROLLER_BUTTON_DPAD_UP, "Up");
SET_BUTTON_MAP(SDL_CONTROLLER_BUTTON_DPAD_DOWN, "Down");
SET_BUTTON_MAP(SDL_CONTROLLER_BUTTON_DPAD_LEFT, "Left");
SET_BUTTON_MAP(SDL_CONTROLLER_BUTTON_DPAD_RIGHT, "Right");
SET_BUTTON_MAP(SDL_CONTROLLER_BUTTON_Y, "Triangle");
SET_BUTTON_MAP(SDL_CONTROLLER_BUTTON_A, "Cross");
SET_BUTTON_MAP(SDL_CONTROLLER_BUTTON_X, "Square");
SET_BUTTON_MAP(SDL_CONTROLLER_BUTTON_B, "Circle");
SET_BUTTON_MAP(SDL_CONTROLLER_BUTTON_LEFTSHOULDER, "L1");
SET_BUTTON_MAP(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, "R1");
SET_BUTTON_MAP(SDL_CONTROLLER_BUTTON_LEFTSTICK, "L3");
SET_BUTTON_MAP(SDL_CONTROLLER_BUTTON_RIGHTSTICK, "R3");
SET_BUTTON_MAP(SDL_CONTROLLER_BUTTON_START, "Start");
SET_BUTTON_MAP(SDL_CONTROLLER_BUTTON_BACK, "Select");
#undef SET_BUTTON_MAP
}
}
void SDLHostInterface::HandleSDLControllerAxisEventForController(const SDL_Event* ev)
{
// Log_DevPrintf("axis %d %d", ev->caxis.axis, ev->caxis.value);
Controller* controller = m_system->GetController(0);
if (!controller)
return;
static constexpr int deadzone = 8192;
const bool negative = (ev->caxis.value < 0);
const bool active = (std::abs(ev->caxis.value) >= deadzone);
// FIXME
if (ev->caxis.axis == SDL_CONTROLLER_AXIS_TRIGGERLEFT)
{
auto button = controller->GetButtonCodeByName("L2");
if (button)
controller->SetButtonState(button.value(), active);
}
else if (ev->caxis.axis == SDL_CONTROLLER_AXIS_TRIGGERRIGHT)
{
auto button = controller->GetButtonCodeByName("R2");
if (button)
controller->SetButtonState(button.value(), active);
}
else
{
SDL_GameControllerButton negative_button, positive_button;
if (ev->caxis.axis & 1)
{
negative_button = SDL_CONTROLLER_BUTTON_DPAD_UP;
positive_button = SDL_CONTROLLER_BUTTON_DPAD_DOWN;
}
else
{
negative_button = SDL_CONTROLLER_BUTTON_DPAD_LEFT;
positive_button = SDL_CONTROLLER_BUTTON_DPAD_RIGHT;
}
if (m_controller_button_mapping[negative_button] >= 0)
controller->SetButtonState(m_controller_button_mapping[negative_button], negative && active);
if (m_controller_button_mapping[positive_button] >= 0)
controller->SetButtonState(m_controller_button_mapping[positive_button], !negative && active);
}
}
void SDLHostInterface::HandleSDLControllerButtonEventForController(const SDL_Event* ev)
{
// Log_DevPrintf("button %d %s", ev->cbutton.button, ev->cbutton.state == SDL_PRESSED ? "pressed" : "released");
Controller* controller = m_system->GetController(0);
if (!controller)
return;
if (m_controller_button_mapping[ev->cbutton.button] >= 0)
controller->SetButtonState(m_controller_button_mapping[ev->cbutton.button], ev->cbutton.state == SDL_PRESSED);
}
void SDLHostInterface::DrawImGui() void SDLHostInterface::DrawImGui()
{ {
DrawMainMenuBar(); DrawMainMenuBar();
@ -1285,6 +1327,7 @@ void SDLHostInterface::DoResume()
return; return;
} }
UpdateControllerMapping();
ResetPerformanceCounters(); ResetPerformanceCounters();
ClearImGuiFocus(); ClearImGuiFocus();
} }
@ -1304,6 +1347,7 @@ void SDLHostInterface::DoStartDisc()
return; return;
} }
UpdateControllerMapping();
ResetPerformanceCounters(); ResetPerformanceCounters();
ClearImGuiFocus(); ClearImGuiFocus();
} }
@ -1319,6 +1363,7 @@ void SDLHostInterface::DoStartBIOS()
return; return;
} }
UpdateControllerMapping();
ResetPerformanceCounters(); ResetPerformanceCounters();
ClearImGuiFocus(); ClearImGuiFocus();
} }
@ -1355,6 +1400,7 @@ void SDLHostInterface::DoLoadState(u32 index)
} }
} }
UpdateControllerMapping();
ResetPerformanceCounters(); ResetPerformanceCounters();
ClearImGuiFocus(); ClearImGuiFocus();
} }

View file

@ -3,8 +3,8 @@
#include "YBaseLib/Timer.h" #include "YBaseLib/Timer.h"
#include "common/gl/program.h" #include "common/gl/program.h"
#include "common/gl/texture.h" #include "common/gl/texture.h"
#include "core/host_interface.h"
#include "core/host_display.h" #include "core/host_display.h"
#include "core/host_interface.h"
#include <SDL.h> #include <SDL.h>
#include <array> #include <array>
#include <deque> #include <deque>
@ -15,6 +15,8 @@
class System; class System;
class AudioStream; class AudioStream;
class Controller;
class SDLHostInterface final : public HostInterface class SDLHostInterface final : public HostInterface
{ {
public: public:
@ -32,6 +34,27 @@ public:
void Run(); void Run();
private: private:
enum class KeyboardControllerAction
{
Up,
Down,
Left,
Right,
Triangle,
Cross,
Square,
Circle,
L1,
R1,
L2,
R2,
Start,
Select,
Count
};
using KeyboardControllerActionMap = std::array<s32, static_cast<int>(KeyboardControllerAction::Count)>;
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";
@ -58,6 +81,7 @@ private:
void QueueSwitchGPURenderer(); void QueueSwitchGPURenderer();
void SwitchGPURenderer(); void SwitchGPURenderer();
void UpdateFullscreen(); void UpdateFullscreen();
void UpdateControllerMapping();
// We only pass mouse input through if it's grabbed // We only pass mouse input through if it's grabbed
void DrawImGui(); void DrawImGui();
@ -77,6 +101,13 @@ private:
void HandleSDLEvent(const SDL_Event* event); void HandleSDLEvent(const SDL_Event* event);
void HandleSDLKeyEvent(const SDL_Event* event); void HandleSDLKeyEvent(const SDL_Event* event);
void UpdateKeyboardControllerMapping();
bool HandleSDLKeyEventForController(const SDL_Event* event);
void UpdateControllerControllerMapping();
void HandleSDLControllerAxisEventForController(const SDL_Event* event);
void HandleSDLControllerButtonEventForController(const SDL_Event* event);
void DrawMainMenuBar(); void DrawMainMenuBar();
void DrawQuickSettingsMenu(); void DrawQuickSettingsMenu();
void DrawDebugMenu(); void DrawDebugMenu();
@ -91,7 +122,10 @@ private:
std::string m_settings_filename; std::string m_settings_filename;
KeyboardControllerActionMap m_keyboard_button_mapping;
std::map<int, SDL_GameController*> m_sdl_controllers; std::map<int, SDL_GameController*> m_sdl_controllers;
std::array<s32, SDL_CONTROLLER_BUTTON_MAX> m_controller_button_mapping;
u32 m_switch_gpu_renderer_event_id = 0; u32 m_switch_gpu_renderer_event_id = 0;