From fa729f139ce0d7dce7addc4e1da26c1cc870db4d Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Mon, 28 Sep 2026 11:43:34 +0200 Subject: [PATCH] Read the default browser from mimeapps.list when launching (#13520) * Read the default browser from mimeapps.list when launching omarchy-launch-browser and omarchy-launch-webapp asked xdg-settings for the default browser on every launch, ~72ms through several POSIX shell scripts. The new omarchy-cmd-default-browser reads the http handler from the config-dir mimeapps.list files the way xdg-mime does, desktop-specific list first, taking the first listed entry whose command is installed, in ~10ms. With nothing set there it falls back to xdg-settings as before. Co-Authored-By: Claude Opus 5.5 * Let the first copy of a browser entry decide whether it is installed A user-local entry whose browser is gone could be rescued by a system copy of the same ID, which the launchers never read. xdg-mime judges the first copy found, as the launchers use it, so do the same. Co-Authored-By: Claude Opus 5.5 --------- Co-authored-by: Claude Opus 5.5 --- bin/omarchy-cmd-default-browser | 58 +++++++++++++++++ bin/omarchy-launch-browser | 5 +- bin/omarchy-launch-webapp | 4 +- test/shell.d/default-browser-lookup-test.sh | 72 +++++++++++++++++++++ 4 files changed, 132 insertions(+), 7 deletions(-) create mode 100755 bin/omarchy-cmd-default-browser create mode 100644 test/shell.d/default-browser-lookup-test.sh diff --git a/bin/omarchy-cmd-default-browser b/bin/omarchy-cmd-default-browser new file mode 100755 index 00000000..f2ce9aa3 --- /dev/null +++ b/bin/omarchy-cmd-default-browser @@ -0,0 +1,58 @@ +#!/bin/bash + +# omarchy:summary=Print the desktop entry ID of the default web browser +# omarchy:hidden=true + +# Read the http handler from the config-dir mimeapps.list files the way xdg-mime +# does, desktop-specific list first, taking the first listed entry that is +# installed. xdg-settings reaches the same answer through several shell scripts +# in ~70ms, which every browser and web app launch would otherwise pay. +IFS=: read -ra data_dirs <<<"${XDG_DATA_HOME:-$HOME/.local/share}:${XDG_DATA_DIRS:-/usr/local/share:/usr/share}" +IFS=: read -ra config_dirs <<<"${XDG_CONFIG_HOME:-$HOME/.config}:${XDG_CONFIG_DIRS:-/etc/xdg}" +IFS=: read -ra desktops <<<"${XDG_CURRENT_DESKTOP:-}" + +# Installed means the command of the entry the launchers will read, the first +# one found, still resolves: an entry of Omarchy's own can outlive the browser +# it launched. +installed() { + local data_dir command + + for data_dir in "${data_dirs[@]}"; do + [[ -r $data_dir/applications/$1 ]] || continue + command=$(sed -n 's/^Exec=\([^ ]*\).*/\1/p' "$data_dir/applications/$1" | head -1) + [[ -n $command ]] && command -v "$command" >/dev/null + return + done + + return 1 +} + +for config_dir in "${config_dirs[@]}"; do + for desktop in "${desktops[@]}" ""; do + list="$config_dir/${desktop:+${desktop,,}-}mimeapps.list" + [[ -f $list ]] || continue + + entries=$(awk ' + /^\[Default Applications\]/ { section = 1; next } + /^\[/ { section = 0 } + section && index($0, "x-scheme-handler/http=") == 1 { print substr($0, 23); exit } + ' "$list") + + IFS=';' read -ra entries <<<"$entries" + for entry in "${entries[@]}"; do + if [[ -n $entry ]] && installed "$entry"; then + echo "$entry" + exit 0 + fi + done + done +done + +# Nothing set in a config dir: let xdg-settings search the data dirs and caches. +# It consults $BROWSER too, which the session points back at omarchy-launch-browser. +browser=$(env -u BROWSER xdg-settings get default-web-browser 2>/dev/null) +if [[ -z $browser ]]; then + browser=$(xdg-mime query default x-scheme-handler/https 2>/dev/null) +fi + +[[ -n $browser ]] && echo "$browser" diff --git a/bin/omarchy-launch-browser b/bin/omarchy-launch-browser index 044b220d..5bc69b72 100755 --- a/bin/omarchy-launch-browser +++ b/bin/omarchy-launch-browser @@ -3,10 +3,7 @@ # omarchy:summary=Launch the default browser as determined by xdg-settings. # omarchy:args=[url] -default_browser=$(env -u BROWSER xdg-settings get default-web-browser) -if [[ -z $default_browser ]]; then - default_browser=$(xdg-mime query default x-scheme-handler/https) -fi +default_browser=$(omarchy-cmd-default-browser) browser_exec=$(sed -n 's/^Exec=\([^ ]*\).*/\1/p' {~/.local,~/.nix-profile,/usr}/share/applications/$default_browser 2>/dev/null | head -1) if $browser_exec --help 2>/dev/null | grep -q MOZ_LOG; then diff --git a/bin/omarchy-launch-webapp b/bin/omarchy-launch-webapp index ff4d0c6e..5c250683 100755 --- a/bin/omarchy-launch-webapp +++ b/bin/omarchy-launch-webapp @@ -3,9 +3,7 @@ # omarchy:summary=Launch a URL as a web app in the default supported browser # omarchy:args= -# The session sets BROWSER to omarchy-launch-browser, which sends xdg-settings -# down a slower path (~100ms against ~30ms) to the same answer. -browser=$(env -u BROWSER xdg-settings get default-web-browser) +browser=$(omarchy-cmd-default-browser) case $browser in google-chrome* | brave* | microsoft-edge* | opera* | vivaldi* | helium*) ;; diff --git a/test/shell.d/default-browser-lookup-test.sh b/test/shell.d/default-browser-lookup-test.sh new file mode 100644 index 00000000..958fb0c4 --- /dev/null +++ b/test/shell.d/default-browser-lookup-test.sh @@ -0,0 +1,72 @@ +#!/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" +mkdir -p "$mock_bin" "$test_tmp/config" "$test_tmp/data/applications" "$test_tmp/system/applications" + +export HOME="$test_tmp/home" +export XDG_CONFIG_HOME="$test_tmp/config" +export XDG_CONFIG_DIRS="$test_tmp/etc" +export XDG_DATA_HOME="$test_tmp/data" +export XDG_DATA_DIRS="$test_tmp/system" +export XDG_CURRENT_DESKTOP=Hyprland +export BROWSER=omarchy-launch-browser +export OMARCHY_TEST_XDG_SETTINGS_LOG="$test_tmp/xdg-settings" + +cat >"$mock_bin/xdg-settings" <<'SH' +#!/bin/bash +printf '%s\n' "${BROWSER:-unset}" >"$OMARCHY_TEST_XDG_SETTINGS_LOG" +echo fallback.desktop +SH +export PATH="$mock_bin:$ROOT/bin:$PATH" + +for browser in chromium firefox brave; do + printf '#!/bin/bash\n' >"$mock_bin/$browser" +done +chmod +x "$mock_bin"/* + +echo "Exec=chromium %U" >"$XDG_DATA_HOME/applications/chromium.desktop" +echo "Exec=firefox %u" >"$XDG_DATA_HOME/applications/firefox.desktop" +echo "Exec=brave %U" >"$XDG_DATA_DIRS/applications/brave-browser.desktop" +# An entry of Omarchy's own left behind by a browser since removed, shadowing +# a system copy that the launchers would never read. +echo "Exec=removed-browser %U" >"$XDG_DATA_HOME/applications/removed.desktop" +echo "Exec=brave %U" >"$XDG_DATA_DIRS/applications/removed.desktop" + +cat >"$XDG_CONFIG_HOME/mimeapps.list" <<'EOF' +[Added Associations] +x-scheme-handler/http=firefox.desktop; + +[Default Applications] +text/html=firefox.desktop +x-scheme-handler/http=missing.desktop;removed.desktop;chromium.desktop; +EOF + +[[ $(omarchy-cmd-default-browser) == "chromium.desktop" ]] || + fail "default browser is the first installed http handler in mimeapps.list" +[[ ! -e $OMARCHY_TEST_XDG_SETTINGS_LOG ]] || + fail "default browser skips xdg-settings when mimeapps.list names one" + +cat >"$XDG_CONFIG_HOME/hyprland-mimeapps.list" <<'EOF' +[Default Applications] +x-scheme-handler/http=brave-browser.desktop +EOF + +[[ $(omarchy-cmd-default-browser) == "brave-browser.desktop" ]] || + fail "default browser prefers the desktop-specific mimeapps.list" + +rm "$XDG_CONFIG_HOME/hyprland-mimeapps.list" +printf '[Default Applications]\nx-scheme-handler/http=missing.desktop\n' >"$XDG_CONFIG_HOME/mimeapps.list" + +[[ $(omarchy-cmd-default-browser) == "fallback.desktop" ]] || + fail "default browser falls back to xdg-settings when no listed handler is installed" +[[ $(<"$OMARCHY_TEST_XDG_SETTINGS_LOG") == "unset" ]] || + fail "default browser unsets BROWSER before asking xdg-settings" + +pass "default browser reads mimeapps.list and falls back to xdg-settings"