From a806645510abed77468a76b5a23944a2cd8a043f Mon Sep 17 00:00:00 2001 From: Sepalani Date: Tue, 22 Apr 2025 15:51:52 +0400 Subject: [PATCH] IOS/KBD: Add keyboard support for SDL3 backend --- Source/Core/Common/CMakeLists.txt | 5 + Source/Core/Common/Keyboard.cpp | 174 ++++++++++++++++-- Source/Core/Common/Keyboard.h | 55 +++++- Source/Core/Core/IOS/USB/USB_KBD.cpp | 174 ++---------------- Source/Core/Core/IOS/USB/USB_KBD.h | 14 +- Source/Core/DolphinQt/Host.cpp | 12 ++ Source/Core/DolphinQt/Settings.cpp | 7 + .../ControllerInterface/SDL/SDL.cpp | 115 +++++++++++- 8 files changed, 364 insertions(+), 192 deletions(-) diff --git a/Source/Core/Common/CMakeLists.txt b/Source/Core/Common/CMakeLists.txt index 996f5401b0..970196c5ad 100644 --- a/Source/Core/Common/CMakeLists.txt +++ b/Source/Core/Common/CMakeLists.txt @@ -351,6 +351,11 @@ if(USE_UPNP) target_link_libraries(common PRIVATE Miniupnpc::miniupnpc) endif() +if(ENABLE_SDL) + target_link_libraries(common PRIVATE SDL3::SDL3) + target_compile_definitions(common PRIVATE -DHAVE_SDL3) +endif() + if(ENABLE_LLVM) find_package(LLVM CONFIG) if(LLVM_FOUND) diff --git a/Source/Core/Common/Keyboard.cpp b/Source/Core/Common/Keyboard.cpp index fca7aa9db2..dd7e11e617 100644 --- a/Source/Core/Common/Keyboard.cpp +++ b/Source/Core/Common/Keyboard.cpp @@ -4,12 +4,25 @@ #include "Common/Keyboard.h" #include +#include #include #ifdef _WIN32 #include +#elif defined(HAVE_SDL3) +#include +#include #endif +#ifdef HAVE_SDL3 +// Will be overridden by Dolphin's SDL InputBackend +u32 Common::KeyboardContext::s_sdl_init_event_type(-1); +u32 Common::KeyboardContext::s_sdl_update_event_type(-1); +u32 Common::KeyboardContext::s_sdl_quit_event_type(-1); +#endif + +#include "Core/Config/MainSettings.h" + namespace { // Crazy ugly @@ -173,59 +186,183 @@ u8 MapVirtualKeyToHID(u8 virtual_key, int keyboard_layout) #else u8 MapVirtualKeyToHID(u8 virtual_key, int keyboard_layout) { + // SDL3 keyboard state uses scan codes already based on HID usage id return virtual_key; } #endif + +std::weak_ptr s_keyboard_context; +std::mutex s_keyboard_context_mutex; + +// Will be updated by DolphinQt's Host: +// - SetRenderHandle +// - SetFullscreen +Common::KeyboardContext::HandlerState s_handler_state{}; } // Anonymous namespace namespace Common { -bool Common::IsVirtualKeyPressed(int virtual_key) +KeyboardContext::KeyboardContext() +{ + if (Config::Get(Config::MAIN_WII_KEYBOARD)) + Init(); +} + +KeyboardContext::~KeyboardContext() +{ + if (Config::Get(Config::MAIN_WII_KEYBOARD)) + Quit(); +} + +void KeyboardContext::Init() +{ +#if !defined(_WIN32) && defined(HAVE_SDL3) + SDL_Event event{s_sdl_init_event_type}; + SDL_PushEvent(&event); + m_keyboard_state = SDL_GetKeyboardState(nullptr); +#endif + m_is_ready = true; +} + +void KeyboardContext::Quit() +{ + m_is_ready = false; +#if !defined(_WIN32) && defined(HAVE_SDL3) + SDL_Event event{s_sdl_quit_event_type}; + SDL_PushEvent(&event); +#endif +} + +void* KeyboardContext::HandlerState::GetHandle() const +{ +#ifdef _WIN32 + if (is_rendering_to_main && !is_fullscreen) + return main_handle; +#endif + return renderer_handle; +} + +void KeyboardContext::NotifyInit() +{ + if (auto self = s_keyboard_context.lock()) + self->Init(); +} + +void KeyboardContext::NotifyHandlerChanged(const KeyboardContext::HandlerState& state) +{ + s_handler_state = state; + if (s_keyboard_context.expired()) + return; +#if !defined(_WIN32) && defined(HAVE_SDL3) + SDL_Event event{s_sdl_update_event_type}; + SDL_PushEvent(&event); +#endif +} + +void KeyboardContext::NotifyQuit() +{ + if (auto self = s_keyboard_context.lock()) + self->Quit(); +} + +void* KeyboardContext::GetWindowHandle() +{ + return s_handler_state.GetHandle(); +} + +std::shared_ptr KeyboardContext::GetInstance() +{ + const std::lock_guard guard(s_keyboard_context_mutex); + std::shared_ptr ptr = s_keyboard_context.lock(); + if (!ptr) + { + ptr = std::shared_ptr(new KeyboardContext); + s_keyboard_context = ptr; + } + return ptr; +} + +HIDPressedState KeyboardContext::GetPressedState(int keyboard_layout) const +{ + return m_is_ready ? HIDPressedState{.modifiers = PollHIDModifiers(), + .pressed_keys = PollHIDPressedKeys(keyboard_layout)} : + HIDPressedState{}; +} + +bool KeyboardContext::IsVirtualKeyPressed(int virtual_key) const { #ifdef _WIN32 return (GetAsyncKeyState(virtual_key) & 0x8000) != 0; +#elif defined(HAVE_SDL3) + if (virtual_key >= SDL_SCANCODE_COUNT) + return false; + return m_keyboard_state[virtual_key]; #else - // TODO: do it for non-Windows platforms + // TODO: Android implementation return false; #endif } -u8 Common::PollHIDModifiers() +u8 KeyboardContext::PollHIDModifiers() const { u8 modifiers = 0; -#ifdef _WIN32 + using VkHidPair = std::pair; + // References: // https://learn.microsoft.com/windows/win32/inputdev/virtual-key-codes + // https://wiki.libsdl.org/SDL3/SDL_Scancode // https://www.usb.org/document-library/device-class-definition-hid-111 + // + // HID modifiers: + // Bit 0 - LEFT CTRL + // Bit 1 - LEFT SHIFT + // Bit 2 - LEFT ALT + // Bit 3 - LEFT GUI + // Bit 4 - RIGHT CTRL + // Bit 5 - RIGHT SHIFT + // Bit 6 - RIGHT ALT + // Bit 7 - RIGHT GUI static const std::vector MODIFIERS_MAP{ - {VK_LCONTROL, 0x01}, // HID modifier: Bit 0 - LEFT CTRL - {VK_LSHIFT, 0x02}, // HID modifier: Bit 1 - LEFT SHIFT - {VK_LMENU, 0x04}, // HID modifier: Bit 2 - LEFT ALT - {VK_LWIN, 0x08}, // HID modifier: Bit 3 - LEFT GUI - {VK_RCONTROL, 0x10}, // HID modifier: Bit 4 - RIGHT CTRL - {VK_RSHIFT, 0x20}, // HID modifier: Bit 5 - RIGHT SHIFT - {VK_RMENU, 0x40}, // HID modifier: Bit 6 - RIGHT ALT - {VK_RWIN, 0x80} // HID modifier: Bit 7 - RIGHT GUI +#ifdef _WIN32 + {VK_LCONTROL, 0x01}, {VK_LSHIFT, 0x02}, {VK_LMENU, 0x04}, + {VK_LWIN, 0x08}, {VK_RCONTROL, 0x10}, {VK_RSHIFT, 0x20}, + {VK_RMENU, 0x40}, {VK_RWIN, 0x80} +#elif defined(HAVE_SDL3) + {SDL_SCANCODE_LCTRL, 0x01}, {SDL_SCANCODE_LSHIFT, 0x02}, {SDL_SCANCODE_LALT, 0x04}, + {SDL_SCANCODE_LGUI, 0x08}, {SDL_SCANCODE_RCTRL, 0x10}, {SDL_SCANCODE_RSHIFT, 0x20}, + {SDL_SCANCODE_RALT, 0x40}, {SDL_SCANCODE_RGUI, 0x80} +#else + // TODO: Android implementation +#endif }; + for (const auto& [virtual_key, hid_modifier] : MODIFIERS_MAP) { if (IsVirtualKeyPressed(virtual_key)) modifiers |= hid_modifier; } -#else - // TODO: Implementation for non-Windows platforms -#endif + return modifiers; } -HIDPressedKeys PollHIDPressedKeys(int keyboard_layout) +HIDPressedKeys KeyboardContext::PollHIDPressedKeys(int keyboard_layout) const { HIDPressedKeys pressed_keys{}; auto it = pressed_keys.begin(); #ifdef _WIN32 - for (std::size_t virtual_key = 0; virtual_key < KEYBOARD_STATE_SIZE; ++virtual_key) + const std::size_t begin = 0; + const std::size_t end = KEYBOARD_STATE_SIZE; +#elif defined(HAVE_SDL3) + const std::size_t begin = SDL_SCANCODE_A; + const std::size_t end = SDL_SCANCODE_LCTRL; +#else + const std::size_t begin = 0; + const std::size_t end = 0; +#endif + + for (std::size_t virtual_key = begin; virtual_key < end; ++virtual_key) { if (!IsVirtualKeyPressed(static_cast(virtual_key))) continue; @@ -234,9 +371,6 @@ HIDPressedKeys PollHIDPressedKeys(int keyboard_layout) if (++it == pressed_keys.end()) break; } -#else - // TODO: Implementation for non-Windows platforms -#endif return pressed_keys; } } // namespace Common diff --git a/Source/Core/Common/Keyboard.h b/Source/Core/Common/Keyboard.h index 8fcac2b0c1..d0d23a0a8e 100644 --- a/Source/Core/Common/Keyboard.h +++ b/Source/Core/Common/Keyboard.h @@ -4,6 +4,7 @@ #pragma once #include +#include #include "Common/CommonTypes.h" @@ -17,7 +18,55 @@ enum using HIDPressedKeys = std::array; -bool IsVirtualKeyPressed(int virtual_key); -u8 PollHIDModifiers(); -HIDPressedKeys PollHIDPressedKeys(int keyboard_layout); +struct HIDPressedState +{ + u8 modifiers = 0; + HIDPressedKeys pressed_keys{}; + + auto operator<=>(const HIDPressedState&) const = default; +}; + +class KeyboardContext +{ +public: + ~KeyboardContext(); + + struct HandlerState + { + void* main_handle = nullptr; + void* renderer_handle = nullptr; + bool is_fullscreen = false; + bool is_rendering_to_main = false; + + void* GetHandle() const; + }; + + static void NotifyInit(); + static void NotifyHandlerChanged(const HandlerState& state); + static void NotifyQuit(); + static void* GetWindowHandle(); + static std::shared_ptr GetInstance(); + + HIDPressedState GetPressedState(int keyboard_layout) const; + +#ifdef HAVE_SDL3 + static u32 s_sdl_init_event_type; + static u32 s_sdl_update_event_type; + static u32 s_sdl_quit_event_type; +#endif + +private: + KeyboardContext(); + + void Init(); + void Quit(); + bool IsVirtualKeyPressed(int virtual_key) const; + u8 PollHIDModifiers() const; + HIDPressedKeys PollHIDPressedKeys(int keyboard_layout) const; + + bool m_is_ready = false; +#ifdef HAVE_SDL3 + const bool* m_keyboard_state = nullptr; +#endif +}; } // namespace Common diff --git a/Source/Core/Core/IOS/USB/USB_KBD.cpp b/Source/Core/Core/IOS/USB/USB_KBD.cpp index 3270bc8449..9200b185c5 100644 --- a/Source/Core/Core/IOS/USB/USB_KBD.cpp +++ b/Source/Core/Core/IOS/USB/USB_KBD.cpp @@ -15,161 +15,7 @@ namespace IOS::HLE { -namespace -{ -// Crazy ugly -#ifdef _WIN32 -constexpr std::array s_key_codes_qwerty{ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x2A, // Backspace - 0x2B, // Tab - 0x00, 0x00, - 0x00, // Clear - 0x28, // Return - 0x00, 0x00, - 0x00, // Shift - 0x00, // Control - 0x00, // ALT - 0x48, // Pause - 0x39, // Capital - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x29, // Escape - 0x00, 0x00, 0x00, 0x00, - 0x2C, // Space - 0x4B, // Prior - 0x4E, // Next - 0x4D, // End - 0x4A, // Home - 0x50, // Left - 0x52, // Up - 0x4F, // Right - 0x51, // Down - 0x00, 0x00, 0x00, - 0x46, // Print screen - 0x49, // Insert - 0x4C, // Delete - 0x00, - // 0 -> 9 - 0x27, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, - // A -> Z - 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, - 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, - // Numpad 0 -> 9 - 0x62, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x61, - 0x55, // Multiply - 0x57, // Add - 0x00, // Separator - 0x56, // Subtract - 0x63, // Decimal - 0x54, // Divide - // F1 -> F12 - 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, - // F13 -> F24 - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x53, // Numlock - 0x47, // Scroll lock - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // Modifier keys - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x33, // ';' - 0x2E, // Plus - 0x36, // Comma - 0x2D, // Minus - 0x37, // Period - 0x38, // '/' - 0x35, // '~' - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x2F, // '[' - 0x32, // '\' - 0x30, // ']' - 0x34, // ''' - 0x00, // - 0x00, // Nothing interesting past this point. -}; - -constexpr std::array s_key_codes_azerty{ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x2A, // Backspace - 0x2B, // Tab - 0x00, 0x00, - 0x00, // Clear - 0x28, // Return - 0x00, 0x00, - 0x00, // Shift - 0x00, // Control - 0x00, // ALT - 0x48, // Pause - 0x39, // Capital - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x29, // Escape - 0x00, 0x00, 0x00, 0x00, - 0x2C, // Space - 0x4B, // Prior - 0x4E, // Next - 0x4D, // End - 0x4A, // Home - 0x50, // Left - 0x52, // Up - 0x4F, // Right - 0x51, // Down - 0x00, 0x00, 0x00, - 0x46, // Print screen - 0x49, // Insert - 0x4C, // Delete - 0x00, - // 0 -> 9 - 0x27, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, - // A -> Z - 0x14, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x33, 0x11, 0x12, 0x13, - 0x04, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1D, 0x1B, 0x1C, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, - // Numpad 0 -> 9 - 0x62, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x61, - 0x55, // Multiply - 0x57, // Add - 0x00, // Separator - 0x56, // Subtract - 0x63, // Decimal - 0x54, // Divide - // F1 -> F12 - 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, - // F13 -> F24 - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x53, // Numlock - 0x47, // Scroll lock - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // Modifier keys - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x30, // '$' - 0x2E, // Plus - 0x10, // Comma - 0x00, // Minus - 0x36, // Period - 0x37, // '/' - 0x34, // ' ' - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x2D, // ')' - 0x32, // '\' - 0x2F, // '^' - 0x00, // ' ' - 0x38, // '!' - 0x00, // Nothing interesting past this point. -}; -#else -constexpr std::array s_key_codes_qwerty{}; - -constexpr std::array s_key_codes_azerty{}; -#endif -} // Anonymous namespace - -USB_KBD::MessageData::MessageData(MessageType type, const State& state) +USB_KBD::MessageData::MessageData(MessageType type, const Common::HIDPressedState& state) : msg_type{Common::swap32(static_cast(type))}, modifiers{state.modifiers}, pressed_keys{state.pressed_keys} { @@ -192,11 +38,21 @@ std::optional USB_KBD::Open(const OpenRequest& request) m_message_queue = {}; m_previous_state = {}; + m_keyboard_context = Common::KeyboardContext::GetInstance(); // m_message_queue.emplace(MessageType::KeyboardConnect, {}); return Device::Open(request); } +std::optional USB_KBD::Close(u32 fd) +{ + INFO_LOG_FMT(IOS, "USB_KBD: Close"); + + m_keyboard_context.reset(); + + return Device::Close(fd); +} + std::optional USB_KBD::Write(const ReadWriteRequest& request) { // Stubbed. @@ -218,11 +74,13 @@ std::optional USB_KBD::IOCtl(const IOCtlRequest& request) void USB_KBD::Update() { - if (!Config::Get(Config::MAIN_WII_KEYBOARD) || Core::WantsDeterminism() || !m_is_active) + if (!Config::Get(Config::MAIN_WII_KEYBOARD) || Core::WantsDeterminism() || !m_is_active || + !m_keyboard_context) + { return; + } - const State current_state{.modifiers = Common::PollHIDModifiers(), - .pressed_keys = Common::PollHIDPressedKeys(m_keyboard_layout)}; + const auto current_state = m_keyboard_context->GetPressedState(m_keyboard_layout); if (current_state == m_previous_state) return; diff --git a/Source/Core/Core/IOS/USB/USB_KBD.h b/Source/Core/Core/IOS/USB/USB_KBD.h index 76feb34485..31ac3ee77a 100644 --- a/Source/Core/Core/IOS/USB/USB_KBD.h +++ b/Source/Core/Core/IOS/USB/USB_KBD.h @@ -19,6 +19,7 @@ public: USB_KBD(EmulationKernel& ios, const std::string& device_name); std::optional Open(const OpenRequest& request) override; + std::optional Close(u32 fd) override; std::optional Write(const ReadWriteRequest& request) override; std::optional IOCtl(const IOCtlRequest& request) override; void Update() override; @@ -31,14 +32,6 @@ private: Event = 2 }; - struct State - { - u8 modifiers = 0; - Common::HIDPressedKeys pressed_keys{}; - - auto operator<=>(const State&) const = default; - }; - #pragma pack(push, 1) struct MessageData { @@ -48,13 +41,14 @@ private: u8 unk2 = 0; Common::HIDPressedKeys pressed_keys{}; - MessageData(MessageType msg_type, const State& state); + MessageData(MessageType msg_type, const Common::HIDPressedState& state); }; static_assert(std::is_trivially_copyable_v, "MessageData must be trivially copyable, as it's copied into emulated memory."); #pragma pack(pop) std::queue m_message_queue; - State m_previous_state; + Common::HIDPressedState m_previous_state; int m_keyboard_layout = Common::KBD_LAYOUT_QWERTY; + std::shared_ptr m_keyboard_context; }; } // namespace IOS::HLE diff --git a/Source/Core/DolphinQt/Host.cpp b/Source/Core/DolphinQt/Host.cpp index ea7b1920c6..4be29baf69 100644 --- a/Source/Core/DolphinQt/Host.cpp +++ b/Source/Core/DolphinQt/Host.cpp @@ -16,6 +16,8 @@ #include #endif +#include "Common/Keyboard.h" + #include "Core/Config/MainSettings.h" #include "Core/ConfigManager.h" #include "Core/Core.h" @@ -60,6 +62,11 @@ void Host::SetRenderHandle(void* handle) { m_render_to_main = Config::Get(Config::MAIN_RENDER_TO_MAIN); + Common::KeyboardContext::NotifyHandlerChanged({.main_handle = m_main_window_handle.load(), + .renderer_handle = handle, + .is_fullscreen = m_render_fullscreen.load(), + .is_rendering_to_main = m_render_to_main.load()}); + if (m_render_handle == handle) return; @@ -174,6 +181,11 @@ void Host::SetRenderFullscreen(bool fullscreen) { m_render_fullscreen = fullscreen; + Common::KeyboardContext::NotifyHandlerChanged({.main_handle = m_main_window_handle.load(), + .renderer_handle = m_render_handle.load(), + .is_fullscreen = fullscreen, + .is_rendering_to_main = m_render_to_main.load()}); + if (g_gfx && g_gfx->IsFullscreen() != fullscreen && g_ActiveConfig.ExclusiveFullscreenEnabled()) { RunWithGPUThreadInactive([fullscreen] { g_gfx->SetFullscreen(fullscreen); }); diff --git a/Source/Core/DolphinQt/Settings.cpp b/Source/Core/DolphinQt/Settings.cpp index 3046009fbb..3684d560ff 100644 --- a/Source/Core/DolphinQt/Settings.cpp +++ b/Source/Core/DolphinQt/Settings.cpp @@ -25,6 +25,7 @@ #include "Common/Config/Config.h" #include "Common/Contains.h" #include "Common/FileUtil.h" +#include "Common/Keyboard.h" #include "Core/AchievementManager.h" #include "Core/Config/GraphicsSettings.h" @@ -938,7 +939,13 @@ bool Settings::IsUSBKeyboardConnected() const void Settings::SetUSBKeyboardConnected(bool connected) { if (IsUSBKeyboardConnected() != connected) + { Config::SetBaseOrCurrent(Config::MAIN_WII_KEYBOARD, connected); + if (connected) + Common::KeyboardContext::NotifyInit(); + else + Common::KeyboardContext::NotifyQuit(); + } } bool Settings::IsWiiSpeakMuted() const diff --git a/Source/Core/InputCommon/ControllerInterface/SDL/SDL.cpp b/Source/Core/InputCommon/ControllerInterface/SDL/SDL.cpp index b5057f09d7..8df8905dc3 100644 --- a/Source/Core/InputCommon/ControllerInterface/SDL/SDL.cpp +++ b/Source/Core/InputCommon/ControllerInterface/SDL/SDL.cpp @@ -3,6 +3,7 @@ #include "InputCommon/ControllerInterface/SDL/SDL.h" +#include #include #include #include @@ -14,6 +15,7 @@ #include #include "Common/Event.h" +#include "Common/Keyboard.h" #include "Common/Logging/Log.h" #include "Common/ScopeGuard.h" #include "Common/Thread.h" @@ -21,10 +23,83 @@ #include "Core/Config/MainSettings.h" #include "Core/HW/SI/SI.h" #include "Core/HW/SI/SI_Device.h" +#include "Core/Host.h" #include "InputCommon/ControllerInterface/ControllerInterface.h" #include "InputCommon/ControllerInterface/SDL/SDLGamepad.h" +namespace +{ +using UniqueSDLWindow = std::unique_ptr; + +// Based on sdl2-compat 76eb981a4c376bcaf615c0af37d46512ba45cfb8 +SDL_Window* SDL_CreateWindowFrom(void* handle) +{ + SDL_PropertiesID props = SDL_CreateProperties(); + if (!props) + return nullptr; + + if (const char* hint = SDL_GetHint("SDL_VIDEO_WINDOW_SHARE_PIXEL_FORMAT"); hint) + { + // This hint is a pointer (in string form) of the address of + // the window to share a pixel format with + SDL_Window* other_window = nullptr; + if (SDL_sscanf(hint, "%p", &other_window)) + { + void* ptr = SDL_GetPointerProperty(SDL_GetWindowProperties(other_window), + SDL_PROP_WINDOW_WIN32_HWND_POINTER, nullptr); + SDL_SetPointerProperty(props, SDL_PROP_WINDOW_CREATE_WIN32_PIXEL_FORMAT_HWND_POINTER, ptr); + SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_OPENGL_BOOLEAN, true); + } + } + + if (SDL_GetHintBoolean("SDL_VIDEO_FOREIGN_WINDOW_OPENGL", false)) + SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_OPENGL_BOOLEAN, true); + + if (SDL_GetHintBoolean("SDL_VIDEO_FOREIGN_WINDOW_VULKAN", false)) + SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_VULKAN_BOOLEAN, true); + + SDL_SetPointerProperty(props, "sdl2-compat.external_window", handle); + SDL_Window* window = SDL_CreateWindowWithProperties(props); + SDL_DestroyProperties(props); + + // SDL3 has per-window text input, so we must enable on this window if it's active + if (SDL_EventEnabled(SDL_EVENT_TEXT_INPUT)) + { + props = SDL_CreateProperties(); + SDL_SetNumberProperty(props, SDL_PROP_TEXTINPUT_TYPE_NUMBER, SDL_TEXTINPUT_TYPE_TEXT); + SDL_SetNumberProperty(props, SDL_PROP_TEXTINPUT_CAPITALIZATION_NUMBER, SDL_CAPITALIZE_NONE); + SDL_SetBooleanProperty(props, SDL_PROP_TEXTINPUT_AUTOCORRECT_BOOLEAN, false); + SDL_StartTextInputWithProperties(window, props); + SDL_DestroyProperties(props); + } + + return window; +} + +std::optional UpdateKeyboardHandle(UniqueSDLWindow* unique_window) +{ + std::optional error; + + void* keyboard_handle = Common::KeyboardContext::GetWindowHandle(); + SDL_Window* keyboard_window = SDL_CreateWindowFrom(keyboard_handle); + if (keyboard_window == nullptr) + error = SDL_GetError(); + + unique_window->reset(keyboard_window); + if (error.has_value()) + return error; + + // SDL aggressive hooking might make the window borderless sometimes + if (!Host_RendererIsFullscreen()) + { + SDL_SetWindowFullscreen(keyboard_window, 0); + SDL_SetWindowBordered(keyboard_window, true); + } + return error; +} +} // namespace + namespace ciface::SDL { @@ -45,6 +120,7 @@ private: Uint32 m_stop_event_type; Uint32 m_populate_event_type; std::thread m_hotplug_thread; + UniqueSDLWindow m_keyboard_window{nullptr, SDL_DestroyWindow}; }; std::unique_ptr CreateInputBackend(ControllerInterface* controller_interface) @@ -180,7 +256,7 @@ InputBackend::InputBackend(ControllerInterface* controller_interface) return; } - const Uint32 custom_events_start = SDL_RegisterEvents(2); + const Uint32 custom_events_start = SDL_RegisterEvents(5); if (custom_events_start == static_cast(-1)) { ERROR_LOG_FMT(CONTROLLERINTERFACE, "SDL failed to register custom events"); @@ -188,6 +264,9 @@ InputBackend::InputBackend(ControllerInterface* controller_interface) } m_stop_event_type = custom_events_start; m_populate_event_type = custom_events_start + 1; + Common::KeyboardContext::s_sdl_init_event_type = custom_events_start + 2; + Common::KeyboardContext::s_sdl_update_event_type = custom_events_start + 3; + Common::KeyboardContext::s_sdl_quit_event_type = custom_events_start + 4; // Drain all of the events and add the initial joysticks before returning. Otherwise, the // individual joystick events as well as the custom populate event will be handled _after_ @@ -287,6 +366,40 @@ bool InputBackend::HandleEventAndContinue(const SDL_Event& e) { return false; } + else if (e.type == Common::KeyboardContext::s_sdl_init_event_type) + { + if (!SDL_InitSubSystem(SDL_INIT_VIDEO)) + { + ERROR_LOG_FMT(IOS_USB, "SDL failed to init subsystem to capture keyboard input: {}", + SDL_GetError()); + return true; + } + + if (const auto error = UpdateKeyboardHandle(&m_keyboard_window); error.has_value()) + { + ERROR_LOG_FMT(IOS_USB, "SDL failed to attach window to capture keyboard input: {}", *error); + return true; + } + } + else if (e.type == Common::KeyboardContext::s_sdl_update_event_type) + { + if (!SDL_WasInit(SDL_INIT_VIDEO)) + return true; + + // Release previous SDLWindow + m_keyboard_window.reset(); + + if (const auto error = UpdateKeyboardHandle(&m_keyboard_window); error.has_value()) + { + ERROR_LOG_FMT(IOS_USB, "SDL failed to switch window to capture keyboard input: {}", *error); + return true; + } + } + else if (e.type == Common::KeyboardContext::s_sdl_quit_event_type) + { + m_keyboard_window.reset(); + SDL_QuitSubSystem(SDL_INIT_VIDEO); + } return true; }