Files
lhns--steam-frame-nix/modules/vr-keyboard-controllers.nix
T
Pierre Kisters af32cf72ce VR keyboard swipe with two controllers: the path from the controller bridge
With both lasers on the keyboard SteamVR forwards per poll only one laser's
movement, so the pressing one may get no touchmoves and its swipe became a
tap. keyboard.vr.swipe.twoHanded (default on; turns on the controller
bridge): a press is attributed to the hand whose laser hit is nearest to it
(within 30 px, the trigger as a tiebreak; without recent frames the first
frame after the press decides, within 60 px). The path comes from runs of
one source: the touch's own moves while they flow, that hand's laser hit
per bridge frame once they pause for 60 ms, touchmoves again if frames
pause. A touchend disagreeing with the laser during a bridge run ends at
the laser. Touchmoves on the other hand's laser move the attribution.
Without fresh frames: touch events only, as before. A second press during
a swipe stays Steam's tap (one text field, one word at a time).

The bridge sends full rate only on demand now: hub.js demand(ms) goes
back through the relay (CDP binding __sfuiCtlIn) to systemui's
__sfuiCtl.demand; the swipe patch asks from the press to the release.

Tests: gesture-input with the two-controller recording and synthetic bridge
frames (a pressing hand without touchmoves, attribution with both lasers on
the keyboard, a stale bridge, frames stopping mid-swipe, re-attribution,
interleaved hover and presses); hub demand.
2026-10-01 03:45:47 +02:00

68 lines
3.0 KiB
Nix

# The controller bridge for VR keyboard features (internal option
# steamFrame.keyboard.vr.controllers.enable, set by the features using it,
# keyboard.vr.touchTyping and keyboard.vr.swipe.twoHanded): both controllers
# relative to Steam's VR keyboard, from SteamVR's dashboard to Steam's UI.
# - vr-keyboard-controllers/bridge-patch.js (SteamVR systemui, 8087; patch
# "vr-keyboard-controllers"): the keyboard's pose, per hand the tip (where
# the laser starts), the laser's hit and the trigger (geometry.js), ~90 Hz
# while relevant or asked for (demand); CDP binding __sfuiCtlOut.
# - vr-keyboard-controllers/relay.mjs (user service
# vr-keyboard-controllers-relay): to Steam's SharedJSContext; demand back
# (CDP binding __sfuiCtlIn there).
# - vr-keyboard-controllers/hub.js: window.__sfuiControllers there (page px,
# subscribe, demand); consumer patches take it as an extraArg.
# Tests: vr-keyboard-controllers/check.nix (flake check
# `vr-keyboard-controllers`).
{ config, pkgs, lib, ... }:
let
cfg = config.steamFrame.keyboard.vr.controllers;
uiLib = import ./steam-ui-patches/lib { inherit pkgs; };
checks = import ./vr-keyboard-controllers/check.nix { inherit pkgs; };
in {
imports = [ ./session.nix ./steam-ui-patches.nix ];
options.steamFrame.keyboard.vr.controllers = {
enable = lib.mkOption {
type = lib.types.bool;
default = false;
internal = true;
description = "The controller bridge (set by the VR keyboard features that use it).";
};
files = lib.mkOption {
type = lib.types.attrsOf lib.types.path;
readOnly = true;
internal = true;
default = { geometry = ./vr-keyboard-controllers/geometry.js; hub = ./vr-keyboard-controllers/hub.js; };
description = "geometry.js and hub.js, for consumer patches' extraArgs.";
};
};
config = lib.mkMerge [
{
steamFrame.uiPatches.patches = lib.optional cfg.enable {
name = "vr-keyboard-controllers";
endpoint = "http://127.0.0.1:8087";
target.title = "systemui";
patch = pkgs.runCommand "vr-keyboard-controllers.js" { nativeBuildInputs = [ pkgs.nodejs ]; } ''
: ${checks}
cp ${uiLib.mkPatch {
name = "vr-keyboard-controllers";
src = ./vr-keyboard-controllers/bridge-patch.js;
extraArgs = [ ./vr-keyboard-controllers/geometry.js ];
}} $out
node --check $out
'';
unpatch = ./vr-keyboard-controllers/unpatch.js;
};
steamFrame.session.services.${if cfg.enable then "restart" else "stop"} = [ "vr-keyboard-controllers-relay.service" ];
}
(lib.mkIf cfg.enable {
systemd.user.services.vr-keyboard-controllers-relay = {
Unit.Description = "Relay of the VR keyboard's controller frames (SteamVR dashboard -> Steam UI)";
Service = { ExecStart = "${pkgs.nodejs}/bin/node ${./vr-keyboard-controllers/relay.mjs}"; Restart = "always"; RestartSec = 5; };
Install.WantedBy = [ "default.target" ];
};
})
];
}