Phase 1a: focus spoofing + hook observability
Two problems surfaced in testing: (1) no way to tell whether the injected hook was actually the input source, and (2) the final design needs the tool window focused for Steam RPT capture, which would pause/silence games that react to focus loss. Both are addressed here. - Focus spoofing (hook/focus_spoof): find the game's main window, subclass it to rewrite/swallow WM_ACTIVATE/ACTIVATEAPP/NCACTIVATE/KILLFOCUS, and inline- hook GetForegroundWindow/GetActiveWindow/GetFocus to always report the game as active. The game keeps running and polling while unfocused. - Status back-channel (protocol v2): the DLL reports attached/focus-spoof flags, game pid/hwnd, a heartbeat, and a cumulative XInputGetState counter. The host overlay turns the counter into a live poll rate, so "is the hook working" is directly observable. - Synthetic test-input toggle in the host: forwards a known automated pattern (stick circle + periodic A) to prove forwarding independent of the physical pad. - hook_selftest extended to assert the status channel; passes. Documented the windowed/borderless requirement and the new observable test flow in the README. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
// forwarded pad state the host publishes each frame.
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
|
||||
#include <windows.h>
|
||||
@@ -54,6 +55,45 @@ public:
|
||||
return read_pads(*block_, out, count);
|
||||
}
|
||||
|
||||
// --- Status back-channel (hook -> host diagnostics) --------------------
|
||||
|
||||
// Record that the game queried a controller slot; the host turns the
|
||||
// cumulative count into a poll rate to prove the hook is live.
|
||||
void note_query(std::uint32_t user_index)
|
||||
{
|
||||
if (block_ != nullptr)
|
||||
{
|
||||
block_->status.xinput_queries.fetch_add(1, std::memory_order_relaxed);
|
||||
block_->status.last_user_index = user_index;
|
||||
}
|
||||
}
|
||||
|
||||
void mark_attached()
|
||||
{
|
||||
if (block_ != nullptr)
|
||||
{
|
||||
block_->status.game_pid = GetCurrentProcessId();
|
||||
block_->status.attached = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void mark_focus_spoof(bool active, std::uint64_t game_hwnd)
|
||||
{
|
||||
if (block_ != nullptr)
|
||||
{
|
||||
block_->status.focus_spoof = active ? 1u : 0u;
|
||||
block_->status.game_hwnd = game_hwnd;
|
||||
}
|
||||
}
|
||||
|
||||
void heartbeat()
|
||||
{
|
||||
if (block_ != nullptr)
|
||||
{
|
||||
block_->status.heartbeat.fetch_add(1, std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
SharedMemory shm_;
|
||||
SharedBlock* block_ = nullptr;
|
||||
|
||||
Reference in New Issue
Block a user