mirror of
https://github.com/Droid-Deck/DroidDeck.git
synced 2026-11-11 11:01:07 +00:00
vulkan is the default desktop renderer (settings list it first). When labwc cannot start on a GPU renderer the desktop falls back to pixman as before, and now records the renderer with the patched wlroots and Linux driver it failed on, so later desktops go straight to pixman instead of waiting 20 s each time; an app update, a different driver, or choosing a renderer again in the settings (which deletes the record) retries.
199 lines
11 KiB
Bash
199 lines
11 KiB
Bash
#!/bin/bash
|
|
# The desktop session: labwc nested as a client of the app's compositor, with LXQt's panel and
|
|
# file manager started from its autostart, and Xwayland for the X11 programs (the Steam client,
|
|
# most emulators). Run by bannerlator-session in place of gamescope for mode "lxqt".
|
|
# Staged by the app over the desktop package's copy on every launch, so a fix here reaches an
|
|
# installed desktop without re-downloading the package.
|
|
set -u
|
|
export XDG_SESSION_TYPE=wayland XDG_CURRENT_DESKTOP=LXQt XDG_SESSION_DESKTOP=lxqt
|
|
export QT_QPA_PLATFORM=wayland GDK_BACKEND=wayland,x11 SDL_VIDEODRIVER=wayland,x11
|
|
export QT_QPA_PLATFORMTHEME=lxqt
|
|
# Firefox's child processes sandbox themselves with seccomp and namespaces, neither of which
|
|
# behaves under proot: the content process ends up unable to read the font files its parent can,
|
|
# reports "no fonts" and dies - the tab crash, with the chrome around it still drawing text.
|
|
# The prefs for this are only advisory and a profile can override them; the environment is not.
|
|
export MOZ_DISABLE_CONTENT_SANDBOX=1 MOZ_DISABLE_GMP_SANDBOX=1 MOZ_DISABLE_RDD_SANDBOX=1
|
|
export MOZ_DISABLE_SOCKET_PROCESS_SANDBOX=1 MOZ_DISABLE_UTILITY_SANDBOX=1
|
|
# pixman composites in software and gives programs shm buffers only, so a Vulkan program on it
|
|
# fails at its swapchain. vulkan (or gles2) composite on the GPU and give programs DMA-BUFs, but
|
|
# stock wlroots allocates through gbm on a real DRM node and the KGSL stand-in is not one ("unable
|
|
# to create allocator"): they need the app's patched wlroots (tools/wlroots), whose Vulkan
|
|
# allocator exports the buffers instead. BL_WLR_RENDERER picks; see the fallback at the end.
|
|
export WLR_RENDERER=${BL_WLR_RENDERER:-vulkan}
|
|
WLROOTS_DIR=/usr/local/lib/droiddeck-wlroots
|
|
BASE_LD_LIBRARY_PATH=${LD_LIBRARY_PATH:-}
|
|
use_renderer() {
|
|
export WLR_RENDERER=$1
|
|
if [ "$1" != pixman ] && [ -f "$WLROOTS_DIR/libwlroots-0.20.so" ]; then
|
|
export LD_LIBRARY_PATH=$WLROOTS_DIR${BASE_LD_LIBRARY_PATH:+:$BASE_LD_LIBRARY_PATH}
|
|
elif [ -n "$BASE_LD_LIBRARY_PATH" ]; then
|
|
export LD_LIBRARY_PATH=$BASE_LD_LIBRARY_PATH
|
|
else
|
|
unset LD_LIBRARY_PATH
|
|
fi
|
|
}
|
|
use_renderer "$WLR_RENDERER"
|
|
# A GPU renderer that failed to start here is not tried again on every desktop start (each try costs
|
|
# up to 20 s): the failure is kept with what it failed on - the renderer, the patched wlroots and the
|
|
# Linux driver - so an app update or a different driver retries, and so does picking a renderer
|
|
# again in the app's settings (which deletes the file).
|
|
FAILED_FILE=$HOME/.droiddeck-renderer-failed
|
|
renderer_key() {
|
|
echo "$WLR_RENDERER $(md5sum "$WLROOTS_DIR/libwlroots-0.20.so" 2>/dev/null | cut -d' ' -f1) ${BL_VK_DRIVER:-runtime}"
|
|
}
|
|
if [ "$WLR_RENDERER" != pixman ] && [ "$(cat "$FAILED_FILE" 2>/dev/null)" = "$(renderer_key)" ]; then
|
|
echo "== droiddeck-desktop: $WLR_RENDERER failed to start here before; using pixman (choose a renderer in the settings to retry)"
|
|
use_renderer pixman
|
|
fi
|
|
# The desktop's own log, beside the session's, so labwc and the programs on it can be read apart
|
|
# from the session that started them. The session log keeps the milestones either way.
|
|
if [ -n "${BL_DEBUG_DIR:-}" ]; then
|
|
mkdir -p "$BL_DEBUG_DIR" 2>/dev/null || true
|
|
exec >>"$BL_DEBUG_DIR/desktop.log" 2>&1
|
|
echo "== droiddeck-desktop $(date -u '+%Y-%m-%d %H:%M:%S UTC') renderer=$WLR_RENDERER"
|
|
fi
|
|
export WLR_BACKENDS=wayland
|
|
mkdir -p "$HOME/.config/labwc" "$HOME/.config/lxqt"
|
|
# Our defaults for labwc and the panel, refreshed every session. What a user changes in the
|
|
# panel's own settings lands in panel.conf and is kept; only rc.xml and autostart are ours.
|
|
# The nested output takes the app's surface size; left alone, wlroots' Wayland backend opens
|
|
# a 1280x720 window and the whole desktop is upscaled from that.
|
|
sed "s/@MODE@/${BL_WIDTH:-1280}x${BL_HEIGHT:-720}/" /etc/xdg/labwc/rc.xml > "$HOME/.config/labwc/rc.xml"
|
|
cp -f /etc/xdg/labwc/autostart "$HOME/.config/labwc/autostart"
|
|
# panel.conf is the user's once the panel has written it, except the one our first package
|
|
# shipped: it never named its panel ([General] panels=), so lxqt-panel built an empty default
|
|
# one. That file is replaced; one that names its panels is kept.
|
|
if ! grep -q '^panels=' "$HOME/.config/lxqt/panel.conf" 2>/dev/null; then
|
|
cp -f /etc/xdg/lxqt/panel.conf "$HOME/.config/lxqt/panel.conf"
|
|
fi
|
|
[ -f "$HOME/.config/lxqt/lxqt.conf" ] || cp /etc/xdg/lxqt/lxqt.conf "$HOME/.config/lxqt/lxqt.conf"
|
|
# pcmanfm-qt paints the desktop; left unconfigured it paints it black, over swaybg's wallpaper.
|
|
if [ ! -f "$HOME/.config/pcmanfm-qt/lxqt/settings.conf" ]; then
|
|
mkdir -p "$HOME/.config/pcmanfm-qt/lxqt"
|
|
printf '[Desktop]\nWallpaper=/usr/share/backgrounds/steamdeck.png\nWallpaperMode=zoom\nBgColor=#1b1030\nShowHidden=false\n' \
|
|
> "$HOME/.config/pcmanfm-qt/lxqt/settings.conf"
|
|
fi
|
|
# The runtime directory outlives a session. lxqt-panel and pcmanfm-qt are single-instance
|
|
# programs that leave a lock and a socket there; finding last session's, they decide they are
|
|
# already running and exit without a word - a desktop with a wallpaper and nothing on it.
|
|
# Only the app's own compositor socket (wayland-0) is kept.
|
|
find "$XDG_RUNTIME_DIR" -mindepth 1 -maxdepth 1 ! -name 'wayland-0*' ! -name 'keymap.xkb' -exec rm -rf {} + 2>/dev/null
|
|
# fontconfig's rules (sans-serif -> DejaVu Sans and the rest) live as links in /etc/fonts/conf.d,
|
|
# and the rootfs has none: applications then find fonts but no default font. Firefox logs
|
|
# "no fonts" and draws pages without text.
|
|
if [ -d /usr/share/fontconfig/conf.default ] && ! ls /etc/fonts/conf.d/*.conf >/dev/null 2>&1; then
|
|
for f in /usr/share/fontconfig/conf.default/*.conf; do ln -sf "$f" /etc/fonts/conf.d/; done
|
|
echo "== fontconfig: linked $(ls /etc/fonts/conf.d/*.conf | wc -l) default rules"
|
|
fi
|
|
# Those rules map sans-serif and friends onto whatever a full distribution installs, and this
|
|
# rootfs has one family. Naming it outright is what gives every toolkit a default font: without
|
|
# one Gecko reports "no fonts" with families found, and draws pages with no text at all.
|
|
if [ ! -f /etc/fonts/local.conf ]; then
|
|
{ echo '<?xml version="1.0"?><!DOCTYPE fontconfig SYSTEM "urn:fontconfig:fonts.dtd"><fontconfig>'
|
|
for generic in sans-serif sans serif monospace mono cursive fantasy system-ui ui-sans-serif; do
|
|
case $generic in
|
|
serif) face="DejaVu Serif" ;;
|
|
mono*|ui-mono) face="DejaVu Sans Mono" ;;
|
|
*) face="DejaVu Sans" ;;
|
|
esac
|
|
printf '<alias binding="weak"><family>%s</family><prefer><family>%s</family></prefer></alias>\n' "$generic" "$face"
|
|
done
|
|
echo '</fontconfig>'
|
|
} > /etc/fonts/local.conf
|
|
echo "== fontconfig: wrote local.conf (DejaVu as the default family)"
|
|
fi
|
|
# The cache in the image was built before those rules existed; rebuild it once, and only once.
|
|
if [ ! -f /var/cache/fontconfig/.droiddeck-built ] && command -v fc-cache >/dev/null 2>&1; then
|
|
fc-cache -f >/dev/null 2>&1 && touch /var/cache/fontconfig/.droiddeck-built
|
|
echo "== fontconfig: cache rebuilt, default sans is $(fc-match sans-serif 2>/dev/null)"
|
|
fi
|
|
# The menu's "Steam" / "Steam Big Picture" entries and one entry per installed game: all of them
|
|
# hand off to the app (bannerlator-steam-launch), since the client cannot draw on this desktop.
|
|
export BL_DESKTOP=1
|
|
ln -sf /usr/local/bin/bannerlator-steam-launch /usr/local/bin/steamdeck-steam 2>/dev/null
|
|
ln -sf /usr/local/bin/bannerlator-steam-shim /usr/local/bin/steam 2>/dev/null
|
|
python3 /usr/local/bin/bannerlator-desktop-games 2>&1 | sed 's/^/[games] /'
|
|
# Player 1 on the controller in every emulator that has no profile of its own yet - now, and again
|
|
# every half minute: an emulator started for the first time writes its own config, which is then
|
|
# given the controller for its next start without waiting for the next desktop.
|
|
python3 /usr/local/bin/bannerlator-pad-defaults 2>&1 | sed 's/^/[pads] /'
|
|
( while sleep 30; do python3 /usr/local/bin/bannerlator-pad-defaults 2>&1 | sed 's/^/[pads] /'; done ) &
|
|
# Games and emulators from the menu run through droiddeck-gpu (their own gamescope on the app's
|
|
# compositor) while the desktop is on pixman, where they could not draw with the GPU.
|
|
# BL_DESKTOP_GPU=on/off overrides; each entry keeps "Run in a desktop window" either way.
|
|
gpu_entries() { python3 /usr/local/bin/droiddeck-desktop-gpu 2>&1 | sed 's/^/[gpu] /'; }
|
|
gpu_entries
|
|
|
|
# ~/.droiddeck-desktop-debug: labwc's own debug log (views and layer surfaces mapping) too. The GPU
|
|
# renderers are experimental and always log in full, so a desktop that does not come up says why.
|
|
DEBUG=; { [ -f "$HOME/.droiddeck-desktop-debug" ] || [ "$WLR_RENDERER" != pixman ]; } && DEBUG=-d
|
|
run_labwc() {
|
|
echo "== droiddeck-desktop: labwc ($WLR_RENDERER${LD_LIBRARY_PATH:+, wlroots from $WLROOTS_DIR}) on $WAYLAND_DISPLAY"
|
|
# A session bus: the panel's menus, the file manager and the portal all expect one.
|
|
if command -v dbus-run-session >/dev/null 2>&1; then
|
|
dbus-run-session -- labwc $DEBUG -C "$HOME/.config/labwc"
|
|
else
|
|
labwc $DEBUG -C "$HOME/.config/labwc"
|
|
fi
|
|
}
|
|
pids_named() {
|
|
local p
|
|
for p in /proc/[0-9]*; do
|
|
[ "$(cat "$p/comm" 2>/dev/null)" = "$1" ] && echo "${p#/proc/}"
|
|
done
|
|
}
|
|
# labwc answers on its own socket (the one beside the app's wayland-0) once its event loop runs; a
|
|
# labwc stuck on the GPU never does.
|
|
labwc_answers() {
|
|
local sock
|
|
for sock in "$XDG_RUNTIME_DIR"/wayland-*; do
|
|
case $sock in *.lock|*/wayland-0) continue ;; esac
|
|
[ -S "$sock" ] || continue
|
|
WAYLAND_DISPLAY=${sock##*/} timeout 3 wayland-info >/dev/null 2>&1 && return 0
|
|
done
|
|
return 1
|
|
}
|
|
if [ "$WLR_RENDERER" = pixman ]; then
|
|
run_labwc
|
|
exit $?
|
|
fi
|
|
# A GPU renderer this device cannot start either ends the desktop at once or leaves it hanging on
|
|
# the loading screen. Either way the desktop comes up on pixman instead, and the log says why.
|
|
started=$(date +%s)
|
|
run_labwc &
|
|
labwc_job=$!
|
|
answered=
|
|
while kill -0 "$labwc_job" 2>/dev/null; do
|
|
if labwc_answers; then answered=1; break; fi
|
|
[ $(( $(date +%s) - started )) -ge 20 ] && break
|
|
sleep 1
|
|
done
|
|
if [ -n "$answered" ]; then
|
|
echo "== droiddeck-desktop: labwc on $WLR_RENDERER is up after $(( $(date +%s) - started )) s"
|
|
wait "$labwc_job"
|
|
exit $?
|
|
fi
|
|
if kill -0 "$labwc_job" 2>/dev/null; then
|
|
echo "== droiddeck-desktop: labwc on $WLR_RENDERER did not answer in 20 s; stopping it"
|
|
kill -TERM $(pids_named labwc) 2>/dev/null
|
|
sleep 2
|
|
kill -KILL $(pids_named labwc) 2>/dev/null
|
|
for prog in lxqt-panel pcmanfm-qt swaybg Xwayland; do kill -KILL $(pids_named "$prog") 2>/dev/null; done
|
|
wait "$labwc_job" 2>/dev/null
|
|
rc=124
|
|
else
|
|
wait "$labwc_job"
|
|
rc=$?
|
|
fi
|
|
if [ "$rc" -ne 0 ] && [ $(( $(date +%s) - started )) -lt 40 ]; then
|
|
echo "== droiddeck-desktop: labwc on $WLR_RENDERER failed at start (exit $rc); starting on pixman instead"
|
|
renderer_key > "$FAILED_FILE"
|
|
use_renderer pixman
|
|
DEBUG=; [ -f "$HOME/.droiddeck-desktop-debug" ] && DEBUG=-d
|
|
find "$XDG_RUNTIME_DIR" -mindepth 1 -maxdepth 1 ! -name 'wayland-0*' ! -name 'keymap.xkb' -exec rm -rf {} + 2>/dev/null
|
|
gpu_entries
|
|
run_labwc
|
|
rc=$?
|
|
fi
|
|
exit $rc
|