// Copyright 2026 Dolphin Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "Common/Keyboard.h" #include #include #include #ifdef _WIN32 #include #endif #include "Common/FileUtil.h" #include "Common/IniFile.h" #include "Core/Config/MainSettings.h" namespace { // Crazy ugly #ifdef _WIN32 constexpr std::size_t KEYBOARD_STATE_SIZE = 256; constexpr std::array VK_HID_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 VK_HID_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. }; u8 MapVirtualKeyToHID(u8 virtual_key, int keyboard_layout) { switch (keyboard_layout) { case Common::KBD_LAYOUT_AZERTY: return VK_HID_AZERTY[virtual_key]; case Common::KBD_LAYOUT_QWERTY: default: return VK_HID_QWERTY[virtual_key]; } } #else u8 MapVirtualKeyToHID(u8 virtual_key, int keyboard_layout) { return virtual_key; } #endif std::weak_ptr s_keyboard_context; std::mutex s_keyboard_context_mutex; } // Anonymous namespace namespace Common { KeyboardContext::KeyboardContext() { if (Config::Get(Config::MAIN_WII_KEYBOARD)) Init(); } KeyboardContext::~KeyboardContext() { if (Config::Get(Config::MAIN_WII_KEYBOARD)) Quit(); } void KeyboardContext::Init() { UpdateLayout(); m_is_ready = true; } void KeyboardContext::Quit() { m_is_ready = false; } 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() const { return m_is_ready ? HIDPressedState{.modifiers = PollHIDModifiers(), .pressed_keys = PollHIDPressedKeys()} : HIDPressedState{}; } bool KeyboardContext::IsVirtualKeyPressed(int virtual_key) const { #ifdef _WIN32 return (GetAsyncKeyState(virtual_key) & 0x8000) != 0; #else // TODO: do it for non-Windows platforms return false; #endif } 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://www.usb.org/document-library/device-class-definition-hid-111 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 }; 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 KeyboardContext::PollHIDPressedKeys() 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) { if (!IsVirtualKeyPressed(static_cast(virtual_key))) continue; *it = MapVirtualKeyToHID(static_cast(virtual_key), m_keyboard_layout); if (++it == pressed_keys.end()) break; } #else // TODO: Implementation for non-Windows platforms #endif return pressed_keys; } void KeyboardContext::UpdateLayout() { Common::IniFile ini; ini.Load(File::GetUserPath(F_DOLPHINCONFIG_IDX)); ini.GetOrCreateSection("USB Keyboard")->Get("Layout", &m_keyboard_layout, KBD_LAYOUT_QWERTY); } } // namespace Common