VR keyboard controller bridge: no traffic while idle

The relay no longer enables the Runtime domain on systemui (bindings work
without it; it streamed the page's console and context events). Full-rate
frames only while a tip is within 10 cm of the keyboard or a trigger is
pulled (not for a laser resting on it); idle frames every 1 s instead of
250 ms. Nothing while the keyboard is hidden.
This commit is contained in:
Pierre Kisters committed 2026-10-01 03:33:07 +02:00
1 parent 224715d926
commit 63d589c261
4 files changed
+14 -12

No files matched your search

+3 -3
View File
@@ -219,9 +219,9 @@ to the laser only. Depends on Steam and SteamVR UI internals (see
animated `trigger` component (not yet verified). The
`vr-keyboard-controllers-relay` user service carries these frames to
Steam's `SharedJSContext`: up to ~90 Hz (45 Hz and more under load)
while a hand is within 10 cm of the keyboard, its laser is on it or its
trigger is pulled, else every 250 ms, and only while SteamVR shows the
keyboard. The laser hit matches SteamVR's laser to a few px.
while a hand's tip is within 10 cm of the keyboard or its trigger is
pulled, else every second; nothing while the keyboard is hidden. The
laser hit matches SteamVR's laser to a few px.
- **Touch typing** (patch `vr-keyboard-touch`, `SharedJSContext`): per hand
`tracker.js` turns the tip's path into press and release, then calls the
keyboard's own `HandleTouchStart` / `HandleTouchEnd` with the key under
@@ -17,18 +17,18 @@
// the laser's hit on the keyboard (geometry.js), the trigger from the
// render model's animated "trigger" component (null without one).
// Frames go out through the CDP binding __sfuiCtlOut(json) at TICK_MS while
// a hand is relevant (tip within NEAR m of the keyboard, laser on it, or
// trigger pulled), else every IDLE_MS; one { keyboard: null } frame when the
// keyboard goes. Frame (hub.js adds page px):
// a hand is relevant (tip within NEAR m of the keyboard or trigger pulled),
// else every IDLE_MS; one { keyboard: null } frame when the keyboard goes.
// Nothing at all while the keyboard is hidden (one DOM lookup per POLL_MS). Frame (hub.js adds page px):
// { seq, t, moving, keyboard: { width } | null,
// hands: { left, right: null | { tip: { u, v, d }, ray: { u, v, dist } | null,
// trigger: 0..1 | null } } }
// Never touches SteamVR's input or the lasers. Debugging: __sfuiCtl.log,
// __sfuiCtl.last (the last frame).
((find, sigs, opts, hooks, GEO) => {
const VERSION = 2;
const VERSION = 3;
const G = window;
const TICK_MS = 11, IDLE_MS = 250, POLL_MS = 250, MOVE_QUIET_MS = 300, TIP_CACHE_MS = 5000;
const TICK_MS = 11, IDLE_MS = 1000, POLL_MS = 250, MOVE_QUIET_MS = 300, TIP_CACHE_MS = 5000;
const NEAR = 0.1; // m
const TRIGGER_DEG = 12.5; // frame controller: trigger component's full travel
const PROBE = 'sfui-ctl-keyboard';
@@ -136,7 +136,7 @@
return { tip: GEO.toKeyboard(pose.translation, S.kb), ray: GEO.rayHit(pose, S.kb), trigger: trigger(hand, m.rm) };
}
const relevant = (h) => !!h && ((Math.abs(h.tip.d) < NEAR && h.tip.u > -0.2 && h.tip.u < 1.2 && h.tip.v > -0.2 && h.tip.v < 0.7) ||
(h.ray && h.ray.u >= 0 && h.ray.u <= 1 && h.ray.v >= 0 && h.ray.v <= 0.6) || h.trigger >= 0.5);
h.trigger >= 0.5);
// ---- loops ---------------------------------------------------------------------
let active = false;
+2 -2
View File
@@ -8,8 +8,8 @@
// last the last frame (null: none / lost)
// Each frame's hand points (tip, ray) get the keyboard page's px: x, y
// (CSS px of the keyboard popup) and onKeyboard (inside the page), if the
// keyboard popup exists. Frames come every ~11 ms while a hand is near the
// keyboard, its laser on it or its trigger pulled, else every 250 ms, and
// keyboard popup exists. Frames come at up to ~90 Hz while a hand's tip is
// within 10 cm of the keyboard or its trigger is pulled, else every 1 s, and
// only while SteamVR shows the keyboard (then one with keyboard null).
(() => {
const VERSION = 1;
+3 -1
View File
@@ -46,7 +46,9 @@ async function connect(side) {
if (m.method === 'Runtime.bindingCalled' && m.params.name === cfg.binding) forward(m.params.payload);
};
const closed = new Promise((res) => { ws.onclose = res; });
if (cfg.binding) { send('Runtime.enable'); send('Runtime.addBinding', { name: cfg.binding }); }
// No Runtime.enable: bindings work without it, and it would stream the
// page's console and context events here.
if (cfg.binding) send('Runtime.addBinding', { name: cfg.binding });
live[side] = (expression) => send('Runtime.evaluate', { expression });
console.log(`${side}: connected`);
await closed;