SDL: Migrate to common host interface
It now supports controllers again. But you have to bind them in Qt.
This commit is contained in:
parent
6cfad33b8e
commit
156a360d86
24
README.md
24
README.md
|
@ -27,7 +27,6 @@ Other features include:
|
|||
- Direct booting of homebrew executables
|
||||
- Digital and analog controllers for input (rumble is forwarded to host)
|
||||
- Qt and SDL frontends for desktop
|
||||
- Qt frontend has graphical configuration, and controller binding
|
||||
- Automatic content scanning - game titles/regions are provided by redump.org
|
||||
|
||||
## System Requirements
|
||||
|
@ -142,26 +141,25 @@ click the button next to button name, and press the key/button you want to use w
|
|||
|
||||
**Currently, it is only possible to bind one input to each controller button/axis. Multiple bindings per button are planned for the future.**
|
||||
|
||||
## Default keyboard bindings for SDL frontend
|
||||
Keyboard bindings in the SDL frontend are currently not customizable. For reference:
|
||||
- **D-Pad:** W/A/S/D or Up/Left/Down/Right
|
||||
- **Triangle/Square/Circle/Cross:** I/J/L/K or Numpad8/Numpad4/Numpad6/Numpad2
|
||||
## Bindings for SDL frontend
|
||||
Keyboard bindings in the SDL frontend are currently not customizable in the frontend itself. You should use the Qt frontend to set up your key/controller bindings first.
|
||||
|
||||
## Default bindings
|
||||
Controller 1:
|
||||
- **D-Pad:** W/A/S/D
|
||||
- **Triangle/Square/Circle/Cross:** Numpad8/Numpad4/Numpad6/Numpad2
|
||||
- **L1/R1:** Q/E
|
||||
- **L2/L2:** 1/3
|
||||
- **Start:** Enter
|
||||
- **Select:** Backspace
|
||||
|
||||
Gamepads are automatically detected and supported. Tested with an Xbox One controller.
|
||||
To access the menus with the controller, press the right stick down and use the D-Pad to navigate, A to select.
|
||||
|
||||
## Useful hotkeys for SDL frontend
|
||||
- **F1-F8:** Quick load/save (hold shift to save)
|
||||
- **F11:** Toggle fullscreen
|
||||
Hotkeys:
|
||||
- **Escape:** Power off console
|
||||
- **ALT+ENTER:** Toggle fullscreen
|
||||
- **Tab:** Temporarily disable speed limiter
|
||||
- **Pause/Break:** Pause/resume emulation
|
||||
- **Space:** Frame step
|
||||
- **End:** Toggle software renderer
|
||||
- **Page Up/Down:** Increase/decrease resolution scale in hardware renderers
|
||||
- **End:** Toggle software renderer
|
||||
|
||||
## Tests
|
||||
- Passes amidog's CPU and GTE tests in both interpreter and recompiler modes, partial passing of CPX tests
|
||||
|
|
|
@ -6,6 +6,7 @@ add_executable(duckstation-sdl
|
|||
opengl_host_display.h
|
||||
sdl_host_interface.cpp
|
||||
sdl_host_interface.h
|
||||
sdl_key_names.h
|
||||
)
|
||||
|
||||
target_include_directories(duckstation-sdl PRIVATE ${SDL2_INCLUDE_DIRS})
|
||||
|
|
|
@ -63,6 +63,7 @@
|
|||
<ClInclude Include="imgui_impl_sdl.h" />
|
||||
<ClInclude Include="opengl_host_display.h" />
|
||||
<ClInclude Include="sdl_host_interface.h" />
|
||||
<ClInclude Include="sdl_key_names.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Manifest Include="duckstation-sdl.manifest" />
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
<ClInclude Include="sdl_host_interface.h" />
|
||||
<ClInclude Include="d3d11_host_display.h" />
|
||||
<ClInclude Include="imgui_impl_sdl.h" />
|
||||
<ClInclude Include="sdl_key_names.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Manifest Include="duckstation-sdl.manifest" />
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "frontend-common/sdl_controller_interface.h"
|
||||
#include "imgui_impl_sdl.h"
|
||||
#include "opengl_host_display.h"
|
||||
#include "sdl_key_names.h"
|
||||
#include <cinttypes>
|
||||
#include <cmath>
|
||||
#include <imgui.h>
|
||||
|
@ -190,11 +191,18 @@ bool SDLHostInterface::AcquireHostDisplay()
|
|||
}
|
||||
#endif
|
||||
|
||||
// Switch to fullscreen if requested.
|
||||
if (m_settings.start_fullscreen)
|
||||
SetFullscreen(true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SDLHostInterface::ReleaseHostDisplay()
|
||||
{
|
||||
if (m_fullscreen)
|
||||
SetFullscreen(false);
|
||||
|
||||
// restore vsync, since we don't want to burn cycles at the menu
|
||||
m_display->SetVSync(true);
|
||||
}
|
||||
|
@ -217,17 +225,35 @@ std::unique_ptr<AudioStream> SDLHostInterface::CreateAudioStream(AudioBackend ba
|
|||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<ControllerInterface> SDLHostInterface::CreateControllerInterface()
|
||||
{
|
||||
return std::make_unique<SDLControllerInterface>();
|
||||
}
|
||||
|
||||
std::optional<CommonHostInterface::HostKeyCode> SDLHostInterface::GetHostKeyCode(const std::string_view key_code) const
|
||||
{
|
||||
const std::optional<u32> code = SDLKeyNames::ParseKeyString(key_code);
|
||||
if (!code)
|
||||
return std::nullopt;
|
||||
|
||||
return static_cast<HostKeyCode>(*code);
|
||||
}
|
||||
|
||||
void SDLHostInterface::UpdateInputMap()
|
||||
{
|
||||
CommonHostInterface::UpdateInputMap(*m_settings_interface.get());
|
||||
}
|
||||
|
||||
void SDLHostInterface::OnSystemCreated()
|
||||
{
|
||||
HostInterface::OnSystemCreated();
|
||||
CommonHostInterface::OnSystemCreated();
|
||||
|
||||
UpdateKeyboardControllerMapping();
|
||||
ClearImGuiFocus();
|
||||
}
|
||||
|
||||
void SDLHostInterface::OnSystemPaused(bool paused)
|
||||
{
|
||||
HostInterface::OnSystemPaused(paused);
|
||||
CommonHostInterface::OnSystemPaused(paused);
|
||||
|
||||
if (!paused)
|
||||
ClearImGuiFocus();
|
||||
|
@ -235,14 +261,17 @@ void SDLHostInterface::OnSystemPaused(bool paused)
|
|||
|
||||
void SDLHostInterface::OnSystemDestroyed()
|
||||
{
|
||||
HostInterface::OnSystemDestroyed();
|
||||
CommonHostInterface::OnSystemDestroyed();
|
||||
}
|
||||
|
||||
void SDLHostInterface::OnControllerTypeChanged(u32 slot)
|
||||
void SDLHostInterface::OnRunningGameChanged()
|
||||
{
|
||||
HostInterface::OnControllerTypeChanged(slot);
|
||||
CommonHostInterface::OnRunningGameChanged();
|
||||
|
||||
UpdateKeyboardControllerMapping();
|
||||
if (m_system && !m_system->GetRunningTitle().empty())
|
||||
SDL_SetWindowTitle(m_window, m_system->GetRunningTitle().c_str());
|
||||
else
|
||||
SDL_SetWindowTitle(m_window, "DuckStation");
|
||||
}
|
||||
|
||||
void SDLHostInterface::RunLater(std::function<void()> callback)
|
||||
|
@ -256,29 +285,35 @@ void SDLHostInterface::RunLater(std::function<void()> callback)
|
|||
|
||||
void SDLHostInterface::SaveSettings()
|
||||
{
|
||||
INISettingsInterface si(GetSettingsFileName());
|
||||
m_settings_copy.Save(si);
|
||||
m_settings_copy.Save(*m_settings_interface.get());
|
||||
m_settings_interface->Save();
|
||||
}
|
||||
|
||||
void SDLHostInterface::UpdateSettings()
|
||||
{
|
||||
HostInterface::UpdateSettings([this]() { m_settings = m_settings_copy; });
|
||||
CommonHostInterface::UpdateSettings([this]() { m_settings = m_settings_copy; });
|
||||
}
|
||||
|
||||
void SDLHostInterface::SetFullscreen(bool enabled)
|
||||
bool SDLHostInterface::IsFullscreen() const
|
||||
{
|
||||
return m_fullscreen;
|
||||
}
|
||||
|
||||
bool SDLHostInterface::SetFullscreen(bool enabled)
|
||||
{
|
||||
if (m_fullscreen == enabled)
|
||||
return;
|
||||
return true;
|
||||
|
||||
SDL_SetWindowFullscreen(m_window, enabled ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
|
||||
|
||||
// We set the margin only in windowed mode, the menu bar is drawn on top in fullscreen.
|
||||
// We set the margin only in windowed mode, the menu bar is not drawn fullscreen.
|
||||
m_display->SetDisplayTopMargin(enabled ? 0 : static_cast<int>(20.0f * ImGui::GetIO().DisplayFramebufferScale.x));
|
||||
|
||||
int window_width, window_height;
|
||||
SDL_GetWindowSize(m_window, &window_width, &window_height);
|
||||
m_display->WindowResized(window_width, window_height);
|
||||
m_fullscreen = enabled;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<SDLHostInterface> SDLHostInterface::Create()
|
||||
|
@ -288,7 +323,7 @@ std::unique_ptr<SDLHostInterface> SDLHostInterface::Create()
|
|||
|
||||
bool SDLHostInterface::Initialize()
|
||||
{
|
||||
if (!HostInterface::Initialize())
|
||||
if (!CommonHostInterface::Initialize())
|
||||
return false;
|
||||
|
||||
// Change to the user directory so that all default/relative paths in the config are after this.
|
||||
|
@ -309,6 +344,10 @@ bool SDLHostInterface::Initialize()
|
|||
}
|
||||
|
||||
ImGui::NewFrame();
|
||||
|
||||
// process events to pick up controllers before updating input map
|
||||
ProcessEvents();
|
||||
UpdateInputMap();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -325,21 +364,27 @@ void SDLHostInterface::Shutdown()
|
|||
if (m_window)
|
||||
DestroySDLWindow();
|
||||
|
||||
HostInterface::Shutdown();
|
||||
CommonHostInterface::Shutdown();
|
||||
}
|
||||
|
||||
void SDLHostInterface::LoadSettings()
|
||||
{
|
||||
// Settings need to be loaded prior to creating the window for OpenGL bits.
|
||||
INISettingsInterface si(GetSettingsFileName());
|
||||
m_settings_copy.Load(si);
|
||||
m_settings_interface = std::make_unique<INISettingsInterface>(GetSettingsFileName());
|
||||
m_settings_copy.Load(*m_settings_interface.get());
|
||||
m_settings = m_settings_copy;
|
||||
m_fullscreen = m_settings_copy.start_fullscreen;
|
||||
}
|
||||
|
||||
void SDLHostInterface::ReportError(const char* message)
|
||||
{
|
||||
const bool was_fullscreen = IsFullscreen();
|
||||
if (was_fullscreen)
|
||||
SetFullscreen(false);
|
||||
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "DuckStation", message, m_window);
|
||||
|
||||
if (was_fullscreen)
|
||||
SetFullscreen(true);
|
||||
}
|
||||
|
||||
void SDLHostInterface::ReportMessage(const char* message)
|
||||
|
@ -349,6 +394,10 @@ void SDLHostInterface::ReportMessage(const char* message)
|
|||
|
||||
bool SDLHostInterface::ConfirmMessage(const char* message)
|
||||
{
|
||||
const bool was_fullscreen = IsFullscreen();
|
||||
if (was_fullscreen)
|
||||
SetFullscreen(false);
|
||||
|
||||
SDL_MessageBoxData mbd = {};
|
||||
mbd.flags = SDL_MESSAGEBOX_INFORMATION;
|
||||
mbd.window = m_window;
|
||||
|
@ -369,13 +418,23 @@ bool SDLHostInterface::ConfirmMessage(const char* message)
|
|||
|
||||
int button_id = 0;
|
||||
SDL_ShowMessageBox(&mbd, &button_id);
|
||||
return (button_id == 0);
|
||||
const bool result = (button_id == 0);
|
||||
|
||||
if (was_fullscreen)
|
||||
SetFullscreen(true);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void SDLHostInterface::HandleSDLEvent(const SDL_Event* event)
|
||||
{
|
||||
ImGui_ImplSDL2_ProcessEvent(event);
|
||||
// g_sdl_controller_interface.ProcessSDLEvent(event);
|
||||
|
||||
if (m_controller_interface &&
|
||||
static_cast<SDLControllerInterface*>(m_controller_interface.get())->ProcessSDLEvent(event))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event->type)
|
||||
{
|
||||
|
@ -400,23 +459,12 @@ void SDLHostInterface::HandleSDLEvent(const SDL_Event* event)
|
|||
case SDL_KEYDOWN:
|
||||
case SDL_KEYUP:
|
||||
{
|
||||
if (!ImGui::GetIO().WantCaptureKeyboard)
|
||||
HandleSDLKeyEvent(event);
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_CONTROLLERDEVICEADDED:
|
||||
case SDL_CONTROLLERDEVICEREMOVED:
|
||||
// g_sdl_controller_interface.SetDefaultBindings();
|
||||
break;
|
||||
|
||||
case SDL_CONTROLLERBUTTONDOWN:
|
||||
case SDL_CONTROLLERBUTTONUP:
|
||||
{
|
||||
if (event->type == SDL_CONTROLLERBUTTONDOWN && event->cbutton.button == SDL_CONTROLLER_BUTTON_RIGHTSTICK)
|
||||
if (!ImGui::GetIO().WantCaptureKeyboard && event->key.repeat == 0)
|
||||
{
|
||||
// focus the menu bar
|
||||
m_focus_main_menu_bar = true;
|
||||
const HostKeyCode code = static_cast<HostKeyCode>(static_cast<u32>(event->key.keysym.sym) |
|
||||
static_cast<u32>(event->key.keysym.mod) << 16);
|
||||
const bool pressed = (event->type == SDL_KEYDOWN);
|
||||
HandleHostKeyEvent(code, pressed);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -435,208 +483,24 @@ void SDLHostInterface::HandleSDLEvent(const SDL_Event* event)
|
|||
}
|
||||
}
|
||||
|
||||
void SDLHostInterface::HandleSDLKeyEvent(const SDL_Event* event)
|
||||
void SDLHostInterface::ProcessEvents()
|
||||
{
|
||||
const bool repeat = event->key.repeat != 0;
|
||||
if (!repeat && HandleSDLKeyEventForController(event))
|
||||
return;
|
||||
|
||||
const bool pressed = (event->type == SDL_KEYDOWN);
|
||||
switch (event->key.keysym.scancode)
|
||||
for (;;)
|
||||
{
|
||||
case SDL_SCANCODE_F1:
|
||||
case SDL_SCANCODE_F2:
|
||||
case SDL_SCANCODE_F3:
|
||||
case SDL_SCANCODE_F4:
|
||||
case SDL_SCANCODE_F5:
|
||||
case SDL_SCANCODE_F6:
|
||||
case SDL_SCANCODE_F7:
|
||||
case SDL_SCANCODE_F8:
|
||||
{
|
||||
if (!pressed)
|
||||
{
|
||||
const u32 index = event->key.keysym.scancode - SDL_SCANCODE_F1 + 1;
|
||||
if (event->key.keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT))
|
||||
SaveState(true, index);
|
||||
else
|
||||
LoadState(true, index);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_SCANCODE_RETURN:
|
||||
case SDL_SCANCODE_KP_ENTER:
|
||||
{
|
||||
if ((event->key.keysym.mod & (KMOD_LALT | KMOD_RALT)) && !pressed)
|
||||
SetFullscreen(!m_fullscreen);
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_SCANCODE_TAB:
|
||||
{
|
||||
if (!repeat)
|
||||
{
|
||||
m_speed_limiter_temp_disabled = pressed;
|
||||
UpdateSpeedLimiterState();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_SCANCODE_PAUSE:
|
||||
{
|
||||
if (pressed)
|
||||
PauseSystem(!m_paused);
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_SCANCODE_SPACE:
|
||||
{
|
||||
if (pressed)
|
||||
DoFrameStep();
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_SCANCODE_HOME:
|
||||
{
|
||||
if (pressed && !repeat && m_system)
|
||||
{
|
||||
m_settings.speed_limiter_enabled = !m_settings.speed_limiter_enabled;
|
||||
m_settings_copy.speed_limiter_enabled = m_settings.speed_limiter_enabled;
|
||||
UpdateSpeedLimiterState();
|
||||
AddOSDMessage(m_settings.speed_limiter_enabled ? "Speed limiter enabled." : "Speed limiter disabled.");
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_SCANCODE_END:
|
||||
{
|
||||
if (pressed)
|
||||
ToggleSoftwareRendering();
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_SCANCODE_PAGEUP:
|
||||
case SDL_SCANCODE_PAGEDOWN:
|
||||
{
|
||||
if (pressed)
|
||||
ModifyResolutionScale(event->key.keysym.scancode == SDL_SCANCODE_PAGEUP ? 1 : -1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void SDLHostInterface::UpdateKeyboardControllerMapping()
|
||||
{
|
||||
m_keyboard_button_mapping.fill(-1);
|
||||
|
||||
const Controller* controller = m_system ? m_system->GetController(0) : nullptr;
|
||||
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 ? m_system->GetController(0) : nullptr) != 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:
|
||||
SDL_Event ev;
|
||||
if (SDL_PollEvent(&ev))
|
||||
HandleSDLEvent(&ev);
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
#undef DO_ACTION
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void SDLHostInterface::DrawImGuiWindows()
|
||||
{
|
||||
DrawMainMenuBar();
|
||||
if (!m_fullscreen)
|
||||
DrawMainMenuBar();
|
||||
|
||||
HostInterface::DrawImGuiWindows();
|
||||
CommonHostInterface::DrawImGuiWindows();
|
||||
|
||||
if (!m_system)
|
||||
DrawPoweredOffWindow();
|
||||
|
@ -652,26 +516,11 @@ void SDLHostInterface::DrawImGuiWindows()
|
|||
|
||||
void SDLHostInterface::DrawMainMenuBar()
|
||||
{
|
||||
// We skip drawing the menu bar if we're in fullscreen and the mouse pointer isn't in range.
|
||||
const float SHOW_THRESHOLD = 20.0f;
|
||||
if (m_fullscreen && !m_system &&
|
||||
ImGui::GetIO().MousePos.y >= (SHOW_THRESHOLD * ImGui::GetIO().DisplayFramebufferScale.x) &&
|
||||
!ImGui::IsWindowFocused(ImGuiFocusedFlags_AnyWindow))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ImGui::BeginMainMenuBar())
|
||||
return;
|
||||
|
||||
const bool system_enabled = static_cast<bool>(m_system);
|
||||
|
||||
if (m_focus_main_menu_bar)
|
||||
{
|
||||
ImGui::OpenPopup("System");
|
||||
m_focus_main_menu_bar = false;
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenu("System"))
|
||||
{
|
||||
if (ImGui::MenuItem("Start Disc", nullptr, false, !system_enabled))
|
||||
|
@ -1441,14 +1290,7 @@ void SDLHostInterface::Run()
|
|||
{
|
||||
while (!m_quit_request)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
SDL_Event ev;
|
||||
if (SDL_PollEvent(&ev))
|
||||
HandleSDLEvent(&ev);
|
||||
else
|
||||
break;
|
||||
}
|
||||
ProcessEvents();
|
||||
|
||||
if (m_system && !m_paused)
|
||||
{
|
||||
|
@ -1460,8 +1302,6 @@ void SDLHostInterface::Run()
|
|||
}
|
||||
}
|
||||
|
||||
// g_sdl_controller_interface.UpdateControllerRumble();
|
||||
|
||||
// rendering
|
||||
{
|
||||
DrawImGuiWindows();
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#include "common/gl/texture.h"
|
||||
#include "core/host_display.h"
|
||||
#include "core/host_interface.h"
|
||||
#include "frontend-common/sdl_controller_interface.h"
|
||||
#include "frontend-common/common_host_interface.h"
|
||||
#include <SDL.h>
|
||||
#include <array>
|
||||
#include <deque>
|
||||
|
@ -15,9 +15,9 @@
|
|||
class System;
|
||||
class AudioStream;
|
||||
|
||||
class Controller;
|
||||
class INISettingsInterface;
|
||||
|
||||
class SDLHostInterface final : public HostInterface
|
||||
class SDLHostInterface final : public CommonHostInterface
|
||||
{
|
||||
public:
|
||||
SDLHostInterface();
|
||||
|
@ -40,42 +40,17 @@ protected:
|
|||
bool AcquireHostDisplay() override;
|
||||
void ReleaseHostDisplay() override;
|
||||
std::unique_ptr<AudioStream> CreateAudioStream(AudioBackend backend) override;
|
||||
std::unique_ptr<ControllerInterface> CreateControllerInterface() override;
|
||||
|
||||
void OnSystemCreated() override;
|
||||
void OnSystemPaused(bool paused) override;
|
||||
void OnSystemDestroyed() override;
|
||||
void OnControllerTypeChanged(u32 slot) override;
|
||||
void OnRunningGameChanged() override;
|
||||
|
||||
std::optional<HostKeyCode> GetHostKeyCode(const std::string_view key_code) const override;
|
||||
void UpdateInputMap() override;
|
||||
|
||||
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)>;
|
||||
|
||||
struct ControllerData
|
||||
{
|
||||
SDL_GameController* controller;
|
||||
SDL_Haptic* haptic;
|
||||
u32 controller_index;
|
||||
float last_rumble_strength;
|
||||
};
|
||||
|
||||
bool HasSystem() const { return static_cast<bool>(m_system); }
|
||||
|
||||
#ifdef WIN32
|
||||
|
@ -99,8 +74,8 @@ private:
|
|||
void SaveSettings();
|
||||
void UpdateSettings();
|
||||
|
||||
bool IsFullscreen() const { return m_fullscreen; }
|
||||
void SetFullscreen(bool enabled);
|
||||
bool IsFullscreen() const override;
|
||||
bool SetFullscreen(bool enabled) override;
|
||||
|
||||
// We only pass mouse input through if it's grabbed
|
||||
void DrawImGuiWindows() override;
|
||||
|
@ -109,10 +84,7 @@ private:
|
|||
void DoFrameStep();
|
||||
|
||||
void HandleSDLEvent(const SDL_Event* event);
|
||||
void HandleSDLKeyEvent(const SDL_Event* event);
|
||||
|
||||
void UpdateKeyboardControllerMapping();
|
||||
bool HandleSDLKeyEventForController(const SDL_Event* event);
|
||||
void ProcessEvents();
|
||||
|
||||
void DrawMainMenuBar();
|
||||
void DrawQuickSettingsMenu();
|
||||
|
@ -125,15 +97,12 @@ private:
|
|||
|
||||
SDL_Window* m_window = nullptr;
|
||||
std::unique_ptr<HostDisplayTexture> m_app_icon_texture;
|
||||
|
||||
KeyboardControllerActionMap m_keyboard_button_mapping;
|
||||
|
||||
std::unique_ptr<INISettingsInterface> m_settings_interface;
|
||||
u32 m_run_later_event_id = 0;
|
||||
|
||||
bool m_fullscreen = false;
|
||||
bool m_quit_request = false;
|
||||
bool m_frame_step_request = false;
|
||||
bool m_focus_main_menu_bar = false;
|
||||
bool m_settings_window_open = false;
|
||||
bool m_about_window_open = false;
|
||||
|
||||
|
|
|
@ -0,0 +1,353 @@
|
|||
#pragma once
|
||||
#include "common/string.h"
|
||||
#include "common/types.h"
|
||||
#include <SDL.h>
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
|
||||
namespace SDLKeyNames {
|
||||
|
||||
static const std::map<int, const char*> s_sdl_key_names = {{SDLK_RETURN, "Return"},
|
||||
{SDLK_ESCAPE, "Escape"},
|
||||
{SDLK_BACKSPACE, "Backspace"},
|
||||
{SDLK_TAB, "Tab"},
|
||||
{SDLK_SPACE, "Space"},
|
||||
{SDLK_EXCLAIM, "Exclam"},
|
||||
{SDLK_QUOTEDBL, "QuoteDbl"},
|
||||
{SDLK_HASH, "Hash"},
|
||||
{SDLK_PERCENT, "Percent"},
|
||||
{SDLK_DOLLAR, "Dollar"},
|
||||
{SDLK_AMPERSAND, "Ampersand"},
|
||||
{SDLK_QUOTE, "Apostrophe"},
|
||||
{SDLK_LEFTPAREN, "ParenLeft"},
|
||||
{SDLK_RIGHTPAREN, "ParenRight"},
|
||||
{SDLK_ASTERISK, "Asterisk"},
|
||||
{SDLK_PLUS, "PLus"},
|
||||
{SDLK_COMMA, "Comma"},
|
||||
{SDLK_MINUS, "Minus"},
|
||||
{SDLK_PERIOD, "Period"},
|
||||
{SDLK_SLASH, "Slash"},
|
||||
{SDLK_0, "0"},
|
||||
{SDLK_1, "1"},
|
||||
{SDLK_2, "2"},
|
||||
{SDLK_3, "3"},
|
||||
{SDLK_4, "4"},
|
||||
{SDLK_5, "5"},
|
||||
{SDLK_6, "6"},
|
||||
{SDLK_7, "7"},
|
||||
{SDLK_8, "8"},
|
||||
{SDLK_9, "9"},
|
||||
{SDLK_COLON, "Colon"},
|
||||
{SDLK_SEMICOLON, "Semcolon"},
|
||||
{SDLK_LESS, "Less"},
|
||||
{SDLK_EQUALS, "Equal"},
|
||||
{SDLK_GREATER, "Greater"},
|
||||
{SDLK_QUESTION, "Question"},
|
||||
{SDLK_AT, "AT"},
|
||||
{SDLK_LEFTBRACKET, "BracketLeft"},
|
||||
{SDLK_BACKSLASH, "Backslash"},
|
||||
{SDLK_RIGHTBRACKET, "BracketRight"},
|
||||
{SDLK_CARET, "Caret"},
|
||||
{SDLK_UNDERSCORE, "Underscore"},
|
||||
{SDLK_BACKQUOTE, "Backquote"},
|
||||
{SDLK_a, "A"},
|
||||
{SDLK_b, "B"},
|
||||
{SDLK_c, "C"},
|
||||
{SDLK_d, "D"},
|
||||
{SDLK_e, "E"},
|
||||
{SDLK_f, "F"},
|
||||
{SDLK_g, "G"},
|
||||
{SDLK_h, "H"},
|
||||
{SDLK_i, "I"},
|
||||
{SDLK_j, "J"},
|
||||
{SDLK_k, "K"},
|
||||
{SDLK_l, "L"},
|
||||
{SDLK_m, "M"},
|
||||
{SDLK_n, "N"},
|
||||
{SDLK_o, "O"},
|
||||
{SDLK_p, "P"},
|
||||
{SDLK_q, "Q"},
|
||||
{SDLK_r, "R"},
|
||||
{SDLK_s, "S"},
|
||||
{SDLK_t, "T"},
|
||||
{SDLK_u, "U"},
|
||||
{SDLK_v, "V"},
|
||||
{SDLK_w, "W"},
|
||||
{SDLK_x, "X"},
|
||||
{SDLK_y, "Y"},
|
||||
{SDLK_z, "Z"},
|
||||
{SDLK_CAPSLOCK, "CapsLock"},
|
||||
{SDLK_F1, "F1"},
|
||||
{SDLK_F2, "F2"},
|
||||
{SDLK_F3, "F3"},
|
||||
{SDLK_F4, "F4"},
|
||||
{SDLK_F5, "F5"},
|
||||
{SDLK_F6, "F6"},
|
||||
{SDLK_F7, "F7"},
|
||||
{SDLK_F8, "F8"},
|
||||
{SDLK_F9, "F9"},
|
||||
{SDLK_F10, "F10"},
|
||||
{SDLK_F11, "F11"},
|
||||
{SDLK_F12, "F12"},
|
||||
{SDLK_PRINTSCREEN, "Print"},
|
||||
{SDLK_SCROLLLOCK, "ScrollLock"},
|
||||
{SDLK_PAUSE, "Pause"},
|
||||
{SDLK_INSERT, "Insert"},
|
||||
{SDLK_HOME, "Home"},
|
||||
{SDLK_PAGEUP, "PageUp"},
|
||||
{SDLK_DELETE, "Delete"},
|
||||
{SDLK_END, "End"},
|
||||
{SDLK_PAGEDOWN, "PageDown"},
|
||||
{SDLK_RIGHT, "Right"},
|
||||
{SDLK_LEFT, "Left"},
|
||||
{SDLK_DOWN, "Down"},
|
||||
{SDLK_UP, "Up"},
|
||||
{SDLK_NUMLOCKCLEAR, "NumLock"},
|
||||
{SDLK_KP_DIVIDE, "Keypad+Divide"},
|
||||
{SDLK_KP_MULTIPLY, "Keypad+Multiply"},
|
||||
{SDLK_KP_MINUS, "Keypad+Minus"},
|
||||
{SDLK_KP_PLUS, "Keypad+Plus"},
|
||||
{SDLK_KP_ENTER, "Keypad+Return"},
|
||||
{SDLK_KP_1, "Keypad+1"},
|
||||
{SDLK_KP_2, "Keypad+2"},
|
||||
{SDLK_KP_3, "Keypad+3"},
|
||||
{SDLK_KP_4, "Keypad+4"},
|
||||
{SDLK_KP_5, "Keypad+5"},
|
||||
{SDLK_KP_6, "Keypad+6"},
|
||||
{SDLK_KP_7, "Keypad+7"},
|
||||
{SDLK_KP_8, "Keypad+8"},
|
||||
{SDLK_KP_9, "Keypad+9"},
|
||||
{SDLK_KP_0, "Keypad+0"},
|
||||
{SDLK_KP_PERIOD, "Keypad+Period"},
|
||||
{SDLK_APPLICATION, "Application"},
|
||||
{SDLK_POWER, "Power"},
|
||||
{SDLK_KP_EQUALS, "Keypad+Equal"},
|
||||
{SDLK_F13, "F13"},
|
||||
{SDLK_F14, "F14"},
|
||||
{SDLK_F15, "F15"},
|
||||
{SDLK_F16, "F16"},
|
||||
{SDLK_F17, "F17"},
|
||||
{SDLK_F18, "F18"},
|
||||
{SDLK_F19, "F19"},
|
||||
{SDLK_F20, "F20"},
|
||||
{SDLK_F21, "F21"},
|
||||
{SDLK_F22, "F22"},
|
||||
{SDLK_F23, "F23"},
|
||||
{SDLK_F24, "F24"},
|
||||
{SDLK_EXECUTE, "Execute"},
|
||||
{SDLK_HELP, "Help"},
|
||||
{SDLK_MENU, "Menu"},
|
||||
{SDLK_SELECT, "Select"},
|
||||
{SDLK_STOP, "Stop"},
|
||||
{SDLK_AGAIN, "Again"},
|
||||
{SDLK_UNDO, "Undo"},
|
||||
{SDLK_CUT, "Cut"},
|
||||
{SDLK_COPY, "Copy"},
|
||||
{SDLK_PASTE, "Paste"},
|
||||
{SDLK_FIND, "Find"},
|
||||
{SDLK_MUTE, "Mute"},
|
||||
{SDLK_VOLUMEUP, "VolumeUp"},
|
||||
{SDLK_VOLUMEDOWN, "VolumeDown"},
|
||||
{SDLK_KP_COMMA, "Keypad+Comma"},
|
||||
{SDLK_KP_EQUALSAS400, "Keypad+EqualAS400"},
|
||||
{SDLK_ALTERASE, "AltErase"},
|
||||
{SDLK_SYSREQ, "SysReq"},
|
||||
{SDLK_CANCEL, "Cancel"},
|
||||
{SDLK_CLEAR, "Clear"},
|
||||
{SDLK_PRIOR, "Prior"},
|
||||
{SDLK_RETURN2, "Return2"},
|
||||
{SDLK_SEPARATOR, "Separator"},
|
||||
{SDLK_OUT, "Out"},
|
||||
{SDLK_OPER, "Oper"},
|
||||
{SDLK_CLEARAGAIN, "ClearAgain"},
|
||||
{SDLK_CRSEL, "CrSel"},
|
||||
{SDLK_EXSEL, "ExSel"},
|
||||
{SDLK_KP_00, "Keypad+00"},
|
||||
{SDLK_KP_000, "Keypad+000"},
|
||||
{SDLK_THOUSANDSSEPARATOR, "ThousandsSeparator"},
|
||||
{SDLK_DECIMALSEPARATOR, "DecimalSeparator"},
|
||||
{SDLK_CURRENCYUNIT, "CurrencyUnit"},
|
||||
{SDLK_CURRENCYSUBUNIT, "CurrencySubunit"},
|
||||
{SDLK_KP_LEFTPAREN, "Keypad+ParenLeft"},
|
||||
{SDLK_KP_RIGHTPAREN, "Keypad+ParenRight"},
|
||||
{SDLK_KP_LEFTBRACE, "Keypad+LeftBrace"},
|
||||
{SDLK_KP_RIGHTBRACE, "Keypad+RightBrace"},
|
||||
{SDLK_KP_TAB, "Keypad+Tab"},
|
||||
{SDLK_KP_BACKSPACE, "Keypad+Backspace"},
|
||||
{SDLK_KP_A, "Keypad+A"},
|
||||
{SDLK_KP_B, "Keypad+B"},
|
||||
{SDLK_KP_C, "Keypad+C"},
|
||||
{SDLK_KP_D, "Keypad+D"},
|
||||
{SDLK_KP_E, "Keypad+E"},
|
||||
{SDLK_KP_F, "Keypad+F"},
|
||||
{SDLK_KP_XOR, "Keypad+XOR"},
|
||||
{SDLK_KP_POWER, "Keypad+Power"},
|
||||
{SDLK_KP_PERCENT, "Keypad+Percent"},
|
||||
{SDLK_KP_LESS, "Keypad+Less"},
|
||||
{SDLK_KP_GREATER, "Keypad+Greater"},
|
||||
{SDLK_KP_AMPERSAND, "Keypad+Ampersand"},
|
||||
{SDLK_KP_DBLAMPERSAND, "Keypad+AmpersandDbl"},
|
||||
{SDLK_KP_VERTICALBAR, "Keypad+Bar"},
|
||||
{SDLK_KP_DBLVERTICALBAR, "Keypad+BarDbl"},
|
||||
{SDLK_KP_COLON, "Keypad+Colon"},
|
||||
{SDLK_KP_HASH, "Keypad+Hash"},
|
||||
{SDLK_KP_SPACE, "Keypad+Space"},
|
||||
{SDLK_KP_AT, "Keypad+At"},
|
||||
{SDLK_KP_EXCLAM, "Keypad+Exclam"},
|
||||
{SDLK_KP_MEMSTORE, "Keypad+MemStore"},
|
||||
{SDLK_KP_MEMRECALL, "Keypad+MemRecall"},
|
||||
{SDLK_KP_MEMCLEAR, "Keypad+MemClear"},
|
||||
{SDLK_KP_MEMADD, "Keypad+MemAdd"},
|
||||
{SDLK_KP_MEMSUBTRACT, "Keypad+MemSubtract"},
|
||||
{SDLK_KP_MEMMULTIPLY, "Keypad+MemMultiply"},
|
||||
{SDLK_KP_MEMDIVIDE, "Keypad+MemDivide"},
|
||||
{SDLK_KP_PLUSMINUS, "Keypad+PlusMinus"},
|
||||
{SDLK_KP_CLEAR, "Keypad+Clear"},
|
||||
{SDLK_KP_CLEARENTRY, "Keypad+ClearEntry"},
|
||||
{SDLK_KP_BINARY, "Keypad+Binary"},
|
||||
{SDLK_KP_OCTAL, "Keypad+Octal"},
|
||||
{SDLK_KP_DECIMAL, "Keypad+Decimal"},
|
||||
{SDLK_KP_HEXADECIMAL, "Keypad+Hexadecimal"},
|
||||
{SDLK_LCTRL, "LeftControl"},
|
||||
{SDLK_LSHIFT, "LeftShift"},
|
||||
{SDLK_LALT, "LeftAlt"},
|
||||
{SDLK_LGUI, "Super_L"},
|
||||
{SDLK_RCTRL, "RightCtrl"},
|
||||
{SDLK_RSHIFT, "RightShift"},
|
||||
{SDLK_RALT, "RightAlt"},
|
||||
{SDLK_RGUI, "RightSuper"},
|
||||
{SDLK_MODE, "Mode"},
|
||||
{SDLK_AUDIONEXT, "MediaNext"},
|
||||
{SDLK_AUDIOPREV, "MediaPrevious"},
|
||||
{SDLK_AUDIOSTOP, "MediaStop"},
|
||||
{SDLK_AUDIOPLAY, "MediaPlay"},
|
||||
{SDLK_AUDIOMUTE, "VolumeMute"},
|
||||
{SDLK_MEDIASELECT, "MediaSelect"},
|
||||
{SDLK_WWW, "WWW"},
|
||||
{SDLK_MAIL, "Mail"},
|
||||
{SDLK_CALCULATOR, "Calculator"},
|
||||
{SDLK_COMPUTER, "Computer"},
|
||||
{SDLK_AC_SEARCH, "Search"},
|
||||
{SDLK_AC_HOME, "Home"},
|
||||
{SDLK_AC_BACK, "Back"},
|
||||
{SDLK_AC_FORWARD, "Forward"},
|
||||
{SDLK_AC_STOP, "Stop"},
|
||||
{SDLK_AC_REFRESH, "Refresh"},
|
||||
{SDLK_AC_BOOKMARKS, "Bookmarks"},
|
||||
{SDLK_BRIGHTNESSDOWN, "BrightnessDown"},
|
||||
{SDLK_BRIGHTNESSUP, "BrightnessUp"},
|
||||
{SDLK_DISPLAYSWITCH, "DisplaySwitch"},
|
||||
{SDLK_KBDILLUMTOGGLE, "IllumToggle"},
|
||||
{SDLK_KBDILLUMDOWN, "IllumDown"},
|
||||
{SDLK_KBDILLUMUP, "IllumUp"},
|
||||
{SDLK_EJECT, "Eject"},
|
||||
{SDLK_SLEEP, "Sleep"},
|
||||
{SDLK_APP1, "App1"},
|
||||
{SDLK_APP2, "App2"},
|
||||
{SDLK_AUDIOREWIND, "MediaRewind"},
|
||||
{SDLK_AUDIOFASTFORWARD, "MediaFastForward"}};
|
||||
|
||||
struct SDLKeyModifierEntry
|
||||
{
|
||||
SDL_Keymod mod;
|
||||
SDL_Keymod mod_mask;
|
||||
SDL_Keycode key_left;
|
||||
SDL_Keycode key_right;
|
||||
const char* name;
|
||||
};
|
||||
|
||||
static const std::array<SDLKeyModifierEntry, 5> s_sdl_key_modifiers = {
|
||||
{{KMOD_LSHIFT, static_cast<SDL_Keymod>(KMOD_LSHIFT | KMOD_RSHIFT), SDLK_LSHIFT, SDLK_RSHIFT, "Shift"},
|
||||
{KMOD_LCTRL, static_cast<SDL_Keymod>(KMOD_LCTRL | KMOD_LCTRL), SDLK_LCTRL, SDLK_RCTRL, "Control"},
|
||||
{KMOD_LALT, static_cast<SDL_Keymod>(KMOD_LALT | KMOD_RALT), SDLK_LALT, SDLK_RALT, "Alt"},
|
||||
{KMOD_LGUI, static_cast<SDL_Keymod>(KMOD_LGUI | KMOD_RGUI), SDLK_LGUI, SDLK_RGUI, "Meta"}}};
|
||||
|
||||
const char* GetKeyName(SDL_Keycode key)
|
||||
{
|
||||
const auto it = s_sdl_key_names.find(key);
|
||||
return it == s_sdl_key_names.end() ? nullptr : it->second;
|
||||
}
|
||||
|
||||
std::optional<SDL_Keycode> GetKeyCodeForName(const std::string_view key_name)
|
||||
{
|
||||
for (const auto& it : s_sdl_key_names)
|
||||
{
|
||||
if (key_name == it.second)
|
||||
return it.first;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
u32 KeyEventToInt(const SDL_Event* event)
|
||||
{
|
||||
u32 code = static_cast<u32>(event->key.keysym.sym);
|
||||
|
||||
const SDL_Keymod mods = static_cast<SDL_Keymod>(event->key.keysym.mod);
|
||||
if (mods & (KMOD_LSHIFT | KMOD_RSHIFT))
|
||||
code |= static_cast<u32>(KMOD_LSHIFT) << 16;
|
||||
if (mods & (KMOD_LCTRL | KMOD_RCTRL))
|
||||
code |= static_cast<u32>(KMOD_LCTRL) << 16;
|
||||
if (mods & (KMOD_LALT | KMOD_RALT))
|
||||
code |= static_cast<u32>(KMOD_LALT) << 16;
|
||||
if (mods & (KMOD_LGUI | KMOD_RGUI))
|
||||
code |= static_cast<u32>(KMOD_LGUI) << 16;
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
bool KeyEventToString(const SDL_Event* event, String& out_string)
|
||||
{
|
||||
const SDL_Keycode key = event->key.keysym.sym;
|
||||
const SDL_Keymod mods = static_cast<SDL_Keymod>(event->key.keysym.mod);
|
||||
const char* key_name = GetKeyName(event->key.keysym.sym);
|
||||
if (!key_name)
|
||||
return false;
|
||||
|
||||
out_string.Clear();
|
||||
|
||||
for (const SDLKeyModifierEntry& mod : s_sdl_key_modifiers)
|
||||
{
|
||||
if (mods & mod.mod_mask && key != mod.key_left && key != mod.key_right)
|
||||
{
|
||||
out_string.AppendString(mod.name);
|
||||
out_string.AppendCharacter('+');
|
||||
}
|
||||
}
|
||||
|
||||
out_string.AppendString(key_name);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::optional<u32> ParseKeyString(const std::string_view key_str)
|
||||
{
|
||||
u32 modifiers = 0;
|
||||
std::string_view::size_type pos = 0;
|
||||
for (;;)
|
||||
{
|
||||
std::string_view::size_type plus_pos = key_str.find('+', pos);
|
||||
if (plus_pos == std::string_view::npos)
|
||||
break;
|
||||
|
||||
const std::string_view mod_part = key_str.substr(pos, plus_pos - pos);
|
||||
for (const SDLKeyModifierEntry& mod : s_sdl_key_modifiers)
|
||||
{
|
||||
if (mod_part == mod.name)
|
||||
{
|
||||
modifiers |= static_cast<int>(mod.mod);
|
||||
break;
|
||||
}
|
||||
}
|
||||
pos = plus_pos + 1;
|
||||
}
|
||||
|
||||
std::optional<SDL_Keycode> key_code = GetKeyCodeForName(key_str.substr(pos));
|
||||
if (!key_code)
|
||||
return std::nullopt;
|
||||
|
||||
return static_cast<u32>(key_code.value()) | (modifiers << 16);
|
||||
}
|
||||
} // namespace SDLKeyNames
|
Loading…
Reference in New Issue