Phase 1a: per-slot poll viz + input-path diagnostics
Surfaced by a game (Life is Strange: Before the Storm) that ignores controller input when it lacks true OS focus even though it still polls XInput. To find the focus-gated detection path, instrument the hook. Protocol v3 status back-channel now reports: - per-slot XInputGetState and XInputGetCapabilities counters (replacing the single aggregate), so the overlay shows exactly which slots the game polls and how fast; - focus-API call counts (GetForegroundWindow/GetActiveWindow/GetFocus) to see whether the game consults the APIs we spoof; - input-path diagnostics: whether the process registered Raw Input for a gamepad usage and whether it set RIDEV_INPUTSINK (background delivery), and whether a DirectInput dll is loaded. Host overlay gains a per-slot poll table and an "Input path" section. The DLL refreshes input diagnostics each worker tick via GetRegisteredRawInputDevices. hook_selftest updated for per-slot counters; passes. This is diagnostic-only: once a real run shows which path LiS uses, the targeted focus fix (e.g. forcing RIDEV_INPUTSINK or DI background coop) follows. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -16,6 +16,7 @@ HWND g_game_hwnd = nullptr;
|
||||
WNDPROC g_orig_proc = nullptr;
|
||||
bool g_unicode = true;
|
||||
std::vector<safetyhook::InlineHook> g_focus_hooks;
|
||||
IpcClient* g_focus_ipc = nullptr;
|
||||
|
||||
struct EnumContext
|
||||
{
|
||||
@@ -85,16 +86,28 @@ LRESULT CALLBACK subclass_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam
|
||||
|
||||
HWND WINAPI hk_GetForegroundWindow()
|
||||
{
|
||||
if (g_focus_ipc != nullptr)
|
||||
{
|
||||
g_focus_ipc->note_focus_query(FocusApi_Foreground);
|
||||
}
|
||||
return g_game_hwnd;
|
||||
}
|
||||
|
||||
HWND WINAPI hk_GetActiveWindow()
|
||||
{
|
||||
if (g_focus_ipc != nullptr)
|
||||
{
|
||||
g_focus_ipc->note_focus_query(FocusApi_Active);
|
||||
}
|
||||
return g_game_hwnd;
|
||||
}
|
||||
|
||||
HWND WINAPI hk_GetFocus()
|
||||
{
|
||||
if (g_focus_ipc != nullptr)
|
||||
{
|
||||
g_focus_ipc->note_focus_query(FocusApi_Focus);
|
||||
}
|
||||
return g_game_hwnd;
|
||||
}
|
||||
|
||||
@@ -110,6 +123,7 @@ void hook_export(HMODULE module, const char* name, void* detour)
|
||||
|
||||
bool install_focus_spoof(IpcClient& ipc)
|
||||
{
|
||||
g_focus_ipc = &ipc;
|
||||
if (g_game_hwnd != nullptr)
|
||||
{
|
||||
return true; // already active
|
||||
@@ -142,6 +156,40 @@ bool install_focus_spoof(IpcClient& ipc)
|
||||
return true;
|
||||
}
|
||||
|
||||
void update_input_diagnostics(IpcClient& ipc)
|
||||
{
|
||||
// Inspect how the game currently reads input, to find a focus-gated path that
|
||||
// would explain a controller working only when the game has true focus.
|
||||
bool raw_registered = false;
|
||||
bool raw_gamepad = false;
|
||||
bool raw_gamepad_sink = false;
|
||||
|
||||
UINT count = 0;
|
||||
if (GetRegisteredRawInputDevices(nullptr, &count, sizeof(RAWINPUTDEVICE)) == 0 && count > 0)
|
||||
{
|
||||
std::vector<RAWINPUTDEVICE> devices(count);
|
||||
const UINT got = GetRegisteredRawInputDevices(devices.data(), &count, sizeof(RAWINPUTDEVICE));
|
||||
if (got != static_cast<UINT>(-1))
|
||||
{
|
||||
raw_registered = got > 0;
|
||||
for (UINT i = 0; i < got; ++i)
|
||||
{
|
||||
// Generic Desktop (0x01) joystick (0x04) / gamepad (0x05).
|
||||
const bool is_pad =
|
||||
devices[i].usUsagePage == 0x01 && (devices[i].usUsage == 0x04 || devices[i].usUsage == 0x05);
|
||||
if (is_pad)
|
||||
{
|
||||
raw_gamepad = true;
|
||||
raw_gamepad_sink = (devices[i].dwFlags & RIDEV_INPUTSINK) != 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const bool dinput = GetModuleHandleW(L"dinput8.dll") != nullptr || GetModuleHandleW(L"dinput.dll") != nullptr;
|
||||
ipc.set_input_diagnostics(raw_registered, raw_gamepad, raw_gamepad_sink, dinput);
|
||||
}
|
||||
|
||||
void remove_focus_spoof()
|
||||
{
|
||||
if (g_game_hwnd != nullptr && g_orig_proc != nullptr)
|
||||
@@ -158,6 +206,7 @@ void remove_focus_spoof()
|
||||
g_focus_hooks.clear();
|
||||
g_game_hwnd = nullptr;
|
||||
g_orig_proc = nullptr;
|
||||
g_focus_ipc = nullptr;
|
||||
}
|
||||
|
||||
} // namespace coop::hook
|
||||
|
||||
Reference in New Issue
Block a user