diff --git a/README.md b/README.md index 28bf005..46dee97 100644 --- a/README.md +++ b/README.md @@ -75,12 +75,6 @@ is removed from this list once done — so the top item is always next. The self-verifiable tooling / UI / input items come first; the game-pipeline items that need a real game (and Remote Play) to fully validate come last. -- **Move the synthetic-input toggle to the Controllers panel, under Debug details.** - The "Forward synthetic test input" checkbox is a controller-debugging aid, so move - it out of the Injection panel into `ControllersPanel` and gate it behind - `debug_details`. The publish path is unchanged (the Injection panel already - substitutes the synthetic pattern in `publish`); the flag just moves with it (or is - passed from Controllers into the publish call). - **Mouse & keyboard forwarding (messages + polling-state hooks).** Forward guest clicks and keystrokes into the unfocused game via a new **MKB hook subsystem** in `coop_hook.dll` — the toggle *is* the hook (not installed → no forwarding). diff --git a/host/src/controllers_panel.cpp b/host/src/controllers_panel.cpp index 76cfe88..26e1fc3 100644 --- a/host/src/controllers_panel.cpp +++ b/host/src/controllers_panel.cpp @@ -91,6 +91,21 @@ void ControllersPanel::draw(const InputSource& input, const HookStatusView& stat } #endif + // Synthetic test input (debug aid): drives the game with a non-human pattern so + // forwarding can be proven without a real controller. Only meaningful once the + // XInput hook is attached. + if (debug_details) + { + ImGui::BeginDisabled(!status.attached); + ImGui::Checkbox("Forward synthetic test input", &test_input_); + ImGui::EndDisabled(); + if (test_input_) + { + ImGui::SameLine(); + ImGui::TextDisabled("(ignores your controller)"); + } + } + // --- Guest pads the host receives from RPT ----------------------------- ImGui::SeparatorText("Incoming (host receives)"); const auto& pads = input.pads(); diff --git a/host/src/controllers_panel.hpp b/host/src/controllers_panel.hpp index 730702e..2c4bd3f 100644 --- a/host/src/controllers_panel.hpp +++ b/host/src/controllers_panel.hpp @@ -18,9 +18,17 @@ class ControllersPanel { public: // `status` is the hook's back-channel (per-slot poll counters); `debug_details` - // reveals the raw axis values and the per-slot poll-rate table. + // reveals the raw axis values, the per-slot poll-rate table, and the synthetic + // test-input toggle. void draw(const InputSource& input, const HookStatusView& status, bool debug_details); + // Whether the operator enabled "Forward synthetic test input" (a controller debug + // aid). The host feeds this to InjectionPanel, which substitutes a synthetic pad. + [[nodiscard]] bool test_input() const + { + return test_input_; + } + #ifdef COOP_WITH_STEAM // Whether the operator has opted into Steam Input. It's off by default: simply // initializing Steam Input activates Steam's in-process XInput interception, @@ -44,6 +52,8 @@ public: #endif private: + bool test_input_ = false; // "Forward synthetic test input" (debug aid, default off) + // Sampled to turn the hook's cumulative per-slot counters into poll rates. unsigned long long last_state_count_[kMaxPads] = {}; double state_rate_[kMaxPads] = {}; diff --git a/host/src/injection_panel.cpp b/host/src/injection_panel.cpp index d483a30..08a9111 100644 --- a/host/src/injection_panel.cpp +++ b/host/src/injection_panel.cpp @@ -584,17 +584,6 @@ void InjectionPanel::draw(bool debug_details) ImGui::TextColored(status_color_, "%s", status_.c_str()); } - // Synthetic input only reaches the game if the XInput hook is installed and the - // target is actually alive to receive it. - ImGui::BeginDisabled(injected_ && (!want_input_ || target_state_ != TargetState::Alive)); - ImGui::Checkbox("Forward synthetic test input", &test_input_); - ImGui::EndDisabled(); - if (test_input_) - { - ImGui::SameLine(); - ImGui::TextDisabled("(ignores your controller)"); - } - draw_hook_status(debug_details); ImGui::End(); diff --git a/host/src/injection_panel.hpp b/host/src/injection_panel.hpp index 7bff396..8e7014f 100644 --- a/host/src/injection_panel.hpp +++ b/host/src/injection_panel.hpp @@ -48,6 +48,13 @@ public: // test-input mode is on, a synthetic pattern is sent instead of `pads`. void publish(const std::array& pads); + // Enable/disable synthetic test input. The toggle itself lives in the Controllers + // panel (a controller-debug aid); the host feeds its state here each frame. + void set_test_input(bool on) + { + test_input_ = on; + } + // The injected game's main window, as reported by the hook (null if none). A // terminated target's HWND is stale/invalid, so report none -- the capture and // audio panels then drop to idle instead of chasing a dead window. diff --git a/host/src/main.cpp b/host/src/main.cpp index 98418da..23ca852 100644 --- a/host/src/main.cpp +++ b/host/src/main.cpp @@ -144,6 +144,7 @@ int run() } #endif input->poll(); + injection.set_test_input(controllers.test_input()); // toggle lives in the Controllers panel injection.publish(input->pads()); injection.tick(); // refresh target liveness before the mirror panels read game_hwnd() const HWND game = injection.game_hwnd();