Release held keys/buttons when MKB forwarding stops (no sticky inputs)
forward_mkb_frame early-returned the whole mouse block when mirroring was off or ImGui wanted the mouse, and the top-level gates returned when the subsystem was off / focus was lost / the game died. A key or mouse button held at that moment never got its KeyUp/MouseUp, so it stuck DOWN in the guest -- a held mouse button fires continuously, a held key walks forever -- contradicting the "send the up so nothing sticks" intent. Track what we've forwarded as held (g_mouse_down / g_key_down) and release it whenever we stop forwarding for any reason: the can't-forward gate, ImGui grabbing the keyboard/mouse, or the mouse-not-mirroring path all now release held inputs before returning. Normal down/up still flips the held state. Fix by inspection: forward_mkb_frame needs a live ImGui context + injection panel, so it isn't unit-tested; the logic is a straightforward held-state release. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -88,30 +88,59 @@ int imgui_key_to_vk(ImGuiKey k)
|
||||
int g_last_gx = 0;
|
||||
int g_last_gy = 0;
|
||||
|
||||
// What we've forwarded as currently-held, so we can always release it -- even when forwarding stops
|
||||
// (mirror off, focus lost, ImGui grabbed the input, the game died) -- so nothing sticks in the guest.
|
||||
bool g_mouse_down[3] = {};
|
||||
bool g_key_down[256] = {}; // indexed by VK
|
||||
|
||||
void release_held_keys(InjectionPanel& injection)
|
||||
{
|
||||
for (int vk = 0; vk < 256; ++vk)
|
||||
{
|
||||
if (g_key_down[vk])
|
||||
{
|
||||
injection.push_mkb(MkbEvent{Mkb_KeyUp, static_cast<std::uint32_t>(vk), 0, 0});
|
||||
g_key_down[vk] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void release_held_mouse(InjectionPanel& injection)
|
||||
{
|
||||
for (int b = 0; b < 3; ++b)
|
||||
{
|
||||
if (g_mouse_down[b])
|
||||
{
|
||||
injection.push_mkb(MkbEvent{Mkb_MouseUp, static_cast<std::uint32_t>(b), g_last_gx, g_last_gy});
|
||||
g_mouse_down[b] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void forward_mkb_frame(InjectionPanel& injection, HWND host_hwnd, bool mirroring, bool source_hooked)
|
||||
{
|
||||
if (!injection.mkb_enabled())
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Under RPT the guest's MKB lands on our (focused) window; only forward then, so
|
||||
// the operator's own desktop use isn't injected into the game.
|
||||
if (GetForegroundWindow() != host_hwnd)
|
||||
{
|
||||
return;
|
||||
}
|
||||
const HWND game = injection.game_hwnd();
|
||||
if (game == nullptr || !IsWindow(game))
|
||||
// Under RPT the guest's MKB lands on our (focused) window; only forward then, so the operator's
|
||||
// own desktop use isn't injected. If we can't forward for ANY reason -- subsystem off, we lost
|
||||
// focus, or the game is gone -- release everything we're still holding first, so a key/button
|
||||
// held at that moment doesn't stick down in the guest.
|
||||
if (!injection.mkb_enabled() || GetForegroundWindow() != host_hwnd || game == nullptr || !IsWindow(game))
|
||||
{
|
||||
release_held_keys(injection);
|
||||
release_held_mouse(injection);
|
||||
return;
|
||||
}
|
||||
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
// --- Keyboard (always, unless ImGui is using it for e.g. a text field) ---
|
||||
if (!io.WantCaptureKeyboard)
|
||||
// --- Keyboard (unless ImGui is using it for e.g. a text field -- then release what we hold) ---
|
||||
if (io.WantCaptureKeyboard)
|
||||
{
|
||||
release_held_keys(injection);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (ImGuiKey k = ImGuiKey_NamedKey_BEGIN; k < ImGuiKey_NamedKey_END; k = static_cast<ImGuiKey>(k + 1))
|
||||
{
|
||||
@@ -123,10 +152,12 @@ void forward_mkb_frame(InjectionPanel& injection, HWND host_hwnd, bool mirroring
|
||||
if (ImGui::IsKeyPressed(k, false))
|
||||
{
|
||||
injection.push_mkb(MkbEvent{Mkb_KeyDown, static_cast<std::uint32_t>(vk), 0, 0});
|
||||
g_key_down[vk & 0xFF] = true;
|
||||
}
|
||||
if (ImGui::IsKeyReleased(k))
|
||||
{
|
||||
injection.push_mkb(MkbEvent{Mkb_KeyUp, static_cast<std::uint32_t>(vk), 0, 0});
|
||||
g_key_down[vk & 0xFF] = false;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < io.InputQueueCharacters.Size; ++i)
|
||||
@@ -139,9 +170,12 @@ void forward_mkb_frame(InjectionPanel& injection, HWND host_hwnd, bool mirroring
|
||||
}
|
||||
}
|
||||
|
||||
// --- Mouse (clicks + wheel only, and only while mirroring) ---
|
||||
if (!mirroring || io.WantCaptureMouse)
|
||||
// --- Mouse (clicks + wheel only, and only while mirroring and ImGui isn't using the mouse) ---
|
||||
// When we're not forwarding the mouse, still release any button we hold (below), so it can't stick.
|
||||
const bool forwarding_mouse = mirroring && !io.WantCaptureMouse;
|
||||
if (!forwarding_mouse)
|
||||
{
|
||||
release_held_mouse(injection);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -191,10 +225,12 @@ void forward_mkb_frame(InjectionPanel& injection, HWND host_hwnd, bool mirroring
|
||||
if (on_game && ImGui::IsMouseClicked(button))
|
||||
{
|
||||
injection.push_mkb(MkbEvent{Mkb_MouseDown, static_cast<std::uint32_t>(button), mx, my});
|
||||
g_mouse_down[button] = true;
|
||||
}
|
||||
if (ImGui::IsMouseReleased(button)) // send the up even off-game, so nothing sticks
|
||||
{
|
||||
injection.push_mkb(MkbEvent{Mkb_MouseUp, static_cast<std::uint32_t>(button), mx, my});
|
||||
g_mouse_down[button] = false;
|
||||
}
|
||||
}
|
||||
if (on_game && io.MouseWheel != 0.0f)
|
||||
|
||||
Reference in New Issue
Block a user