Files
CoopAllTheThings/hook/src/focus_spoof.hpp
BlackMark 9f5b7c3272 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>
2026-06-21 05:20:17 +02:00

32 lines
1.3 KiB
C++

// Makes the injected game believe it always has foreground focus, so it keeps
// running and polling input while the tool's window holds the real OS focus
// (required for Steam RPT to capture the tool). Without this, games that pause
// or stop polling on focus loss are unusable in the final design.
#pragma once
#include "ipc_client.hpp"
namespace coop::hook
{
// Finds the game's main window, subclasses it to suppress deactivation messages,
// and hooks the focus-query APIs to always report the game as active. Returns
// true once spoofing is active; safe to retry until the window exists. Reports
// status through `ipc`.
bool install_focus_spoof(IpcClient& ipc);
// Reports how the game currently reads input (Raw Input gamepad usage + sink
// flag, DirectInput presence) so the host can identify a focus-gated path. Cheap
// to call each tick.
void update_input_diagnostics(IpcClient& ipc);
// While the focus subsystem is installed, free the operator's mouse each tick if the
// host requested cursor release (covers games that ClipCursor once rather than every
// frame, and a runtime toggle from clip -> release). No-op when clipping is allowed.
void release_cursor_tick();
// Restores the original window procedure and removes the focus API hooks.
void remove_focus_spoof();
} // namespace coop::hook