Files
DeeJanuz--frametop/gaze/ft-gazectl
DeeJanuzandClaude Opus 5.5 a7f9a8dd80 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>
2026-10-01 17:50:52 -06:00

64 lines
2.2 KiB
Python
Executable File

#!/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?)")
st = ask("ft_gazed", "status", 0.3)
if st is None:
print("note: the gaze service (ft-gazed) isn't running, so the pointer has no gaze to follow")
elif r == "ok on":
try:
calibrated = (json.loads(st).get("checks") or {}).get("calibrated")
except ValueError:
calibrated = None
if calibrated is False:
print("note: no calibration for this eye tracker yet; it opens in the headset "
"(if it can't, ft-gazectl status says why: checks.problem)")
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()