mirror of
https://github.com/tmux/tmux.git
synced 2026-10-13 06:04:34 +00:00
Merge branch 'master' into 4902-image-support
# Conflicts: # Makefile.am # configure.ac # format.c # image-sixel.c # regress/tty-keys-replies.sh # resize.c # screen-redraw.c # screen-write.c # screen.c # tmux.1 # tmux.h # tty-features.c # tty.c
This commit is contained in:
87
regress/alt-screen-saved-cursor.sh
Normal file
87
regress/alt-screen-saved-cursor.sh
Normal file
@@ -0,0 +1,87 @@
|
||||
#!/bin/sh
|
||||
|
||||
# 1049h saves the cursor when it enters the alternate screen, and neither 47l
|
||||
# nor 1049l clears it. If the screen was resized after it was saved, 1049l can
|
||||
# restore a row below the saved grid it reflows.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest$$ -f/dev/null"
|
||||
DIR=$(mktemp -d)
|
||||
trap '$TMUX kill-server 2>/dev/null; rm -rf $DIR' 0 1 15
|
||||
|
||||
exit_status=0
|
||||
|
||||
# Wait until the pane's program has set its path (OSC 7) to $1.
|
||||
wait_path() {
|
||||
_i=0
|
||||
while [ "$($TMUX display -p '#{pane_path}' 2>/dev/null)" != "$1" ]; do
|
||||
if ! $TMUX has 2>/dev/null; then
|
||||
echo "FAIL: $2: the server died"
|
||||
exit 1
|
||||
fi
|
||||
_i=$((_i + 1))
|
||||
if [ $_i -ge 400 ]; then
|
||||
echo "FAIL: $2: timed out waiting for $1"
|
||||
exit_status=1
|
||||
return 1
|
||||
fi
|
||||
sleep 0.05
|
||||
done
|
||||
}
|
||||
|
||||
# Respawn the pane, $2 by $3, running $1. Where the program waits for a file
|
||||
# in $DIR, the test resizes the window and then creates it.
|
||||
run() {
|
||||
rm -f $DIR/go*
|
||||
$TMUX resize-window -x $2 -y $3 || exit 1
|
||||
$TMUX respawn-pane -k "$1; exec sleep 1000" || exit 1
|
||||
}
|
||||
|
||||
check() {
|
||||
if [ "$2" != "$3" ]; then
|
||||
echo "FAIL: $1: '$2', not '$3'"
|
||||
exit_status=1
|
||||
fi
|
||||
}
|
||||
|
||||
$TMUX new -d -x 10 -y 5 'sleep 1000' || exit 1
|
||||
$TMUX set -g window-size manual || exit 1
|
||||
|
||||
# Outside the alternate screen (47l): the cursor after a full line goes to
|
||||
# the last column, as with no 47l. Unchanged.
|
||||
run "printf 'abcdefghij\\033[?1049h\\033[?47l\\033[?1049lX\\033]7;done\\007'" 10 5
|
||||
if wait_path done "1049l outside the alternate screen"; then
|
||||
check "1049l outside the alternate screen" \
|
||||
"$($TMUX capture-pane -p | head -1)" abcdefghiX
|
||||
fi
|
||||
|
||||
# A full line, 1049h, a wider screen, 1049l: the cursor is after the line's
|
||||
# last character and X follows it. Unchanged.
|
||||
run "printf 'abcdefghij\\033[?1049h\\033]7;in\\007'; while [ ! -e $DIR/go ]; do sleep 0.05; done; printf '\\033[?1049lX\\033]7;done\\007'" 10 5
|
||||
if wait_path in "full line, then wider"; then
|
||||
$TMUX resize-window -x 20 || exit 1
|
||||
touch $DIR/go
|
||||
wait_path done "full line, then wider" &&
|
||||
check "full line, then wider" \
|
||||
"$($TMUX capture-pane -p | head -1)" abcdefghijX
|
||||
fi
|
||||
|
||||
# cy 8 saved 10 high; 47l, 20x4, 47h, 40x4, 1049l. The cursor goes to the
|
||||
# last row of the saved grid, which is 4 high.
|
||||
run "printf '\\033[9;1Hrow\\033[?1049h\\033[?47l\\033]7;one\\007'; while [ ! -e $DIR/go1 ]; do sleep 0.05; done; printf '\\033[?47h\\033]7;two\\007'; while [ ! -e $DIR/go2 ]; do sleep 0.05; done; printf '\\033[?1049lX\\033]7;done\\007'" 40 10
|
||||
if wait_path one "saved taller"; then
|
||||
$TMUX resize-window -x 20 -y 4 || exit 1
|
||||
touch $DIR/go1
|
||||
if wait_path two "saved taller"; then
|
||||
$TMUX resize-window -x 40 -y 4 || exit 1
|
||||
touch $DIR/go2
|
||||
wait_path done "saved taller" &&
|
||||
check "saved taller" \
|
||||
"$($TMUX capture-pane -p | grep row)" rowX
|
||||
fi
|
||||
fi
|
||||
|
||||
exit $exit_status
|
||||
72
regress/client-exit-resize.sh
Executable file
72
regress/client-exit-resize.sh
Executable file
@@ -0,0 +1,72 @@
|
||||
#!/bin/sh
|
||||
|
||||
# A client that has sent MSG_EXITING can still send MSG_RESIZE. The server
|
||||
# must not use the tty it closed for MSG_EXITING. Here the client is stopped
|
||||
# and sent SIGTERM and then SIGWINCH for a size with no pixel size, so the
|
||||
# server would ask the terminal for one.
|
||||
|
||||
PATH=/bin:/usr/bin:/usr/local/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest$$ -f/dev/null"
|
||||
trap '$TMUX kill-server 2>/dev/null' 0 1 15
|
||||
|
||||
$TMUX new -d 'sleep 1000' || exit 1
|
||||
$TMUX set-hook -g client-resized 'set -g @resized 1' || exit 1
|
||||
python3 - $TMUX <<'EOF2' || exit 1
|
||||
import fcntl, os, pty, signal, struct, subprocess, sys, termios, time
|
||||
|
||||
tmux = sys.argv[1:]
|
||||
|
||||
def size(fd, rows, cols, xpixel, ypixel):
|
||||
fcntl.ioctl(fd, termios.TIOCSWINSZ,
|
||||
struct.pack("HHHH", rows, cols, cols * xpixel, rows * ypixel))
|
||||
|
||||
master, slave = pty.openpty()
|
||||
size(slave, 20, 60, 8, 16)
|
||||
client = os.fork()
|
||||
if client == 0:
|
||||
os.setsid()
|
||||
fcntl.ioctl(slave, termios.TIOCSCTTY, 0)
|
||||
for fd in (0, 1, 2):
|
||||
os.dup2(slave, fd)
|
||||
os.environ["TERM"] = "xterm"
|
||||
os.execvp(tmux[0], tmux + ["attach"])
|
||||
os.set_blocking(master, False)
|
||||
|
||||
# Wait until the server has handled the resize the client sends when it
|
||||
# attaches.
|
||||
for i in range(200):
|
||||
try:
|
||||
os.read(master, 65536)
|
||||
except OSError:
|
||||
pass
|
||||
out = subprocess.run(tmux + ["show", "-gv", "@resized"],
|
||||
stdout=subprocess.PIPE, stderr=subprocess.DEVNULL).stdout
|
||||
if out.strip() == b"1":
|
||||
break
|
||||
time.sleep(0.05)
|
||||
else:
|
||||
print("the client did not attach")
|
||||
sys.exit(1)
|
||||
|
||||
# SIGTERM is sent before SIGWINCH, so the client handles it first whether
|
||||
# it handles pending signals in signal number order or in the order sent.
|
||||
os.kill(client, signal.SIGSTOP)
|
||||
os.waitpid(client, os.WUNTRACED)
|
||||
os.kill(client, signal.SIGTERM)
|
||||
size(master, 23, 61, 0, 0)
|
||||
os.kill(client, signal.SIGWINCH)
|
||||
os.kill(client, signal.SIGCONT)
|
||||
for i in range(200):
|
||||
if os.waitpid(client, os.WNOHANG)[0] != 0:
|
||||
break
|
||||
time.sleep(0.05)
|
||||
else:
|
||||
print("the client did not exit")
|
||||
sys.exit(1)
|
||||
EOF2
|
||||
sleep 1
|
||||
$TMUX has || { echo "the server died"; exit 1; }
|
||||
exit 0
|
||||
@@ -226,10 +226,24 @@ test_format "#{e|/|f|2:10,3}" "3.33"
|
||||
# Default number of decimal places for float is two.
|
||||
test_format "#{e|*|f:2.5,2}" "5.00"
|
||||
|
||||
# Division by zero must not crash the server (result is unspecified).
|
||||
$TMUX display-message -p "#{e|/|:5,0}" >/dev/null 2>&1
|
||||
$TMUX display-message -p "#{e|/|f:5,0}" >/dev/null 2>&1
|
||||
assert_alive "division by zero"
|
||||
# An integer expression with a side or result that is NaN or out of the range
|
||||
# of a long long expands to empty: division or modulus by zero, nan or inf, or
|
||||
# 1e30. LLONG_MAX written out is 2^63 as a double, which is out of range; the
|
||||
# largest double below 2^63 and -2^63 still convert. Comparisons are integer
|
||||
# expressions too. Floating point gives inf.
|
||||
test_format "#{e|/|:5,0}" ""
|
||||
test_format "#{e|m|:0,0}" ""
|
||||
test_format "#{e|m|:7,0}" ""
|
||||
test_format "#{e|+|:nan,0}" ""
|
||||
test_format "#{e|+|:-inf,1}" ""
|
||||
test_format "#{e|-|:1e30,1e30}" ""
|
||||
test_format "#{e|*|:1e30,1}" ""
|
||||
test_format "#{e|+|:9223372036854775807,0}" ""
|
||||
test_format "#{e|<|:nan,1}" ""
|
||||
test_format "#{e|+|:9223372036854774784,0}" "9223372036854774784"
|
||||
test_format "#{e|-|:-9223372036854775808,0}" "-9223372036854775808"
|
||||
test_format "#{e|/|f:5,0}" "inf"
|
||||
assert_alive "expressions with no integer value"
|
||||
|
||||
|
||||
# --- ASCII and repeat ----------------------------------------------------
|
||||
|
||||
@@ -79,6 +79,7 @@ history_limit
|
||||
history_size
|
||||
host
|
||||
host_short
|
||||
image_support
|
||||
insert_flag
|
||||
keypad_cursor_flag
|
||||
keypad_flag
|
||||
|
||||
@@ -23,7 +23,7 @@ $TMUX new-session -d -x 10 -y 4 "
|
||||
$TMUX set -g status off || exit 1
|
||||
$TMUX set -g scroll-on-clear off || exit 1
|
||||
$TMUX set -as terminal-features ',*:sixel@' || exit 1
|
||||
$TMUX set -as terminal-features ',*:kitty@' || exit 1
|
||||
$TMUX set -as terminal-features ',*:kittygraphics@' || exit 1
|
||||
$TMUX set -as terminal-features ',*:RGB' || exit 1
|
||||
$TMUX2 new-session -d -x 10 -y 4 "$TMUX attach-session" || exit 1
|
||||
$TMUX2 set -g status off || exit 1
|
||||
|
||||
@@ -109,7 +109,7 @@ with tempfile.TemporaryDirectory(prefix='tmux-kitty-layers-') as tmp:
|
||||
if run(inner, 'display-message', '-p', '#{image_support}') == '0':
|
||||
raise SystemExit(0)
|
||||
run(inner, 'set', '-g', 'status', 'off')
|
||||
run(inner, 'set', '-as', 'terminal-features', ',*:kitty')
|
||||
run(inner, 'set', '-as', 'terminal-features', ',*:kittygraphics')
|
||||
pane = run(outer, 'new-session', '-dP', '-F', '#{pane_id}',
|
||||
'-x', '40', '-y', '12', 'exec sh')
|
||||
run(outer, 'set', '-g', 'status', 'off')
|
||||
|
||||
@@ -112,7 +112,7 @@ with tempfile.TemporaryDirectory(prefix='tmux-kitty-protocol-') as tmp:
|
||||
raise SystemExit(0)
|
||||
run('set', '-g', 'status', 'off')
|
||||
run('set', '-as', 'terminal-features', ',*:sixel@')
|
||||
run('set', '-as', 'terminal-features', ',*:kitty@')
|
||||
run('set', '-as', 'terminal-features', ',*:kittygraphics@')
|
||||
run('set', '-as', 'terminal-features', ',*:RGB')
|
||||
subprocess.run(outer + ['new-session', '-d', '-x', '40', '-y', '12'], check=True)
|
||||
subprocess.run(outer + ['set', '-g', 'status', 'off'], check=True)
|
||||
|
||||
@@ -55,12 +55,12 @@ $TMUX new-session -d -s inner -x 40 -y 20 "printf '$HEADER'; exec sh" ||
|
||||
sleep 0.3
|
||||
|
||||
[ "$($TMUX display-message -p '#{image_support}')" = 0 ] && exit 0
|
||||
$TMUX set -as terminal-features ',*:kitty' || exit 1
|
||||
$TMUX set -as terminal-features ',*:kittygraphics' || exit 1
|
||||
|
||||
$TMUX2 new-session -d -x 40 -y 20 || exit 1
|
||||
OUTER=$($TMUX2 list-panes -F '#{pane_id}' | head -1)
|
||||
[ -n "$OUTER" ] || fail "No outer pane."
|
||||
$TMUX2 set -as terminal-features ',*:kitty@' || fail "disable outer kitty failed"
|
||||
$TMUX2 set -as terminal-features ',*:kittygraphics@' || fail "disable outer kitty failed"
|
||||
$TMUX2 pipe-pane -t "$OUTER" -O "cat >$TMP" || fail "pipe-pane failed"
|
||||
$TMUX2 send-keys -t "$OUTER" -l "$TMUX attach -t inner" || fail "send attach failed"
|
||||
$TMUX2 send-keys -t "$OUTER" Enter || fail "send enter failed"
|
||||
|
||||
@@ -57,7 +57,7 @@ scroll_markers()
|
||||
$TMUX2 new-session -d -x 40 -y 20 || exit 1
|
||||
OUTER=$($TMUX2 list-panes -F '#{pane_id}' | head -1)
|
||||
[ -n "$OUTER" ] || fail "No outer pane."
|
||||
$TMUX2 set -as terminal-features ',*:kitty@' ||
|
||||
$TMUX2 set -as terminal-features ',*:kittygraphics@' ||
|
||||
fail "disable outer kitty failed"
|
||||
: >"$TMP"
|
||||
$TMUX2 pipe-pane -t "$OUTER" -O "cat >$TMP" || fail "pipe-pane failed"
|
||||
@@ -76,11 +76,11 @@ scroll_markers()
|
||||
N=$(grep -ac MARKER "$TMP")
|
||||
}
|
||||
|
||||
scroll_markers ',*:kitty,*:imagescroll'
|
||||
scroll_markers ',*:kittygraphics,*:imagescroll'
|
||||
[ "$N" -eq 0 ] ||
|
||||
fail "existing text was redrawn ($N lines) scrolling with imagescroll"
|
||||
|
||||
scroll_markers ',*:kitty'
|
||||
scroll_markers ',*:kittygraphics'
|
||||
[ "$N" -gt 0 ] ||
|
||||
fail "text was not redrawn scrolling without imagescroll"
|
||||
|
||||
|
||||
88
regress/input-combine-width.sh
Normal file
88
regress/input-combine-width.sh
Normal file
@@ -0,0 +1,88 @@
|
||||
#!/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
|
||||
@@ -40,7 +40,7 @@ start_pane tabs 12 3 'A\tB\033[2g\r\033[IC'
|
||||
check_raw_matches tabs \
|
||||
'L 0 \(0\) flags=EXTENDED\[[0-9a-f]+\]' \
|
||||
'C 0,1 data=\(7,7, \) flags=TAB\[[0-9a-f]+\]' \
|
||||
'C 0,2 data=\(1,1,!\) flags=PADDING\[[0-9a-f]+\]' \
|
||||
'C 0,2 data=\(0,0,\) flags=PADDING\[[0-9a-f]+\]' \
|
||||
'C 0,8 data=\(1,1,B\) flags=NONE\[0\]' \
|
||||
'C 0,0 data=\(1,1,C\) flags=NONE\[0\]'
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ check_capture wide 'AあB'
|
||||
check_raw_matches wide \
|
||||
'L 0 \(0\) flags=EXTENDED\[[0-9a-f]+\]' \
|
||||
'C 0,1 data=\(2,3,あ\) flags=NONE\[0\] attr=NONE\[0\]' \
|
||||
'C 0,2 data=\(1,1,!\) flags=PADDING\[[0-9a-f]+\] attr=NONE\[0\]' \
|
||||
'C 0,2 data=\(0,0,\) flags=PADDING\[[0-9a-f]+\] attr=NONE\[0\]' \
|
||||
'C 0,3 data=\(1,1,B\) flags=NONE\[0\]'
|
||||
|
||||
start_pane combining 8 3 'e\314\201x'
|
||||
@@ -20,21 +20,21 @@ start_pane emoji 10 3 '\360\237\230\200Z'
|
||||
check_raw_matches emoji \
|
||||
'L 0 \(0\) flags=EXTENDED\[[0-9a-f]+\]' \
|
||||
'C 0,0 data=\(2,4,😀\) flags=NONE\[0\] attr=NONE\[0\]' \
|
||||
'C 0,1 data=\(1,1,!\) flags=PADDING\[[0-9a-f]+\] attr=NONE\[0\]' \
|
||||
'C 0,1 data=\(0,0,\) flags=PADDING\[[0-9a-f]+\] attr=NONE\[0\]' \
|
||||
'C 0,2 data=\(1,1,Z\) flags=NONE\[0\]'
|
||||
|
||||
start_pane flag 10 3 '\360\237\207\254\360\237\207\247!'
|
||||
check_raw_matches flag \
|
||||
'L 0 \(0\) flags=EXTENDED\[[0-9a-f]+\]' \
|
||||
'C 0,0 data=\(2,8,🇬🇧\) flags=NONE\[0\] attr=NONE\[0\]' \
|
||||
'C 0,1 data=\(1,1,!\) flags=PADDING\[[0-9a-f]+\] attr=NONE\[0\]' \
|
||||
'C 0,1 data=\(0,0,\) flags=PADDING\[[0-9a-f]+\] attr=NONE\[0\]' \
|
||||
'C 0,2 data=\(1,1,!\) flags=NONE\[0\]'
|
||||
|
||||
start_pane variation 10 3 '*\357\270\217!'
|
||||
check_raw_matches variation \
|
||||
'L 0 \(0\) flags=EXTENDED\[[0-9a-f]+\]' \
|
||||
'C 0,0 data=\(2,[0-9]+,.*\) flags=NONE\[0\] attr=NONE\[0\]' \
|
||||
'C 0,1 data=\(1,1,!\) flags=PADDING\[[0-9a-f]+\] attr=NONE\[0\]' \
|
||||
'C 0,1 data=\(0,0,\) flags=PADDING\[[0-9a-f]+\] attr=NONE\[0\]' \
|
||||
'C 0,2 data=\(1,1,!\) flags=NONE\[0\]'
|
||||
|
||||
start_pane invalid 10 3 '\377A'
|
||||
@@ -69,14 +69,14 @@ check_raw_matches overwrite-wide-pad \
|
||||
start_pane overwrite-wide-with-wide 10 3 'A\343\201\202B\r\033[1C\347\225\214'
|
||||
check_raw_matches overwrite-wide-with-wide \
|
||||
'C 0,1 data=\(2,3,界\) flags=NONE\[0\] attr=NONE\[0\]' \
|
||||
'C 0,2 data=\(1,1,!\) flags=PADDING\[[0-9a-f]+\] attr=NONE\[0\]' \
|
||||
'C 0,2 data=\(0,0,\) flags=PADDING\[[0-9a-f]+\] attr=NONE\[0\]' \
|
||||
'C 0,3 data=\(1,1,B\) flags=NONE\[0\]'
|
||||
|
||||
start_pane wide-right-edge 4 3 'ABC\343\201\202Z'
|
||||
check_raw_matches wide-right-edge \
|
||||
'L 0 \(0\) flags=WRAPPED\[[0-9a-f]+\]' \
|
||||
'C 1,0 data=\(2,3,あ\) flags=NONE\[0\] attr=NONE\[0\]' \
|
||||
'C 1,1 data=\(1,1,!\) flags=PADDING\[[0-9a-f]+\] attr=NONE\[0\]' \
|
||||
'C 1,1 data=\(0,0,\) flags=PADDING\[[0-9a-f]+\] attr=NONE\[0\]' \
|
||||
'C 1,2 data=\(1,1,Z\) flags=NONE\[0\]'
|
||||
|
||||
exit $exit_status
|
||||
|
||||
60
regress/input-reflow-wide.sh
Normal file
60
regress/input-reflow-wide.sh
Normal file
@@ -0,0 +1,60 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Wide-character padding must not count towards the reflowed line width.
|
||||
|
||||
. ./input-common.inc
|
||||
|
||||
check_reflow()
|
||||
{
|
||||
# Resizing can move the first physical line into history.
|
||||
$TMUX capture-pane -pN"$2" -t "$1:" -S - -E - |
|
||||
normalize_capture >"$TMP"
|
||||
printf '%s\n' "${3:-$text}" >"$EXP"
|
||||
cmp "$TMP" "$EXP" || fail "$1 reflow (width $width, flags -pN$2)"
|
||||
}
|
||||
|
||||
text='中文文档甲 中文文档乙 中文文档丙 中文文档丁 中文文档戊 中文文档己'
|
||||
|
||||
# Default and RGB backgrounds exercise compact and extended padding cells.
|
||||
for padding in compact extended; do
|
||||
style=''
|
||||
if [ "$padding" = extended ]; then
|
||||
style='\033[48;2;1;2;3m'
|
||||
fi
|
||||
width=96
|
||||
start_pane_history "wide-$padding" 96 18 "${style}${text}\\n"
|
||||
check_reflow "wide-$padding" ''
|
||||
for width in 47 96 47 96; do
|
||||
$TMUX resize-window -t "wide-$padding:" -x "$width" -y 18 ||
|
||||
exit 1
|
||||
check_reflow "wide-$padding" J
|
||||
if [ "$width" = 96 ]; then
|
||||
check_reflow "wide-$padding" ''
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
# Include both a split between wide characters and an odd-width boundary.
|
||||
text='中文文档'
|
||||
start_pane_history boundary 8 18 "${text}\\n"
|
||||
for width in 4 8 3 8; do
|
||||
$TMUX resize-window -t boundary: -x "$width" -y 18 || exit 1
|
||||
check_reflow boundary J
|
||||
case "$width" in
|
||||
4)
|
||||
check_reflow boundary '' '中文
|
||||
文档'
|
||||
;;
|
||||
3)
|
||||
check_reflow boundary '' '中
|
||||
文
|
||||
文
|
||||
档'
|
||||
;;
|
||||
8)
|
||||
check_reflow boundary ''
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
exit $exit_status
|
||||
29
regress/menu-pane-killed.sh
Executable file
29
regress/menu-pane-killed.sh
Executable file
@@ -0,0 +1,29 @@
|
||||
#!/bin/sh
|
||||
|
||||
# A menu opened for a pane is drawn again after the pane is killed. Drawing it
|
||||
# must not use the pane. The terminal is a pane of a second server.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest$$ -f/dev/null"
|
||||
TMUX2="$TEST_TMUX -Ltest2$$ -f/dev/null"
|
||||
trap '$TMUX kill-server 2>/dev/null; $TMUX2 kill-server 2>/dev/null' 0 1 15
|
||||
|
||||
$TMUX new -d 'sleep 1000' || exit 1
|
||||
$TMUX2 new -d "unset TMUX; exec $TMUX attach" || exit 1
|
||||
n=0
|
||||
while [ -z "$($TMUX lsc 2>/dev/null)" ]; do
|
||||
n=$((n + 1))
|
||||
[ $n -gt 100 ] && exit 1
|
||||
sleep 0.1
|
||||
done
|
||||
|
||||
P=$($TMUX splitw -d -P -F '#{pane_id}' 'sleep 1000') || exit 1
|
||||
$TMUX display-menu -c "$($TMUX lsc -F '#{client_name}')" -t $P one 1 '' ||
|
||||
exit 1
|
||||
$TMUX kill-pane -t $P
|
||||
sleep 1
|
||||
$TMUX has || { echo "the server died"; exit 1; }
|
||||
exit 0
|
||||
48
regress/ncv.sh
Normal file
48
regress/ncv.sh
Normal file
@@ -0,0 +1,48 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Check that attributes listed in ncv are not used with colours.
|
||||
|
||||
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"
|
||||
|
||||
fail() {
|
||||
echo "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
$TMUX kill-server 2>/dev/null
|
||||
$TMUX2 kill-server 2>/dev/null
|
||||
|
||||
TMP=$(mktemp)
|
||||
trap "rm -f $TMP; $TMUX kill-server 2>/dev/null; $TMUX2 kill-server 2>/dev/null" 0 1 15
|
||||
|
||||
$TMUX2 new -d -x20 -y5 \
|
||||
"printf '\033[2;4mPLAIN\033[0m'; exec sleep 100" || exit 1
|
||||
$TMUX2 set -g status-left '' || exit 1
|
||||
$TMUX2 set -g status-right '' || exit 1
|
||||
$TMUX2 set -g status-format[0] \
|
||||
'#[fg=black,bg=green,underscore,dim]MARK' || exit 1
|
||||
$TMUX2 set -as terminal-overrides ',screen:ncv=18' || exit 1
|
||||
|
||||
$TMUX new -d -x20 -y5 || exit 1
|
||||
$TMUX set -g status off || exit 1
|
||||
$TMUX send -l "$TMUX2 attach" || exit 1
|
||||
$TMUX send Enter || exit 1
|
||||
sleep 1
|
||||
|
||||
$TMUX capturep -pe >$TMP || exit 1
|
||||
ESC=$(printf '\033')
|
||||
grep -q "${ESC}\\[30m${ESC}\\[42mMARK" $TMP ||
|
||||
fail "coloured status text missing"
|
||||
grep -q "${ESC}\\[2;4mPLAIN" $TMP ||
|
||||
fail "attributes without colours were filtered"
|
||||
tail -1 $TMP | grep -q "${ESC}\\[4m" && fail "underline not filtered"
|
||||
tail -1 $TMP | grep -q "${ESC}\\[2m" && fail "dim not filtered"
|
||||
|
||||
exit 0
|
||||
43
regress/session-group-name.sh
Normal file
43
regress/session-group-name.sh
Normal file
@@ -0,0 +1,43 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Reusing a session name must not reuse an unrelated session group.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
trap '$TMUX kill-server 2>/dev/null' 0 1 15
|
||||
|
||||
$TMUX new -d -s a || exit 1
|
||||
$TMUX new -d -t a -s b || exit 1
|
||||
$TMUX kill-session -t a || exit 1
|
||||
|
||||
# Occupy the first suffix too, without creating a session of that name.
|
||||
$TMUX new -d -t a-1 -s other || exit 1
|
||||
$TMUX new -d -s a || exit 1
|
||||
AW=$($TMUX display -p -t a: '#{window_id}')
|
||||
BW=$($TMUX display -p -t b: '#{window_id}')
|
||||
$TMUX new -d -t a -s c || exit 1
|
||||
|
||||
[ "$($TMUX display -p -t a: '#{session_group}')" = a-2 ] || exit 1
|
||||
[ "$($TMUX display -p -t c: '#{session_group}')" = a-2 ] || exit 1
|
||||
[ "$($TMUX display -p -t b: '#{session_group_list}')" = b ] || exit 1
|
||||
[ "$($TMUX display -p -t a: '#{window_id}')" = "$AW" ] || exit 1
|
||||
[ "$($TMUX display -p -t c: '#{window_id}')" = "$AW" ] || exit 1
|
||||
[ "$AW" != "$BW" ] || exit 1
|
||||
|
||||
# Window changes propagate only within the new group.
|
||||
$TMUX new-window -d -t a || exit 1
|
||||
[ "$($TMUX display -p -t c: '#{session_windows}')" = 2 ] || exit 1
|
||||
[ "$($TMUX display -p -t b: '#{session_windows}')" = 1 ] || exit 1
|
||||
|
||||
# An existing session still joins its group; a group-only target still works.
|
||||
$TMUX new -d -t c -s d || exit 1
|
||||
[ "$($TMUX display -p -t d: '#{session_group}')" = a-2 ] || exit 1
|
||||
$TMUX new -d -t a-1 -s e || exit 1
|
||||
[ "$($TMUX display -p -t e: '#{session_group}')" = a-1 ] || exit 1
|
||||
[ "$($TMUX display -p -t e: '#{window_id}')" = \
|
||||
"$($TMUX display -p -t other: '#{window_id}')" ] || exit 1
|
||||
|
||||
exit 0
|
||||
101
regress/tty-keys-backtab.sh
Normal file
101
regress/tty-keys-backtab.sh
Normal file
@@ -0,0 +1,101 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Extended Shift+Tab must remain BackTab when the pane does not request
|
||||
# modifyOtherKeys=2, including when the input carries implied Meta.
|
||||
|
||||
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 -y24 \
|
||||
'stty raw -echo; exec cat -tv' || exit 1
|
||||
$INNER set-option -g status off || exit 1
|
||||
$INNER set-option -g assume-paste-time 0 || exit 1
|
||||
$INNER set-option -s extended-keys 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 -y24 "$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
|
||||
|
||||
expected_output=
|
||||
|
||||
assert_key()
|
||||
{
|
||||
name=$1
|
||||
expected_output=$expected_output$2
|
||||
bytes=$(printf '%s' "$3" | od -An -v -tx1)
|
||||
$OUTER send-keys -H $bytes || exit 1
|
||||
i=0
|
||||
while [ "$i" -lt 100 ]; do
|
||||
actual=$($INNER capture-pane -p | tr -d '\n')
|
||||
[ "$actual" = "$expected_output" ] && return 0
|
||||
sleep 0.05
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "$name: got '$actual', expected '$expected_output'"
|
||||
}
|
||||
|
||||
pane_state='#{pane_key_mode}:#{pane_current_command}'
|
||||
for mode in 0 1 2; do
|
||||
for format in xterm csi-u; do
|
||||
$INNER new-window \
|
||||
"printf '\033[>4;${mode}m'; stty raw -echo; exec cat -tv" ||
|
||||
exit 1
|
||||
key_mode="Ext $mode"
|
||||
[ "$mode" -eq 0 ] && key_mode=VT10x
|
||||
i=0
|
||||
while [ "$($INNER display-message -p "$pane_state")" != \
|
||||
"$key_mode:cat" ]; do
|
||||
[ "$i" -lt 100 ] || fail "pane did not enter mode $mode"
|
||||
sleep 0.05
|
||||
i=$((i + 1))
|
||||
done
|
||||
$INNER set-option -s extended-keys-format "$format" || exit 1
|
||||
expected_output=
|
||||
# Every modifier combination containing Shift.
|
||||
for modifier in 2 4 6 8; do
|
||||
if [ "$mode" -eq 2 ]; then
|
||||
if [ "$format" = xterm ]; then
|
||||
expected="^[[27;${modifier};9~"
|
||||
else
|
||||
expected="^[[9;${modifier}u"
|
||||
fi
|
||||
else
|
||||
expected='^[[Z'
|
||||
fi
|
||||
case_name="mode $mode, $format, modifier $modifier"
|
||||
assert_key "$case_name, CSI u input" "$expected" \
|
||||
"$(printf '\033[9;%su' "$modifier")"
|
||||
assert_key "$case_name, xterm input" "$expected" \
|
||||
"$(printf '\033[27;%s;9~' "$modifier")"
|
||||
done
|
||||
done
|
||||
done
|
||||
|
||||
exit 0
|
||||
@@ -43,8 +43,22 @@ def matches(name, value):
|
||||
return "ignorefkeys" in features
|
||||
if name == "XDA":
|
||||
return kind == "ReplyTest"
|
||||
if name == "XDA-kitty":
|
||||
return kind == "kitty(0.43.0)" and "kitty" in features
|
||||
if name in ("XDA-kitty", "XDA-kitty-nosync"):
|
||||
required = {
|
||||
"256", "RGB", "bpaste", "clipboard", "mouse",
|
||||
"strikethrough", "title", "ccolour", "cstyle", "extkeys",
|
||||
"focus", "hyperlinks", "osc7", "usstyle", "kittygraphics"}
|
||||
if name == "XDA-kitty":
|
||||
required.add("sync")
|
||||
elif "sync" in features:
|
||||
return False
|
||||
return kind == "kitty(0.43.0)" and required <= features
|
||||
if name == "XDA-ghostty":
|
||||
return kind == "ghostty 1.2.0" and {
|
||||
"kittygraphics", "margins", "sync", "usstyle", "progressbar"} <= features
|
||||
if name == "XDA-WezTerm":
|
||||
return kind == "WezTerm 20240203-110809-5046fc22" and {
|
||||
"sixel", "margins", "usstyle"} <= features
|
||||
if name == "sync":
|
||||
return "sync" in features
|
||||
if name == "characters":
|
||||
@@ -57,6 +71,9 @@ reports = [
|
||||
("DA2", b"\033[>85;1;0c"),
|
||||
("XDA", b"\033P>|ReplyTest\033\\"),
|
||||
("XDA-kitty", b"\033P>|kitty(0.43.0)\033\\"),
|
||||
("XDA-kitty-nosync", b"\033P>|kitty(0.43.0)\033\\"),
|
||||
("XDA-ghostty", b"\033P>|ghostty 1.2.0\033\\"),
|
||||
("XDA-WezTerm", b"\033P>|WezTerm 20240203-110809-5046fc22\033\\"),
|
||||
("sync", b"\033[?2026;2$y"),
|
||||
("characters", b"\033[8;17;61t"),
|
||||
("pixels", b"\033[4;480;800t"),
|
||||
@@ -83,6 +100,8 @@ for name, report in reports:
|
||||
else:
|
||||
run("set-option", "-s", "escape-time", "60000")
|
||||
run("set-option", "-g", "@seen", "")
|
||||
if name == "XDA-kitty-nosync":
|
||||
run("set-option", "-as", "terminal-features", ",*:sync@")
|
||||
run("bind-key", "-n", "Escape", "set-option", "-gF",
|
||||
"@seen", "#{@seen}E")
|
||||
pid, fd = os.forkpty()
|
||||
@@ -143,6 +162,12 @@ for name, report in reports:
|
||||
assert actual == baseline, (context, "unexpected effect", actual)
|
||||
else:
|
||||
assert matches(name, actual), (context, "missing effect", actual)
|
||||
if name in ("XDA-kitty", "XDA-kitty-nosync", "XDA-ghostty"):
|
||||
assert run("display-message", "-p", "#{I/f:kittygraphics}") == "1", (
|
||||
context, "kittygraphics feature lookup failed")
|
||||
if name == "XDA-kitty-nosync":
|
||||
assert run("display-message", "-p", "#{I/f:sync}") == "0", (
|
||||
context, "kitty enabled synchronized updates")
|
||||
assert run("show-option", "-gv", "@seen") == (
|
||||
"E" if prefixed else ""), (context, "Escape count")
|
||||
cases += 1
|
||||
|
||||
Reference in New Issue
Block a user