From 72da20f5e1fdddfc140cf4aa04bfa19e721cfab1 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Mon, 13 Jan 2025 22:00:50 +0100 Subject: [PATCH] Qt/input: allow keypresses in raw mouse handler --- rpcs3/Input/raw_mouse_config.cpp | 25 ++++ rpcs3/Input/raw_mouse_config.h | 3 + rpcs3/Input/raw_mouse_handler.cpp | 120 +++++++++++++++++--- rpcs3/Input/raw_mouse_handler.h | 19 +++- rpcs3/rpcs3qt/gui_application.cpp | 2 +- rpcs3/rpcs3qt/raw_mouse_settings_dialog.cpp | 30 +++++ rpcs3/rpcs3qt/raw_mouse_settings_dialog.h | 1 + 7 files changed, 179 insertions(+), 21 deletions(-) diff --git a/rpcs3/Input/raw_mouse_config.cpp b/rpcs3/Input/raw_mouse_config.cpp index 5f9839ac72..cc5506dd16 100644 --- a/rpcs3/Input/raw_mouse_config.cpp +++ b/rpcs3/Input/raw_mouse_config.cpp @@ -43,6 +43,15 @@ std::string raw_mouse_config::get_button_name(std::string_view value) return std::string(value); } + if (value.starts_with(key_prefix)) + { + s64 scan_code{}; + if (try_to_int64(&scan_code, value.substr(key_prefix.size()), s32{smin}, s32{smax})) + { + return get_key_name(static_cast(scan_code)); + } + } + return ""; } @@ -58,6 +67,22 @@ std::string raw_mouse_config::get_button_name(s32 button_code) return ""; } +std::string raw_mouse_config::get_key_name(s32 scan_code) +{ +#ifdef _WIN32 + TCHAR name_buf[MAX_PATH] {}; + if (!GetKeyNameTextW(scan_code, name_buf, MAX_PATH)) + { + cfg_log.error("raw_mouse_config: GetKeyNameText failed: %s", fmt::win_error{GetLastError(), nullptr}); + return {}; + } + return wchar_to_utf8(name_buf); +#else + static_cast(scan_code); + return ""; +#endif +} + raw_mice_config::raw_mice_config() { for (u32 i = 0; i < ::size32(players); i++) diff --git a/rpcs3/Input/raw_mouse_config.h b/rpcs3/Input/raw_mouse_config.h index a237ad2554..2791725637 100644 --- a/rpcs3/Input/raw_mouse_config.h +++ b/rpcs3/Input/raw_mouse_config.h @@ -42,8 +42,11 @@ public: cfg::string& get_button_by_index(int index); cfg::string& get_button(int code); + static constexpr std::string_view key_prefix = "Key "; + static std::string get_button_name(std::string_view value); static std::string get_button_name(s32 button_code); + static std::string get_key_name(s32 scan_code); }; struct raw_mice_config : cfg::node diff --git a/rpcs3/Input/raw_mouse_handler.cpp b/rpcs3/Input/raw_mouse_handler.cpp index 6d24b46bf9..d60dfaec0b 100644 --- a/rpcs3/Input/raw_mouse_handler.cpp +++ b/rpcs3/Input/raw_mouse_handler.cpp @@ -27,17 +27,17 @@ static inline void draw_overlay_cursor(u32 index, s32 x_pos, s32 y_pos, s32 x_ma [[maybe_unused]] static inline void draw_overlay_cursor(u32, s32, s32, s32, s32) {} #endif -#ifdef _WIN32 const std::unordered_map raw_mouse::btn_pairs = { { 0, {}}, - { RI_MOUSE_BUTTON_1_UP, mouse_button{ RI_MOUSE_BUTTON_1_DOWN, RI_MOUSE_BUTTON_1_UP }}, - { RI_MOUSE_BUTTON_2_UP, mouse_button{ RI_MOUSE_BUTTON_2_DOWN, RI_MOUSE_BUTTON_2_UP }}, - { RI_MOUSE_BUTTON_3_UP, mouse_button{ RI_MOUSE_BUTTON_3_DOWN, RI_MOUSE_BUTTON_3_UP }}, - { RI_MOUSE_BUTTON_4_UP, mouse_button{ RI_MOUSE_BUTTON_4_DOWN, RI_MOUSE_BUTTON_4_UP }}, - { RI_MOUSE_BUTTON_5_UP, mouse_button{ RI_MOUSE_BUTTON_5_DOWN, RI_MOUSE_BUTTON_5_UP }}, -}; +#ifdef _WIN32 + { RI_MOUSE_BUTTON_1_UP, mouse_button{ RI_MOUSE_BUTTON_1_DOWN, RI_MOUSE_BUTTON_1_UP, 0, false }}, + { RI_MOUSE_BUTTON_2_UP, mouse_button{ RI_MOUSE_BUTTON_2_DOWN, RI_MOUSE_BUTTON_2_UP, 0, false }}, + { RI_MOUSE_BUTTON_3_UP, mouse_button{ RI_MOUSE_BUTTON_3_DOWN, RI_MOUSE_BUTTON_3_UP, 0, false }}, + { RI_MOUSE_BUTTON_4_UP, mouse_button{ RI_MOUSE_BUTTON_4_DOWN, RI_MOUSE_BUTTON_4_UP, 0, false }}, + { RI_MOUSE_BUTTON_5_UP, mouse_button{ RI_MOUSE_BUTTON_5_DOWN, RI_MOUSE_BUTTON_5_UP, 0, false }}, #endif +}; LOG_CHANNEL(input_log, "Input"); @@ -89,12 +89,19 @@ raw_mouse::mouse_button raw_mouse::get_mouse_button(const cfg::string& button) { const std::string value = button.to_string(); -#ifdef _WIN32 if (const auto it = raw_mouse_button_map.find(value); it != raw_mouse_button_map.cend()) { return ::at32(btn_pairs, it->second); } -#endif + + if (value.starts_with(raw_mouse_config::key_prefix)) + { + s64 scan_code{}; + if (try_to_int64(&scan_code, value.substr(raw_mouse_config::key_prefix.size()), s32{smin}, s32{smax})) + { + return mouse_button{ 0, 0, static_cast(scan_code), true }; + } + } return {}; } @@ -161,6 +168,8 @@ void raw_mouse::update_values(const RAWMOUSE& state) // Get mouse buttons for (const auto& [button, btn] : m_buttons) { + if (btn.is_key) continue; + // Only update the value if either down or up flags are present if ((state.usButtonFlags & btn.down)) { @@ -240,6 +249,30 @@ void raw_mouse::update_values(const RAWMOUSE& state) } } } + +void raw_mouse::update_values(s32 scan_code, bool pressed) +{ + ensure(m_handler != nullptr); + + if (std::exchange(m_reload_requested, false)) + { + reload_config(); + } + + if (m_handler->is_for_gui()) + { + m_handler->key_press_callback(m_device_name, scan_code, pressed); + return; + } + + // Get mouse buttons + for (const auto& [button, btn] : m_buttons) + { + if (!btn.is_key || btn.scan_code != scan_code) return; + + m_handler->Button(m_index, button, pressed); + } +} #endif raw_mouse_handler::~raw_mouse_handler() @@ -419,6 +452,12 @@ void raw_mouse_handler::register_raw_input_devices() .dwFlags = 0, .hwndTarget = mouse.window_handle() }); + raw_input_devices.push_back(RAWINPUTDEVICE { + .usUsagePage = HID_USAGE_PAGE_GENERIC, + .usUsage = HID_USAGE_GENERIC_KEYBOARD, + .dwFlags = 0, + .hwndTarget = mouse.window_handle() + }); { std::lock_guard lock(g_registered_handlers_mutex); @@ -459,6 +498,13 @@ void raw_mouse_handler::unregister_raw_input_devices() const .dwFlags = RIDEV_REMOVE, .hwndTarget = nullptr }); + raw_input_devices.push_back(RAWINPUTDEVICE { + .usUsagePage = HID_USAGE_PAGE_GENERIC, + .usUsage = HID_USAGE_GENERIC_KEYBOARD, + .dwFlags = 0, + .hwndTarget = nullptr + }); + if (!RegisterRawInputDevices(raw_input_devices.data(), ::size32(raw_input_devices), sizeof(RAWINPUTDEVICE))) { input_log.error("raw_mouse_handler: RegisterRawInputDevices (unregister) failed: %s", fmt::win_error{GetLastError(), nullptr}); @@ -566,7 +612,7 @@ void raw_mouse_handler::handle_native_event(const MSG& msg) return; } - if (msg.message != WM_INPUT) + if (msg.message != WM_INPUT && msg.message != WM_KEYDOWN && msg.message != WM_KEYUP) { return; } @@ -585,26 +631,64 @@ void raw_mouse_handler::handle_native_event(const MSG& msg) return; } + if ((raw_input.header.dwType == RIM_TYPEMOUSE || raw_input.header.dwType == RIM_TYPEKEYBOARD) && + g_cfg_raw_mouse.reload_requested.exchange(false)) + { + std::lock_guard lock(m_raw_mutex); + + for (auto& [handle, mouse] : m_raw_mice) + { + mouse.request_reload(); + } + } + switch (raw_input.header.dwType) { case RIM_TYPEMOUSE: { std::lock_guard lock(m_raw_mutex); - if (g_cfg_raw_mouse.reload_requested.exchange(false)) - { - for (auto& [handle, mouse] : m_raw_mice) - { - mouse.request_reload(); - } - } - if (auto it = m_raw_mice.find(raw_input.header.hDevice); it != m_raw_mice.end()) { it->second.update_values(raw_input.data.mouse); } break; } + case RIM_TYPEKEYBOARD: + { + const RAWKEYBOARD& keyboard = raw_input.data.keyboard; + + // Ignore key overrun state and keys not mapped to any virtual key code + if (keyboard.MakeCode == KEYBOARD_OVERRUN_MAKE_CODE || keyboard.VKey >= UCHAR_MAX) + { + break; + } + + WORD scan_code; + + if (keyboard.MakeCode) + { + // Compose the full scan code value with its extended byte + scan_code = MAKEWORD(keyboard.MakeCode & 0x7f, ((keyboard.Flags & RI_KEY_E0) ? 0xe0 : ((keyboard.Flags & RI_KEY_E1) ? 0xe1 : 0x00))); + } + else + { + // Scan code value may be empty for some buttons (for example multimedia buttons) + // Try to get the scan code from the virtual key code + scan_code = LOWORD(MapVirtualKey(keyboard.VKey, MAPVK_VK_TO_VSC_EX)); + } + + const LONG scan_code_extended = static_cast(MAKELPARAM(0, (HIBYTE(scan_code) ? KF_EXTENDED : 0x00) | LOBYTE(scan_code))); + const bool pressed = !(keyboard.Flags & RI_KEY_BREAK); + + std::lock_guard lock(m_raw_mutex); + + for (auto& [handle, mouse] : m_raw_mice) + { + mouse.update_values(scan_code_extended, pressed); + } + break; + } default: { break; diff --git a/rpcs3/Input/raw_mouse_handler.h b/rpcs3/Input/raw_mouse_handler.h index adef7c5624..9ab7945f20 100644 --- a/rpcs3/Input/raw_mouse_handler.h +++ b/rpcs3/Input/raw_mouse_handler.h @@ -22,6 +22,7 @@ public: #ifdef _WIN32 void update_values(const RAWMOUSE& state); + void update_values(s32 scan_code, bool pressed); #endif const std::string& device_name() const { return m_device_name; } @@ -34,11 +35,11 @@ private: { int down = 0; int up = 0; + s32 scan_code = 0; + bool is_key = false; }; -#ifdef _WIN32 static const std::unordered_map btn_pairs; -#endif void reload_config(); static mouse_button get_mouse_button(const cfg::string& button); @@ -86,6 +87,19 @@ public: } } + void set_key_press_callback(std::function cb) + { + m_key_press_callback = std::move(cb); + } + + void key_press_callback(const std::string& device_name, s32 scan_code, bool pressed) + { + if (m_key_press_callback) + { + m_key_press_callback(device_name, scan_code, pressed); + } + } + void update_devices(); #ifdef _WIN32 @@ -107,6 +121,7 @@ private: bool m_is_for_gui = false; std::map m_raw_mice; std::function m_mouse_press_callback; + std::function m_key_press_callback; std::unique_ptr>> m_thread; }; diff --git a/rpcs3/rpcs3qt/gui_application.cpp b/rpcs3/rpcs3qt/gui_application.cpp index b5c2041527..9f5dc53cb7 100644 --- a/rpcs3/rpcs3qt/gui_application.cpp +++ b/rpcs3/rpcs3qt/gui_application.cpp @@ -1187,7 +1187,7 @@ bool gui_application::native_event_filter::nativeEventFilter([[maybe_unused]] co if (eventType == "windows_generic_MSG") { - if (MSG* msg = static_cast(message); msg && msg->message == WM_INPUT) + if (MSG* msg = static_cast(message); msg && (msg->message == WM_INPUT || msg->message == WM_KEYDOWN || msg->message == WM_KEYUP)) { if (auto* handler = g_fxo->try_get(); handler && handler->type == mouse_handler::raw) { diff --git a/rpcs3/rpcs3qt/raw_mouse_settings_dialog.cpp b/rpcs3/rpcs3qt/raw_mouse_settings_dialog.cpp index 3936598995..eaacc9c524 100644 --- a/rpcs3/rpcs3qt/raw_mouse_settings_dialog.cpp +++ b/rpcs3/rpcs3qt/raw_mouse_settings_dialog.cpp @@ -74,6 +74,10 @@ raw_mouse_settings_dialog::raw_mouse_settings_dialog(QWidget* parent) { mouse_press(device_name, button_code, pressed); }); + g_raw_mouse_handler->set_key_press_callback([this](const std::string& device_name, s32 scan_code, bool pressed) + { + key_press(device_name, scan_code, pressed); + }); m_buttons = new QButtonGroup(this); connect(m_buttons, &QButtonGroup::idClicked, this, &raw_mouse_settings_dialog::on_button_click); @@ -374,6 +378,32 @@ void raw_mouse_settings_dialog::mouse_press(const std::string& device_name, s32 reactivate_buttons(); } +void raw_mouse_settings_dialog::key_press(const std::string& device_name, s32 scan_code, bool pressed) +{ + if (m_button_id < 0 || !pressed) // Let's only react to key presses + { + return; + } + + const int player = m_tab_widget->currentIndex(); + const std::string current_device_name = get_current_device_name(player); + + if (device_name != current_device_name) + { + return; + } + + auto& config = ::at32(g_cfg_raw_mouse.players, player); + config->get_button_by_index(m_button_id % button_count).from_string(fmt::format("%s%d", raw_mouse_config::key_prefix, scan_code)); + + if (auto button = m_buttons->button(m_button_id)) + { + button->setText(QString::fromStdString(raw_mouse_config::get_key_name(scan_code))); + } + + reactivate_buttons(); +} + void raw_mouse_settings_dialog::handle_device_change(const std::string& device_name) { if (is_device_active(device_name)) diff --git a/rpcs3/rpcs3qt/raw_mouse_settings_dialog.h b/rpcs3/rpcs3qt/raw_mouse_settings_dialog.h index d55abf8658..16aced5a77 100644 --- a/rpcs3/rpcs3qt/raw_mouse_settings_dialog.h +++ b/rpcs3/rpcs3qt/raw_mouse_settings_dialog.h @@ -31,6 +31,7 @@ private: void reset_config(); void on_button_click(int id); void mouse_press(const std::string& device_name, s32 button_code, bool pressed); + void key_press(const std::string& device_name, s32 scan_code, bool pressed); void handle_device_change(const std::string& device_name); bool is_device_active(const std::string& device_name); std::string get_current_device_name(int player);