mirror of
https://github.com/baketnk/frame-yap.git
synced 2026-10-06 01:00:04 +02:00
Stabilize raw overlay pointer rendering and trace click delivery
This commit is contained in:
1 parent
9b4bf9f047
commit
9e608193e3
6 files changed
+49
-36
No files matched your search
+5
-3
@@ -24,9 +24,11 @@ preview, status and control surfaces use independently rasterized antialiased ed
|
||||
and restrained baked neon halos rather than GPU bloom. The recording indicator and
|
||||
selected controls remain distinguishable by their labels, not color alone. Rounded
|
||||
control hit areas exclude their clipped corners.
|
||||
Rendering/uploads occur only for changed content, page, settings, or pointer
|
||||
feedback; static frames are reused. The caller may call `draw(Panel)` at 10 ms
|
||||
intervals. Tracking transforms do not require repainting the canvas.
|
||||
Rendering/uploads occur only for changed content, page or settings; laser hover
|
||||
and button down/up are hit-tested without a raw-texture upload, to reduce
|
||||
compositor flicker reported during hover. Static frames are reused.
|
||||
The caller may call `draw(Panel)` at 10 ms intervals. Tracking transforms do
|
||||
not require repainting the canvas.
|
||||
|
||||
The complete transcript preview is paginated by glyph width and four-line
|
||||
height; Previous and Next navigate it without changing the source transcript.
|
||||
|
||||
+5
-3
@@ -89,6 +89,7 @@ int main(int argc, char** argv) {
|
||||
const auto end = std::chrono::steady_clock::now() + std::chrono::seconds(30);
|
||||
std::string status;
|
||||
bool held = false;
|
||||
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) {
|
||||
@@ -103,9 +104,9 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
return "Unknown";
|
||||
};
|
||||
while (std::chrono::steady_clock::now() < end) {
|
||||
while (!quit_check && std::chrono::steady_clock::now() < end) {
|
||||
for (auto action : overlay.poll()) {
|
||||
if (action == frameyap::UiAction::Quit) return 0;
|
||||
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.";
|
||||
@@ -113,10 +114,11 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
auto next = overlay.controls_status();
|
||||
if (next != status) { status = next; std::cout << status << std::endl; }
|
||||
overlay.draw({"Controls ONLY - " + std::string(held ? "HOLD recognized" : "no microphone or typing"),
|
||||
overlay.draw({overlay.pointer_status(),
|
||||
status, last_action, true, held});
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
}
|
||||
std::cout << "Final " << overlay.pointer_status() << std::endl;
|
||||
} else {
|
||||
const auto end = std::chrono::steady_clock::now() + std::chrono::seconds(5);
|
||||
while (std::chrono::steady_clock::now() < end) {
|
||||
|
||||
+19
-6
@@ -66,6 +66,8 @@ struct Overlay::Impl {
|
||||
bool focus = true;
|
||||
bool persist_mount = true;
|
||||
vr::EVRInputError action_update_error = vr::VRInputError_None;
|
||||
unsigned pointer_downs = 0, pointer_ups = 0, pointer_actions = 0, pointer_resets = 0;
|
||||
std::string last_pointer_event = "none";
|
||||
|
||||
Impl(const std::string& assets, const std::string& font, std::optional<Mount> requested, bool persist)
|
||||
: settings_path(default_mount_settings_path()),
|
||||
@@ -236,22 +238,28 @@ struct Overlay::Impl {
|
||||
case vr::VREvent_OverlayShown:
|
||||
focus = true; reset_input(result); break;
|
||||
case vr::VREvent_OverlayGamepadFocusLost:
|
||||
surface.reset_pointers(); ++pointer_resets;
|
||||
last_pointer_event = "gamepad focus lost"; break;
|
||||
case vr::VREvent_OverlayFocusChanged:
|
||||
// Dashboard laser/gamepad focus can change between the two grip
|
||||
// squeezes. It is not action activity or controller tracking loss:
|
||||
// invalidate pointer presses, but leave a physical grip gesture
|
||||
// armed. Hidden overlays and true OpenVR input capture still reset.
|
||||
surface.reset_pointers(); break;
|
||||
// This global focus notification also fires when the dashboard
|
||||
// laser enters our overlay. Do not erase a press between down/up;
|
||||
// a release must still hit the same enabled control.
|
||||
last_pointer_event = "overlay focus changed"; break;
|
||||
case vr::VREvent_MouseMove:
|
||||
surface.pointer_move(event.data.mouse.cursorIndex, event.data.mouse.x, H - event.data.mouse.y);
|
||||
break;
|
||||
case vr::VREvent_MouseButtonDown:
|
||||
++pointer_downs;
|
||||
last_pointer_event = "down button=" + std::to_string(event.data.mouse.button);
|
||||
if (event.data.mouse.button == vr::VRMouseButton_Left)
|
||||
surface.pointer_down(event.data.mouse.cursorIndex, event.data.mouse.x, H - event.data.mouse.y);
|
||||
break;
|
||||
case vr::VREvent_MouseButtonUp:
|
||||
++pointer_ups;
|
||||
last_pointer_event = "up button=" + std::to_string(event.data.mouse.button);
|
||||
if (event.data.mouse.button == vr::VRMouseButton_Left) {
|
||||
auto event_result = surface.pointer_up(event.data.mouse.cursorIndex, event.data.mouse.x, H - event.data.mouse.y);
|
||||
if (event_result.action || event_result.mount || event_result.recenter) ++pointer_actions;
|
||||
if (event_result.action) result.push_back(*event_result.action);
|
||||
if (event_result.mount) {
|
||||
mount = *event_result.mount;
|
||||
@@ -328,13 +336,18 @@ std::string Overlay::controls_status() {
|
||||
vr::VRControllerState_t raw{};
|
||||
const bool raw_ok = device < impl_->poses.size() && impl_->system->GetControllerState(device, &raw, sizeof(raw));
|
||||
result += std::string(i == 0 ? "\nLeft:" : "\nRight:") +
|
||||
" err=" + std::to_string(int(error)) + " bound=" + (data.bActive ? "Y" : "N") +
|
||||
" err=" + std::to_string(int(error)) + " active=" + (data.bActive ? "Y" : "N") +
|
||||
" down=" + (data.bState ? "Y" : "N") + " pose=" + (tracked ? "Y" : "N") +
|
||||
" origin=" + (origin_ok ? "Y" : "N") +
|
||||
" raw=" + (raw_ok ? ((raw.ulButtonPressed & vr::ButtonMaskFromId(vr::k_EButton_Grip)) ? "down" : "up") : "n/a");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
std::string Overlay::pointer_status() const {
|
||||
return "Pointer down=" + std::to_string(impl_->pointer_downs) + " up=" + std::to_string(impl_->pointer_ups) +
|
||||
" hits=" + std::to_string(impl_->pointer_actions) + " resets=" + std::to_string(impl_->pointer_resets) +
|
||||
" last=" + impl_->last_pointer_event;
|
||||
}
|
||||
|
||||
int registration(const std::string& manifest, bool remove, bool autostart) {
|
||||
try {
|
||||
|
||||
@@ -25,6 +25,7 @@ public:
|
||||
std::vector<UiAction> poll();
|
||||
void draw(const Panel& panel);
|
||||
std::string controls_status(); // diagnostic only, no input delivery
|
||||
std::string pointer_status() const; // diagnostic counters, no input delivery
|
||||
private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl_;
|
||||
|
||||
+13
-22
@@ -103,7 +103,7 @@ struct PanelSurface::Impl {
|
||||
Mount mount;
|
||||
bool settings = false, dirty = true;
|
||||
std::string placement_note;
|
||||
std::array<int, 2> pressed{{-1, -1}}, hovered{{-1, -1}};
|
||||
std::array<int, 2> pressed{{-1, -1}};
|
||||
std::vector<std::string> lines;
|
||||
size_t page = 0;
|
||||
static constexpr size_t lines_per_page = 4;
|
||||
@@ -241,9 +241,7 @@ struct PanelSurface::Impl {
|
||||
return -1;
|
||||
}
|
||||
void reset() {
|
||||
if (std::any_of(pressed.begin(), pressed.end(), [](int i) { return i >= 0; }) ||
|
||||
std::any_of(hovered.begin(), hovered.end(), [](int i) { return i >= 0; })) dirty = true;
|
||||
pressed.fill(-1); hovered.fill(-1);
|
||||
pressed.fill(-1);
|
||||
}
|
||||
bool render(const Panel& p) {
|
||||
if (p.recording != panel.recording || p.enabled != panel.enabled ||
|
||||
@@ -296,16 +294,14 @@ struct PanelSurface::Impl {
|
||||
bool on = enabled(b.id);
|
||||
bool selected = (b.id == Control::Review && !settings) || (b.id == Control::Settings && settings) ||
|
||||
(mounting(b.id) && *mounting(b.id) == mount);
|
||||
bool hover = std::find(hovered.begin(), hovered.end(), int(i)) != hovered.end();
|
||||
bool down = std::find(pressed.begin(), pressed.end(), int(i)) != pressed.end();
|
||||
const Color fill = !on ? Color{17, 23, 33, 255} : down ? Color{35, 77, 88, 255} :
|
||||
hover || selected ? Color{26, 55, 68, 255} : card;
|
||||
const Color fill = !on ? Color{17, 23, 33, 255} :
|
||||
selected ? Color{26, 55, 68, 255} : card;
|
||||
const Color accent = b.id == Control::Record && panel.recording ? pink : cyan;
|
||||
const bool highlighted = on && (hover || selected || b.id == Control::Record ||
|
||||
const bool highlighted = on && (selected || b.id == Control::Record ||
|
||||
(b.id == Control::Insert && panel.transcript.size()));
|
||||
rounded(b.r, std::min(16, b.r.h / 3), fill,
|
||||
!on ? Color{35, 46, 59, 255} : highlighted ? accent : Color{67, 93, 112, 255},
|
||||
highlighted ? (hover || down ? .40f : .23f) : 0.f, highlighted ? 2 : 1);
|
||||
highlighted ? .23f : 0.f, highlighted ? 2 : 1);
|
||||
const auto label = b.id == Control::Record && panel.recording ? "Stop" : b.label;
|
||||
text(label, b.r.x + 16, b.r.y + b.r.h / 2 + 9, 27, on ? ink : Color{81, 96, 113, 255}, b.r.x + b.r.w - 8);
|
||||
if (mounting(b.id) && selected) text("ON", b.r.x + b.r.w - 56, b.r.y + 38, 23, cyan, b.r.x + b.r.w - 12);
|
||||
@@ -319,32 +315,27 @@ PanelSurface::~PanelSurface() = default;
|
||||
bool PanelSurface::render(const Panel& p) { return impl_->render(p); }
|
||||
const std::vector<unsigned char>& PanelSurface::pixels() const { return impl_->pixels; }
|
||||
bool PanelSurface::available(UiAction a) const { return impl_->available(a); }
|
||||
void PanelSurface::pointer_move(unsigned cursor, float x, float y) {
|
||||
if (cursor >= impl_->hovered.size()) return;
|
||||
const int next = impl_->hit(x, y);
|
||||
if (next != impl_->hovered[cursor]) { impl_->hovered[cursor] = next; impl_->dirty = true; }
|
||||
void PanelSurface::pointer_move(unsigned, float, float) {
|
||||
// Hit-test on down/up only. SetOverlayRaw can flicker in SteamVR when each
|
||||
// laser hover frame causes another full RGBA upload.
|
||||
}
|
||||
void PanelSurface::pointer_down(unsigned cursor, float x, float y) {
|
||||
if (cursor >= impl_->pressed.size()) return;
|
||||
impl_->pressed[cursor] = impl_->hit(x, y);
|
||||
pointer_move(cursor, x, y);
|
||||
impl_->dirty = true;
|
||||
}
|
||||
SurfaceEvent PanelSurface::pointer_up(unsigned cursor, float x, float y) {
|
||||
SurfaceEvent result;
|
||||
if (cursor >= impl_->pressed.size()) return result;
|
||||
int index = std::exchange(impl_->pressed[cursor], -1);
|
||||
pointer_move(cursor, x, y);
|
||||
impl_->dirty = true;
|
||||
if (index < 0 || impl_->hit(x, y) != index) return result;
|
||||
auto c = buttons[index].id;
|
||||
if (c == Control::Record) result.action = impl_->panel.recording ? UiAction::EndRecord : UiAction::BeginRecord;
|
||||
else if (auto a = action(c)) result.action = a;
|
||||
else if (auto m = mounting(c)) { impl_->mount = *m; result.mount = *m; impl_->reset(); }
|
||||
else if (auto m = mounting(c)) { impl_->mount = *m; result.mount = *m; impl_->reset(); impl_->dirty = true; }
|
||||
else if (c == Control::Recenter) { result.recenter = true; impl_->reset(); }
|
||||
else if (c == Control::Review || c == Control::Settings) { impl_->settings = c == Control::Settings; impl_->reset(); }
|
||||
else if (c == Control::Prev) --impl_->page;
|
||||
else if (c == Control::Next) ++impl_->page;
|
||||
else if (c == Control::Review || c == Control::Settings) { impl_->settings = c == Control::Settings; impl_->reset(); impl_->dirty = true; }
|
||||
else if (c == Control::Prev) { --impl_->page; impl_->dirty = true; }
|
||||
else if (c == Control::Next) { ++impl_->page; impl_->dirty = true; }
|
||||
return result;
|
||||
}
|
||||
void PanelSurface::reset_pointers() { impl_->reset(); }
|
||||
|
||||
@@ -131,8 +131,12 @@ int main(int argc, char** argv) {
|
||||
assert(surface.pixels() == replaced);
|
||||
p.status = std::string(4096, 's'); p.detail = std::string(4096, 'd');
|
||||
assert(surface.render(p)); assert(!surface.render(p));
|
||||
surface.pointer_move(0, 900, 610); assert(surface.render(p));
|
||||
// Laser motion and a press do not re-upload raw pixels. A completed
|
||||
// action still reaches the caller; meaningful panel changes redraw.
|
||||
surface.pointer_move(0, 900, 610); assert(!surface.render(p));
|
||||
assert(click(surface, 900, 610).action == UiAction::Quit);
|
||||
surface.pointer_move(0, 900, 610); assert(!surface.render(p));
|
||||
surface.pointer_down(0, 900, 610); assert(!surface.render(p));
|
||||
assert(surface.pointer_up(0, 900, 610).action == UiAction::Quit);
|
||||
assert(!surface.render(p));
|
||||
std::cout << "panel checks passed (no OpenVR, microphone or input injection)\n";
|
||||
}
|
||||
Reference in new issue
Block a user