Files
tmux/regress/tty-keys-focus-escape.sh
Nicholas Marriott 059df9643c Tests for 5675.
2026-10-06 18:50:37 +01:00

144 lines
3.4 KiB
Bash

#!/bin/sh
# An Escape followed by a focus report must remain a separate key. The
# report's leading Escape must not be consumed as the second half of M-Escape,
# and the focus change must still reach the client and the pane.
PATH=/bin:/usr/bin
TERM=screen
export PATH TERM
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
INNER="$TEST_TMUX -LtestI$$ -f/dev/null"
OUTER="$TEST_TMUX -LtestO$$ -f/dev/null"
fail()
{
echo "$*" >&2
exit 1
}
cleanup()
{
$OUTER kill-server 2>/dev/null
$INNER kill-server 2>/dev/null
}
trap cleanup 0
trap 'exit 1' 1 2 15
$INNER new-session -d -s inner -x80 -y10 \
"printf '\033[?1004h'; stty raw -echo; exec cat -v" || exit 1
$INNER set-option -g status off || exit 1
$INNER set-option -g assume-paste-time 0 || exit 1
$INNER set-option -g focus-events on || exit 1
$INNER set-option -s escape-time 1000 || exit 1
$INNER unbind-key -a -T root || exit 1
$OUTER new-session -d -s outer -x80 -y10 "$INNER attach -t inner" || exit 1
$OUTER set-option -g assume-paste-time 0 || exit 1
i=0
while [ -z "$($INNER list-clients -F '#{client_name}')" ]; do
[ "$i" -lt 100 ] || fail 'inner client did not attach'
sleep 0.05
i=$((i + 1))
done
bind_key()
{
$INNER bind-key -n "$1" send-keys -l "$2" || exit 1
}
bind_key Escape E
bind_key M-Escape A
bind_key M-Up U
send_raw()
{
# Hex arguments avoid command separators in a partial sequence.
bytes=$(printf '%s' "$1" | od -An -v -tx1)
$OUTER send-keys -H $bytes || exit 1
}
capture()
{
$INNER capture-pane -p | tr -d '\n'
}
focused()
{
case $($INNER list-clients -F '#{client_flags}') in
*focused*)
echo yes
;;
*)
echo no
;;
esac
}
# Start unfocused, whatever the state after attaching, once the pane has
# enabled focus reporting.
i=0
while :; do
send_raw "$(printf '\033[I\033[O')"
sleep 0.05
case $(capture) in
*'^[[O')
[ "$(focused)" = no ] && break
;;
esac
[ "$i" -lt 100 ] || fail 'pane did not receive focus reports'
i=$((i + 1))
done
expected_output=$(capture)
# A focus report is forwarded to the pane directly, while the Escape binding
# runs from the command queue, so the pane may see them in either order. The
# second expected output, if not empty, is that other order.
assert_events()
{
name=$1
expected=$expected_output$2
if [ -n "$3" ]; then
alternative=$expected_output$3
else
alternative=$expected
fi
expected_focus=$4
send_raw "$5"
if [ "$#" -eq 6 ]; then
sleep 0.05
send_raw "$6"
fi
i=0
while [ "$i" -lt 100 ]; do
actual=$(capture)
actual_focus=$(focused)
if [ "$actual" = "$expected" ] ||
[ "$actual" = "$alternative" ]; then
if [ "$actual_focus" = "$expected_focus" ]; then
expected_output=$actual
return 0
fi
fi
sleep 0.05
i=$((i + 1))
done
fail "$name: got '$actual' focused=$actual_focus," \
"expected '$expected' focused=$expected_focus"
}
assert_events 'FocusIn' '^[[I' '' yes "$(printf '\033[I')"
assert_events 'FocusOut' '^[[O' '' no "$(printf '\033[O')"
assert_events 'Escape and FocusIn' 'E^[[I' '^[[IE' yes \
"$(printf '\033\033[I')"
assert_events 'Escape and FocusOut' 'E^[[O' '^[[OE' no \
"$(printf '\033\033[O')"
assert_events 'split FocusIn' 'E^[[I' '^[[IE' yes \
"$(printf '\033\033[')" 'I'
assert_events 'FocusOut split after two Escapes' 'E^[[O' '^[[OE' no \
"$(printf '\033\033')" '[O'
assert_events 'M-Escape' A '' no "$(printf '\033\033')"
assert_events 'M-Up' U '' no "$(printf '\033\033[A')"
exit 0