From 8af37014d58918da714c741954de92881dcb2554 Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Sat, 3 Oct 2026 09:53:10 +0200 Subject: fix(status): watch for create, so a mode's first write is seen Qt's atomic write builds the file as an unnamed O_TMPFILE. When the target does not exist yet it links that straight into place, and the only event naming the file is IN_CREATE: no close_write, no moved_to. statusctl watch did not listen for create, so the first write of each mode in a session went unseen. On a fresh session that is the write that matters: a Teams call started an hour in left the waybar pill reading off until some later rename happened to redraw it. The test reproduces the event with a hard link, which is what the linkat from Qt's temp file looks like to inotify. Co-Authored-By: Claude Opus 5.5 --- desktop/modules/status/statusctl | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'desktop/modules/status/statusctl') diff --git a/desktop/modules/status/statusctl b/desktop/modules/status/statusctl index 839377e..87c2cac 100755 --- a/desktop/modules/status/statusctl +++ b/desktop/modules/status/statusctl @@ -59,7 +59,8 @@ read_mode() { # Write through a temporary file and rename, so no reader ever sees a # half-written value. This is also what FileView does on the QML side, and it -# is why a watcher has to listen for moved_to as well as close_write. +# is why a watcher has to listen for moved_to as well as close_write, and +# create for FileView's first write (see watch below). write_mode() { local want="$1" tmp tmp="$(mktemp "$DIR/.status.$mode.XXXXXX")" || exit 1 @@ -142,13 +143,19 @@ case "$action" in # the file, so a watch held on the old inode dies with it. This is the # same trap the mail watcher hit with Xapian. # + # create is needed as well: Qt's atomic write builds the file as an + # unnamed O_TMPFILE, and when the target does not exist yet it links + # that straight into place. The only event naming the file is then + # IN_CREATE, so without it the first write of every session, the one + # that matters most, goes unseen until some later write. + # # inotifywait must die with us. Piped straight into the while loop it # would be a pipeline sibling, not a child, so a plain kill on this # process (which is exactly how waybar stops and respawns its exec # children on every reload) leaves it running, watching a directory # nobody reads anymore. Process substitution makes it a real child # whose PID we can hold and kill from a trap. - exec 3< <(inotifywait -q -m -e close_write,moved_to,delete --format '%f' "$DIR" 2>/dev/null) + exec 3< <(inotifywait -q -m -e create,close_write,moved_to,delete --format '%f' "$DIR" 2>/dev/null) watcher=$! trap 'kill "$watcher" 2>/dev/null' EXIT TERM INT # Presentation also follows status.gaming, so a game starting or -- cgit v1.2.3