Skip to content

fix: don't block startup on the desktop portal - #5270

Open
shivankgarg98 wants to merge 1 commit into
Alexays:masterfrom
shivankgarg98:fix/portal-async-appearance
Open

fix: don't block startup on the desktop portal#5270
shivankgarg98 wants to merge 1 commit into
Alexays:masterfrom
shivankgarg98:fix/portal-async-appearance

Conversation

@shivankgarg98

Copy link
Copy Markdown
Contributor

What does this PR do?
Waybar reads org.freedesktop.appearance color-scheme through org.freedesktop.portal.Settings to pick a light or dark stylesheet. Both halves of that read are synchronous and sit ahead of setupCss(), so the bar cannot draw until the portal answers:

  • the Gio::DBus::Proxy is constructed with default flags, so it asks the bus to spawn the portal and waits for the activation, and
  • the Read itself uses call_sync() with the default 25 s timeout.

A portal that is slow to activate therefore delays the whole bar. This is a long-standing complaint (#1266, #2675, #3140, #3714); every answer in those threads is a workaround such as masking a portal backend or rewriting the activation environment, because Waybar itself has no way to opt out.

Pass DO_NOT_AUTO_START so Waybar stops being the thing that launches the portal, skip the call when the name has no owner, and issue the Read asynchronously. The result is delivered through the existing signal_appearance_changed() path that Client already connects to setupCss(), so a late answer still restyles the bar. Watching g-name-owner covers a portal that only appears after Waybar starts.

Reproduced with a user drop-in that makes activation slow:

mkdir -p ~/.config/systemd/user/xdg-desktop-portal.service.d
printf '[Service]\nExecStartPre=/bin/sleep 30\n' >
~/.config/systemd/user/xdg-desktop-portal.service.d/slow.conf
systemctl --user daemon-reload
systemctl --user stop xdg-desktop-portal.service
waybar -c min.jsonc -s min.css

Time between the "Using configuration file" and "Using CSS file" log lines, which is the window where no bar is on screen:

before 25.001 s
after 0.001 s

Also verified on the session bus that no Settings.Read is issued while the portal has no owner, that exactly one is issued once it appears, and that Waybar no longer triggers the portal's D-Bus activation.

Trade-off worth naming: getStyle() now runs before the reply arrives, so a user with style-light.css/style-dark.css gets style.css first and the appearance-specific sheet a moment later, through the existing signal_appearance_changed() -> setupCss() path. Measured here that second pass lands 11 ms after the first, i.e. inside one frame. Changing the system appearance while Waybar runs is unaffected and produces the same log sequence as before the patch.

Related issues
Closes #1266, #2675, #3140, #3714

Checklist

  • Code is formatted with clang-format
  • Builds locally (ninja -C build)
  • Man page updated for any new/changed user-facing option (man/)
  • Tested against the affected module(s)

Waybar reads org.freedesktop.appearance color-scheme through
org.freedesktop.portal.Settings to pick a light or dark stylesheet. Both
halves of that read are synchronous and sit ahead of setupCss(), so the
bar cannot draw until the portal answers:

  - the Gio::DBus::Proxy is constructed with default flags, so it asks
    the bus to *spawn* the portal and waits for the activation, and
  - the Read itself uses call_sync() with the default 25 s timeout.

A portal that is slow to activate therefore delays the whole bar. This
is a long-standing complaint (Alexays#1266, Alexays#2675, Alexays#3140, Alexays#3714); every answer
in those threads is a workaround such as masking a portal backend or
rewriting the activation environment, because Waybar itself has no way
to opt out.

Pass DO_NOT_AUTO_START so Waybar stops being the thing that launches the
portal, skip the call when the name has no owner, and issue the Read
asynchronously. The result is delivered through the existing
signal_appearance_changed() path that Client already connects to
setupCss(), so a late answer still restyles the bar. Watching
g-name-owner covers a portal that only appears after Waybar starts.

Reproduced with a user drop-in that makes activation slow:

  mkdir -p ~/.config/systemd/user/xdg-desktop-portal.service.d
  printf '[Service]\nExecStartPre=/bin/sleep 30\n' > \
    ~/.config/systemd/user/xdg-desktop-portal.service.d/slow.conf
  systemctl --user daemon-reload
  systemctl --user stop xdg-desktop-portal.service
  waybar -c min.jsonc -s min.css

Time between the "Using configuration file" and "Using CSS file" log
lines, which is the window where no bar is on screen:

  before   25.001 s
  after     0.001 s

Also verified on the session bus that no Settings.Read is issued while
the portal has no owner, that exactly one is issued once it appears, and
that Waybar no longer triggers the portal's D-Bus activation.

Trade-off worth naming: getStyle() now runs before the reply arrives, so a
user with style-light.css/style-dark.css gets style.css first and the
appearance-specific sheet a moment later, through the existing
signal_appearance_changed() -> setupCss() path. Measured here that second
pass lands 11 ms after the first, i.e. inside one frame. Changing the
system appearance while Waybar runs is unaffected and produces the same
log sequence as before the patch.
@shivankgarg98

Copy link
Copy Markdown
Contributor Author

test: waybar-portal-ab /usr/bin/waybar ./build/waybar

#!/bin/bash
# A/B test for "waybar blocks on the desktop portal at startup".
#
#   waybar-portal-ab <baseline-waybar> <patched-waybar>
#
# Makes the portal slow to activate using a *user-level* systemd drop-in (no
# root), then starts each binary with the portal stopped and measures the gap
# between its "Using configuration file" and "Using CSS file" log lines. That
# gap is the window in which no bar is on screen.
#
# Expected: baseline ~25 s (the default GDBus reply timeout), patched ~0 s.
#
# Everything is restored on exit, including on Ctrl-C.

set -u

BASE=${1:-}
PATCHED=${2:-}
if [ -z "$BASE" ] || [ -z "$PATCHED" ]; then
	sed -n '2,14p' "$0" | sed 's/^# \{0,1\}//'
	exit 2
fi
for b in "$BASE" "$PATCHED"; do
	[ -x "$b" ] || { echo "not executable: $b" >&2; exit 2; }
done

WORK=$(mktemp -d "${TMPDIR:-/tmp}/waybar-portal-ab.XXXXXX")
DROPIN=$HOME/.config/systemd/user/xdg-desktop-portal.service.d
SLEEP_FOR=30      # how long portal activation is made to take
WAIT_FOR=40       # must exceed both SLEEP_FOR and the 25 s D-Bus timeout

cleanup() {
	rm -rf "$DROPIN" "$WORK"
	systemctl --user daemon-reload 2>/dev/null
	systemctl --user start xdg-desktop-portal.service 2>/dev/null
	echo "cleaned up (portal: $(systemctl --user is-active xdg-desktop-portal.service 2>/dev/null))"
}
trap cleanup EXIT INT TERM

# A bar with no modules, so nothing clashes with a bar you already have running.
cat > "$WORK/config.jsonc" <<'EOF'
{"layer":"bottom","position":"bottom","height":1,
 "modules-left":[],"modules-center":[],"modules-right":[]}
EOF
echo '* { }' > "$WORK/style.css"

mkdir -p "$DROPIN"
printf '[Service]\nExecStartPre=/bin/sleep %d\n' "$SLEEP_FOR" > "$DROPIN/slow.conf"
systemctl --user daemon-reload

measure() { # $1 label, $2 binary
	systemctl --user stop xdg-desktop-portal.service 2>/dev/null
	sleep 2
	local log="$WORK/$1.log"
	"$2" -c "$WORK/config.jsonc" -s "$WORK/style.css" >"$log" 2>&1 &
	local pid=$!
	sleep "$WAIT_FOR"
	kill "$pid" 2>/dev/null; wait "$pid" 2>/dev/null

	python3 - "$1" "$log" <<'PY'
import re, sys, datetime
label, path = sys.argv[1], sys.argv[2]
def stamp(pattern):
    for line in open(path):
        m = re.match(r'\[([^\]]+)\].*' + pattern, line)
        if m:
            return datetime.datetime.strptime(m.group(1), "%Y-%m-%d %H:%M:%S.%f")
    return None
a, b = stamp("Using configuration file"), stamp("Using CSS file")
if a and b:
    print("  %-10s no bar on screen for %7.3f s" % (label, (b - a).total_seconds()))
else:
    print("  %-10s could not measure (config=%s css=%s)" % (label, a, b))
PY
}

echo "portal activation delayed by ${SLEEP_FOR}s; each run waits ${WAIT_FOR}s"
echo
measure baseline "$BASE"
measure patched  "$PATCHED"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Start time of 25 seconds

1 participant