diff --git a/CHANGES b/CHANGES index eaf7576da..5c4ff9664 100644 --- a/CHANGES +++ b/CHANGES @@ -87,6 +87,9 @@ CHANGES FROM 3.7b TO 3.8 while scrolling or while hovered and disappears after pane-scrollbars-timeout (Michael Grant). +* Add a #{A/count:frames} modifier to show a series of frames as an animation + in the status line (issue 5412 from Fernando Daciuk). + * Change command templates so %% is escaped for single quotes while %1 remains unquoted, preventing single quotes in session or target names from terminating quoted command sections (reported by Aliz Hammond). diff --git a/regress/format-animation.sh b/regress/format-animation.sh new file mode 100644 index 000000000..b62b04444 --- /dev/null +++ b/regress/format-animation.sh @@ -0,0 +1,109 @@ +#!/bin/sh + +# Exercise the A format modifier (format_cycle in format.c). It is expanded +# only in a status format and arms the client cycle timer to redraw the status +# line as the frames advance, so the only place its behaviour shows is a +# rendered status line. +# +# The status line is rendered by an inner tmux attached inside an outer tmux +# pane; capturing the outer pane shows what the inner tmux drew. The inner +# window runs sleep and every other status line is blanked, so the capture +# contains the frame and nothing else. +# +# Each frame lasts 700 milliseconds and the capture is repeated once a second, +# so consecutive samples always land on a different frame: no exact period is +# assumed and nothing in the test asks for a redraw, so seeing the frame change +# means the status line animated on its own. + +PATH=/bin:/usr/bin +TERM=screen +LC_ALL=C.UTF-8 +export TERM LC_ALL + +[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) +TMUX="$TEST_TMUX -LtestA$$ -f/dev/null" +TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null" + +TMP=$(mktemp) +trap "rm -f $TMP; $TMUX kill-server 2>/dev/null; $TMUX2 kill-server 2>/dev/null" \ + 0 1 15 + +fail() { + echo "$*" >&2 + exit 1 +} + +# Capture the outer pane and return the text the inner tmux drew. +capture() { + $TMUX capturep -p >$TMP || fail "capture failed" + tr -d '[:space:]' <$TMP +} + +$TMUX kill-server 2>/dev/null +$TMUX2 kill-server 2>/dev/null + +# Inner: a window which draws nothing and a status line which is only the +# animation, so the outer capture is the current frame on its own. +$TMUX2 new -d -x30 -y8 "sh -c 'exec sleep 100'" || exit 1 +$TMUX2 set -g window-size latest || exit 1 +i=1 +while [ $i -le 4 ]; do + $TMUX2 set -g status-format[$i] "" || exit 1 + i=$((i + 1)) +done +$TMUX2 set -g status-format[0] "#{A/7:AAAA,BBBB,CCCC}" || exit 1 + +$TMUX new -d -x30 -y8 || exit 1 +$TMUX set -g status off || exit 1 +$TMUX set -g window-size manual || exit 1 +$TMUX set -g default-terminal "tmux-256color" || exit 1 +$TMUX send -l "$TMUX2 attach" || exit 1 +$TMUX send Enter || exit 1 +sleep 1 + +# Sample the frames. Every sample must be one of the frames and at least two +# different ones must be seen; status-interval is 15 seconds so only the cycle +# timer can have redrawn the status line. +seen="" +i=0 +while [ $i -lt 4 ]; do + [ $i -eq 0 ] || sleep 1 + frame=$(capture) + case "$frame" in + AAAA|BBBB|CCCC) ;; + *) fail "status line is '$frame', not a frame" ;; + esac + case " $seen " in + *" $frame "*) ;; + *) seen="$seen $frame" ;; + esac + i=$((i + 1)) +done +set -- $seen +[ $# -ge 2 ] || fail "status line did not animate, only saw$seen" + +# Outside a status format there is no animation at all. +out=$($TMUX2 display-message -p "#{A:ZZZZ,YYYY}") || exit 1 +[ -z "$out" ] || fail "display-message gave '$out', want empty" +out=$($TMUX2 list-panes -F "#{A:ZZZZ,YYYY}") || exit 1 +[ -z "$out" ] || fail "list-panes gave '$out', want empty" + +# Nor inside #(), where the frames would change the command on every frame. +$TMUX2 set -g status-format[0] '#(echo "[#{A:ZZZZ,YYYY}]")' || exit 1 +sleep 1 +out=$(capture) +case "$out" in +*ZZZZ*|*YYYY*) fail "job command saw a frame: '$out'" ;; +*"[]"*) ;; +*) fail "job output is '$out', want []" ;; +esac + +# Empty frames and a missing or bad count must not upset the server. +for f in '#{A:}' '#{A/0:a,b}' '#{A/x:a,b}' '#{A:,,,}'; do + $TMUX2 set -g status-format[0] "$f" || fail "setting $f failed" + $TMUX2 has-session >/dev/null 2>&1 || fail "server lost with $f" +done +sleep 1 +$TMUX2 has-session >/dev/null 2>&1 || fail "server lost after empty frames" + +exit 0