diff options
Diffstat (limited to 'desktop')
| -rw-r--r-- | desktop/modules/status/README.md | 9 | ||||
| -rwxr-xr-x | desktop/modules/status/statusctl | 45 | ||||
| -rwxr-xr-x | desktop/modules/status/test-statusctl.sh | 41 |
3 files changed, 92 insertions, 3 deletions
diff --git a/desktop/modules/status/README.md b/desktop/modules/status/README.md index 4d18342..7ac6b93 100644 --- a/desktop/modules/status/README.md +++ b/desktop/modules/status/README.md @@ -130,6 +130,15 @@ waybar's built-in `idle_inhibitor`, which cannot be kept alongside it: that module owns its own inhibitor object, so both would have to be released before the screen could lock. +The watch emits three classes, not two. `status.gaming` turns the mode on +without touching the manual half, so a game would otherwise leave the widget +reading `deactivated` while the effects were asserted. `gaming` is its own +class with its own icon, and `toggle` is refused while it holds: the detector +reasserts the file within its poll, so the write would not stick. `set` is +still allowed, since the manual half is worth setting for when the game exits. +The watch therefore follows `status.gaming` as well as its own file, and emits +only on a real change so an unchanged rewrite draws nothing. + ## The check ./test-statusctl.sh diff --git a/desktop/modules/status/statusctl b/desktop/modules/status/statusctl index 571beef..531951b 100755 --- a/desktop/modules/status/statusctl +++ b/desktop/modules/status/statusctl @@ -82,15 +82,34 @@ tooltip() { case "$1" in activated) printf '%s: on' "$mode" ;; deactivated) printf '%s: off' "$mode" ;; + gaming) printf '%s: on (game running)' "$mode" ;; esac } +# The shell's game detector owns status.gaming and presentation is the OR of +# the two, so the CLI has to read it to agree with the shell. It is not a mode +# in MODES: nothing here may write it, and only presentation is affected. +gaming_on() { + local v="" + [[ -e "$DIR/status.gaming" ]] && v="$(tr -d '[:space:]' < "$DIR/status.gaming" 2>/dev/null)" + [[ "$v" == "1" ]] +} + # A missing file is the off state, not a distinct condition: read_mode already # reads it as 0, and this is the same path `get` uses. There is no "down": a # file cannot report whether a watcher is alive, and a missing one is exactly # what a fresh session looks like. +# +# A running game reports its own state rather than plain activated, so the +# widget can show why the mode is on and that a click will not turn it off. state_now() { - [[ "$(read_mode)" == "1" ]] && printf 'activated' || printf 'deactivated' + if [[ "$mode" == "presentation" ]] && gaming_on; then + printf 'gaming' + elif [[ "$(read_mode)" == "1" ]]; then + printf 'activated' + else + printf 'deactivated' + fi } case "$action" in @@ -106,6 +125,15 @@ case "$action" in esac ;; toggle) + # A running game pins presentation on and the detector reasserts it + # within its poll, so a toggle here would flip back on its own. Refuse + # it instead of writing a value that does not stick. `set` still works: + # it is the explicit verb, and the manual half is worth setting for + # when the game exits. + if [[ "$mode" == "presentation" ]] && gaming_on; then + printf '%s: presentation held on by a running game\n' "${0##*/}" >&2 + exit 1 + fi [[ "$(read_mode)" == "1" ]] && write_mode 0 || write_mode 1 ;; watch) @@ -123,9 +151,20 @@ case "$action" in exec 3< <(inotifywait -q -m -e 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 + # ending redraws the widget. Emitting only on a real change keeps a + # write of an unchanged value from firing a duplicate line. + last="$(state_now)" while read -r changed <&3; do - [[ "$changed" == "status.$mode" ]] || continue - emit "$(state_now)" + case "$changed" in + "status.$mode") ;; + status.gaming) [[ "$mode" == "presentation" ]] || continue ;; + *) continue ;; + esac + now="$(state_now)" + [[ "$now" == "$last" ]] && continue + last="$now" + emit "$now" done ;; *) diff --git a/desktop/modules/status/test-statusctl.sh b/desktop/modules/status/test-statusctl.sh index 7b1b188..38da27c 100755 --- a/desktop/modules/status/test-statusctl.sh +++ b/desktop/modules/status/test-statusctl.sh @@ -92,5 +92,46 @@ kill "$watcher2" 2>/dev/null wait "$watcher2" 2>/dev/null check "watch reports deactivated when absent" "1" "$(grep -c '"class": *"deactivated"' "$out2")" +# A running game is reported as its own state, not as plain activated: the +# widget draws a different icon and the tooltip says why the mode is on. +rm -f "$tmp/status.presentation" +printf '1\n' > "$tmp/status.gaming" +out3="$tmp/watch3.out" +"$ctl" presentation watch > "$out3" 2>/dev/null & +watcher3=$! +sleep 0.5 +kill "$watcher3" 2>/dev/null +wait "$watcher3" 2>/dev/null +check "watch reports gaming" "1" "$(grep -c '"class": *"gaming"' "$out3")" + +# The gaming file is the shell's, so presentation's own file stays off and +# `get` keeps reporting the manual half rather than the OR. +check "get reads the manual half" "0" "$("$ctl" presentation get)" + +# Toggling while a game runs is refused: the detector would reassert it, so a +# write here would not stick. +"$ctl" presentation toggle 2>/dev/null +check "toggle refused while gaming" "1" "$?" +check "toggle wrote nothing" "0" "$("$ctl" presentation get)" + +# The refusal is specific to presentation; another mode toggles normally with +# a game running. +"$ctl" dnd set 0 +"$ctl" dnd toggle +check "dnd toggles while gaming" "1" "$("$ctl" dnd get)" + +# A game ending redraws the widget, which is why the watch follows +# status.gaming as well as its own file. +out4="$tmp/watch4.out" +"$ctl" presentation watch > "$out4" 2>/dev/null & +watcher4=$! +sleep 0.3 +printf '0\n' > "$tmp/status.gaming.tmp" +mv -f "$tmp/status.gaming.tmp" "$tmp/status.gaming" +sleep 0.5 +kill "$watcher4" 2>/dev/null +wait "$watcher4" 2>/dev/null +check "game ending redraws" "1" "$(grep -c '"class": *"deactivated"' "$out4")" + printf '\n%d passed, %d failed\n' "$pass" "$fail" [[ "$fail" -eq 0 ]] |
