Default the x11 headless desktop to GNOME (Openbox still opt-in)
Bare Openbox rendered only a blank slate root — usable but not the desktop wanted. Make the X11/NVENC capture path render a full GNOME session by default, with Openbox available via HEADLESS_DESKTOP=openbox for minimal/low-power hosts. - files/headless-desktop-gnome.service: full Ubuntu GNOME session forced onto the X11 path (XDG_SESSION_TYPE=x11, no dbus-run-session so it shares the systemd user bus). Renamed the Openbox unit to headless-desktop-openbox.service. - lib/headless.sh: HEADLESS_DESKTOP (default gnome) selects the unit template + the packages to install (gnome-session/gnome-shell vs openbox/xsetroot). - install.sh: step message + usage document HEADLESS_DESKTOP. - status.sh: the :0 desktop check now reports which desktop is running (reads _NET_WM_NAME off the supporting-wm-check window, e.g. "GNOME Shell"). - docs: TROUBLESHOOTING §13 + FOLLOWUPS P3 updated for the GNOME default and the openbox toggle. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -249,9 +249,12 @@ TROUBLESHOOTING.md §13.
|
|||||||
|
|
||||||
**Done so far**: the *desktop* piece of the x11 backend is now reproducible.
|
**Done so far**: the *desktop* piece of the x11 backend is now reproducible.
|
||||||
`install.sh` detects `capture = x11` (via `capture_backend_is_x11`) and installs
|
`install.sh` detects `capture = x11` (via `capture_backend_is_x11`) and installs
|
||||||
+ enables `files/headless-desktop.service` (Openbox on `:0`) so the headless
|
+ enables `headless-desktop.service` so the headless Xorg has a desktop to
|
||||||
Xorg has a window manager to render — without it, capture is a black screen.
|
render — without it, capture is a black screen. Desktop is selectable via
|
||||||
`status.sh` gained a matching check (FAIL if no WM on `:0`). Still outstanding:
|
`HEADLESS_DESKTOP`: `gnome` (default — full Ubuntu GNOME session, X11 path) or
|
||||||
|
`openbox` (lightweight bare WM); each ships as its own unit template
|
||||||
|
(`files/headless-desktop-{gnome,openbox}.service`). `status.sh` gained a matching
|
||||||
|
check (FAIL if no desktop on `:0`, and reports which one). Still outstanding:
|
||||||
the `--backend x11|wlr` flag/auto-detect, the `config.sh` x11 conf variant, and
|
the `--backend x11|wlr` flag/auto-detect, the `config.sh` x11 conf variant, and
|
||||||
shipping the `xorg-headless.service` + `xorg-headless.conf` templates.
|
shipping the `xorg-headless.service` + `xorg-headless.conf` templates.
|
||||||
|
|
||||||
|
|||||||
@@ -490,12 +490,19 @@ DISPLAY=:0 xlsclients # only 'sunshine' = no WM/des
|
|||||||
DISPLAY=:0 xprop -root _NET_SUPPORTING_WM_CHECK # 'not found' = no window manager
|
DISPLAY=:0 xprop -root _NET_SUPPORTING_WM_CHECK # 'not found' = no window manager
|
||||||
```
|
```
|
||||||
|
|
||||||
Fix: run a window manager on `:0`. The installer ships a `headless-desktop.service`
|
Fix: run a desktop on `:0`. The installer ships a `headless-desktop.service`
|
||||||
(Openbox) for exactly this and enables it whenever it detects `capture = x11`:
|
for exactly this and enables it whenever it detects `capture = x11`. It defaults
|
||||||
|
to a full **GNOME** session (`gnome-session --session=ubuntu`, forced to the X11
|
||||||
|
path); set `HEADLESS_DESKTOP=openbox` for a lightweight bare WM instead (lower
|
||||||
|
overhead, but no panel/launcher — right-click menu only). The unit forces
|
||||||
|
`XDG_SESSION_TYPE=x11` and does **not** wrap GNOME in `dbus-run-session` — GNOME
|
||||||
|
must share the systemd *user* bus the service already inherits.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
systemctl --user enable --now headless-desktop.service
|
systemctl --user enable --now headless-desktop.service
|
||||||
DISPLAY=:0 xprop -root _NET_SUPPORTING_WM_CHECK # now reports a 'window id'
|
DISPLAY=:0 xprop -root _NET_SUPPORTING_WM_CHECK # now reports a 'window id'
|
||||||
|
# read the running desktop's name off that window:
|
||||||
|
DISPLAY=:0 xprop -id <id> _NET_WM_NAME # e.g. "GNOME Shell"
|
||||||
```
|
```
|
||||||
|
|
||||||
`status.sh` checks for this directly: with `capture = x11` it now FAILs if no
|
`status.sh` checks for this directly: with `capture = x11` it now FAILs if no
|
||||||
|
|||||||
30
files/headless-desktop-gnome.service
Normal file
30
files/headless-desktop-gnome.service
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=GNOME session on the headless Xorg (gives Sunshine something to capture)
|
||||||
|
# Without a desktop running on :0, Sunshine's x11 capture grabs an empty black
|
||||||
|
# X root window — pairing, NVENC, and input all work but the client sees only
|
||||||
|
# black. This runs a full GNOME session on :0 so the stream shows a real
|
||||||
|
# desktop. Only relevant to the X11/NVENC capture backend (capture = x11); the
|
||||||
|
# wlr backend's compositor renders for itself.
|
||||||
|
Requires=xorg-headless.service
|
||||||
|
After=xorg-headless.service
|
||||||
|
PartOf=graphical-session.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
# Force the X11 session path (not Wayland) onto the existing headless Xorg.
|
||||||
|
# No dbus-run-session wrapper: GNOME integrates with the systemd *user* bus,
|
||||||
|
# which this service already inherits — a fresh bus would break that.
|
||||||
|
Environment=DISPLAY=:0
|
||||||
|
Environment=XDG_SESSION_TYPE=x11
|
||||||
|
Environment=XDG_CURRENT_DESKTOP=ubuntu:GNOME
|
||||||
|
Environment=GNOME_SHELL_SESSION_MODE=ubuntu
|
||||||
|
# Wait for the X server to accept connections before launching the session.
|
||||||
|
ExecStartPre=/bin/sh -c 'for i in $(seq 1 20); do xset -display :0 -q >/dev/null 2>&1 && exit 0; sleep 0.5; done; exit 1'
|
||||||
|
ExecStart=/usr/bin/gnome-session --session=ubuntu
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=2s
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
# Lingering user manager reaches default.target without a graphical login,
|
||||||
|
# matching the headless-boot drop-in pattern used for Sunshine.
|
||||||
|
WantedBy=default.target
|
||||||
@@ -59,6 +59,9 @@ Environment overrides:
|
|||||||
HEADLESS_HOSTS Comma-separated hostnames that default to headless mode.
|
HEADLESS_HOSTS Comma-separated hostnames that default to headless mode.
|
||||||
Unset by default; anything not listed defaults to mirror.
|
Unset by default; anything not listed defaults to mirror.
|
||||||
Override per-invocation with --headless or --mirror.
|
Override per-invocation with --headless or --mirror.
|
||||||
|
HEADLESS_DESKTOP Desktop to render on the headless Xorg :0 for the X11/NVENC
|
||||||
|
capture path (capture = x11): 'gnome' (default) or 'openbox'
|
||||||
|
(lightweight, for minimal hosts). Ignored on wlr/kms.
|
||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,7 +197,7 @@ main() {
|
|||||||
# (this backend is hand-configured; see FOLLOWUPS.md P3). Harmless no-op on
|
# (this backend is hand-configured; see FOLLOWUPS.md P3). Harmless no-op on
|
||||||
# the wlr/kms backends, whose capture source renders for itself.
|
# the wlr/kms backends, whose capture source renders for itself.
|
||||||
if capture_backend_is_x11; then
|
if capture_backend_is_x11; then
|
||||||
step "Installing headless desktop (Openbox on :0 for the X11 capture path)"
|
step "Installing headless desktop (${HEADLESS_DESKTOP:-gnome} on :0 for the X11 capture path)"
|
||||||
install_headless_desktop
|
install_headless_desktop
|
||||||
fi
|
fi
|
||||||
# NOTE: the headless prestart drop-in needs the sunshine unit to already
|
# NOTE: the headless prestart drop-in needs the sunshine unit to already
|
||||||
|
|||||||
@@ -48,30 +48,52 @@ capture_backend_is_x11() {
|
|||||||
|
|
||||||
HEADLESS_DESKTOP_UNIT="headless-desktop.service"
|
HEADLESS_DESKTOP_UNIT="headless-desktop.service"
|
||||||
|
|
||||||
# Pull the lightweight WM (Openbox) we drive on :0, plus xsetroot for the
|
# Which desktop to render on the headless Xorg :0. Default is a full GNOME
|
||||||
# solid-background touch. Idempotent — pkg_install skips what's present.
|
# session (the familiar Ubuntu desktop). Set HEADLESS_DESKTOP=openbox for a
|
||||||
|
# lightweight bare WM instead — lower overhead, better for a truly minimal or
|
||||||
|
# low-power host, but no panel/launcher out of the box (right-click menu only).
|
||||||
|
: "${HEADLESS_DESKTOP:=gnome}"
|
||||||
|
|
||||||
|
# Install the packages the chosen desktop needs. Idempotent — pkg_install
|
||||||
|
# skips what's already present.
|
||||||
ensure_headless_desktop_packages() {
|
ensure_headless_desktop_packages() {
|
||||||
command -v openbox-session >/dev/null 2>&1 || pkg_install openbox
|
case "$HEADLESS_DESKTOP" in
|
||||||
if ! command -v xsetroot >/dev/null 2>&1; then
|
gnome)
|
||||||
case "$DISTRO" in
|
case "$DISTRO" in
|
||||||
debian) pkg_install x11-xserver-utils ;;
|
debian) pkg_install gnome-session gnome-shell ubuntu-session ;;
|
||||||
arch) pkg_install xorg-xsetroot ;;
|
arch) pkg_install gnome-shell gnome-session ;;
|
||||||
esac
|
esac
|
||||||
fi
|
;;
|
||||||
|
openbox)
|
||||||
|
command -v openbox-session >/dev/null 2>&1 || pkg_install openbox
|
||||||
|
# xsetroot paints a solid root so the bare desktop is visibly non-black.
|
||||||
|
if ! command -v xsetroot >/dev/null 2>&1; then
|
||||||
|
case "$DISTRO" in
|
||||||
|
debian) pkg_install x11-xserver-utils ;;
|
||||||
|
arch) pkg_install xorg-xsetroot ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
err "Unknown HEADLESS_DESKTOP='$HEADLESS_DESKTOP' (expected: gnome | openbox)"
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
# Install + enable the Openbox session that renders onto the headless Xorg
|
# Install + enable a desktop session that renders onto the headless Xorg
|
||||||
# display (:0), so the X11/NVENC capture path has something to show instead of
|
# display (:0), so the X11/NVENC capture path shows a real desktop instead of a
|
||||||
# a black root window. Idempotent: safe to re-run. Bound to xorg-headless.service
|
# black root window. Picks the GNOME or Openbox unit template per
|
||||||
|
# $HEADLESS_DESKTOP. Idempotent: safe to re-run. Bound to xorg-headless.service
|
||||||
# and to default.target (the lingering-user manager reaches it without a login).
|
# and to default.target (the lingering-user manager reaches it without a login).
|
||||||
install_headless_desktop() {
|
install_headless_desktop() {
|
||||||
local src="$SCRIPT_DIR/files/headless-desktop.service"
|
local src="$SCRIPT_DIR/files/headless-desktop-${HEADLESS_DESKTOP}.service"
|
||||||
if [[ ! -f "$src" ]]; then
|
if [[ ! -f "$src" ]]; then
|
||||||
err "Desktop unit source missing: $src"
|
err "Desktop unit source missing: $src (HEADLESS_DESKTOP=$HEADLESS_DESKTOP)"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
ensure_headless_desktop_packages
|
ensure_headless_desktop_packages || return 1
|
||||||
|
|
||||||
local unit_dir="$HOME/.config/systemd/user"
|
local unit_dir="$HOME/.config/systemd/user"
|
||||||
mkdir -p "$unit_dir"
|
mkdir -p "$unit_dir"
|
||||||
@@ -82,7 +104,7 @@ install_headless_desktop() {
|
|||||||
# headless box with no session yet, fall back to enable-only so it comes up
|
# headless box with no session yet, fall back to enable-only so it comes up
|
||||||
# on next boot via default.target.
|
# on next boot via default.target.
|
||||||
if systemctl --user enable --now "$HEADLESS_DESKTOP_UNIT" >/dev/null 2>&1; then
|
if systemctl --user enable --now "$HEADLESS_DESKTOP_UNIT" >/dev/null 2>&1; then
|
||||||
ok "Enabled $HEADLESS_DESKTOP_UNIT (Openbox session on :0)"
|
ok "Enabled $HEADLESS_DESKTOP_UNIT ($HEADLESS_DESKTOP session on :0)"
|
||||||
else
|
else
|
||||||
systemctl --user enable "$HEADLESS_DESKTOP_UNIT" >/dev/null 2>&1 || true
|
systemctl --user enable "$HEADLESS_DESKTOP_UNIT" >/dev/null 2>&1 || true
|
||||||
warn "Installed $HEADLESS_DESKTOP_UNIT but couldn't start it now — it will start on next login/boot."
|
warn "Installed $HEADLESS_DESKTOP_UNIT but couldn't start it now — it will start on next login/boot."
|
||||||
|
|||||||
14
status.sh
14
status.sh
@@ -186,15 +186,19 @@ case "${CAP:-}" in
|
|||||||
# yet have no WM/desktop drawing anything — Sunshine then captures an empty
|
# yet have no WM/desktop drawing anything — Sunshine then captures an empty
|
||||||
# black root window (pairing/NVENC/input all work; client sees only black).
|
# black root window (pairing/NVENC/input all work; client sees only black).
|
||||||
if command -v xprop >/dev/null 2>&1; then
|
if command -v xprop >/dev/null 2>&1; then
|
||||||
wm_check=$( (if [[ $EUID -eq 0 && "$SUSER" != "$(id -un)" ]]; then sudo -u "$SUSER" DISPLAY=:0 xprop -root _NET_SUPPORTING_WM_CHECK; else DISPLAY=:0 xprop -root _NET_SUPPORTING_WM_CHECK; fi) 2>/dev/null)
|
_xp(){ if [[ $EUID -eq 0 && "$SUSER" != "$(id -un)" ]]; then sudo -u "$SUSER" DISPLAY=:0 xprop "$@"; else DISPLAY=:0 xprop "$@"; fi; }
|
||||||
if grep -q 'window id' <<<"$wm_check"; then
|
wm_win=$(_xp -root _NET_SUPPORTING_WM_CHECK 2>/dev/null | grep -o '0x[0-9a-f]*' | head -1)
|
||||||
pass "a window manager is running on :0 (something to capture)"
|
if [[ -n $wm_win ]]; then
|
||||||
|
# Read the WM's advertised name (e.g. "GNOME Shell", "Openbox") so the
|
||||||
|
# report says which desktop is actually rendering, not just that one is.
|
||||||
|
wm_name=$(_xp -id "$wm_win" _NET_WM_NAME 2>/dev/null | sed -n 's/.*= "\(.*\)"/\1/p')
|
||||||
|
pass "a desktop is running on :0${wm_name:+ (${wm_name})} — something to capture"
|
||||||
else
|
else
|
||||||
fail "no window manager on :0 — capture will be a black screen" \
|
fail "no window manager on :0 — capture will be a black screen" \
|
||||||
"Start a desktop on the headless Xorg: 'systemctl --user enable --now headless-desktop.service' (install.sh installs it for the x11 backend). See TROUBLESHOOTING.md §13."
|
"Start a desktop on the headless Xorg: 'systemctl --user enable --now headless-desktop.service' (install.sh installs it for the x11 backend; HEADLESS_DESKTOP=gnome|openbox). See TROUBLESHOOTING.md §13."
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
note "xprop not installed — skipping window-manager-on-:0 check (install x11-utils to enable it)"
|
note "xprop not installed — skipping desktop-on-:0 check (install x11-utils to enable it)"
|
||||||
fi
|
fi
|
||||||
# wlr env leaking into an x11 unit (stale sway drop-in — TROUBLESHOOTING §13)
|
# wlr env leaking into an x11 unit (stale sway drop-in — TROUBLESHOOTING §13)
|
||||||
if [[ -n $UNIT ]]; then
|
if [[ -n $UNIT ]]; then
|
||||||
|
|||||||
Reference in New Issue
Block a user