mirror of
https://github.com/baketnk/frame-yap.git
synced 2026-10-06 05:00:04 +02:00
Open SteamVR binding editor directly from Bindings button
This commit is contained in:
1 parent
019b8132bb
commit
3c03f88f17
8 files changed
+37
-102
No files matched your search
@@ -28,9 +28,8 @@ See [third-party notes](docs/third-party.md). No GitHub release is published yet
|
|||||||
**Left grip (when active):** double-tap for the same explicit Enter action.
|
**Left grip (when active):** double-tap for the same explicit Enter action.
|
||||||
Nothing inserts or submits automatically after transcription.
|
Nothing inserts or submits automatically after transcription.
|
||||||
- **Overlay:** Record/Stop, Cancel, paginated preview, Insert, Enter and Quit.
|
- **Overlay:** Record/Stop, Cancel, paginated preview, Insert, Enter and Quit.
|
||||||
Review, settings and bindings share one Inconsolata/neon-framed surface.
|
Review and settings share one Inconsolata/neon-framed surface.
|
||||||
**Bindings → Edit in SteamVR** opens the runtime binding editor; the tab shows
|
**Bindings** requests SteamVR's binding editor directly. Existing SteamVR overrides may supersede defaults. World-space by
|
||||||
runtime-provided control names. Existing SteamVR overrides may supersede defaults. World-space by
|
|
||||||
default; settings offer left wrist, right wrist and head mounting, plus recenter.
|
default; settings offer left wrist, right wrist and head mounting, plus recenter.
|
||||||
Dashboard lasers provide clickable controls. Settings → Lasers anytime is an
|
Dashboard lasers provide clickable controls. Settings → Lasers anytime is an
|
||||||
opt-in, default-off system-wide laser mode while the panel is visible; it may
|
opt-in, default-off system-wide laser mode while the panel is visible; it may
|
||||||
|
|||||||
+1
-1
@@ -62,7 +62,7 @@ Recording… 00:04 [ Cancel ]
|
|||||||
- Default Frame bindings: hold right X to speak, release to finish; B cancels,
|
- Default Frame bindings: hold right X to speak, release to finish; B cancels,
|
||||||
A inserts with a trailing space, Y inserts pending text then sends Enter.
|
A inserts with a trailing space, Y inserts pending text then sends Enter.
|
||||||
The alternate right-grip tap-then-hold gesture and left-grip double-tap Enter
|
The alternate right-grip tap-then-hold gesture and left-grip double-tap Enter
|
||||||
remain available. All are remappable through the Bindings tab's SteamVR editor.
|
remain available. All are remappable through the Bindings button's SteamVR editor.
|
||||||
A click-to-start/stop overlay button provides
|
A click-to-start/stop overlay button provides
|
||||||
a binding-independent alternative. Bound recording to 20 seconds; discard
|
a binding-independent alternative. Bound recording to 20 seconds; discard
|
||||||
accidental taps (initial threshold: 200 ms).
|
accidental taps (initial threshold: 200 ms).
|
||||||
|
|||||||
+11
-15
@@ -19,7 +19,7 @@ Sans face if present. Glyph coverage depends on the selected face; full CJK
|
|||||||
coverage is not claimed.
|
coverage is not claimed.
|
||||||
|
|
||||||
`src/panel_surface.*` renders **one 1000×680 RGBA canvas** for review, settings,
|
`src/panel_surface.*` renders **one 1000×680 RGBA canvas** for review, settings,
|
||||||
bindings, status and controls. `src/overlay_texture.*` uploads this CPU canvas into one
|
status and controls. `src/overlay_texture.*` uploads this CPU canvas into one
|
||||||
persistent Vulkan RGBA8 image and submits it with `SetOverlayTexture`. The image,
|
persistent Vulkan RGBA8 image and submits it with `SetOverlayTexture`. The image,
|
||||||
staging allocation and command buffer are reused; tabs do not create extra
|
staging allocation and command buffer are reused; tabs do not create extra
|
||||||
overlays or render targets. The rounded mint-to-blue perimeter,
|
overlays or render targets. The rounded mint-to-blue perimeter,
|
||||||
@@ -65,21 +65,17 @@ doubling an existing trailing space). Enter inserts any pending review and then
|
|||||||
queues Enter; with no pending text it queues Enter only. A failed text step never
|
queues Enter; with no pending text it queues Enter only. A failed text step never
|
||||||
proceeds to Enter. Recording never automatically inserts or submits.
|
proceeds to Enter. Recording never automatically inserts or submits.
|
||||||
|
|
||||||
### Bindings menu
|
### Bindings button
|
||||||
|
|
||||||
The **Bindings** tab shows the runtime-provided hand/control names for PTT,
|
**Bindings** requests SteamVR's in-headset binding editor directly for the
|
||||||
Cancel, Insert, Enter and both grip gestures. **Edit in SteamVR** requests the
|
current process/action set, without changing the FrameYap tab. SteamVR owns
|
||||||
in-headset binding editor for the current process/action set. SteamVR owns
|
remapping, persistence and the full binding view. The Review tab shows a request
|
||||||
remapping and persistence; the tab refreshes its origin labels once per second
|
or error note after the call. Opening it clears pending pointer presses and
|
||||||
while visible. Unknown/unavailable origins are reported instead of showing
|
rearms controller gestures from neutral.
|
||||||
bundled defaults as if they were live bindings. Long labels show a truncation
|
|
||||||
marker; the editor is the full binding view. Opening it clears pending pointer
|
|
||||||
presses and rearms controller gestures from neutral.
|
|
||||||
|
|
||||||
OpenVR 2.15.6 provides `GetActionOrigins`, `GetOriginLocalizedName` and
|
OpenVR 2.15.6 provides `OpenBindingUI`. Frame's installed controller profile
|
||||||
`OpenBindingUI`, not a generic 2D button-glyph API. Frame's installed controller
|
references left/right SVG diagrams for SteamVR's own editor. FrameYap
|
||||||
profile references left/right SVG diagrams for SteamVR's own editor. FrameYap
|
does not copy runtime artwork into its package.
|
||||||
uses runtime text labels; it does not copy runtime artwork into its package.
|
|
||||||
Editor availability and artwork rendering still require headset acceptance.
|
Editor availability and artwork rendering still require headset acceptance.
|
||||||
|
|
||||||
Normal priority with Lasers anytime off is the practical baseline: the wearer
|
Normal priority with Lasers anytime off is the practical baseline: the wearer
|
||||||
@@ -247,7 +243,7 @@ ctest --test-dir build-ui --output-on-failure
|
|||||||
./build-ui/frameyap_panel_test assets/fonts/Inconsolata-Regular.ttf /tmp/frameyap-ui
|
./build-ui/frameyap_panel_test assets/fonts/Inconsolata-Regular.ttf /tmp/frameyap-ui
|
||||||
```
|
```
|
||||||
|
|
||||||
The last command writes `-review.ppm`, `-settings.ppm`, `-bindings.ppm` and `-recording.ppm` to the
|
The last command writes `-review.ppm`, `-settings.ppm` and `-recording.ppm` to the
|
||||||
supplied prefix. The native build includes these tests too; tests never initialize
|
supplied prefix. The native build includes these tests too; tests never initialize
|
||||||
OpenVR or touch the real mounting preference. Physical pointing, tracking loss,
|
OpenVR or touch the real mounting preference. Physical pointing, tracking loss,
|
||||||
recentring and readability still require a separately authorized headset check.
|
recentring and readability still require a separately authorized headset check.
|
||||||
|
|||||||
+2
-2
@@ -12,8 +12,8 @@ initialize OpenVR, open a microphone, run ASR, download files or inject input.
|
|||||||
- Remappable SteamVR actions. The default Steam Frame binding maps right X
|
- Remappable SteamVR actions. The default Steam Frame binding maps right X
|
||||||
(hold to record, release to transcribe) to the existing PTT action using the
|
(hold to record, release to transcribe) to the existing PTT action using the
|
||||||
observed `frame_controller` profile. Right B cancels, A inserts + space, and Y
|
observed `frame_controller` profile. Right B cancels, A inserts + space, and Y
|
||||||
inserts pending text + Enter (or Enter only with no preview). A Bindings tab
|
inserts pending text + Enter (or Enter only with no preview). The Bindings button
|
||||||
shows runtime origin labels and opens SteamVR's remapping editor. Grip bindings remain, but both grip
|
requests SteamVR's remapping editor directly. Grip bindings remain, but both grip
|
||||||
actions were inactive in the observed dashboard check; do not rely on them.
|
actions were inactive in the observed dashboard check; do not rely on them.
|
||||||
If left grip becomes active, two short taps request explicit Enter.
|
If left grip becomes active, two short taps request explicit Enter.
|
||||||
For grip gestures, first squeeze <=250 ms; second squeeze begins <=350 ms
|
For grip gestures, first squeeze <=250 ms; second squeeze begins <=350 ms
|
||||||
|
|||||||
+1
-32
@@ -92,7 +92,6 @@ struct Overlay::Impl {
|
|||||||
unsigned overlay_shown_events = 0, overlay_hidden_events = 0, image_loaded_events = 0, image_failed_events = 0;
|
unsigned overlay_shown_events = 0, overlay_hidden_events = 0, image_loaded_events = 0, image_failed_events = 0;
|
||||||
unsigned overlay_focus_events = 0, global_focus_events = 0, input_focus_captured_events = 0;
|
unsigned overlay_focus_events = 0, global_focus_events = 0, input_focus_captured_events = 0;
|
||||||
std::string last_pointer_event = "none";
|
std::string last_pointer_event = "none";
|
||||||
std::chrono::steady_clock::time_point next_binding_refresh{};
|
|
||||||
|
|
||||||
Impl(const std::string& assets, const std::string& font, std::optional<Mount> requested, bool persist)
|
Impl(const std::string& assets, const std::string& font, std::optional<Mount> requested, bool persist)
|
||||||
: settings_path(default_mount_settings_path()),
|
: settings_path(default_mount_settings_path()),
|
||||||
@@ -293,34 +292,6 @@ struct Overlay::Impl {
|
|||||||
}
|
}
|
||||||
return {true, data.bState};
|
return {true, data.bState};
|
||||||
}
|
}
|
||||||
void refresh_bindings() {
|
|
||||||
if (!surface.bindings_visible()) { next_binding_refresh = {}; return; }
|
|
||||||
const auto now = std::chrono::steady_clock::now();
|
|
||||||
if (now < next_binding_refresh) return;
|
|
||||||
next_binding_refresh = now + std::chrono::seconds(1);
|
|
||||||
std::array<std::string, 6> labels;
|
|
||||||
constexpr std::array<size_t, 6> order{{2, 3, 4, 5, 0, 1}};
|
|
||||||
for (size_t row = 0; row < order.size(); ++row) {
|
|
||||||
std::array<vr::VRInputValueHandle_t, vr::k_unMaxActionOriginCount> origins{};
|
|
||||||
const auto error = input->GetActionOrigins(action_set, actions[order[row]], origins.data(), uint32_t(origins.size()));
|
|
||||||
if (error != vr::VRInputError_None) { labels[row] = "Binding information unavailable"; continue; }
|
|
||||||
for (auto origin : origins) {
|
|
||||||
if (origin == vr::k_ulInvalidInputValueHandle) continue;
|
|
||||||
std::array<char, 256> name{};
|
|
||||||
if (input->GetOriginLocalizedName(origin, name.data(), uint32_t(name.size()),
|
|
||||||
vr::VRInputString_Hand | vr::VRInputString_InputSource) != vr::VRInputError_None || !name[0]) {
|
|
||||||
if (!labels[row].empty()) labels[row] += " / ";
|
|
||||||
labels[row] += "Bound (name unavailable)";
|
|
||||||
} else {
|
|
||||||
name.back() = '\0';
|
|
||||||
if (!labels[row].empty()) labels[row] += " / ";
|
|
||||||
labels[row] += name.data();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (labels[row].empty()) labels[row] = "Unbound / controller unavailable";
|
|
||||||
}
|
|
||||||
surface.set_bindings(std::move(labels));
|
|
||||||
}
|
|
||||||
std::vector<UiAction> poll() {
|
std::vector<UiAction> poll() {
|
||||||
std::vector<UiAction> result;
|
std::vector<UiAction> result;
|
||||||
system->GetDeviceToAbsoluteTrackingPose(vr::TrackingUniverseStanding, 0, poses.data(), uint32_t(poses.size()));
|
system->GetDeviceToAbsoluteTrackingPose(vr::TrackingUniverseStanding, 0, poses.data(), uint32_t(poses.size()));
|
||||||
@@ -390,9 +361,8 @@ struct Overlay::Impl {
|
|||||||
reset_input(result);
|
reset_input(result);
|
||||||
const auto error = input->OpenBindingUI(nullptr, action_set, vr::k_ulInvalidInputValueHandle, false);
|
const auto error = input->OpenBindingUI(nullptr, action_set, vr::k_ulInvalidInputValueHandle, false);
|
||||||
surface.set_binding_note(error == vr::VRInputError_None
|
surface.set_binding_note(error == vr::VRInputError_None
|
||||||
? "SteamVR editor requested. Changes appear here when available."
|
? "SteamVR binding editor requested."
|
||||||
: "SteamVR could not open bindings. Try its controller settings.");
|
: "SteamVR could not open bindings. Try its controller settings.");
|
||||||
next_binding_refresh = {};
|
|
||||||
}
|
}
|
||||||
if (event_result.lasers_anytime) {
|
if (event_result.lasers_anytime) {
|
||||||
const bool enabled = *event_result.lasers_anytime;
|
const bool enabled = *event_result.lasers_anytime;
|
||||||
@@ -427,7 +397,6 @@ struct Overlay::Impl {
|
|||||||
// experiment compares. SteamVR's separate permission gate is never changed here.
|
// experiment compares. SteamVR's separate permission gate is never changed here.
|
||||||
set.nPriority = action_priority();
|
set.nPriority = action_priority();
|
||||||
action_update_error = input->UpdateActionState(&set, sizeof(set), 1);
|
action_update_error = input->UpdateActionState(&set, sizeof(set), 1);
|
||||||
refresh_bindings();
|
|
||||||
if (action_update_error != vr::VRInputError_None) {
|
if (action_update_error != vr::VRInputError_None) {
|
||||||
reset_input(result); return result;
|
reset_input(result); return result;
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-28
@@ -2,6 +2,7 @@
|
|||||||
#include <ft2build.h>
|
#include <ft2build.h>
|
||||||
#include FT_FREETYPE_H
|
#include FT_FREETYPE_H
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
@@ -25,15 +26,14 @@ struct Rect {
|
|||||||
rounded_distance(px, py, x, y, w, h, std::min(16, h / 3)) <= 0.f;
|
rounded_distance(px, py, x, y, w, h, std::min(16, h / 3)) <= 0.f;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
enum class Control { Review, Settings, Bindings, OpenBindings, Prev, Next, Record, Cancel, Insert, Enter, Quit,
|
enum class Control { Review, Settings, Bindings, Prev, Next, Record, Cancel, Insert, Enter, Quit,
|
||||||
World, Left, Right, Head, Recenter, LasersAnytime, AdvancedDebug };
|
World, Left, Right, Head, Recenter, LasersAnytime, AdvancedDebug };
|
||||||
enum class Tab { Review, Settings, Bindings };
|
enum class Tab { Review, Settings };
|
||||||
struct Button { Rect r; Control id; const char* label; };
|
struct Button { Rect r; Control id; const char* label; };
|
||||||
constexpr std::array<Button, 18> buttons{{
|
constexpr std::array<Button, 17> buttons{{
|
||||||
{{32, 138, 180, 46}, Control::Review, "Review"},
|
{{32, 138, 180, 46}, Control::Review, "Review"},
|
||||||
{{226, 138, 180, 46}, Control::Settings, "Settings"},
|
{{226, 138, 180, 46}, Control::Settings, "Settings"},
|
||||||
{{420, 138, 180, 46}, Control::Bindings, "Bindings"},
|
{{420, 138, 180, 46}, Control::Bindings, "Bindings"},
|
||||||
{{32, 410, 420, 48}, Control::OpenBindings, "Edit in SteamVR"},
|
|
||||||
{{32, 406, 154, 44}, Control::Prev, "Previous"},
|
{{32, 406, 154, 44}, Control::Prev, "Previous"},
|
||||||
{{838, 406, 130, 44}, Control::Next, "Next"},
|
{{838, 406, 130, 44}, Control::Next, "Next"},
|
||||||
{{32, 574, 176, 68}, Control::Record, "Record"},
|
{{32, 574, 176, 68}, Control::Record, "Record"},
|
||||||
@@ -108,7 +108,6 @@ struct PanelSurface::Impl {
|
|||||||
Tab tab = Tab::Review;
|
Tab tab = Tab::Review;
|
||||||
bool dirty = true, lasers_anytime = false, advanced_debug = false;
|
bool dirty = true, lasers_anytime = false, advanced_debug = false;
|
||||||
std::string placement_note, binding_note;
|
std::string placement_note, binding_note;
|
||||||
std::array<std::string, 6> bindings{};
|
|
||||||
std::array<int, 2> pressed{{-1, -1}};
|
std::array<int, 2> pressed{{-1, -1}};
|
||||||
std::vector<std::string> lines;
|
std::vector<std::string> lines;
|
||||||
size_t page = 0;
|
size_t page = 0;
|
||||||
@@ -247,7 +246,6 @@ struct PanelSurface::Impl {
|
|||||||
if (c == Control::Prev || c == Control::Next) return tab == Tab::Review;
|
if (c == Control::Prev || c == Control::Next) return tab == Tab::Review;
|
||||||
if (mounting(c) || c == Control::Recenter || c == Control::LasersAnytime ||
|
if (mounting(c) || c == Control::Recenter || c == Control::LasersAnytime ||
|
||||||
c == Control::AdvancedDebug) return tab == Tab::Settings;
|
c == Control::AdvancedDebug) return tab == Tab::Settings;
|
||||||
if (c == Control::OpenBindings) return tab == Tab::Bindings;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
bool enabled(Control c) const {
|
bool enabled(Control c) const {
|
||||||
@@ -302,20 +300,7 @@ struct PanelSurface::Impl {
|
|||||||
for (size_t i = 0; i < std::min(size_t(2), detail.size()); ++i)
|
for (size_t i = 0; i < std::min(size_t(2), detail.size()); ++i)
|
||||||
text(detail[i], 32, 486 + int(i) * 30, 24, muted, 936);
|
text(detail[i], 32, 486 + int(i) * 30, 24, muted, 936);
|
||||||
if (detail.size() > 2) text("[detail truncated]", 730, 546, 22, pink, 968);
|
if (detail.size() > 2) text("[detail truncated]", 730, 546, 22, pink, 968);
|
||||||
} else if (tab == Tab::Bindings) {
|
if (!binding_note.empty()) text(binding_note, 32, 203, 20, muted, 968);
|
||||||
constexpr std::array<const char*, 6> names{{"Hold to record", "Cancel / discard", "Insert + space",
|
|
||||||
"Insert + Enter", "Double tap: Enter", "Tap then hold: record"}};
|
|
||||||
for (size_t i = 0; i < names.size(); ++i) {
|
|
||||||
const int y = 229 + int(i) * 30;
|
|
||||||
text(names[i], 32, y, 23, muted, 335);
|
|
||||||
auto label = wrap(bindings[i].empty() ? "Waiting for SteamVR..." : bindings[i], 23, 582);
|
|
||||||
text(label.front(), 346, y, 23, ink, 928);
|
|
||||||
if (label.size() > 1) text("...", 930, y, 23, muted, 968);
|
|
||||||
}
|
|
||||||
text("Enter inserts pending text, then presses Enter; no text = Enter only.", 32, 484, 22, muted, 968);
|
|
||||||
text("Normal mode: bindings with dashboard closed; UI with it open.", 32, 512, 22, muted, 968);
|
|
||||||
text(binding_note.empty() ? "SteamVR owns remapping. Multiple controls may appear per action." : binding_note,
|
|
||||||
32, 539, 21, muted, 968);
|
|
||||||
} else {
|
} else {
|
||||||
text("MOUNT AND INTERACTION", 32, 224, 22, muted, 968);
|
text("MOUNT AND INTERACTION", 32, 224, 22, muted, 968);
|
||||||
text("Full logs may contain speech/text/paths. No saved audio clips.", 32, 520, 20, pink, 968);
|
text("Full logs may contain speech/text/paths. No saved audio clips.", 32, 520, 20, pink, 968);
|
||||||
@@ -329,7 +314,6 @@ struct PanelSurface::Impl {
|
|||||||
bool on = enabled(b.id);
|
bool on = enabled(b.id);
|
||||||
bool selected = (b.id == Control::Review && tab == Tab::Review) ||
|
bool selected = (b.id == Control::Review && tab == Tab::Review) ||
|
||||||
(b.id == Control::Settings && tab == Tab::Settings) ||
|
(b.id == Control::Settings && tab == Tab::Settings) ||
|
||||||
(b.id == Control::Bindings && tab == Tab::Bindings) ||
|
|
||||||
(mounting(b.id) && *mounting(b.id) == mount) ||
|
(mounting(b.id) && *mounting(b.id) == mount) ||
|
||||||
(b.id == Control::LasersAnytime && lasers_anytime) ||
|
(b.id == Control::LasersAnytime && lasers_anytime) ||
|
||||||
(b.id == Control::AdvancedDebug && advanced_debug);
|
(b.id == Control::AdvancedDebug && advanced_debug);
|
||||||
@@ -380,19 +364,15 @@ SurfaceEvent PanelSurface::pointer_up(unsigned cursor, float x, float y) {
|
|||||||
else if (c == Control::Recenter) { result.recenter = true; impl_->reset(); }
|
else if (c == Control::Recenter) { result.recenter = true; impl_->reset(); }
|
||||||
else if (c == Control::LasersAnytime) result.lasers_anytime = !impl_->lasers_anytime;
|
else if (c == Control::LasersAnytime) result.lasers_anytime = !impl_->lasers_anytime;
|
||||||
else if (c == Control::AdvancedDebug) result.advanced_debug = !impl_->advanced_debug;
|
else if (c == Control::AdvancedDebug) result.advanced_debug = !impl_->advanced_debug;
|
||||||
else if (c == Control::OpenBindings) { result.open_bindings = true; impl_->reset(); }
|
else if (c == Control::Bindings) { result.open_bindings = true; impl_->reset(); }
|
||||||
else if (c == Control::Review || c == Control::Settings || c == Control::Bindings) {
|
else if (c == Control::Review || c == Control::Settings) {
|
||||||
impl_->tab = c == Control::Review ? Tab::Review : c == Control::Settings ? Tab::Settings : Tab::Bindings;
|
impl_->tab = c == Control::Review ? Tab::Review : Tab::Settings;
|
||||||
impl_->reset(); impl_->dirty = true;
|
impl_->reset(); impl_->dirty = true;
|
||||||
}
|
}
|
||||||
else if (c == Control::Prev) { --impl_->page; impl_->dirty = true; }
|
else if (c == Control::Prev) { --impl_->page; impl_->dirty = true; }
|
||||||
else if (c == Control::Next) { ++impl_->page; impl_->dirty = true; }
|
else if (c == Control::Next) { ++impl_->page; impl_->dirty = true; }
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
bool PanelSurface::bindings_visible() const { return impl_->tab == Tab::Bindings; }
|
|
||||||
void PanelSurface::set_bindings(std::array<std::string, 6> labels) {
|
|
||||||
if (impl_->bindings != labels) { impl_->bindings = std::move(labels); impl_->dirty = true; }
|
|
||||||
}
|
|
||||||
void PanelSurface::set_binding_note(std::string note) {
|
void PanelSurface::set_binding_note(std::string note) {
|
||||||
if (impl_->binding_note != note) { impl_->binding_note = std::move(note); impl_->dirty = true; }
|
if (impl_->binding_note != note) { impl_->binding_note = std::move(note); impl_->dirty = true; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "overlay.hpp"
|
#include "overlay.hpp"
|
||||||
#include "config.hpp"
|
#include "config.hpp"
|
||||||
#include <array>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -34,10 +33,7 @@ public:
|
|||||||
void set_placement_note(std::string note);
|
void set_placement_note(std::string note);
|
||||||
void set_lasers_anytime(bool enabled);
|
void set_lasers_anytime(bool enabled);
|
||||||
void set_advanced_debug(bool enabled);
|
void set_advanced_debug(bool enabled);
|
||||||
// PTT, Cancel, Insert, Enter, left-grip gesture, right-grip gesture.
|
|
||||||
void set_bindings(std::array<std::string, 6> labels);
|
|
||||||
void set_binding_note(std::string note);
|
void set_binding_note(std::string note);
|
||||||
bool bindings_visible() const;
|
|
||||||
bool available(UiAction action) const;
|
bool available(UiAction action) const;
|
||||||
private:
|
private:
|
||||||
struct Impl;
|
struct Impl;
|
||||||
|
|||||||
+12
-17
@@ -139,31 +139,26 @@ int main(int argc, char** argv) {
|
|||||||
no_action(click(surface, 680, 420)); // laser toggle only exists on Settings
|
no_action(click(surface, 680, 420)); // laser toggle only exists on Settings
|
||||||
no_action(click(surface, 680, 474)); // debug toggle only exists on Settings
|
no_action(click(surface, 680, 474)); // debug toggle only exists on Settings
|
||||||
|
|
||||||
// Binding navigation is not a delivery action; settings and paging are hidden.
|
// Bindings opens SteamVR directly, from either tab, without replacing review.
|
||||||
assert(!surface.bindings_visible());
|
|
||||||
surface.pointer_down(1, 480, 610);
|
surface.pointer_down(1, 480, 610);
|
||||||
no_action(click(surface, 500, 160));
|
auto editor = click(surface, 500, 160);
|
||||||
assert(surface.bindings_visible());
|
|
||||||
no_action(surface.pointer_up(1, 480, 610)); // switching tabs invalidates old approval
|
|
||||||
surface.set_bindings({"Right X", "Unbound / controller unavailable", "Left Y", "Right Y", "Left grip", "Right grip"});
|
|
||||||
assert(surface.render(p)); assert(!surface.render(p));
|
|
||||||
surface.set_bindings({"Right X", "Unbound / controller unavailable", "Left Y", "Right Y", "Left grip", "Right grip"});
|
|
||||||
assert(!surface.render(p));
|
|
||||||
if (argc >= 3) snapshot(surface, std::string(argv[2]) + "-bindings.ppm");
|
|
||||||
no_action(click(surface, 680, 260));
|
|
||||||
no_action(click(surface, 900, 430));
|
|
||||||
no_action(click(surface, 680, 420));
|
|
||||||
no_action(click(surface, 680, 474));
|
|
||||||
const auto editor = click(surface, 200, 434);
|
|
||||||
assert(editor.open_bindings && !editor.action && !editor.mount && !editor.recenter && !editor.lasers_anytime);
|
assert(editor.open_bindings && !editor.action && !editor.mount && !editor.recenter && !editor.lasers_anytime);
|
||||||
no_action(surface.pointer_up(0, 200, 434)); // one launch per deliberate click
|
no_action(surface.pointer_up(1, 480, 610)); // opening editor invalidates old approval
|
||||||
|
no_action(surface.pointer_up(0, 500, 160)); // one launch per deliberate click
|
||||||
surface.set_binding_note("SteamVR could not open bindings. Try its controller settings.");
|
surface.set_binding_note("SteamVR could not open bindings. Try its controller settings.");
|
||||||
assert(surface.render(p)); assert(!surface.render(p));
|
assert(surface.render(p)); assert(!surface.render(p));
|
||||||
assert(click(surface, 480, 610).action == UiAction::Insert);
|
assert(click(surface, 480, 610).action == UiAction::Insert);
|
||||||
assert(click(surface, 680, 610).action == UiAction::Enter);
|
assert(click(surface, 680, 610).action == UiAction::Enter);
|
||||||
assert(click(surface, 280, 610).action == UiAction::Cancel);
|
assert(click(surface, 280, 610).action == UiAction::Cancel);
|
||||||
no_action(click(surface, 100, 160));
|
no_action(click(surface, 100, 160));
|
||||||
assert(!surface.bindings_visible());
|
editor = click(surface, 500, 160);
|
||||||
|
assert(editor.open_bindings && !editor.action);
|
||||||
|
no_action(click(surface, 200, 434)); // old secondary editor button is gone
|
||||||
|
no_action(click(surface, 290, 160)); // switch to Settings
|
||||||
|
editor = click(surface, 500, 160);
|
||||||
|
assert(editor.open_bindings && !editor.action && !editor.mount);
|
||||||
|
assert(click(surface, 680, 260).mount == Mount::Head); // still in Settings
|
||||||
|
no_action(click(surface, 100, 160));
|
||||||
|
|
||||||
// Long UTF-8, newlines and malformed bytes are bounded, paginated and navigable.
|
// Long UTF-8, newlines and malformed bytes are bounded, paginated and navigable.
|
||||||
p.transcript.clear();
|
p.transcript.clear();
|
||||||
|
|||||||
Reference in new issue
Block a user