mirror of
https://github.com/DeeJanuz/frametop.git
synced 2026-10-15 09:00:42 +02:00
06cdb2ed8bd5a46d5d4a19b98696a3e3bbac49a2
59
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
06cdb2ed8b |
Resize from any corner, for hands, the gaze and lasers
User, 2026-10-10: Vision Pro-style resize from all four corners. Only the bottom right worked by laser (the .resize handle), and a resize kept the screen centred. - One resize handle per panel still (SteamVR's 128 overlays are tight: a third remote display once didn't fit), but it moves to the corner being aimed at (vr.cpp UpdateCorner): a laser's ray (a controller's, or the pointer helper's device, which the gaze and the hand ray drive) landing within a fifth of the screen's height (5-20 cm) of a corner, or a hand's pinch at one (handinput's corners()). Its texture turns for that corner, it brightens the closer the aim (from the controls' idle 0.55 to 1), and it's lit blue while resizing. With no aim it lingers 0.4 s, then goes back to the bottom right. - Any corner resizes and the opposite one stays put: screens and remote displays keep their aspect (the corner follows the diagonal, ResizeFromCorner, on the cylinder for curved ones: SurfacePose), floating windows take the pixels from that corner (ft-floatd's new size is placed keeping the opposite corner, keepX/keepY; every corner of a window resizes now, the title bar's ends included). - Near hands: a pinch at any corner resizes the same way (the carry now gives the pose as well as the width). - Gaze pinches (gazehand.h): with the pointer on a screen's resize handle or grab bar, the pinch is a press after 0.12 s still (POINTER_GAZE_CONTROL_HOLD), not 0.5: nothing to click there, and the hand drags the resize. The chrome overlays are announced to the helper as they're made, so its ray and the gaze find a new remote display's handle at once. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
99da5e8942 |
Hands: the touch zone's margin hides the ray but leaves pinches to the helper
The near report to the pointer helper ("near <ns> <L> <R>") was one flag
for two things: the hand's ray hides, and its pinches and grips are
ft-screens'. With the 0.075 m zone, its 2.5 cm leave margin and 200 ms
linger (there so the ray doesn't flicker at the edge) would still have
swallowed pinches made just outside the zone, the live test's complaint.
Now ft-screens says 1 when the hand is within the zone itself (or
pressing, grabbing, or waiting on "grab that"): its gestures are ours; 2
in the margin: the ray stays hidden, the gestures are the helper's (ray or
gaze); 0 otherwise. The helper takes 1 as the claim and 1 or 2 as near for
the ray; an older helper takes 2 as 1, as before. ft-screens' own pinch
and fist begins need the zone itself too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
8d3de8e13f |
Merge branch 'hands-gazechord' into hands-next: the gaze chord, hand input switches
Conflicts: - ft-pointer.cpp, the gaze pinch comment: typing's "the rules for all gestures hold (..., typing, ...)" kept, gazechord's chord and POINTER_GAZE_PINCH paragraphs added after it, and a line that a hand typing holds back makes no chord. - ft-pointer.cpp, the hands settings: gazechord's gazePinchOn, nearOn, handsTrack kept; typingHold dropped for typing's typing::Params. - ft-pointer.cpp, mayPoint: both conditions (handsOn, and not a side typing holds back). - ft-pointer.cpp, beginGazePinch: gazechord's busy line (a chord isn't busy), without the old sinceKey/typingHold lines. The chord's pinch goes through typing.h's Why in the gesture loop before startGesture, like any pinch, so a hand typing holds back can't make one. - docs/reference.md settings line: typing's and reach's keys, plus POINTER_GAZE_PINCH. - hands/README.md near input: typing's hold-back sentence and gazechord's HANDS_NEAR=on. - session/frametop.conf.example: reach's HANDS_GRAB_ROTATE* and HANDS_GRAB_THAT* lines, then gazechord's HANDS_NEAR line. Reconciled: gazehand's Situation loses sinceKey and typingHold, and Refused its "ignored (typing)": the helper had stopped setting them on hands-typing (typing.h holds hands back first), so that guard could never fire. gazehand-test's key checks now drive typing.h: keys hold a hand back (2 s now, was 1), POINTER_PINCH_TYPING=0 is no guard, and a chord's pinch by a hand on the keys is held back while one lifted off them goes ahead. The chord's own pinch counts as a gesture held for typing's "busy" (free if out of the keyboard zone when keys come), as the left's hold does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
24d2d12397 |
pointer: the gaze chord in the helper, and hand methods as their own switches
The helper sends the chord's right button (btn b) apart from the left's press, so both reach the panel; the chord ends with its pinch, its hand lost, the pointer going, a controller on its side, or the calibration panel. POINTER_GAZE_PINCH (1) turns clicks at the gaze off on their own. HANDS_NEAR=on runs the handoff for ft-screens' direct touch without POINTER_HANDS (HANDS_NEAR=1 keeps its meaning), and the relay feeds it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
56fb3b3d82 |
pointer: typing holds hands back: no pinches, grips or ray, and ft-screens hears it
The helper feeds typing.h the relay's keys and each hands frame. A hand it holds back makes no pinch, middle pinch or grip (ray, gaze or plain), and has no ray: one that drove the pointer drops it (to the mouse or the gaze, not the controllers) and can't claim it again until lifted off the keys. A hand pointing or holding a gesture out of the keyboard zone isn't held back by the other hand's keys. ft-screens gets "typing <L> <R>" on a change and every second. The old 1 s guards (beginHold, gazehand's) give way to it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
93adab7302 |
Hand ray: the torso faces the head and the hands (StereoKit's body model)
Ideas from StereoKit (MIT, 07b9f0d), credited where a formula comes from it: - The torso faces the head's forward counted twice plus the direction to each wrist seen (POINTER_RAY_TORSO_WRISTS, on), so looking aside while pointing swings the shoulders, and the ray, less. probe-moves replay: the shoulder's share of a still hand's ray motion 20 -> 18%, p90 2.0-3.7 -> 1.7-3.6 deg/s; the tap capture: nothing worse. - The ray can turn toward the hand's own direction, from the palm's axes (wrist, middle, ring and little knuckles) tilted -30/8 deg (POINTER_RAY_HAND_WEIGHT, 0). Off: every weight measured worse (at 0.3: judder 0.07-0.14 -> 0.11-0.33 deg rms per 90 Hz tick, raw jumps over 3 deg 159 -> 330). The gate still tests the shoulder's ray. - The knuckle point can be pinned to the palm's axes during a pinch, easing back on the steady shoulder's schedule (POINTER_RAY_ANCHOR, 0). Off: our pinch point is the knuckles already, and pinned it shook as much or more and kicked no less on release, fitted or not. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
ac5111c552 |
Merge branch 'experimental' into hands-full
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
9def5f1762 |
Gaze calibration panel: a gaze precision or gaze drag press takes the dot
While the panel was up, the helper took only "btn trigger 1" and "gazekey left 1" as "take this dot". A left button mapped to gaze precision or gaze drag sends "precision|gazedrag <source> 1" instead, which the panel dropped, so the click did nothing. The panel's answers move to pointer/helper/calpanel.h, with an offline test (pointer/test/calpanel-test.sh). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
e4c51ccf6b |
Pointer: the drawn hands' overlays aren't panels, so the hand ray and pinches pass through them
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
db2e633502 |
Merge branch 'hands-gaze' into hands-full
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
0d6b0c93b5 |
Hand ray: drags move smoothly and hold steady, and a quick tap stays a click
The ray's One Euro filters (new hands/include/fh_smooth.h) run in continuous time and the helper reads the ray every loop, so the pose sent to ft_pointer moves at about 100 Hz instead of jumping at the tracker's 15-30 Hz (the dashboard's dragger made those jumps jerky). The direction is filtered on the sphere. On the probe-moves replay the pose's second difference per 90 Hz tick went from 0.90-0.96 deg rms to 0.24-0.26 at the same lag. While a ray pinch is held the ray pivots on the shoulder as it was at the press, so looking around doesn't move what's dragged; the reach for push and pull is filtered. A pinch's first 0.3 s takes 6 deg of turn before it drags. One failed read of the gestures file no longer ends a drag, and the log says why a drag ended or the ray went off. Settings: POINTER_RAY_SMOOTH_MINCUTOFF, POINTER_RAY_SMOOTH_BETA, POINTER_RAY_SMOOTH_HOLD, POINTER_RAY_STEADY, POINTER_RAY_TAP, POINTER_RAY_TAP_DEADZONE. rayeval reads publish dumps and reports the smoothness at the display's rate. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
d348e0a260 |
Gaze + hands: look and pinch to click, pinch and move to nudge the pointer
With gaze mode and POINTER_HANDS on, the gaze is the pointer and the hand ray stays off (POINTER_GAZE_HAND_RAY=1 brings it back). A pinch clicks where you looked 75 ms before ft-hands saw it; held and moved past 4 deg of hand movement (POINTER_GAZE_PINCH_DEADZONE) it nudges the pointer at 0.3x (POINTER_GAZE_PINCH_GAIN), and the correction is a gaze lesson. Held still for POINTER_GAZE_HOLD it's a press the hand drags. A middle pinch right-clicks at the gaze. One log line per pinch. Rules in pointer/helper/gazehand.h, tested in pointer/tests/gazehand-test.cpp. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
7ccf34c042 |
Merge branch 'hands-view' into hands-full
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
3d1eee828a |
ft-handview: your hands drawn over games, the dashboard and the void with Room View off
Quest-style: a see-through light hand from the tracked skeleton with a bright rim, fading out behind the wrist, over everything. Each hand is a placed, side-by-side overlay facing the head, so it stays put as the head turns and redraws only as the hand moves. Hidden while a hand holds a controller (the pointer helper's word, now also on @ft_handview). frametop-handview.service runs it; nothing installs it yet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
07ce0d9d59 |
Hand ray: the laser stays with one hand, and a pinch clicks where it pointed
Live test 2026-10-09: taps took several tries and the laser jumped between hands. The owner's ray dropping for a moment (closing the fingers turns the hand) handed the laser to the other raised hand at once, for good; a pinch by the other hand pressed where the old owner's ray pointed; a pinch whose ray had just dropped became a press at a stale spot, or only woke the pointer. - The owner keeps the laser through POINTER_RAY_GRACE (0.5 s) of its ray being down; the other hand takes it by pinching, by raising its ray while the owner's is down, or after the grace (pointer/helper/rayowner.h). - A pinch on a ray that dropped within the grace, or is on its way up, brings it back (fh_ray.h ShowForPinch) and is a ray pinch. - Aim lock: the press goes along the ray from just before the fingers began to close, after a pose along it has gone out; a tap that ends before its press clicks there too. - Each ray follows one tracked hand through a brief flip of ft-hands' left/right call. - Log: owner changes with the reason, the owner's ray down and back, and one line per pinch saying what it clicked or why not. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
400fe0ca5f |
Pointer: ray gestures only with the ray still up; presses wait for a fresh wake
A gesture that waited for ft-screens' near report is a ray gesture only if its ray is still up when it starts; a ray pinch's press waits until a pointer the ray just woke is bound (350 ms). keys-test's pause stub knows the relay's new calls. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
d16f774d50 |
Hands: park hand tracking during VR games instead of stopping it
With hand interaction on (POINTER_HANDS=1), the hands have to be there when the dashboard opens during a game (design section 5.5): - ft-hands: while /run/user/UID/frametop-hands/park exists it processes no frames (models stay loaded), publishes no hands, ends gestures as lost, and writes 0 to hands-want every second. - ft-camd: stops copying the mono frames while hands-want is fresh and 0 (a dead ft-hands' stale file doesn't starve other ring readers). - The input relay (game_pause.py) leaves the hand services running when it pauses for a game and parks them instead; it unparks while the dashboard is open (the helper's new "vrdash 1|0") and on resume. - hands/README.md: gestures v3, parking, and the hand ray. - pause-test.py: parking, and "paused" kept off the live helper. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
5d96d211ec |
Pointer: the hand ray drives ft_pointer; ray pinch, push and pull
With POINTER_HANDS=1 and POINTER_HAND_RAY=1 (design sections 2, 5): - Per side, each hands frame goes through fh_ray's gate; the first ray up drives the pointer (a pinch by the other hand's ray takes it). It wakes the pointer and claims the laser unless a controller pressed within 2 s. The mouse takes the pointer back; the ray returns after 1 s of no mouse. - The pose is the ray's origin and direction (a visible beam); our dot only where it meets a panel, no catcher in free space. A controller taking the laser hides our dot until a ray pinch claims it back. - A ray pinch is the left button, 30 ms after a claim it had to make; the direction holds until POINTER_RAY_DEADZONE (2.5 deg), then drags. A middle pinch (fh_gestures v3) is a right click, or scrolling when held and moved. Gaze pinches and grips are unchanged with the ray down. - Push and pull while carrying (POINTER_RAY_PUSH): the origin moves along the ray, distance d0 (h/h0)^3. - ft-screens' "near <capture_ns> <L> <R>": a gesture waits for the report on its frame (60 ms at most) and is left to ft-screens if near. - The ray lets go of a pointer it woke POINTER_RAY_RELEASE after it went; at once when its side's controller is in use or in a game. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
564bb4bdc5 |
Pointer: treadmill role by default, and per-hand last used wins
- The driver's default role is the treadmill (4), hinted from Activate; the helper's POINTER_ROLE defaults to treadmill. Needs the driver reinstalled and a SteamVR restart to take effect. - The "no hand role" release skips a device whose hint is the treadmill. - Hand gestures: none begins on a side whose controller is in use, or by a hand at a controller (by position); a held one ends, as lost, when its side turns controller. - The moving-controller release no longer applies while a bare hand's ray drives the pointer (that ray goes only when its own side turns controller). - update-check.py: the virtual controller's path, and the touch components. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
742bd32c32 |
Pointer: handoff arbiter for hand interaction (log only)
Per side, a controller used recently wins, else the bare hand (pointer/helper/handoff.h, design section 1): touch and presses from the input relay, motion, charging and tracking from SteamVR, and the tracked hands from ft-hands' hands file (handsin.h, which also takes the gestures reader and the head pose history out of ft-pointer.cpp). - The relay's web socket reader (game_pause.py TouchTracker) sends "ctl <side> touch 0|1", "ctl <side> press", "ctl <side> gone" and "ctl ws 0|1" with POINTER_HANDS=1, and the pause state as "paused 1|0". - The helper logs transitions and sends ft-screens "hands <L> <R> <allowed>" (hand|controller|off, 0|1) on a change and every second. Nothing acts on it yet. - fh_gestures v3 adds the middle pinch slots (written as zero for now). - Tests: pointer/tests/handoff-test.cpp (just test-c), input/test/touch-test.py. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
2a675b9121 |
Remote displays: other computers' monitors as Frametop screens
A remote display is a screen of ft-screens whose picture comes from another computer, streamed by Vibepollo with Moonlight's protocol. ft-screens (screens/remote.c) runs one ft-stream per remote screen (101 and up) over a socket pair: ft-stream (stream/, GPLv3: moonlight-common-c and moonlight-embedded's libgamestream, pinned) decodes on the iris decoder into a ring of three RGBA buffers that ft-screens shows as a panel with a screen's controls, layout, profiles, curve, pins and hand cutouts. Mouse, keys and scroll go back to the host; a button's release goes through the stream it was pressed on, as Vibepollo wants, and a press carried onto another remote panel moves the pointer there. A panel out of sight gets 10 fps; the host's sound plays from one stream per host. ft-stream signs in with an API token and the host's pinned public key (stream/host.cpp), and chooses its path: a Steam Link dongle on the Frame's hotspot when it answers, or the network (the layout's "route": auto, network or dongle). ft-layout gets "remote" commands (list, add, connect, disconnect, host route and direct), starts the connected displays with the desktop and keeps disconnected ones parked. ft-gaze and ft-pointer know the remote panels. ft-screens now shuts its VR side down before its streams and waits for SteamVR before exiting (a vrcompositor crash at quit, in standby). docs/remote-displays.md has the design, the spikes and the test results. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
8b9edcfbe2 |
Pointer: a click the pointer dropped as it went off isn't a right click later
In gaze mode a mouse press is held back while you aim, and its release clicks. A release of the right button sets pressRight with the click, so the click goes out as a right one. When the pointer goes off in the same loop (the relay's stand_down sends the release, then "hide", as Frametop pauses), the click is dropped, but pressRight stayed set. The next press after resume, from the mouse, Meta+J or a pinch, then went out as a right click. Dropping the click now forgets pressRight with it. A "hide" read a loop after the release still comes too late to stop the click; that needs the helper's socket to be full as the pause starts, and is noted in the relay's stand_down. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
4828dd46b5 |
Pointer: a release never wakes the pointer
Any "btn" or "scroll" from the relay woke a pointer that was off. When Frametop pauses for a game, the relay's stand_down sends the releases of the clicks held into the pause, and with its pointer already off (the idle timeout or pointer_toggle during a press) it used to send them with no "hide" after. The helper woke on them, connected the virtual controller and took the right hand role during the game. Now a button release or a scroll back to 0 0 doesn't wake it. The release is still handled as before (it ends its press, or goes to the driver), so the driver doesn't reconnect later with the button down. Outside a pause this changes one case: a mouse button let go after a moving controller released the pointer mid-press. That release used to reconnect the controller, recenter it and release there; now it updates the disconnected controller, and ft-screens still gets its "up". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
9d7c8a0b91 |
Experimental (#29)
* Input relay: typing with the pointer helper down no longer ends the relay Typing on a pass-through keyboard tells the helper "typing". With the helper not running (SteamVR off), that send raised ConnectionRefusedError and the relay exited, dropping every grab until systemd restarted it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * ft-steam: open Steam's menu through Steam's own UI steam/ft-steam menu opens the SteamVR dashboard on Steam's menu, or closes the dashboard if it's up, without pointer mode: it asks Steam's UI over its debugging port to show its dashboard overlay (ShowVROverlay, what Steam calls itself) and focus the Steam frame's left menu (MenuStore.OpenMainMenu). ft-steam check says whether those calls still exist, and update-check.py runs it, since a Steam client update can rename them. The CDP client moves from display-settings/steam_settings.py to steam/steamui.py, so both use it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Shortcuts: Steam menu, commands, and modifier taps Key combinations (and mouse and controller buttons) get two new actions: - steam_menu: Open Steam menu / close dashboard (steam/ft-steam menu). - command:CMD: run CMD with sh -c, as the relay's service, with layout/, float/ and steam/ on its PATH. Input Settings offers it for key combinations as Run a command... Both work without pointer mode. A modifier on its own is now a key combination too: a tap, pressed and released with no other key, mouse button, or scroll in between. A bound tap sends the desktop F24 before the release, so Plasma's launcher stays shut. The defaults gain a Meta tap for the Steam menu; this replaces META_DASHBOARD, which only worked in pointer mode. input/test/keys-test.py runs the relay against fake devices with every outgoing socket renamed, so it's safe next to the live relay. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Relay: share a key combination's Meta release with frame-voice A Meta+key combination (Meta+J for gaze_left, say) hides Meta's release from the desktop, and the relay skipped share_key for it too. frame-voice saw Meta go down on @frametop_keys and never come up, so it held all dictated text back, waiting for that release. Keys of grabbed keyboards are now shared as pressed, before key_binding() decides what the desktop gets. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Hands: ft-cutouts, the hand cutouts without pinches and grips hands/ft-cutouts on|off|status starts ft-camd and ft-hands as transient user units with ft-hands' new --no-gestures: hands are published for ft-screens' cutouts, but no pinch or grip is detected, so nothing clicks or drags and a closing hand doesn't raise the tracking rate. It needs a build and ft-camd's capabilities, not hands/run.sh install. Its units conflict with ft-handsctl's, so each stops the other, and they stop with SteamVR. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * ft-cutouts status: only the current run's tracker lines Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Gaze: say why gaze mode can't work yet, and open the calibration whenever it's missing Turning gaze mode on without a calibration opened Calibrate only on the off-to-on change, and only if it could open right then. With the headset off, the eye tracker silent, or the panel not built, or with gaze mode already on when the gaze service started, nothing opened and nothing said why: the pointer just stayed a mouse. - The gaze service now checks every second: gaze mode on, no calibration for the tracker in use, eyes seen -> the full calibration opens. One that closes unfinished opens again only after the headset comes off and on, gaze mode off and on, or Calibrate, so it doesn't loop. A start that fails retries every 10 s. - Its status says why gaze mode can't work yet (checks.problem): not calibrated and opening, open, closed unfinished, or can't open and why. - Input Settings shows that under the Gaze pointer switch, along with the gaze service not installed or not running and our tracker missing its frame grabber. - ft-gazectl on notes a missing calibration. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Gaze: install our eye tracker, prefer it, and say why a calibration dot wasn't taken A user on a fresh install got "Calibration failed: only 0 of 21 dots" with no reason. The installer never installed our tracker, so gaze used SteamVR's, and the only way SteamVR's tracker rejects a dot is losing an eye for most of the look. The panel just showed a red ring. - install.sh: step 9/10 installs our tracker (gaze/tracker/install.sh) after gaze mode, yes by default; it needs sudo, so --yes runs it only when sudo won't prompt. If it fails, gaze keeps SteamVR's tracker. Configs that still say GAZE_TRACKER=steam (the old template) are asked whether to switch. - GAZE_TRACKER=auto, the new default: ours when it's installed (the frame grabber, its unit, and ft-eyes' Python), else SteamVR's. ft-gazed rechecks every second, so installing it switches over. Input Settings lists Own tracker first as recommended, and says how to install it when it's missing (checking the host's /etc through /run/host from the dev container). - The calibration panel has a note line, orange over the instructions: why a dot wasn't taken (an eye lost, a blink, the eyes disagreeing for SteamVR's tracker, from steady_samples' new drop counts; ft-eyes' reply for ours), what a click is still waiting for after 1.5 s, and a failed calibration's most common reason, which the Gaze page shows too. steady_samples keeps the same samples as before (checked on 2037 windows of recordings); a lost eye is named before a blink, since its openness reads 0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * README: link the Frametop Discord Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Wait for a new container to finish setting up before entering it container-up.sh starts the dev container in a scope of its own, so distrobox enter finds it running and skips its wait for distrobox-init. On a fresh install, init was still setting up passwordless sudo when dev-container.sh ran sudo dnf install, and sudo asked for a password with no terminal to read it from. container-up.sh now waits for container_setup_done itself, and the container's sudo calls use -n, so a password prompt fails at once with a clear message. Fixes #9 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Prevent small desktop overlay pointer movements from starting a drag * Clear reported drag state on controller release * Click stability: only a hand controller's press starts it The 3D mouse drives SteamVR's laser through the ft_pointer virtual controller, so its events reach the screens the same way a controller's do. The filter held every press, which turned the mouse's short drags (selecting a character or two, nudging a slider) into clicks. Mark button events from hand controllers and start the filter only on those. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Input relay: retry a new device until udev gives it to the input group A new /dev/input node is root:root 0600 until udev applies GROUP=input. The scan probed each new node once and marked it seen even when the open failed, so a node caught in that gap was never opened. Behind a KVM, a switch brings back a hub of devices at once: on the Frame, four nodes failed with EACCES in one switch, the keyboard was never grabbed, and its keys went to gamescope instead of the desktop screens. A node that isn't readable yet now waits for the next scan. * Screens: take a screen's overlays from one copy in the catcher While a button pressed on a screen is held, UpdateCatcher checks every tick whether the laser is still on one of the screen's overlays. It built that list from s.All().begin() and s.All().end(), but All() returns a std::array by value: iterators into two different temporaries, which is undefined behaviour. A clang build of ft-screens got a garbage length, threw std::length_error, and aborted on the first click, taking KWin and the desktop with it. * Gaze: leave SteamVR's gaze action alone during VR games From curiousjtuber's PR #13: with the gaze service running, SteamVR restarted its eye tracker every 10 to 13 s of Beat Saber, as if the headset came off, and each restart took input focus from the game. The PR stopped every read in a game. Only the action path reaches SteamVR (UpdateActionState on the gaze set at overlay-global priority, then GetEyeTrackingDataRelativeToNow); the mmap and our tracker are read-only files. So only the action is skipped while a scene app runs, and gaze keeps moving the pointer over the dashboard in a game. The action source is only used with --source action. Co-Authored-By: CuriousJ <curious.j.tuber@gmail.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Hands: --record-hz, and the hand recorder's design (hands/rec/DESIGN.md) ft-hands --record-hz N records at most N frame sets a second, for the hand recorder (10). DESIGN.md lays out the recorder: the headset panel, the session runner and its script, the files, review and export, consent. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Hands: ft-handpanel, the hand recorder's headset panel A head-locked SteamVR overlay for the hand recorder (hands/rec/DESIGN.md): 1.2 m ahead, 12 degrees up, 36 degrees wide, drawn with stb_truetype into three shared DMA-BUFs as ft-gazepanel does. It shows the title, step, wrapped instruction, note, countdown, hand chips, near/far bar and a "Paused" cover, driven over @ft_handpanel. It also places the touch target, a 2 cm dot in its own overlay fixed in the room where the head was at the first command for that point, and logs head and controller poses to poses.jsonl at 250 Hz from a thread of its own. Both threads take one lock around OpenVR calls. --no-vr prints each picture's state to stdout (and --dump writes the pictures), for testing without a headset. hands/rec/build.sh builds it in the dev container. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Hands: the recorder's worn check goes by the panel's backlight, as frame-job does Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Hands: the hand recorder's session runner and guided script hands/rec/session.py runs a recording session from script.json: it starts ft-camd and a tracking ft-hands as transient units only if they aren't running, records each section as one take (ft-hands --record-only at 10 sets/s, a new sets-N.bin after each pause), drives ft-handpanel, and writes session.json, calibration.json (identifying fields removed), prompts.jsonl and take.json. Feedback comes from the live hands file and, in the controller sections, from the panel's device poll. It also runs from the command line (--dry-run, --speed, --ring, --no-start). hands/rec/script.json: 11 sections, about 9 minutes without the object and controller sections. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Hands: the hand recorder's window, review and export hands/rec/ft_handrec.py + main.qml (Kirigami, dev container; host launcher hands/rec/ft-handrec): consent (CONSENT.md, asked again when its version changes; profile.json with a random contributor id), the before-you-start checklist with the lighting and free-space checks, the session controls (Space pauses, Esc stops), review with a frame-set viewer that deletes ranges, takes and sessions, export with progress and cancel (warns while the headset is worn), and the upload page (UPLOAD.md, the huggingface-cli command; HF_DATASET is a placeholder). --dry-run runs sessions without processes, for testing. hands/rec/takes.py (standard library): indexes sets.bin and sets-N.bin without reading pixels, reads one set's cameras, keeps deleted ranges in take.json, and exports: deleted sets left out, zstd -10 -T2 at nice 19, manifest.json and SHA256SUMS, nothing left behind on cancel. CONSENT.md and UPLOAD.md are drafts pending a legal review; the window says contributions aren't open yet. The dev container gains zstd. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Hands: upload from the hand recorder's window, export checks, a rehearsal hands/rec/validate.py (standard library; Linux and Windows, Python 3.12+) checks an export before upload and when it's received: SHA256SUMS, an allow-list of files, the manifest's schema and keys, the consent version, a uuid4 contributor, no identifying fields in calibration.json or device.json, every sets.bin.zst decompressed to its end as a stream with each FHSET01 header checked against the manifest, jsonl lines, the total size. It decompresses with compression.zstd, zstandard or the zstd program. validate.py DIR [--json]. hands/rec/hub.py uploads an export with huggingface_hub, as a pull request to contributions/<contributor>/<session>: validate first, refuse a repeat of the same export, check the login (whoami) and access (auth_check), upload_folder(create_pr=True) with the manifest summary as the description, then record the PR under "uploads" in session.json. Errors are explained (terms not accepted, not found, 401/403, network). --dry-run makes no network calls. FT_HANDREC_DATASET overrides HF_DATASET (DeeJanuz/frametop-hands); while the texts are drafts a real upload needs FT_HANDREC_ALLOW_UPLOAD=1. The Upload page shows the login with "Check again" and how to run hf auth login in a terminal (the token never enters the window), then Upload with a phase, progress and Cancel (hub.py as a child process), the PR link, and a warning for an export uploaded before. The manual command stays as the fallback. ft-handrec --hub-dry-run. session.py also saves device.json: cv.cad_from_cal and head from /persist/device_config.json, the labeller's shape, nothing identifying; export copies it. Session ids with a -N suffix are accepted everywhere. hands/rec/rehearse.sh runs it all without the headset: ft-ringplay plays 30 s of a capture into a ring, session.py records a short test script with ft-handpanel --no-vr and a tracker, then export, validate and a dry-run upload (--repo ID uploads for real). It runs in one frame-job scope, deletes its data and stops its processes, also on Ctrl+C. hands/rec/tests/test_validate.py covers good and broken exports and hub.py without the network. The dev container gains python3-huggingface-hub. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * README: Frametop doesn't work on the SteamOS beta yet Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit |
||
|
|
0ac2b10cd7 |
Remove leftovers before a release
- gaze/tracker/lab/eyes_track.py: nothing used it - Input Settings: the pointer role backend, whose page was removed - ft-screens: no log line for every floating-window resize - hands: ft-hands --help gives the palm-down default (1: off), the uninstall removes ft-handsctl's link, .frame-job is ignored - Display Settings: "Save as profile…", not "Save current arrangement" - two stale comments (ft-pointer's grabprobe, ft-gaze) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
964c250858 |
Gaze mode: a double right click or double Meta+K pans and tilts a drag
A drag begun by right during the left's held-back press (or Meta+K during Meta+J's) now lasts while either button or key is held, so pressing the right one again is free: it tilts, as a right press does during any mouse drag. Meta+K during a keyboard drag (held still into one, or the second Meta+K) tilts while held: the head turns the panel, and the mouse can too; let go and the head drags again from there. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
1f46616c75 |
Gaze mode: the mouse's buttons work like Meta+J and Meta+K
The right button's press is held back like the left's: hold it, the pointer stops where you look, move onto the target, and the right click comes on the release (held still, it's a real right press). Right during the left's held-back press now starts a drag where the pointer is, as Meta+J then Meta+K does, in place of a right click there; letting go of either drops it. So once you've moved, the left alone only clicks, and the right starts a drag. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
a2a5a4c98e |
Gaze mode: the mouse only corrects, and left-then-right right-clicks
POINTER_GAZE_MOUSE_MOVE=held, the default (the Gaze page's Mouse movement switch, with why it's on): while the gaze has the pointer, moving the mouse does nothing; it moves the pointer only while a button is held, as a correction. A bumped or drifting mouse can't pull the pointer off what you're looking at, and every mouse move is a correction, so lessons aren't polluted by mouse moves to somewhere else. With the gaze stale for a second, in a game, or with the headset off, the mouse moves the pointer as usual. "free" is the old behaviour. Pressing the right button while the left one's press is held back right-clicks where the pointer is instead (correct with the left, then right-click); both releases are then nothing. Meta+J then Meta+K stays a drag. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
8998fc1ddd |
Gaze learning limit: 55 degrees, past it a quick check; a solid panel
Drops the quick check's second step (
|
||
|
|
362968198b |
Quick check: put the pointer on the dot, and one limit for learning
After the quick check's capture, in gaze mode, the dot stays where it is in the
room (the panel's new "lock") and the gaze has the pointer again. You put the
pointer on the dot as you'd correct a click, with the mouse or Meta+J and the
head. That click clicks nothing: the helper sends it as "calverify", learned
whatever its size. Over POINTER_GAZE_NUDGE_MAX, the capture runs again, up to
3 times. A right click or Meta+K skips it.
POINTER_GAZE_NUDGE_MAX is now the one limit: 15 degrees by default (was 8),
and ft-gazed's own limits (8 for SteamVR's tracker, 25 for ours) follow it. A
keyboard correction past it isn't learned but opens a quick check ("recheck"),
in place of the 30 degree keyboard limit.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
cbad7bc470 |
Keyboard corrections are learned up to 30 degrees
A Meta+J hold's correction is always meant, but it went through the mouse's 8 degree nudge limit. Live, our tracker was 12 degrees off and every correction was dropped without a word. Debug output now says when one is. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
c4c10e6d92 |
Gaze checks and calibration in a panel fixed to the headset
ft-gazepanel (gaze/panel) is a SteamVR overlay that stays put in your view and shows dots at known head-relative directions; the gaze service runs it and drives it (gaze/gazecheck.py): - a one-dot quick check 3 s after the headset goes on, when our tracker asks for a click (reseat), at most every 2 minutes, and from Quick check on the Gaze page; five dots follow if the next 3 lessons are still over 2 degrees off - the full calibration (the probe's three rounds, dark to bright) when gaze mode comes on without one, or from Calibrate; quitting it with still no calibration turns gaze mode off Each dot takes the gaze once it has held still for 0.6 s; a left click or Meta+J takes it at once, a right click or Meta+K closes the panel (the pointer helper hides its dot and passes those presses on while "calpanel" lasts). Our tracker gets clicks and its own calibration; SteamVR's gets lessons per eye, or a new calibration.json. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
dc3a1b8208 |
Keyboard clicks at the gaze: Meta+J and Meta+K
gaze_left and gaze_right (Meta+J and Meta+K by default) click where you look.
A tap clicks where the dot was at the press and tells the gaze tracker it was
right there. Held, the pointer stays put in your view, so turning your head
carries it onto the target; the release clicks there and the correction is a
lesson (judged by the net correction, not the head's path). Held still for
POINTER_GAZE_HOLD it's a real press that the head drags, and Meta+K while
Meta+J aims presses where the dot is now, to correct and then drag.
A Meta combination now also sends the desktop an F24 press and Meta's release
at once: Meta's release no longer opens Plasma's launcher, and the click isn't
Meta+click (KWin's window move and resize, which swallowed right clicks). The
desktop gets Meta back for the next key if it's still held.
Also fixes
|
||
|
|
e16b7588e2 |
Gaze mode is a mouse feature: drop its controller parts
Steam reads the Frame controllers itself, outside SteamVR's bindings, and every press and release it sees takes SteamVR out of laser mode, so controller clicks at the gaze can't be done cleanly (docs/gaze-controllers.md, from the gaze-first branch's tests). - Gaze precision and Gaze drag take a mouse button or a key combination only; the controller source, its aim steering, and POINTER_PRECISION_GAIN, POINTER_PRECISION_DEADZONE and POINTER_GAZE_DRAG_GAIN are gone. - The gaze actions (including Gaze pointer on/off) can't be mapped to controller buttons: the relay ignores them there and doesn't ask the helper for those buttons, and Input Settings no longer offers them. - Last used wins in gaze mode too: picking up a controller hands it the laser, as docs/design.md already said. - The gaze dot shows all the time (POINTER_GAZE_DOT=always, the default; moving brings back the old behaviour), with a switch on the Gaze page. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
d666fa031f |
Gaze first: precision and drag buttons, key combinations, pointer role
Two new actions for mouse buttons, controller buttons, and key
combinations: gaze_precision (hold: the pointer stops where you look and
the button's device steers it, a controller by where it points at
POINTER_PRECISION_GAIN, the mouse by its moves; release: click there) and
gaze_drag (the same with a real press at once, dragging until the
release). The relay sends "precision|gazedrag <source> 1|0" to the
helper, which runs them through the same holds as pinches and grips.
- Key combinations on any keyboard ("key_bindings" in the rules, e.g.
Ctrl+Alt+G for gaze on/off): the last key isn't typed.
- POINTER_GAZE_MOUSE: in gaze mode the left button is a precision button
(the default, as before) or clicks right away (direct).
- In gaze mode a moving controller no longer takes the pointer away.
- POINTER_ROLE (right, left, stylus): the driver takes a "role" command,
so the pointer can stay off the hand holding the precision controller,
which would otherwise take the role back. Needs the rebuilt driver.
- Input Settings: the actions on the Controllers and Buttons pages; on the
Gaze page the mouse choice, the role, the precision sliders, and key
combinations (captured from any keyboard).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
c2739cb0df |
Hands: park hand tracking behind ft-handsctl
Hand tracking no longer starts with SteamVR: hands/run.sh install leaves the units disabled and links hands/ft-handsctl into ~/.local/bin, which turns it on and off (on | off | status | log | cutouts on|off | gestures). With it on, hands show through the screens; pinches and grips move the pointer only with POINTER_HANDS=1, now off by default. ft-camd's service runs the mono cameras only: while the headset is worn, the colour module writes just a half-size image into the top-left quarter of its buffers, which ft-camd can't use yet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
5ca4160a7e |
Hands: fixes from the second headset test (pinches)
- The palm-down filter is off by default: the user's deliberate pinches, hand raised, read 0.90-0.99, like typing. - Typing is told apart by the keyboard instead: the input relay sends the pointer helper "typing" on key presses, and it takes no pinch within POINTER_PINCH_TYPING (1 s) of one. - Grips are still held to hands raised (POINTER_GRIP_BELOW, 0.35 m below the eyes); pinches aren't, since the user's own sat 0.35-0.45 m below, elbow resting. - A grip doesn't begin with the thumb on the index tip: that's a pinch with the other fingers curled, which was taken for a grip. - A pinch's point is the index and middle knuckles: the tips' midpoint moved 1-2 cm as the pinch opened, dragging every release off its press. - ft-hands --gesture-log prints what the detectors measure, 10 times a second. Pinches still aren't reliable enough to use; hand tracking is parked for now in favour of the controllers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
fbd188f5f9 |
ft-pointer: without gaze mode, a pinch is a real press
Pressed when the pinch closes, released when it opens, its hand dragging the pointer in between (at the pinch gain, past the dead zone), like the mouse's button. That's what the gaze probe's Click practice needs: it freezes its own gaze dot at the press and drags it by the pointer's movement until the release. With gaze mode on, the pinch still holds the press back and clicks on the release. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
6d1b04e032 |
ft-pointer: pinch to click, grip to drag
The pointer helper reads ft-hands' gestures. A pinch holds the pointer where the gaze put it and clicks on the release; held, the hand nudges the pointer at half its angle past a 1.5 degree dead zone, which also teaches the gaze tracker. A grip presses where the pointer is, drags with the hand, and releases when the hand opens, unless it began more than 30 cm below the eyes (hands on a desk). Hand movement is taken in the room with the head pose at capture time. Hand use keeps the pointer from the relay's idle release, as gaze mode does. POINTER_HANDS and friends in frametop.conf. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
4493b789fd |
Merge floating-windows into experimental
Floating windows join the keyboard and the hand cutouts. Floating panels don't get cutouts yet: their panel and popups show crops of the client buffer (texture bounds), which the side-by-side cutout buffer doesn't match. KWin gets both the spare outputs and our input method, and the pointer helper's frametop. prefix already covers the float panels. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
5714978e65 |
Add a keyboard for text fields on the desktop
Fixes #7: SteamVR's keyboard never came up for the desktop's apps, and opening it for our panels doesn't work well on the Frame (it's Steam's own panel, mounted in the dashboard's scene, it follows the laser between panels, and it takes the controllers over to SteamVR's laser). - KWin starts input/ft-textinput as the desktop's input method. It tells the input relay when a text field gains or loses focus, and the relay asks ft-screens to open or close the keyboard. The session drops the QT_IM_MODULE=xim and GTK_IM_MODULE=xim that the gamescope session sets, or Qt and GTK apps never report text fields. - The keyboard is ft-screens' own panel (screens/keyboard.cpp): a US laptop layout, typed with a controller's laser or the 3D mouse. It opens 0.7 m in front of you, below your eyes and facing you. It has a grab bar to move it, a Close key, latching Shift, Ctrl and Alt, and repeat on a held key. It's drawn into shared DMA-BUFs, so it doesn't flicker. Its keys reach the focused screen as key presses, so every app takes them. - It steps aside while the Steam menu or Steam's own keyboard is up and comes back after. A layout reset closes it, and it doesn't open without a head pose. - Frametop Input Settings has a Keyboard page: open it for every text field, only while no keyboard is connected (the default), only from a mapped button (the new Open/close keyboard action, for mice and controllers), or never. A switch keeps it open until you close it. - The pointer helper treats every frametop.* overlay as a real panel. The keyboard's shared texture reports 0x0 like SteamVR's scene-graph controls, and the helper had given it their wide catch radius. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
2a9fbdebb4 |
Start Frametop's SteamVR clients only once SteamVR is up (#6)
The pointer and gaze services need steamvr.service to be running (Requisite=), and ft-pointer, ft-screens, and ft-gaze connect as a background app before switching to overlay, so they never start a vrserver of their own. One started from the dev container never finds the headset, which left a reboot stuck in a loop.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit
|
||
|
|
3aea571496 |
Start Frametop's SteamVR clients only once SteamVR is up (#6)
The pointer and gaze services need steamvr.service to be running (Requisite=), and ft-pointer, ft-screens, and ft-gaze connect as a background app before switching to overlay, so they never start a vrserver of their own. One started from the dev container never finds the headset, which left a reboot stuck in a loop.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit
|
||
|
|
6122b7eb15 |
Floating windows: full screen, per-window scale, and drags across panels
Full screen fills the window's own panel (the margin drops to zero). Meta+scroll over a floating window changes its scale at the same size in pixels. Spare outputs are sized to a multiple of KWin's buffer scale, and screens to even sizes: an odd buffer at a fractional scale is a protocol error that disconnected KWin. The 3D mouse's drag lock now crosses onto other Frametop panels unless the pressed one is being carried. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
c2e5e861c8 |
Show floating windows as panels of their own
ft-screens makes a panel for each spare output (frametop.float.N), hidden until ft-floatd floats a window on it. The panel shows only the window's rectangle of the buffer at the density of the screen it came from, its popups and dialogs get small panels over it, pressing its title bar carries it while KWin's pointer stays put, the corner tab resizes the window in pixels, and two more buttons close it and put it back on the desktop. The session adds FLOAT_SLOTS spare outputs to KWin and starts ft-floatd; ft-layout arranges only the screens' outputs, and the pointer helper treats the new panels like screens. Not yet tried in the headset. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
0a88a6b7c9 |
Release a button held on a screen even when the laser lets go between panels
While a button is held on a screen, the laser leaving it no longer takes KWin's pointer, and an invisible catcher overlay sits on the laser whenever it's off every panel, so the release reaches KWin at the pointer's last spot. The pointer helper also reports the mouse's left release as a backstop. Not yet tested in the headset. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
9c04207bb9 |
Let the pointer pass through panels you pick, like a performance overlay
A head-locked performance overlay kept catching the 3D mouse. It has no
input method, so SteamVR's laser passes through it, but the helper hit
tests every visible overlay with ComputeOverlayIntersection, and the dot
stuck to it whenever it crossed that corner of the view.
POINTER_IGNORE in frametop.conf now lists overlay keys the helper leaves
out of the collision, comma-separated shell patterns, so "vendor.app*"
covers a whole app, including panels it opens later. The laser starts
just before the cursor point, so an ignored panel nearer to you doesn't
catch it either.
Frametop Input Settings has a new Ignored panels page. It asks the
helper for SteamVR's overlays ("overlays", answered from the list's
thread once vrcmd has run again, even while the pointer is off), groups
them by app, and has a checkbox per panel and one for the whole app.
Frametop's own screens aren't offered, since ignoring one would leave
nothing to click the app on with the mouse. Entries for apps that
aren't open are listed so they can be removed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
0857a54fab |
Don't let resting controllers take the laser from the mouse
A controller released the 3D mouse's pointer on a single pose sample faster than 0.35 m/s or 2 rad/s, once the mouse had been still for 500 ms. The helper polls about every 8 ms, so one noisy sample was enough: a knock on the desk, or a tracking jump when the headset's cameras pick a resting controller up again (#1). A controller now has to stay over the limit for 100 ms in a row, and only samples with a normal tracking result (Running_OK) count. The new POINTER_CONTROLLER_PICKUP setting (1 by default, 0.5 to 5) scales both limits; it's a slider on the Pointer page of Frametop Input Settings and applies live. A controller picked up for real still gets the laser back through SteamVR's hand role, which follows its touch sensors. The release log now records the speed and spin that triggered it, to tune the default. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
14879023f3 |
Let the 3D mouse click all of SteamVR's Settings page
Most of SteamVR's Settings page took no clicks from the 3D mouse: they went through to a desktop screen behind it, and the page covered the dot. Steam's pages (Library and the rest) are drawn in valve.steam.gamepadui.main, which ComputeOverlayIntersection hits exactly. For SteamVR Settings that overlay is hidden and the page is drawn by the dashboard's scene-graph panel, whose shape OpenVR doesn't give out. The helper guessed a plane and a 0.5 m circle from the panel's transform, but its origin is at the page's left edge and the page's surface is nearer than that plane, so the laser, starting a few cm in front of the guess, started behind the page. On that page only (dashboard open, the main overlay hidden, the line of sight crossing the page's measured area), the laser now starts 25 cm from the eye so SteamVR's own hit test finds the page. The dot is drawn 0.6 m out, in front of the page, and the laser-catching dot sits 8 m out, invisible, with SteamVR's hit dot hidden on it. The beam and SteamVR's hit dot show there, like a controller's; everywhere else nothing changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
0d50efec45 |
Map the Frame controllers' buttons; make gaze clicks correctable
The Frame controllers aren't input devices on the host, so the pointer helper reads them with SteamVR input (vrbuttons.h, actions/), one action set per button, and sends presses to the relay, which does the mapped action like for a mouse button. Only mapped buttons are taken, at an overlay-global priority (SteamVR's experimental "Enable global input from overlays"), and only outside games unless In games is on. Frametop Input Settings gets a Controllers page to map them. Gaze mode: a left press while the gaze has the pointer isn't sent at once. The pointer stops, you drag it onto what you meant with the button held, and the release clicks there; a press held still for POINTER_GAZE_HOLD (0.5 s) becomes a real press, for drags. The dot shows only while the mouse moves it (POINTER_GAZE_SHOW), while a press is held, and as a pulse per click. Outside games the pointer stays on while gaze mode is on. ft-gazed takes a third of each lesson's offset instead of all of it: in the first live test one 6 degree lesson moved everything and put the next target 7 degrees off. Frametop Input Settings gets a Gaze page. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |