focus_spoof_test plays game + host: it creates a top-level window (which
find_main_window picks up), installs the spoof, and checks that
GetForegroundWindow / GetActiveWindow / GetFocus now report that window, that the
status flag flips on install and off on removal, and that a ClipCursor request is
swallowed while cursor release is the default. Skips if the window can't be
enumerated.
With this and d3d9_hook_test, the dedicated-hook-tests item is done. vk_hook's
present path is intentionally not given a separate in-process test: it's exercised
end to end by mock_game_test's vk-inject path ("hook captured Vulkan present
calls"), and the off-thread VkCapture it drives has vk_capture_perf_test -- a
standalone in-process Vulkan swapchain harness would just duplicate that at high
cost.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
114 lines
3.8 KiB
C++
114 lines
3.8 KiB
C++
// In-process self-test for the focus-spoof subsystem (hook/src/focus_spoof.cpp). It plays game +
|
|
// host: creates a top-level window (which find_main_window picks up), installs the spoof, and checks
|
|
// that the focus-query APIs now report that window as foreground/active/focused even though the test
|
|
// isn't actually the foreground app -- and that removal restores them. Also checks the cursor-release
|
|
// gating: with clipping disallowed (the default), a ClipCursor request is swallowed. No game, no Steam.
|
|
#include <cstdio>
|
|
|
|
#include <windows.h>
|
|
|
|
#include "coop/protocol.hpp"
|
|
#include "coop/shared_memory.hpp"
|
|
#include "ipc_client.hpp"
|
|
#include "focus_spoof.hpp"
|
|
|
|
using namespace coop;
|
|
|
|
namespace
|
|
{
|
|
int g_failures = 0;
|
|
void check(bool ok, const char* what)
|
|
{
|
|
std::printf("%s %s\n", ok ? " ok:" : "FAIL:", what);
|
|
if (!ok)
|
|
{
|
|
++g_failures;
|
|
}
|
|
}
|
|
void pump()
|
|
{
|
|
MSG msg;
|
|
while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE))
|
|
{
|
|
TranslateMessage(&msg);
|
|
DispatchMessageW(&msg);
|
|
}
|
|
}
|
|
} // namespace
|
|
|
|
int main()
|
|
{
|
|
SharedMemory shm;
|
|
if (!shm.create(shared_memory_name(GetCurrentProcessId()), sizeof(SharedBlock)))
|
|
{
|
|
std::printf("FAIL: create shared memory\n");
|
|
return 1;
|
|
}
|
|
auto* block = shm.as<SharedBlock>();
|
|
block->version = kProtocolVersion;
|
|
block->sequence.store(0, std::memory_order_relaxed);
|
|
block->magic = kProtocolMagic; // allow_cursor_clip stays 0 (zero-filled) -> release the cursor
|
|
|
|
hook::IpcClient ipc;
|
|
check(ipc.connect(10, 5), "IPC client connect");
|
|
|
|
// A real top-level window for find_main_window to pick up.
|
|
WNDCLASSEXW wc{};
|
|
wc.cbSize = sizeof(wc);
|
|
wc.lpfnWndProc = DefWindowProcW;
|
|
wc.hInstance = GetModuleHandleW(nullptr);
|
|
wc.lpszClassName = L"coop_focus_test";
|
|
RegisterClassExW(&wc);
|
|
HWND win = CreateWindowExW(0, wc.lpszClassName, L"CoopFocusTest", WS_OVERLAPPEDWINDOW, 0, 0, 320, 240, nullptr,
|
|
nullptr, wc.hInstance, nullptr);
|
|
ShowWindow(win, SW_SHOW);
|
|
pump();
|
|
|
|
// Install, retrying a few times in case the window isn't enumerable yet.
|
|
bool installed = false;
|
|
for (int i = 0; i < 20 && !installed; ++i)
|
|
{
|
|
installed = hook::install_focus_spoof(ipc);
|
|
if (!installed)
|
|
{
|
|
pump();
|
|
Sleep(20);
|
|
}
|
|
}
|
|
if (!installed)
|
|
{
|
|
std::printf("SKIP: focus spoof could not find the test window\n");
|
|
DestroyWindow(win);
|
|
UnregisterClassW(wc.lpszClassName, wc.hInstance);
|
|
return 0;
|
|
}
|
|
|
|
// The spoof must report OUR window as foreground/active/focused, even though the test process is
|
|
// not the real foreground app (so a coincidental match is unlikely).
|
|
check(GetForegroundWindow() == win, "GetForegroundWindow spoofed to the game window");
|
|
check(GetActiveWindow() == win, "GetActiveWindow spoofed to the game window");
|
|
check(GetFocus() == win, "GetFocus spoofed to the game window");
|
|
check(block->status.focus_spoof != 0, "status reports focus spoof active");
|
|
|
|
// Cursor release: with clipping disallowed (default), a ClipCursor request must be swallowed.
|
|
RECT want{10, 10, 50, 50};
|
|
ClipCursor(&want); // goes through the hook
|
|
RECT got{};
|
|
GetClipCursor(&got);
|
|
check(!(got.left == want.left && got.top == want.top && got.right == want.right && got.bottom == want.bottom),
|
|
"ClipCursor swallowed while cursor release is active (clip not applied)");
|
|
|
|
// Removal: the spoof is torn down. Check the deterministic signal (the status flag) rather than
|
|
// GetForegroundWindow's value -- a shown test window may legitimately BE the real foreground, so
|
|
// the un-hooked API can still return it.
|
|
hook::remove_focus_spoof();
|
|
check(block->status.focus_spoof == 0, "status reports focus spoof inactive after removal");
|
|
ClipCursor(nullptr); // tidy up
|
|
|
|
DestroyWindow(win);
|
|
UnregisterClassW(wc.lpszClassName, wc.hInstance);
|
|
|
|
std::printf(g_failures == 0 ? "FOCUS SPOOF TEST PASS\n" : "FOCUS SPOOF TEST FAILED (%d)\n", g_failures);
|
|
return g_failures == 0 ? 0 : 1;
|
|
}
|