LazyBoy2/box/start-desktop.sh

133 lines
5.2 KiB
Bash
Raw Normal View History

2026-09-14 09:08:35 +00:00
#!/bin/bash
2026-09-16 14:00:19 +00:00
# Boot the box desktop: one TigerVNC X server (framebuffer + VNC in a single
# event-driven process), a minimal XFCE (xfwm4 + xfdesktop + panel, no session
# manager), AT-SPI for element-level automation, noVNC, then Chromium.
# Never a kiosk, --app, or bare HTML shell: the human and the agent share a
# normal Linux desktop.
2026-09-14 09:08:35 +00:00
set -euo pipefail
export DISPLAY="${DISPLAY:-:1}"
2026-09-16 14:00:19 +00:00
# Traditional Chinese desktop + Taipei time; everything launched from here
# (panel, box-chrome, terminals) inherits these.
export LANG="${LANG:-zh_TW.UTF-8}"
export LC_ALL="${LC_ALL:-zh_TW.UTF-8}"
export LANGUAGE="${LANGUAGE:-zh_TW:zh:en}"
export TZ="${TZ:-Asia/Taipei}"
# AT-SPI: GTK apps and Chromium publish their accessibility trees so the
# agent can read element names/coordinates instead of guessing pixels.
export GTK_MODULES="${GTK_MODULES:-atk-bridge}"
export GTK_A11Y="${GTK_A11Y:-atspi}"
export GNOME_ACCESSIBILITY="${GNOME_ACCESSIBILITY:-1}"
export NO_AT_BRIDGE=0
export XDG_CURRENT_DESKTOP=XFCE
export XDG_CONFIG_HOME=/home/box/.config
export XDG_CACHE_HOME=/tmp/box-cache
export XDG_RUNTIME_DIR=/tmp/box-runtime
ROOT=/tmp/lazyboy
SCREEN_GEOMETRY="${LAZYBOY_SCREEN_GEOMETRY:-1280x720}"
mkdir -p /workspace /home/box /tmp "$ROOT" "$XDG_CACHE_HOME" "$XDG_RUNTIME_DIR" /tmp/.X11-unix
chmod 1777 /workspace /tmp /tmp/.X11-unix
chmod 700 "$XDG_RUNTIME_DIR"
rm -f "$ROOT/ready"
# XFCE configuration is re-seeded from the image on every boot so the desktop
# always looks the way the image defines it; only /home/box/chrome-profile
# (Chromium) is meant to carry state across boots.
hydrate_xfce() {
local xml="$XDG_CONFIG_HOME/xfce4/xfconf/xfce-perchannel-xml"
mkdir -p "$xml" "$XDG_CONFIG_HOME/xfce4/panel" "$XDG_CONFIG_HOME/glib-2.0/settings"
local f
for f in xfce4-panel.xml xfwm4.xml xfce4-desktop.xml; do
cp -f "/usr/share/lazyboy/xfce-skel/$f" "$xml/$f"
done
cp -f /etc/xdg/xfce4/helpers.rc "$XDG_CONFIG_HOME/xfce4/helpers.rc"
}
2026-09-14 09:08:35 +00:00
if [[ -z "${DBUS_SESSION_BUS_ADDRESS:-}" ]]; then
eval "$(dbus-launch --sh-syntax)"
fi
2026-09-16 14:00:19 +00:00
# Tools started later over `docker exec` (box-a11y, box-chrome) must join this
# bus to reach the AT-SPI registry that the desktop apps registered with.
printf 'DBUS_SESSION_BUS_ADDRESS=%s\n' "$DBUS_SESSION_BUS_ADDRESS" >"$ROOT/dbus.env"
2026-09-14 09:08:35 +00:00
display_number="${DISPLAY#:}"
display_number="${display_number%%.*}"
if [[ ! "$display_number" =~ ^[0-9]+$ ]]; then
echo "Unsupported local display: $DISPLAY" >&2
exit 1
fi
if ! xdpyinfo -display "$DISPLAY" >/dev/null 2>&1; then
lock_file="/tmp/.X${display_number}-lock"
lock_pid=""
if [[ -f "$lock_file" ]]; then
lock_pid=$(tr -d '[:space:]' < "$lock_file")
fi
if [[ "$lock_pid" =~ ^[0-9]+$ ]] && kill -0 "$lock_pid" 2>/dev/null; then
echo "Display lock is held by live process $lock_pid" >&2
exit 1
fi
rm -f -- "$lock_file" "/tmp/.X11-unix/X${display_number}"
fi
2026-09-16 14:00:19 +00:00
# Only websockify (same container) talks to VNC; keep the passwordless port
# off the Docker bridge so sibling containers cannot attach.
Xtigervnc "$DISPLAY" -geometry "$SCREEN_GEOMETRY" -depth 24 \
-rfbport 5900 -localhost -SecurityTypes None -AlwaysShared -AcceptCutText -SendCutText \
-desktop LazyBoy -ac -noreset >/tmp/xvnc.log 2>&1 &
for _ in $(seq 1 30); do
2026-09-14 09:08:35 +00:00
if xdpyinfo -display "$DISPLAY" >/dev/null 2>&1; then
break
fi
2026-09-16 14:00:19 +00:00
sleep 0.2
2026-09-14 09:08:35 +00:00
done
if ! xdpyinfo -display "$DISPLAY" >/dev/null 2>&1; then
2026-09-16 14:00:19 +00:00
cat /tmp/xvnc.log >&2
2026-09-14 09:08:35 +00:00
exit 1
fi
2026-09-16 14:00:19 +00:00
# Accessibility bus + registry. toolkit-accessibility is what Chromium and GTK
# consult before exposing their trees over AT-SPI.
for c in /usr/libexec/at-spi-bus-launcher /usr/lib/at-spi2-core/at-spi-bus-launcher; do
if [[ -x "$c" ]]; then
"$c" --launch-immediately >/tmp/atspi-bus.log 2>&1 &
break
fi
done
for c in /usr/libexec/at-spi2-registryd /usr/lib/at-spi2-core/at-spi2-registryd; do
if [[ -x "$c" ]]; then
"$c" >/tmp/atspi-registry.log 2>&1 &
break
fi
done
HOME=/home/box gsettings set org.gnome.desktop.interface toolkit-accessibility true 2>/tmp/gsettings.log || true
hydrate_xfce
xfconfd --daemon >/tmp/xfconfd.log 2>&1 || true
sleep 0.2
xfwm4 --compositor=off --display="$DISPLAY" --sm-client-disable >/tmp/xfwm4.log 2>&1 &
sleep 0.3
xfdesktop --disable-wm-check --sm-client-disable >/tmp/xfdesktop.log 2>&1 &
xfce4-panel --disable-wm-check --sm-client-disable >/tmp/xfce4-panel.log 2>&1 &
# Xvnc keeps the VNC clipboard in sync with the X selections through vncconfig.
if command -v vncconfig >/dev/null 2>&1; then
vncconfig -nowin -display "$DISPLAY" >/tmp/vncconfig.log 2>&1 &
fi
websockify --heartbeat=30 --web /usr/share/novnc 6080 127.0.0.1:5900 >/tmp/novnc.log 2>&1 &
# Chromium: --restore-last-session brings back the previous tabs, so only the
# very first boot of a profile gets the welcome page (otherwise it doubled up).
if [[ -d /home/box/chrome-profile/Default/Sessions ]]; then
box-chrome >/tmp/lazyboy-browser.log 2>&1 &
else
box-chrome /usr/share/lazyboy/welcome.html >/tmp/lazyboy-browser.log 2>&1 &
2026-09-15 09:07:16 +00:00
fi
2026-09-14 09:08:35 +00:00
2026-09-16 14:00:19 +00:00
echo "[lazyboy-box] desktop up display=$DISPLAY geometry=$SCREEN_GEOMETRY vnc=6080 xfce chrome" >/tmp/start-desktop.log
touch "$ROOT/ready"
2026-09-14 09:08:35 +00:00
trap 'node /usr/local/bin/stop-browser.mjs || python3 /usr/local/bin/stop-browser.py; exit 0' TERM INT
while true; do
sleep 3600 &
wait $!
done