Merge master into 4902-image-support

This commit is contained in:
Nicholas Marriott
2026-10-13 21:18:26 +01:00
25 changed files with 2471 additions and 2117 deletions

View File

@@ -378,4 +378,23 @@ done
[ "$i" -lt 50 ] || fail "tagged windows were not killed"
exit_mode q
# --- killing the mode's own session -----------------------------------------
# Killing the attached session also destroys the pane hosting the tree. The
# confirmation callback must finish without redrawing the destroyed mode.
$TMUX set-option -g detach-on-destroy off || exit 1
$TMUX choose-tree -s -t aaa:0 -F 'G7' -O name || exit 1
wait_count ': G7' 2
$TMUX send-keys -t aaa:0 g x || fail "send-keys session kill failed"
wait_for 'Kill session aaa'
$TMUX send-keys -t aaa:0 y || fail "session kill confirmation failed"
i=0
while [ "$i" -lt 50 ]; do
sessions=$($TMUX list-sessions -F '#{session_name}' 2>/dev/null)
client_session=$($TMUX list-clients -F '#{client_session}' 2>/dev/null)
[ "$sessions" = 'zzz' ] && [ "$client_session" = 'zzz' ] && break
sleep 0.2
i=$((i + 1))
done
[ "$i" -lt 50 ] || fail "killing the tree session killed the server"
exit 0

View File

@@ -59,7 +59,13 @@ os.kill(client, signal.SIGTERM)
size(master, 23, 61, 0, 0)
os.kill(client, signal.SIGWINCH)
os.kill(client, signal.SIGCONT)
# macOS drains the controlling terminal when its session leader exits.
# Keep reading output so the client can finish exiting.
for i in range(200):
try:
os.read(master, 65536)
except OSError:
pass
if os.waitpid(client, os.WNOHANG)[0] != 0:
break
time.sleep(0.05)

View File

@@ -109,6 +109,7 @@ pane_bg
pane_bottom
pane_command_duration
pane_command_end_time
pane_command_line
pane_command_running
pane_command_start_time
pane_command_status

View File

@@ -1,88 +0,0 @@
#!/bin/sh
. ./input-common.inc
# Exercise expansion by VS16, a skin tone modifier and a regional indicator.
for kind in variation modifier flag; do
case "$kind" in
variation)
combine_seq='\342\234\224\357\270\217'
text='✔️'
;;
modifier)
combine_seq='\342\230\235\360\237\217\273'
text='☝🏻'
;;
flag)
combine_seq='\360\237\207\254\360\237\207\247'
text='🇬🇧'
;;
esac
start_pane "$kind-overwrite" 10 3 "A\347\225\214BC\r${combine_seq}X"
check_capture "$kind-overwrite" "${text}XBC"
check_cursor "$kind-overwrite" '3,0'
check_raw_matches "$kind-overwrite" \
"C 0,0 data=\(2,[0-9]+,$text\) flags=NONE\[0\]" \
'C 0,1 .*flags=PADDING\[[0-9a-f]+\]' \
'C 0,2 data=\(1,1,X\) flags=NONE\[0\]'
start_pane "$kind-edge" 5 3 "abcd${combine_seq}X"
check_capture "$kind-edge" "abcd
${text}X"
check_cursor "$kind-edge" '3,1'
check_raw_matches "$kind-edge" \
'L 0 \(0\) flags=.*WRAPPED' \
'C 0,4 data=\(1,1, \)' \
"C 1,0 data=\(2,[0-9]+,$text\) flags=NONE\[0\]" \
'C 1,1 .*flags=PADDING\[[0-9a-f]+\]' \
'C 1,2 data=\(1,1,X\) flags=NONE\[0\]'
start_pane "$kind-fit" 5 3 "abc${combine_seq}X"
check_capture "$kind-fit" "abc${text}
X"
check_cursor "$kind-fit" '1,1'
start_pane "$kind-scroll" 5 2 "11111\r\nabcd${combine_seq}X"
check_capture "$kind-scroll" "abcd
${text}X"
check_cursor "$kind-scroll" '3,1'
start_pane "$kind-region" 5 4 \
"top\033[2;1Hold\033[4;1Hend\033[2;3r\033[3;1Habcd${combine_seq}X"
check_capture "$kind-region" "top
abcd
${text}X
end"
check_cursor "$kind-region" '3,2'
start_pane "$kind-insert" 10 3 "ABCDE\r\033[4h${combine_seq}X"
check_capture "$kind-insert" "${text}XABCDE"
check_cursor "$kind-insert" '3,0'
start_pane "$kind-insert-edge" 5 3 \
"\033[2;1HABCDE\033[1;1Habcd\033[4h${combine_seq}X"
check_capture "$kind-insert-edge" "abcd
${text}XAB"
check_cursor "$kind-insert-edge" '3,1'
start_pane "$kind-nowrap" 5 3 "\033[?7labc${combine_seq}"
check_capture "$kind-nowrap" "abc${text}"
check_cursor "$kind-nowrap" '4,0'
check_raw_matches "$kind-nowrap" \
"C 0,3 data=\(2,[0-9]+,$text\) flags=NONE\[0\]" \
'C 0,4 .*flags=PADDING\[[0-9a-f]+\]'
done
# A two-column cluster cannot fit in a one-column pane. Keep its narrow base.
start_pane one-column 1 3 '\342\234\224\357\270\217'
check_capture one-column '✔'
check_cursor one-column '1,0'
check_raw_no_matches one-column 'data=\(2,'
# Without wrapping, even a wide cell filling the pane leaves the cursor inside.
start_pane two-columns-nowrap 2 3 '\033[?7l\342\234\224\357\270\217'
check_capture two-columns-nowrap '✔️'
check_cursor two-columns-nowrap '1,0'
exit $exit_status

142
regress/input-osc133.sh Normal file
View File

@@ -0,0 +1,142 @@
#!/bin/sh
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"
trap '$TMUX kill-server 2>/dev/null' 0
trap 'exit 1' 1 2 15
check()
{
format=$1
expected=$2
actual=$($TMUX -u display-message -p -t "$pane" "$format") || exit 1
if [ "$actual" != "$expected" ]; then
printf 'format: %s\nexpected: %s\nactual: %s\n' \
"$format" "$expected" "$actual"
exit 1
fi
}
emit()
{
printf '\033]133;%s\033\\' "$1" |
$TMUX display-message -I -t "$pane" || exit 1
}
check_command()
{
emit "C;$1"
check '#{pane_command_line}' "$2"
check '#{@started}' "$2"
check '#{pane_command_running}:#{pane_command_status}' '1:'
emit 'D;7'
check '#{pane_command_line}' "$2"
check '#{@finished}' "$2"
check '#{pane_command_running}:#{pane_command_status}' '0:7'
}
$TMUX new-session -d -s test 'sleep 1000' || exit 1
pane=$($TMUX new-window -d -P -F '#{pane_id}' '') || exit 1
other=$($TMUX new-window -d -P -F '#{pane_id}' '') || exit 1
$TMUX set-hook -g pane-command-started \
'set -gF @started "#{hook_command_line}"' || exit 1
$TMUX set-hook -g pane-command-finished \
'set -gF @finished "#{hook_command_line}"' || exit 1
check '#{pane_command_line}:#{pane_command_running}' ':0'
check_command 'cmdline_url=sleep%201' 'sleep 1'
# Prompt markers leave the most recent command available.
emit A
emit B
check '#{pane_command_line}' 'sleep 1'
$TMUX display-message -p -t "$other" '#{pane_command_line}' |
grep -qx '' || exit 1
# Unknown fields are ignored and decoded semicolons belong to the value.
check_command 'other=1;cmdline_url=echo%20one%3B%20echo%20two;other=2' \
'echo one; echo two'
check_command 'cmdline_url=echo%20a+b%2Bc%25%3d%3D' 'echo a+b+c%=='
check_command 'cmdline_url=echo%20%C3%A9%E4%B8%AD' 'echo é中'
check_command 'cmdline_url=echo%20%2520' 'echo %20'
# Use the same cleanup as titles for control characters and format jobs.
check_command 'cmdline_url=echo%20%23%28date%29' 'echo _(date)'
check_command 'cmdline_url=echo%20%5C' 'echo \\'
# Multiline commands retain tabs and newlines as visible escapes, including
# in the event payloads used by hooks.
check_command 'cmdline_url=sleep%201%0Aecho%20done' 'sleep 1\necho done'
check_command 'cmdline_url=echo%09hello%0Aworld' 'echo\thello\nworld'
# The name validator also permits these bytes before clean_name escapes them.
name=$(printf 'tab\tnewline\nname')
$TMUX rename-window -t "$other" "$name" || exit 1
actual=$($TMUX -u display-message -p -t "$other" '#{window_name}') || exit 1
[ "$actual" = 'tab\tnewline\nname' ] || exit 1
$TMUX select-pane -t "$pane" -T "$name" || exit 1
check '#{pane_title}' 'tab\tnewline\nname'
# Every new start clears old metadata if it is missing or invalid.
emit C
check '#{pane_command_line}:#{@started}' ':'
for metadata in \
'cmdline_url=' \
'other=cmdline_url=stale' \
'xcmdline_url=stale' \
'cmdline_url=bad%' \
'cmdline_url=bad%2' \
'cmdline_url=bad%GG' \
'cmdline_url=bad%00tail' \
'cmdline_url=bad%0Dtail' \
'cmdline_url=bad%1Btail' \
'cmdline_url=bad%07tail' \
'cmdline_url=bad%08tail' \
'cmdline_url=bad%0Btail' \
'cmdline_url=bad%0Ctail' \
'cmdline_url=bad%7Ftail' \
'cmdline_url=bad%FF' \
'cmdline_url=bad%C3' \
'cmdline_url=bad%C0%AF'
do
check_command 'cmdline_url=previous' previous
check_command "$metadata" ''
done
# The limit applies to decoded bytes, not the size of the OSC string.
decoded=$(printf '%5000s' '' | tr ' ' x)
check_command "cmdline_url=$decoded" "$decoded"
encoded=$(awk 'BEGIN { for (i = 0; i < 16384; i++) printf "%%78" }')
decoded=$(printf '%16384s' '' | tr ' ' x)
check_command "cmdline_url=$encoded" "$decoded"
check_command "cmdline_url=${encoded}%78" ''
# Complete UTF-8 characters may reach the limit, but overlong lines are
# rejected in full rather than truncated, even through a character.
check_boundary()
{
prefix=$(printf "%${1}s" '' | tr ' ' x)
check_command "cmdline_url=$prefix$2" "$prefix$3"
check_command "cmdline_url=${prefix}x$2" ''
}
check_boundary 16382 '%C3%A9' 'é'
check_boundary 16381 '%E4%B8%AD' '中'
check_boundary 16380 '%F0%9F%98%80' '😀'
# Each hook gets a snapshot, even if multiple commands arrive in one read.
$TMUX set -g @log '' || exit 1
$TMUX set-hook -g pane-command-started \
'set -gF @log "#{@log}|start:#{hook_command_line}"' || exit 1
$TMUX set-hook -g pane-command-finished \
'set -gF @log "#{@log}|end:#{hook_command_line}:#{hook_command_status}"' || exit 1
printf '\033]133;C;cmdline_url=first\007\033]133;D;0\007\033]133;C;cmdline_url=second\007\033]133;D;1\007\033]133;C\007\033]133;D;2\007' |
$TMUX display-message -I -t "$pane" || exit 1
check '#{@log}' '|start:first|end:first:0|start:second|end:second:1|start:|end::2'
check '#{pane_command_line}' ''
exit 0