mirror of
https://github.com/DeeJanuz/frametop.git
synced 2026-10-06 06:00:09 +02:00
Gaze: ft-gaze prints and reads only the sources in use
ft-gaze computed and printed all six sources for every sample: about 1.3 KB of JSON a line
with our tracker (practice2), 120 KB a second through podman's stdio relay for ft-gazed to
json.loads 90 times a second. It also read SteamVR's gaze action for every sample outside
games (UpdateActionState and GetEyeTrackingDataRelativeToNow, two calls into vrserver, 180 a
second), though with our tracker ft-gazed only uses own and mmap1.
- ft-gaze takes --sources LIST (action, mmap1, mmap2, left, right, own, and eye for the EYE
object; all by default, so the probe and ft-eyes-session are unchanged), and with
--watch-stdin a line "sources LIST" on stdin switches them. A source left out isn't read
and prints as {"ok":0} ("eye" as null), so every line keeps the same keys. An older
ft-gaze ignores both, and prints everything as before.
- ft-gazed asks for what it reads: own,mmap1 with our tracker; left,right,mmap1 with
SteamVR's eyes; the source plus mmap1 and mmap2 on the older one-source path. While a
check or the calibration runs or waits to open, all of them, since checks record every
source (the calibration fits the action's correction too) and the fit check reads "eye".
It switches as soon as that changes, well inside the check's 0.45 s settle.
- So the action is read only during checks, or with --source action.
On recorded samples, a line with own and mmap1 is about 700 bytes instead of 1,300
(practice2), and one with left, right and mmap1 about 550 instead of 940 (test1).
gaze/test/idle-test.py now checks that ft-gaze starts with every source for a check and is
then switched to those in use, without the action or own; all its checks pass.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
060b4957f8
commit
0c95d1d731
4 files changed
+136
-25
No files matched your search
+38
-3
@@ -76,6 +76,12 @@ off. A check asked for while idle waits for the tracker to start (at most WAKE_S
|
||||
tracker's next click after it starts again re-seats it, as after the headset was off, so turning
|
||||
gaze mode on after a while opens the quick check.
|
||||
|
||||
ft-gaze prints only the sources this uses ("sources LIST" on its stdin, see wanted_sources):
|
||||
own and mmap1 with our tracker, left, right and mmap1 with SteamVR's eyes, and every source
|
||||
while a check or the calibration runs or is asked for, since those record them all. So
|
||||
SteamVR's gaze action, the one source that calls into vrserver, is read only then (or with
|
||||
--source action).
|
||||
|
||||
Checks and calibration (gaze/gazecheck.py): a one-dot quick check when the headset goes on or
|
||||
our tracker asks for a click, and the full calibration when gaze mode comes on without one,
|
||||
both in a panel fixed to the headset (gaze/panel/ft-gazepanel, which this service runs too).
|
||||
@@ -260,6 +266,7 @@ class Service:
|
||||
self.sel.register(self.eyes_sock, selectors.EVENT_READ, "own")
|
||||
self.checks = Checks(self, self.sel)
|
||||
self.proc = None
|
||||
self.proc_sources = None # what ft-gaze was last told to print
|
||||
self.buf = b""
|
||||
self.restart_at = 0.0
|
||||
self.running = True
|
||||
@@ -459,6 +466,31 @@ class Service:
|
||||
|
||||
# --- ft-gaze ---
|
||||
|
||||
def wanted_sources(self):
|
||||
"""The sources ft-gaze should print (its --sources): what on_sample and the checks read.
|
||||
mmap1 always (blinks, lost eyes, and the headset going on, from its openness and
|
||||
variances); everything while a check runs or waits, since they record every source."""
|
||||
c = self.checks
|
||||
if c.active or c.pending:
|
||||
return "all"
|
||||
kind = self.kind
|
||||
if kind == "own":
|
||||
return "own,mmap1"
|
||||
if kind == "eyes":
|
||||
return "left,right,mmap1"
|
||||
return ",".join(dict.fromkeys((self.source, "mmap1", "mmap2"))) # mmap2: each eye, for the fallback
|
||||
|
||||
def sync_sources(self):
|
||||
want = self.wanted_sources()
|
||||
if not self.proc or want == self.proc_sources:
|
||||
return
|
||||
try:
|
||||
self.proc.stdin.write(f"sources {want}\n".encode())
|
||||
self.proc.stdin.flush()
|
||||
except (OSError, ValueError):
|
||||
return # it's stopping: read_stdout notices
|
||||
self.proc_sources = want
|
||||
|
||||
def start_helper(self):
|
||||
if not HELPER.exists():
|
||||
log(f"ft-gaze isn't built: run {REPO}/gaze/build.sh")
|
||||
@@ -469,9 +501,11 @@ class Service:
|
||||
subprocess.run([str(REPO / "scripts" / "container-up.sh")], env=env, check=False)
|
||||
distrobox = Path.home() / ".local" / "bin" / "distrobox"
|
||||
# ft-gaze quits when its stdin closes: the one thing distrobox passes on.
|
||||
self.proc = subprocess.Popen([str(distrobox), "enter", "dev", "--", str(HELPER), "--watch-stdin"], env=env,
|
||||
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
|
||||
start_new_session=True)
|
||||
sources = self.wanted_sources()
|
||||
self.proc = subprocess.Popen([str(distrobox), "enter", "dev", "--", str(HELPER), "--watch-stdin",
|
||||
"--sources", sources], env=env, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE, start_new_session=True)
|
||||
self.proc_sources = sources
|
||||
os.set_blocking(self.proc.stdout.fileno(), False)
|
||||
os.set_blocking(self.proc.stderr.fileno(), False)
|
||||
self.sel.register(self.proc.stdout, selectors.EVENT_READ, "stdout")
|
||||
@@ -876,6 +910,7 @@ class Service:
|
||||
elif key.data == "stderr" and self.proc:
|
||||
self.read_stderr()
|
||||
self.checks.tick()
|
||||
self.sync_sources()
|
||||
if now >= next_periodic:
|
||||
self.periodic()
|
||||
next_periodic = now + 1.0
|
||||
|
||||
Reference in new issue
Block a user