diff --git a/scripts/officer-set-display.sh b/scripts/officer-set-display.sh new file mode 100755 index 00000000..02d36c7d --- /dev/null +++ b/scripts/officer-set-display.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash +# Pick the largest sane mode on whichever output is connected, at session start. +# +# With no monitor attached the connector's EDID comes from drm.edid_firmware (see the kernel command +# line). That EDID advertises plenty of modes but its *preferred* one is the captured screen's native +# resolution, which may be tiny — GNOME picks preferred, so an 800x480 panel yields an 800x480 +# desktop. monitors.xml is the documented override but its monitor matching proved unreliable here, +# so set the mode directly. No-ops when the mode is already right, so it is safe to run repeatedly. +set -eu + +MAX_WIDTH="${OFFICER_MAX_WIDTH:-1920}" + +# The session's X server may not be up yet when autostart fires. +for _ in $(seq 1 20); do + xrandr --query >/dev/null 2>&1 && break + sleep 0.5 +done +xrandr --query >/dev/null 2>&1 || exit 0 + +output=$(xrandr --query | awk '/ connected/ { print $1; exit }') +[ -n "$output" ] || exit 0 + +# Modes are listed under the output but are not reliably sorted, so pick the widest that fits. +mode=$( + xrandr --query \ + | sed -n "/^${output} connected/,/^[^ ]/p" \ + | awk '/^[[:space:]]+[0-9]+x[0-9]+/ { print $1 }' \ + | awk -F x -v max="$MAX_WIDTH" ' + { w = $1 + 0; h = $2 + 0 } + w <= max && (w > bw || (w == bw && h > bh)) { bw = w; bh = h; best = $0 } + END { if (best) print best }' +) +[ -n "$mode" ] || exit 0 + +current=$(xrandr --query | awk -v o="$output" '$1 == o { print $3 }' | cut -d+ -f1) +[ "$current" = "$mode" ] && exit 0 + +xrandr --output "$output" --mode "$mode" || true diff --git a/scripts/setup-desktop.sh b/scripts/setup-desktop.sh index decc0ee4..4f342685 100755 --- a/scripts/setup-desktop.sh +++ b/scripts/setup-desktop.sh @@ -15,7 +15,7 @@ echo "" DESKTOP_USER="$(whoami)" # --- Step 1: Install GNOME desktop + GDM + x11vnc --- -echo "[1/5] Installing ubuntu-desktop, GDM, x11vnc..." +echo "[1/6] Installing ubuntu-desktop, GDM, x11vnc..." sudo apt update -qq sudo DEBIAN_FRONTEND=noninteractive apt install -y -qq \ ubuntu-desktop \ @@ -29,7 +29,7 @@ echo " Done." # created and the desktop could not authenticate. # --- Step 2: Force GDM onto Xorg + enable auto-login (x11vnc cannot mirror Wayland) --- -echo "[2/5] Forcing Xorg session and auto-login in GDM..." +echo "[2/6] Forcing Xorg session and auto-login in GDM..." GDM_CONF=/etc/gdm3/custom.conf sudo mkdir -p /etc/gdm3 [ -f "$GDM_CONF" ] || echo "[daemon]" | sudo tee "$GDM_CONF" > /dev/null @@ -52,7 +52,7 @@ set_gdm_key AutomaticLogin "$DESKTOP_USER" echo " Xorg forced (WaylandEnable=false), auto-login as $DESKTOP_USER." # --- Step 3: Make GDM the default display manager --- -echo "[3/5] Setting GDM as the default display manager..." +echo "[3/6] Setting GDM as the default display manager..." echo "/usr/sbin/gdm3" | sudo tee /etc/X11/default-display-manager > /dev/null sudo systemctl enable gdm3 >/dev/null 2>&1 || sudo systemctl enable gdm >/dev/null 2>&1 || true sudo systemctl set-default graphical.target >/dev/null 2>&1 || true @@ -61,7 +61,7 @@ sudo systemctl disable lightdm >/dev/null 2>&1 || true echo " Done (takes effect on next reboot)." # --- Step 4: Install Brave browser (native .deb, not snap) --- -echo "[4/5] Installing Brave browser..." +echo "[4/6] Installing Brave browser..." if ! command -v brave-browser-stable > /dev/null 2>&1; then sudo curl -fsSLo /usr/share/keyrings/brave-browser-archive-keyring.gpg \ https://brave-browser-apt-release.s3.brave.com/brave-browser-archive-keyring.gpg @@ -81,7 +81,7 @@ echo '--password-store=basic' | sudo tee /etc/brave/brave-flags.conf > /dev/null echo " Done." # --- Step 5: Remove GNOME Keyring + set default browser (prevents password prompts on login) --- -echo "[5/5] Removing GNOME Keyring and setting default browser..." +echo "[5/6] Removing GNOME Keyring and setting default browser..." sudo apt remove -y --purge gnome-keyring > /dev/null 2>&1 || true rm -rf ~/.local/share/keyrings if command -v brave-browser-stable > /dev/null 2>&1; then @@ -91,6 +91,71 @@ else echo " No supported browser found, skipping default." fi +# --- Step 6: Headless display (keep a desktop when no monitor is attached) --- +echo "[6/6] Configuring the headless display..." + +# x11vnc mirrors :0, but with nothing plugged in the connector has no EDID and no CRTC, so GNOME +# renders nothing and the mirror is black. Replaying a real monitor's EDID makes the connector look +# permanently attached. The EDID has to be captured from a screen that is plugged in *now* — there is +# nothing to copy otherwise — so this step is skipped on a headless run and can be re-run later. +EDID_DIR=/lib/firmware/edid +EDID_FILE="$EDID_DIR/officer-screen.bin" +CONNECTED_SYSFS="" +for c in /sys/class/drm/card*-*/status; do + [ "$(cat "$c" 2>/dev/null)" = "connected" ] || continue + CONNECTED_SYSFS="$(basename "$(dirname "$c")")" + break +done + +if [ -z "$CONNECTED_SYSFS" ] && [ ! -f "$EDID_FILE" ]; then + echo " ! No display connected and no EDID saved — skipping." + echo " Plug a monitor in and re-run this script to capture one, otherwise the remote" + echo " desktop will be black whenever nothing is attached." +else + # cardN-HDMI-A-1 -> HDMI-A-1, which is the name the kernel parameters use (card numbering can + # change between boots; the connector name does not). + if [ -n "$CONNECTED_SYSFS" ]; then + CONNECTOR="${CONNECTED_SYSFS#*-}" + sudo mkdir -p "$EDID_DIR" + sudo cp "/sys/class/drm/$CONNECTED_SYSFS/edid" "$EDID_FILE" + echo " Captured EDID from $CONNECTOR ($(stat -c%s "$EDID_FILE") bytes)." + echo "$CONNECTOR" | sudo tee "$EDID_DIR/officer-connector" > /dev/null + else + CONNECTOR="$(cat "$EDID_DIR/officer-connector" 2>/dev/null || echo HDMI-A-1)" + echo " Reusing the saved EDID for $CONNECTOR." + fi + + # drm.edid_firmware makes the connector report that EDID; the trailing "e" on video= forces it + # enabled with nothing attached. amdgpu is not in the initramfs, so /lib/firmware is readable by + # the time the driver loads and no initramfs rebuild is needed. + KERNEL_ARGS="drm.edid_firmware=$CONNECTOR:edid/officer-screen.bin video=$CONNECTOR:1920x1080e" + if grep -q "drm.edid_firmware=" /etc/default/grub; then + echo " GRUB already carries a forced EDID — leaving it alone." + else + sudo cp /etc/default/grub "/etc/default/grub.bak-$(date +%Y%m%d%H%M%S)" + CURRENT=$(grep "^GRUB_CMDLINE_LINUX_DEFAULT=" /etc/default/grub | sed 's/^[^"]*"//; s/"$//') + sudo sed -i "s|^GRUB_CMDLINE_LINUX_DEFAULT=.*|GRUB_CMDLINE_LINUX_DEFAULT=\"${CURRENT:+$CURRENT }$KERNEL_ARGS\"|" /etc/default/grub + sudo update-grub > /dev/null 2>&1 + echo " Added the forced EDID to the kernel command line (takes effect on reboot)." + fi + + # The EDID's preferred mode is whatever the captured panel was, which may be small. GNOME picks + # preferred, so raise it at session start. + install -d "$HOME/.local/bin" "$HOME/.config/autostart" + install -m 755 "$(dirname "$0")/officer-set-display.sh" "$HOME/.local/bin/officer-set-display.sh" + cat > "$HOME/.config/autostart/officer-set-display.desktop" </dev/null; then echo ""