From 40f92eabdf8598cebd02451afb4424fe15ee6606 Mon Sep 17 00:00:00 2001 From: Diogo Ferreira Date: Tue, 4 Aug 2026 18:16:54 +0100 Subject: [PATCH] Launch apps in their own scope instead of the compositor's cgroup (#6541) * Launch apps in their own scope instead of the compositor's cgroup The launcher ran desktop entries through gtk-launch, so the app inherited quickshell's cgroup, which belongs to wayland-wm@hyprland.desktop.service. A kernel OOM kill there fails the compositor unit and tears down the whole session, dropping the user at SDDM with every window lost. A single runaway app took the desktop down three times in one afternoon. Route launches through uwsm-app so each app gets its own scope under app-graphical.slice. A runaway app now fails its own scope and the session keeps running. The post-install launches had the same inheritance bug in a milder form, where the app landed in the installer terminal's scope and died with it. 0aedef58 patched that with setsid, which detaches the session but leaves cgroup membership behind. A scope fixes it properly. * Detach post-install app launches * Preserve desktop entry launch compatibility --------- Co-authored-by: David Heinemeier Hansson --- bin/omarchy-install-and-launch | 8 +- bin/omarchy-install-editor-emacs | 2 +- bin/omarchy-install-editor-vscode | 2 +- bin/omarchy-install-editor-zed | 2 +- bin/omarchy-install-gaming-heroic | 2 +- bin/omarchy-install-gaming-steam | 2 +- shell/services/AppLibrary.qml | 9 ++- test/shell.d/app-search-test.sh | 6 +- test/shell.d/desktop-entry-launch-test.sh | 99 +++++++++++++++++++++++ 9 files changed, 118 insertions(+), 14 deletions(-) create mode 100644 test/shell.d/desktop-entry-launch-test.sh diff --git a/bin/omarchy-install-and-launch b/bin/omarchy-install-and-launch index 6a161102..53bbfbc9 100755 --- a/bin/omarchy-install-and-launch +++ b/bin/omarchy-install-and-launch @@ -1,6 +1,6 @@ #!/bin/bash -# omarchy:summary=Install a packaged app and gtk-launch it once it finishes +# omarchy:summary=Install a packaged app and launch it once it finishes # omarchy:args= # omarchy:examples=omarchy install and launch Cursor cursor-bin cursor @@ -13,5 +13,9 @@ if [[ -z $name || -z $packages || -z $desktop_id ]]; then exit 1 fi +printf -v install_message '%q' "Installing ${name}..." +printf -v desktop_id_arg '%q' "$desktop_id" + +# The subshell keeps & from backgrounding the package installation too. exec omarchy-launch-floating-terminal-with-presentation \ - "echo 'Installing ${name}...'; omarchy-pkg-add ${packages} && setsid gtk-launch ${desktop_id}" + "echo ${install_message}; omarchy-pkg-add ${packages} && (setsid uwsm-app -- gtk-launch ${desktop_id_arg} >/dev/null 2>&1 &)" diff --git a/bin/omarchy-install-editor-emacs b/bin/omarchy-install-editor-emacs index d932d04d..743b0ce6 100755 --- a/bin/omarchy-install-editor-emacs +++ b/bin/omarchy-install-editor-emacs @@ -6,4 +6,4 @@ echo "Installing Emacs..." omarchy-pkg-aur-add omarchy-emacs && omarchy-install-emacs # emacsclient opens a frame on the running daemon, not a second Emacs -setsid gtk-launch emacsclient +setsid uwsm-app -- gtk-launch emacsclient >/dev/null 2>&1 & diff --git a/bin/omarchy-install-editor-vscode b/bin/omarchy-install-editor-vscode index 1850e779..2705fc10 100755 --- a/bin/omarchy-install-editor-vscode +++ b/bin/omarchy-install-editor-vscode @@ -26,4 +26,4 @@ printf '{\n "update.mode": "none"\n}\n' > ~/.config/Code/User/settings.json # Apply Omarchy theme to VSCode omarchy-theme-set-vscode -setsid gtk-launch code +setsid uwsm-app -- gtk-launch code >/dev/null 2>&1 & diff --git a/bin/omarchy-install-editor-zed b/bin/omarchy-install-editor-zed index 9fa9f0ae..799f86c2 100755 --- a/bin/omarchy-install-editor-zed +++ b/bin/omarchy-install-editor-zed @@ -8,4 +8,4 @@ omarchy-pkg-add zed omazed # Apply Omarchy theme to Zed omazed setup -setsid gtk-launch dev.zed.Zed +setsid uwsm-app -- gtk-launch dev.zed.Zed >/dev/null 2>&1 & diff --git a/bin/omarchy-install-gaming-heroic b/bin/omarchy-install-gaming-heroic index df416a6c..8ba43ce7 100755 --- a/bin/omarchy-install-gaming-heroic +++ b/bin/omarchy-install-gaming-heroic @@ -9,4 +9,4 @@ echo "Installing Heroic Games Launcher..." omarchy-pkg-add heroic-games-launcher-bin omarchy-install-gaming-gpu-lib32 -setsid gtk-launch heroic >/dev/null 2>&1 & +setsid uwsm-app -- gtk-launch heroic >/dev/null 2>&1 & diff --git a/bin/omarchy-install-gaming-steam b/bin/omarchy-install-gaming-steam index 20dce3b6..cefe8ce6 100755 --- a/bin/omarchy-install-gaming-steam +++ b/bin/omarchy-install-gaming-steam @@ -12,4 +12,4 @@ omarchy-install-gaming-gpu-lib32 echo "" echo "Steam will start automatically now. This might take a while..." -setsid gtk-launch steam >/dev/null 2>&1 & +setsid uwsm-app -- gtk-launch steam >/dev/null 2>&1 & diff --git a/shell/services/AppLibrary.qml b/shell/services/AppLibrary.qml index d26f3c82..317af1e8 100644 --- a/shell/services/AppLibrary.qml +++ b/shell/services/AppLibrary.qml @@ -78,10 +78,11 @@ Item { var id = String(desktopId || "") if (!id) return root.beginLaunchFeedback(name) - // Pass the file name with its extension: gtk-launch only appends ".desktop" - // when the argument doesn't already end with it, so ids that themselves end - // in ".desktop" (e.g. org.telegram.desktop) would otherwise never resolve. - Util.execDetached("gtk-launch " + Util.shellQuote(id + ".desktop")) + // Start gtk-launch inside a scope under app-graphical.slice so apps do not + // inherit wayland-wm@.service. Keeping gtk-launch as the desktop-entry + // resolver supports IDs with spaces and entries that UWSM rejects. + // Keep the .desktop suffix or ids like org.telegram.desktop won't resolve. + Util.execDetached("uwsm-app -- gtk-launch " + Util.shellQuote(id + ".desktop")) } function remove(desktopId, name) { diff --git a/test/shell.d/app-search-test.sh b/test/shell.d/app-search-test.sh index c8352afc..06012221 100644 --- a/test/shell.d/app-search-test.sh +++ b/test/shell.d/app-search-test.sh @@ -99,9 +99,9 @@ assert( ) assert( - /function launch\(desktopId, name\) \{[\s\S]*?gtk-launch[\s\S]*?\n \}/.test(appLibraryQml) && - appLibraryQml.includes('Util.execDetached("gtk-launch "'), - 'app library runs desktop entry launch through the shell' + /function launch\(desktopId, name\) \{[\s\S]*?uwsm-app[\s\S]*?\n \}/.test(appLibraryQml) && + appLibraryQml.includes('Util.execDetached("uwsm-app -- gtk-launch "'), + 'app library launches desktop entries through gtk-launch in their own scope' ) assert( diff --git a/test/shell.d/desktop-entry-launch-test.sh b/test/shell.d/desktop-entry-launch-test.sh new file mode 100644 index 00000000..7e6f374f --- /dev/null +++ b/test/shell.d/desktop-entry-launch-test.sh @@ -0,0 +1,99 @@ +#!/bin/bash + +set -euo pipefail + +source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh" + +test_tmp=$(mktemp -d) +trap 'rm -rf "$test_tmp"' EXIT + +mock_bin="$test_tmp/bin" +test_home="$test_tmp/home" +mkdir -p "$mock_bin" "$test_home" + +cat >"$mock_bin/omarchy-pkg-add" <<'SH' +#!/bin/bash +printf 'pkg:%s\n' "$*" >>"$OMARCHY_TEST_LOG" +exit "${OMARCHY_TEST_PKG_STATUS:-0}" +SH + +for command in omarchy-pkg-aur-add omarchy-install-emacs omazed omarchy-theme-set-vscode omarchy-install-gaming-gpu-lib32; do + cat >"$mock_bin/$command" <<'SH' +#!/bin/bash +exit 0 +SH +done + +cat >"$mock_bin/setsid" <<'SH' +#!/bin/bash +printf 'launch:%s\n' "$*" >>"$OMARCHY_TEST_LOG" +SH + +cat >"$mock_bin/omarchy-launch-floating-terminal-with-presentation" <<'SH' +#!/bin/bash +printf '%s\n' "$1" >"$OMARCHY_TEST_PRESENTATION" +SH + +chmod +x "$mock_bin"/* + +export HOME="$test_home" +export OMARCHY_TEST_LOG="$test_tmp/launch.log" +export OMARCHY_TEST_PRESENTATION="$test_tmp/presentation" +export PATH="$mock_bin:$PATH" + +wait_for_launch() { + local expected="$1" + + for ((attempt = 0; attempt < 100; attempt++)); do + grep -Fxq "$expected" "$OMARCHY_TEST_LOG" && return 0 + sleep 0.01 + done + + return 1 +} + +assert_detached_installer_launch() { + local script="$1" + local desktop_id="$2" + + : >"$OMARCHY_TEST_LOG" + bash "$ROOT/bin/$script" + + wait_for_launch "launch:uwsm-app -- gtk-launch $desktop_id" || + fail "$script launches its desktop entry through gtk-launch in a UWSM scope" + grep -Fqx "setsid uwsm-app -- gtk-launch $desktop_id >/dev/null 2>&1 &" "$ROOT/bin/$script" || + fail "$script detaches its scoped desktop-entry launch" + pass "$script detaches its scoped desktop-entry launch" +} + +assert_detached_installer_launch omarchy-install-editor-emacs emacsclient +assert_detached_installer_launch omarchy-install-editor-vscode code +assert_detached_installer_launch omarchy-install-editor-zed dev.zed.Zed +assert_detached_installer_launch omarchy-install-gaming-heroic heroic +assert_detached_installer_launch omarchy-install-gaming-steam steam + +bash "$ROOT/bin/omarchy-install-and-launch" "Example App" "alpha beta" "Disk Usage" +presentation_command=$(<"$OMARCHY_TEST_PRESENTATION") + +[[ $presentation_command == *'echo Installing\ Example\ App...;'* ]] || + fail "generic installer shell-quotes the display name" "$presentation_command" +[[ $presentation_command == *'omarchy-pkg-add alpha beta && (setsid uwsm-app -- gtk-launch Disk\ Usage >/dev/null 2>&1 &)'* ]] || + fail "generic installer waits for packages and detaches only the scoped launch" "$presentation_command" +pass "generic installer waits for packages and detaches only the scoped launch" + +: >"$OMARCHY_TEST_LOG" +bash -c "$presentation_command" +grep -Fxq 'pkg:alpha beta' "$OMARCHY_TEST_LOG" || + fail "generic installer passes every package to the package helper" +wait_for_launch 'launch:uwsm-app -- gtk-launch Disk Usage' || + fail "generic installer preserves a desktop ID containing spaces" +pass "generic installer preserves a desktop ID containing spaces" + +: >"$OMARCHY_TEST_LOG" +if OMARCHY_TEST_PKG_STATUS=1 bash -c "$presentation_command"; then + fail "generic installer propagates package installation failure" +fi +if grep -q '^launch:' "$OMARCHY_TEST_LOG"; then + fail "generic installer does not launch after package installation failure" +fi +pass "generic installer does not launch after package installation failure"