diff --git a/README.md b/README.md index ac68f03..4f5b344 100644 --- a/README.md +++ b/README.md @@ -113,11 +113,6 @@ default** and covers anything the hooked path doesn't. From an in-depth review pass. Each item is fixed test-first (a failing test, then the fix) and lands as its own commit; "verify" items are confirmed real before any change, and dropped if not. -UX: -- **Auto re-attach control** — keep it visible/controllable when not connected (today it can be - enabled-but-invisible). -- **Tooltips on disabled controls** — explain *why* Inject/subsystem controls are disabled. - Code quality: - **Stale comments** — `hook_guard.hpp` top block (still the old destroy-on-remove model), `input_source.hpp` ("future" Steam source), `audio_ring.hpp` (v1/reserved), `shared_memory.hpp` / diff --git a/host/src/injection_panel.cpp b/host/src/injection_panel.cpp index 2f07075..277031c 100644 --- a/host/src/injection_panel.cpp +++ b/host/src/injection_panel.cpp @@ -561,6 +561,10 @@ void InjectionPanel::draw_hook_status(bool debug_details) ImGui::BeginDisabled(target_state_ != TargetState::Alive); draw_subsystem_controls(status); ImGui::EndDisabled(); + if (target_state_ != TargetState::Alive) + { + ImGui::TextDisabled("(connect to a live game to change these)"); // why the toggles are locked + } ImGui::TextDisabled("Controller poll rates are in the Controllers panel."); draw_hook_list(status); @@ -636,40 +640,41 @@ void InjectionPanel::draw(bool debug_details) ImGui::SameLine(); ImGui::TextDisabled("(relaunched %s)", narrow(selected_name_).c_str()); } - // Session-only auto re-attach: tick it (while attached or terminated), then kill + - // relaunch the game and it re-injects itself early -- the kill+relaunch fix for a - // wrong audio format, without picking a target again. - if (!selected_name_.empty()) + ImGui::Separator(); + } + + // Session options for the selected game, shown whether or not we're connected -- so they can be set + // up before launching the game, and an enabled auto re-attach is never hidden after a disconnect. + if (!selected_name_.empty()) + { + // Session-only auto re-attach: tick it, then kill + relaunch the game and it re-injects itself + // early -- the kill+relaunch fix for a wrong audio format, without picking a target again. + ImGui::Checkbox("Auto re-attach this game on relaunch", &auto_reattach_); + if (auto_reattach_ && target_state_ == TargetState::Terminated) { - ImGui::Checkbox("Auto re-attach this game on relaunch", &auto_reattach_); - if (auto_reattach_ && target_state_ == TargetState::Terminated) + ImGui::SameLine(); + ImGui::TextColored(kGrey, "(watching for %s...)", narrow(selected_name_).c_str()); + } + // Opt-in Vulkan capture layer: for Vulkan games that initialize Vulkan immediately (where even + // auto-attach injects too late -- see the red banner), register a per-user implicit layer scoped + // to this game so the next launch is captured from the first frame. Removed when unticked / exit. + if (ImGui::Checkbox("Set up Vulkan layer (for immediate-init Vulkan games)", &vk_layer_enabled_)) + { + if (vk_layer_enabled_) { - ImGui::SameLine(); - ImGui::TextColored(kGrey, "(watching for %s...)", narrow(selected_name_).c_str()); + vk_layer_enabled_ = register_vk_layer(selected_name_); } - // Opt-in Vulkan capture layer: for Vulkan games that initialize Vulkan immediately - // (where even auto-attach injects too late -- see the red banner), register a per-user - // implicit layer scoped to this game so the next launch is captured from the first frame. - // Removed when unticked or the host exits. - if (ImGui::Checkbox("Set up Vulkan layer (for immediate-init Vulkan games)", &vk_layer_enabled_)) + else { - if (vk_layer_enabled_) - { - vk_layer_enabled_ = register_vk_layer(selected_name_); - } - else - { - unregister_vk_layer(); - } - } - if (ImGui::IsItemHovered()) - { - ImGui::SetTooltip("Registers a per-user (HKCU, no admin) implicit Vulkan layer scoped to\n" - "this game, so a relaunch is captured before Vulkan init. Pair with\n" - "Auto re-attach. Removed when you untick it or close the tool."); + unregister_vk_layer(); } } - ImGui::Separator(); + if (ImGui::IsItemHovered()) + { + ImGui::SetTooltip("Registers a per-user (HKCU, no admin) implicit Vulkan layer scoped to\n" + "this game, so a relaunch is captured before Vulkan init. Pair with\n" + "Auto re-attach. Removed when you untick it or close the tool."); + } } ImGui::TextUnformatted("Target window"); @@ -737,6 +742,12 @@ void InjectionPanel::draw(bool debug_details) inject_selected(); } ImGui::EndDisabled(); + // Explain the disabled state on hover (AllowWhenDisabled, since the button is greyed out). + if (!can_inject && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) + { + ImGui::SetTooltip("Pick a target window or process above first.\n" + "If the game already has the DLL (e.g. after a reconnect), this reuses it."); + } if (!status_.empty()) {