Qt/input: allow keypresses in raw mouse handler

This commit is contained in:
Megamouse 2025-01-13 22:00:50 +01:00
parent 78a661db79
commit 72da20f5e1
7 changed files with 179 additions and 21 deletions

View file

@ -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<s32>(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<void>(scan_code);
return "";
#endif
}
raw_mice_config::raw_mice_config()
{
for (u32 i = 0; i < ::size32(players); i++)

View file

@ -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

View file

@ -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<int, raw_mouse::mouse_button> 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<s32>(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<LONG>(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;

View file

@ -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<int, raw_mouse::mouse_button> 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<void(const std::string&, s32, bool)> 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<void*, raw_mouse> m_raw_mice;
std::function<void(const std::string&, s32, bool)> m_mouse_press_callback;
std::function<void(const std::string&, s32, bool)> m_key_press_callback;
std::unique_ptr<named_thread<std::function<void()>>> m_thread;
};

View file

@ -1187,7 +1187,7 @@ bool gui_application::native_event_filter::nativeEventFilter([[maybe_unused]] co
if (eventType == "windows_generic_MSG")
{
if (MSG* msg = static_cast<MSG*>(message); msg && msg->message == WM_INPUT)
if (MSG* msg = static_cast<MSG*>(message); msg && (msg->message == WM_INPUT || msg->message == WM_KEYDOWN || msg->message == WM_KEYUP))
{
if (auto* handler = g_fxo->try_get<MouseHandlerBase>(); handler && handler->type == mouse_handler::raw)
{

View file

@ -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))

View file

@ -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);