#!/bin/sh # Starts the camera gesture pointer. Installed to /usr/local/bin/gesture-control, # launched from configs/sway/config. # # Session-scoped (a sway exec) rather than a systemd unit, for two reasons that point # the same way: # # 1. Privacy. The only thing gesture control can do is move and click the pointer of # the on-screen session. A system unit would hold /dev/video0 open from boot to # shutdown, including while no session exists and there is nothing to point at — # which is exactly the wrong default for a camera in someone's living room. Tying # the camera's lifetime to the session's means "screen is up" and "camera is open" # cannot drift apart. # # 2. Blast radius. Continuous ML inference is a different failure profile from # thinclient-agent, which is the machine's SOLE MQTT control surface and must stay # reliable (see the security-boundary note in thinclient_agent/mqtt_discovery.py). # A crash here, or a camera that wedges, must not be able to take HA control of the # room down with it. This is the same call configs/eww/fullscreen-watcher.sh makes # and for the same reason — a separate process, not another job inside the daemon. # # The runtime "enabled" flag is deliberately NOT checked here. gesture_pointer.py checks # it itself, before it imports OpenCV or MediaPipe, so there is exactly one place that # decides whether the camera is opened rather than two that can disagree. set -eu VENV=/opt/gesture-control/venv LOCK="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}/gesture-control.pid" # exec_always in the sway config re-runs this on every `swaymsg reload`; without this # guard each reload would leave another process holding the camera. if [ -f "$LOCK" ] && kill -0 "$(cat "$LOCK" 2>/dev/null)" 2>/dev/null; then exit 0 fi echo $$ > "$LOCK" trap 'rm -f "$LOCK"' EXIT INT TERM if [ ! -x "$VENV/bin/python3" ]; then echo "gesture-control: not installed on this image (ENABLE_GESTURE_CONTROL was false" >&2 echo " at build time). See live-build/config/hooks/normal/1100-gesture-control.hook.chroot." >&2 exit 0 fi # thinclient_agent.input_control and .runtime_state are stdlib-only and are imported # from inside this venv rather than duplicated — one ydotool call path for the whole # image, not a second one that drifts. PYTHONPATH=/opt/thinclient-agent export PYTHONPATH exec "$VENV/bin/python3" /opt/gesture-control/gesture_pointer.py