Add ft-gazed, the gaze service, and ft-gazectl

ft-gazed runs ft-gaze, drops blinks and dropouts, smooths with a fixation
lock, applies the probe's calibration plus what the pointer has taught
since, and sends the corrected gaze to the pointer helper at 90 Hz. The
helper sends lessons back (a nudge before a click), which are learned and
saved. It only reads the eye tracker; nothing of SteamVR's is written.
gaze/run.sh installs it as a user service that starts with SteamVR;
ft-gazectl turns gaze mode on and off and shows the service's status.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
DeeJanuzandClaude Opus 5.5 committed 2026-09-27 22:51:24 -06:00
1 parent 1d0211dc5e
commit f1c385aa59
5 files changed
+472 -1

No files matched your search

+54
View File
@@ -0,0 +1,54 @@
#!/usr/bin/python3
"""ft-gazectl: turn the gaze pointer on or off, and ask the gaze service how it's doing.
ft-gazectl on|off|toggle gaze mode in the pointer helper (until it restarts; the
setting is POINTER_GAZE in ~/.config/frametop.conf)
ft-gazectl status the gaze service (ft-gazed): samples, lessons, the correction
ft-gazectl forget drop what the pointer's lessons taught (the calibration stays)
ft-gazectl reload the service reads calibration.json again
"""
import json
import os
import socket
import sys
def ask(name, msg, timeout=1.0):
s = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM | socket.SOCK_CLOEXEC)
s.bind(f"\0ft_gazectl.{os.getpid()}")
s.settimeout(timeout)
try:
s.sendto(msg.encode(), "\0" + name)
return s.recv(4096).decode()
except ConnectionRefusedError:
return None
except socket.timeout:
return ""
finally:
s.close()
def main():
cmd = sys.argv[1] if len(sys.argv) > 1 else "status"
if cmd in ("on", "off", "toggle"):
r = ask("ft_pointer_helper", f"gaze {cmd}")
if r is None:
sys.exit("the pointer helper isn't running")
print(f"gaze mode {r.removeprefix('ok ')}" if r else "no answer (an older pointer helper without gaze mode?)")
if ask("ft_gazed", "status", 0.3) is None:
print("note: the gaze service (ft-gazed) isn't running, so the pointer has no gaze to follow")
elif cmd in ("status", "forget", "reload"):
r = ask("ft_gazed", cmd)
if r is None:
sys.exit("the gaze service (ft-gazed) isn't running")
try:
print(json.dumps(json.loads(r), indent=1))
except ValueError:
print(r)
else:
sys.exit(__doc__)
if __name__ == "__main__":
main()