Release the operator cursor for cursor-clipping games

Games that ClipCursor / re-center via SetCursorPos while focused trap the operator's
mouse (the focus spoof makes them think they're always focused), so the operator
can't reach the overlay. The Focus subsystem now inline-hooks ClipCursor and
SetCursorPos (stdcall trampolines): while "release" is requested it forces
ClipCursor(NULL) and swallows the re-centering SetCursorPos; otherwise it passes them
through. It frees any existing clip at install and re-frees each worker tick (covers
one-time clippers and a runtime clip->release toggle).

Host: protocol v10->v11 adds HookControl::allow_cursor_clip (0 = release, the
default). The Injection panel gets a "Release operator cursor" checkbox and an F2
hotkey (InjectionPanel::toggle_cursor_release); default released, since the guest
plays via the pad so the game's clip is operator-only.

Verified: full build x64 + x86 clean; ctest x64 9/9, x86 3/3. The cursor behavior
against a real clipping game (Trails through Daybreak) needs a live injected session
to confirm; logic reviewed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-21 05:20:17 +02:00
parent 327ba1f394
commit 9f5b7c3272
11 changed files with 119 additions and 10 deletions

View File

@@ -12,7 +12,7 @@ namespace coop
// Bump whenever the layout of SharedBlock or CoopPadState changes. The hook
// refuses to attach to a host with a mismatched version.
inline constexpr std::uint32_t kProtocolVersion = 10;
inline constexpr std::uint32_t kProtocolVersion = 11;
// 'COOP' little-endian, used to sanity-check the mapping before trusting it.
inline constexpr std::uint32_t kProtocolMagic = 0x504F4F43u;
@@ -145,6 +145,12 @@ struct HookStatus
struct HookControl
{
std::atomic<std::uint32_t> subsystem_disabled[HookSubsys_Count];
// Cursor handling for cursor-clipping games (part of the Focus subsystem).
// 0 = release the operator's mouse: the hook frees the game's ClipCursor and
// swallows its re-centering SetCursorPos (the default, so the operator can reach
// the overlay). 1 = let the game clip / position the cursor as normal.
std::atomic<std::uint32_t> allow_cursor_clip;
};
// Present-hook video channel. When the video subsystem is installed, the hook