From 7cfe51d80d8c94515fd53b6ffe94e01caf561022 Mon Sep 17 00:00:00 2001 From: mchughalex <25671999+mchughalex@users.noreply.github.com> Date: Tue, 21 Jul 2026 10:10:56 +0100 Subject: [PATCH] input: reconcile mouse-button binds released outside the window --- src/input/mnk/mnk_input_driver.cpp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/input/mnk/mnk_input_driver.cpp b/src/input/mnk/mnk_input_driver.cpp index ccde3dc..2bca444 100644 --- a/src/input/mnk/mnk_input_driver.cpp +++ b/src/input/mnk/mnk_input_driver.cpp @@ -167,6 +167,27 @@ X_RESULT MnkInputDriver::GetState(uint32_t user_index, X_INPUT_STATE* out_state) std::lock_guard lock(state_mutex_); +#if REX_PLATFORM_WIN32 + // A mouse button pressed inside the client area but released outside it + // never delivers its WM_xBUTTONUP (the window does not capture the mouse + // during presses), which would leave the bind - triggers by default - held + // indefinitely. Reconcile against the live button state each poll. + // GetAsyncKeyState reports PHYSICAL buttons while mouse messages follow the + // logical (possibly swapped) mapping, so honor SM_SWAPBUTTON. + { + const bool swapped = GetSystemMetrics(SM_SWAPBUTTON) != 0; + const auto reconcile = [this](VirtualKey vk, int host_vk) { + const uint16_t idx = static_cast(vk); + if (key_down_[idx] && !(GetAsyncKeyState(host_vk) & 0x8000)) { + key_down_[idx] = false; + } + }; + reconcile(VirtualKey::kLButton, swapped ? VK_RBUTTON : VK_LBUTTON); + reconcile(VirtualKey::kRButton, swapped ? VK_LBUTTON : VK_RBUTTON); + reconcile(VirtualKey::kMButton, VK_MBUTTON); + } +#endif + uint16_t buttons = 0; if (IsBindPressed(key_down_, REXCVAR_GET(keybind_a))) buttons |= X_INPUT_GAMEPAD_A;