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:
@@ -19,7 +19,7 @@ namespace
|
||||
// XInputGetStateEx that many games use. Mirrors how Steam/x360ce expose it.
|
||||
constexpr std::uint16_t kGuideButton = 0x0400;
|
||||
|
||||
const IpcClient* g_ipc = nullptr;
|
||||
IpcClient* g_ipc = nullptr;
|
||||
std::vector<safetyhook::InlineHook> g_hooks;
|
||||
|
||||
// Last good snapshot, so a momentary failed IPC read (host mid-write) doesn't
|
||||
@@ -62,6 +62,10 @@ DWORD query_state(DWORD user_index, XINPUT_STATE* state, bool keep_guide)
|
||||
{
|
||||
return ERROR_DEVICE_NOT_CONNECTED;
|
||||
}
|
||||
if (g_ipc != nullptr)
|
||||
{
|
||||
g_ipc->note_query(user_index); // proves to the host the game is polling us
|
||||
}
|
||||
refresh_cache();
|
||||
const CoopPadState& pad = g_cache[user_index];
|
||||
if (!pad.connected)
|
||||
@@ -156,7 +160,7 @@ void hook_ordinal(HMODULE module, WORD ordinal, void* detour)
|
||||
|
||||
} // namespace
|
||||
|
||||
bool install_xinput_hooks(const IpcClient& ipc)
|
||||
bool install_xinput_hooks(IpcClient& ipc)
|
||||
{
|
||||
if (!g_hooks.empty())
|
||||
{
|
||||
@@ -180,7 +184,12 @@ bool install_xinput_hooks(const IpcClient& ipc)
|
||||
hook_export(module, "XInputGetCapabilities", reinterpret_cast<void*>(&hk_XInputGetCapabilities));
|
||||
hook_export(module, "XInputSetState", reinterpret_cast<void*>(&hk_XInputSetState));
|
||||
}
|
||||
return !g_hooks.empty();
|
||||
if (!g_hooks.empty())
|
||||
{
|
||||
g_ipc->mark_attached();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void remove_xinput_hooks()
|
||||
|
||||
Reference in New Issue
Block a user