From b5499eccdf38aed4840323c8d44bd930407cded5 Mon Sep 17 00:00:00 2001 From: baketnk Date: Thu, 24 Sep 2026 13:35:49 -0400 Subject: [PATCH] Keep controls-only overlay canvas static for click diagnosis --- docs/overlay.md | 8 ++++++++ src/main.cpp | 26 +++++++++++++++----------- tests/panel_test.cpp | 2 ++ 3 files changed, 25 insertions(+), 11 deletions(-) diff --git a/docs/overlay.md b/docs/overlay.md index a27bb02..9148610 100644 --- a/docs/overlay.md +++ b/docs/overlay.md @@ -83,6 +83,14 @@ supplied prefix. The native build includes these tests too; tests never initiali OpenVR or touch the real mounting preference. Physical pointing, tracking loss, recentring and readability still require a separately authorized headset check. +The opt-in native `--check-controls` probe logs pointer counters and action +callbacks to the terminal rather than repainting them on the panel. Its canvas +stays static for Record/Cancel/Insert/Enter clicks so those clicks can be checked +without diagnostic `SetOverlayRaw` traffic. Switching tabs or mount still updates +the visible panel. This isolates click-induced compositor flicker from full raw +texture replacements; it does not establish that ordinary state-changing UI +updates are flicker-free. + `assets/actions.json` names six actions: left/right grip, PTT, cancel, insert, Enter. `bindings_frame_controller.json` uses the observed Frame profile's grip click paths; both bound actions were reported tracked/active in the device check. diff --git a/src/main.cpp b/src/main.cpp index ab82ede..ddffea8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -77,7 +77,12 @@ int main(int argc, char** argv) { if (mode == "--check-overlay" || mode == "--check-controls") { frameyap::InstanceLock lock; frameyap::Overlay overlay(options.assets, options.font, options.mount, false); - overlay.draw({"FrameYap five-second visual check", "No audio captured. No text or Enter delivered.", "Controls inactive during this check.", false, false}); + const frameyap::Panel check_panel = mode == "--check-controls" + ? frameyap::Panel{"Controls check - watch terminal for events", "No audio captured. No text or Enter delivered.", + "Click Record, Cancel or tabs. Actions are diagnostic only.", true, false} + : frameyap::Panel{"FrameYap five-second visual check", "No audio captured. No text or Enter delivered.", + "Controls inactive during this check.", false, false}; + overlay.draw(check_panel); for (vr::TrackedDeviceIndex_t i = 0; i < vr::k_unMaxTrackedDeviceCount; ++i) { if (vr::VRSystem()->GetTrackedDeviceClass(i) != vr::TrackedDeviceClass_Controller) continue; char type[256]{}, profile[512]{}; @@ -88,9 +93,8 @@ int main(int argc, char** argv) { if (mode == "--check-controls") { const auto end = std::chrono::steady_clock::now() + std::chrono::seconds(30); std::string status; - bool held = false; + std::string pointer_status; bool quit_check = false; - std::string last_action = "Press panel controls or tap then hold right grip. No mic or typing."; auto action_name = [](frameyap::UiAction action) { switch (action) { case frameyap::UiAction::BeginRecord: return "BeginRecord"; @@ -107,15 +111,16 @@ int main(int argc, char** argv) { while (!quit_check && std::chrono::steady_clock::now() < end) { for (auto action : overlay.poll()) { if (action == frameyap::UiAction::Quit) { quit_check = true; break; } - if (action == frameyap::UiAction::BeginRecord) held = true; - if (action == frameyap::UiAction::EndRecord || action == frameyap::UiAction::Cancel) held = false; - last_action = std::string(action_name(action)) + " received; diagnostic only."; - std::cout << last_action << std::endl; + std::cout << action_name(action) << " received; diagnostic only." << std::endl; } auto next = overlay.controls_status(); if (next != status) { status = next; std::cout << status << std::endl; } - overlay.draw({overlay.pointer_status(), - status, last_action, true, held}); + auto pointer = overlay.pointer_status(); + if (pointer != pointer_status) { pointer_status = pointer; std::cout << pointer_status << std::endl; } + // Keep the canvas fixed for action clicks: otherwise the + // changing counters cause SetOverlayRaw on every down/up. + // Tab/placement changes still redraw the correct controls. + overlay.draw(check_panel); std::this_thread::sleep_for(std::chrono::milliseconds(10)); } std::cout << "Final " << overlay.pointer_status() << std::endl; @@ -123,8 +128,7 @@ int main(int argc, char** argv) { const auto end = std::chrono::steady_clock::now() + std::chrono::seconds(5); while (std::chrono::steady_clock::now() < end) { for (auto action : overlay.poll()) if (action == frameyap::UiAction::Quit) return 0; - overlay.draw({"FrameYap five-second visual check", "No audio captured. No text or Enter delivered.", - "Actions are diagnostic only during this check.", false, false}); + overlay.draw(check_panel); std::this_thread::sleep_for(std::chrono::milliseconds(10)); } } diff --git a/tests/panel_test.cpp b/tests/panel_test.cpp index 69031d6..84bd7a2 100644 --- a/tests/panel_test.cpp +++ b/tests/panel_test.cpp @@ -37,7 +37,9 @@ int main(int argc, char** argv) { no_action(click(surface, 32, 574)); // clipped visual corners are not invisible hit targets no_action(click(surface, 207, 575)); assert(click(surface, 100, 610).action == UiAction::BeginRecord); + assert(!surface.render(p)); // a diagnostic click does not change the canvas assert(click(surface, 280, 610).action == UiAction::Cancel); + assert(!surface.render(p)); no_action(surface.pointer_up(0, 100, 610)); surface.pointer_down(0, 100, 610); no_action(surface.pointer_up(0, 280, 610));