mirror of
https://github.com/lhns/steam-frame-nix.git
synced 2026-10-06 03:00:13 +02:00
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.
60 lines
2.4 KiB
JavaScript
60 lines
2.4 KiB
JavaScript
// hub.js: the controller frames in Steam's SharedJSContext
|
|
// (vr-keyboard-controllers.nix). A single expression for consumer patches'
|
|
// extraArgs (like lib/hooks.js): installs window.__sfuiControllers unless an
|
|
// equal or newer VERSION is there (a newer one takes over the subscribers)
|
|
// and evaluates to it. relay.mjs calls frame(f) with bridge-patch.js's
|
|
// frames and lost() when the SteamVR side goes away.
|
|
// subscribe(fn) -> unsubscribe fn(frame) per frame, fn(null) when lost
|
|
// 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 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;
|
|
const G = globalThis;
|
|
const have = G.__sfuiControllers;
|
|
if (have && have.version >= VERSION) return have;
|
|
const subs = have?.subs ?? new Set();
|
|
const errors = have?.errors ?? { count: 0, last: null };
|
|
|
|
let win = null, winAt = -Infinity;
|
|
function keyboardWindow(now) {
|
|
if (now - winAt < 1000 && win && !win.closed) return win;
|
|
winAt = now;
|
|
win = [...(G.g_PopupManager?.GetPopups?.() || [])].find((p) => p.window?.document.querySelector('[data-key]'))?.window ?? null;
|
|
return win;
|
|
}
|
|
const px = (p, w, h) => {
|
|
if (!p) return p;
|
|
const x = p.u * w, y = p.v * w;
|
|
return { ...p, x, y, onKeyboard: x >= 0 && y >= 0 && x < w && y < h };
|
|
};
|
|
function deliver(f) {
|
|
hub.last = f;
|
|
for (const fn of [...subs]) {
|
|
try { fn(f); } catch (e) { errors.count++; errors.last = String(e?.stack ?? e); }
|
|
}
|
|
}
|
|
function frame(f) {
|
|
if (!f || typeof f !== 'object') return;
|
|
const now = performance.now(), w = f.keyboard ? keyboardWindow(now) : null;
|
|
if (w?.innerWidth) {
|
|
for (const h of ['left', 'right']) {
|
|
const x = f.hands?.[h];
|
|
if (x) f.hands[h] = { ...x, tip: px(x.tip, w.innerWidth, w.innerHeight), ray: px(x.ray, w.innerWidth, w.innerHeight) };
|
|
}
|
|
}
|
|
f.receivedAt = now;
|
|
deliver(f);
|
|
}
|
|
const hub = {
|
|
version: VERSION, subs, errors, last: null,
|
|
frame, lost: () => deliver(null),
|
|
subscribe(fn) { subs.add(fn); return () => subs.delete(fn); },
|
|
};
|
|
G.__sfuiControllers = hub;
|
|
return hub;
|
|
})()
|