133 lines
5.2 KiB
Bash
Executable File
133 lines
5.2 KiB
Bash
Executable File
#!/bin/bash
|
|
# 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.
|
|
set -euo pipefail
|
|
export DISPLAY="${DISPLAY:-:1}"
|
|
# 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"
|
|
}
|
|
|
|
if [[ -z "${DBUS_SESSION_BUS_ADDRESS:-}" ]]; then
|
|
eval "$(dbus-launch --sh-syntax)"
|
|
fi
|
|
# 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"
|
|
|
|
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
|
|
|
|
# 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
|
|
if xdpyinfo -display "$DISPLAY" >/dev/null 2>&1; then
|
|
break
|
|
fi
|
|
sleep 0.2
|
|
done
|
|
if ! xdpyinfo -display "$DISPLAY" >/dev/null 2>&1; then
|
|
cat /tmp/xvnc.log >&2
|
|
exit 1
|
|
fi
|
|
|
|
# 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 &
|
|
fi
|
|
|
|
echo "[lazyboy-box] desktop up display=$DISPLAY geometry=$SCREEN_GEOMETRY vnc=6080 xfce chrome" >/tmp/start-desktop.log
|
|
touch "$ROOT/ready"
|
|
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
|