Add a host->hook control channel (protocol v5 -> v6: HookControl in SharedBlock, per-subsystem "disabled" flags, 0 = install so the zero-filled default is unchanged). The worker now reconciles each subsystem every tick: install what's requested-and-missing, remove what's no longer wanted -- so the audio hooks re-attach the ring and republish format on a reinstall, and XInput/focus clear their stale status flags on removal. Injection panel: a checkbox per subsystem (input forwarding / focus spoof / audio render-hook) toggles it at runtime, showing the requested vs actual installed state from the registry, plus DLL heartbeat liveness. The hook-status section now keys off whether a DLL was injected (host-side) rather than the input-hook "attached" flag, so it stays visible with input unhooked. Guards for dependent features: the synthetic-input control is disabled when input forwarding is off, and the Audio panel explains that mirroring uses loopback (echo) when the render-hook is off. Verified against Phantom Brave via coop_audio_probe: starting with audio requested off installs only input+focus (8 hooks, no capture); re-enabling at runtime installs the audio hooks (13) and capture starts immediately. All four tests pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
75 lines
2.1 KiB
C++
75 lines
2.1 KiB
C++
// Host side of the IPC channel: creates the shared-memory section (named by the
|
|
// target game's pid) and publishes the forwarded controller state each frame.
|
|
#pragma once
|
|
|
|
#include <array>
|
|
#include <cstdint>
|
|
|
|
#include "coop/protocol.hpp"
|
|
#include "coop/shared_memory.hpp"
|
|
#include "input/input_source.hpp"
|
|
|
|
namespace coop
|
|
{
|
|
|
|
// Plain (non-atomic) snapshot of the hook's back-channel for the overlay.
|
|
struct HookStatusView
|
|
{
|
|
bool attached = false; // XInput hooks installed in the game
|
|
bool focus_spoof = false; // focus spoofing active
|
|
std::uint32_t heartbeat = 0; // DLL liveness counter
|
|
std::uint64_t get_state[kMaxPads] = {};
|
|
std::uint64_t get_caps[kMaxPads] = {};
|
|
std::uint64_t focus_calls[FocusApi_Count] = {};
|
|
std::uint32_t game_pid = 0;
|
|
std::uint64_t game_hwnd = 0;
|
|
bool raw_input_registered = false;
|
|
bool raw_input_gamepad = false;
|
|
bool raw_input_gamepad_sink = false;
|
|
bool dinput_loaded = false;
|
|
|
|
// Audio render-hook diagnostics (for the Audio panel's stream-count view).
|
|
std::uint32_t audio_streams_seen = 0;
|
|
AudioStreamInfo audio_streams[kMaxAudioStreams] = {};
|
|
|
|
// Installed-hooks registry (for the Injection panel's hook list).
|
|
std::uint32_t hook_entry_count = 0;
|
|
HookEntry hook_entries[kMaxHookEntries] = {};
|
|
};
|
|
|
|
class IpcServer
|
|
{
|
|
public:
|
|
// Creates and initializes the section for `target_pid`. The injected hook
|
|
// derives the same name from its own pid and opens it.
|
|
bool start(unsigned long target_pid);
|
|
|
|
// Pushes the current pad snapshot to the hook. No-op if not started.
|
|
void publish(const std::array<PadInfo, kMaxPads>& pads);
|
|
|
|
void stop();
|
|
|
|
// Reads the hook's diagnostics back-channel (zeroed if not started).
|
|
[[nodiscard]] HookStatusView hook_status() const;
|
|
|
|
// Request a hook subsystem be installed (true) or removed (false). The hook
|
|
// reconciles on its next tick. No-op if not started.
|
|
void set_subsystem_enabled(std::uint32_t subsystem, bool enabled);
|
|
|
|
[[nodiscard]] bool running() const
|
|
{
|
|
return block_ != nullptr;
|
|
}
|
|
[[nodiscard]] unsigned long target_pid() const
|
|
{
|
|
return target_pid_;
|
|
}
|
|
|
|
private:
|
|
SharedMemory shm_;
|
|
SharedBlock* block_ = nullptr;
|
|
unsigned long target_pid_ = 0;
|
|
};
|
|
|
|
} // namespace coop
|