mirror of
https://github.com/tmux/tmux.git
synced 2026-10-12 05:34:24 +00:00
Merge branch 'master' into command_parser
This commit is contained in:
@@ -105,6 +105,24 @@ drag()
|
||||
sleep 1
|
||||
}
|
||||
|
||||
meta_drag()
|
||||
{
|
||||
scol="$1"
|
||||
srow="$2"
|
||||
ecol="$3"
|
||||
erow="$4"
|
||||
|
||||
seq=$(printf '\033[<8;%s;%sM' "$scol" "$srow")
|
||||
$TMUX2 send-keys -t "$OUTER" -l "$seq" 2>/dev/null
|
||||
sleep 0.2
|
||||
seq=$(printf '\033[<40;%s;%sM' "$ecol" "$erow")
|
||||
$TMUX2 send-keys -t "$OUTER" -l "$seq" 2>/dev/null
|
||||
sleep 0.2
|
||||
seq=$(printf '\033[<8;%s;%sm' "$ecol" "$erow")
|
||||
$TMUX2 send-keys -t "$OUTER" -l "$seq" 2>/dev/null
|
||||
sleep 1
|
||||
}
|
||||
|
||||
cleanup
|
||||
|
||||
check_ok new-session -d -s modal -x 80 -y 24 'cat'
|
||||
@@ -304,5 +322,35 @@ sleep 1
|
||||
must_equal "$(fmt modal:0 '#{window_modal_pane}')" ''
|
||||
must_equal "$(fmt modal:0 '#{pane_id}')" "$p0"
|
||||
|
||||
ignored=$($TMUX new-pane -KdPF '#{pane_id}' -t "$p0" 'cat') ||
|
||||
fail "new-pane -K without -O failed"
|
||||
check_ok kill-pane -t "$ignored"
|
||||
|
||||
$TMUX set -g @modal-prefix no
|
||||
$TMUX set -g @modal-root no
|
||||
$TMUX bind -n z set -g @modal-root yes
|
||||
|
||||
modal=$($TMUX new-pane -OKPF '#{pane_id}' -t "$p0" \
|
||||
-x 20 -y 5 -X 20 -Y 10 'cat') ||
|
||||
fail "new-pane -OK failed"
|
||||
sleep 1
|
||||
$TMUX2 send-keys -t "$OUTER" C-b x z Enter
|
||||
sleep 1
|
||||
must_equal "$($TMUX show -gv @modal-prefix)" no
|
||||
must_equal "$($TMUX show -gv @modal-root)" no
|
||||
case "$($TMUX capture-pane -pt "$modal")" in
|
||||
*xz*) ;;
|
||||
*) fail "keys did not reach key-capturing modal pane" ;;
|
||||
esac
|
||||
left=$(fmt "$modal" '#{pane_left}')
|
||||
top=$(fmt "$modal" '#{pane_top}')
|
||||
meta_drag $((left + 2)) $((top + 2)) $((left + 7)) $((top + 4))
|
||||
new_left=$(fmt "$modal" '#{pane_left}')
|
||||
new_top=$(fmt "$modal" '#{pane_top}')
|
||||
[ "$new_left" -gt "$left" ] || [ "$new_top" -gt "$top" ] ||
|
||||
fail "key-capturing modal pane did not move"
|
||||
check_ok kill-pane -t "$modal"
|
||||
sleep 1
|
||||
|
||||
cleanup
|
||||
exit 0
|
||||
|
||||
@@ -269,6 +269,22 @@ test_customize_mode()
|
||||
assert_alive "customize-mode exit"
|
||||
}
|
||||
|
||||
test_customize_break_pane()
|
||||
{
|
||||
start_client option-break
|
||||
side=$($TMUX split-window -d -P -F '#{pane_id}' \
|
||||
-t option-break:0 'cat') || fail "customize split failed"
|
||||
|
||||
$TMUX customize-mode -t "$side" || fail "customize-mode failed"
|
||||
wait_mode "$side" 1
|
||||
$TMUX break-pane -d -s "$side" || fail "customize break-pane failed"
|
||||
|
||||
assert_alive "customize-mode break-pane"
|
||||
wait_mode "$side" 1
|
||||
$TMUX send-keys -t "$side" q || fail "customize-mode quit failed"
|
||||
wait_mode "$side" 0
|
||||
}
|
||||
|
||||
test_copy_mode()
|
||||
{
|
||||
start_client copy-a 'i=0; while [ $i -lt 200 ]; do echo "copy mutation line $i"; i=$((i + 1)); done; cat'
|
||||
@@ -302,6 +318,7 @@ test_choose_tree
|
||||
test_choose_buffer
|
||||
test_choose_client
|
||||
test_customize_mode
|
||||
test_customize_break_pane
|
||||
test_copy_mode
|
||||
cleanup
|
||||
exit 0
|
||||
|
||||
87
regress/respawn-pane-control-lag.sh
Normal file
87
regress/respawn-pane-control-lag.sh
Normal file
@@ -0,0 +1,87 @@
|
||||
#!/bin/sh
|
||||
|
||||
# respawn-pane frees the pane's buffer and creates an empty one. The pane
|
||||
# offsets and each control client's offsets used to keep pointing into the old
|
||||
# buffer, so if an attached control client had not caught up on the pane's
|
||||
# output, the next read from the new process ran off the end of the new buffer
|
||||
# and the server crashed in input_parse.
|
||||
#
|
||||
# Two ways for a client to be behind are tested: a control client whose output
|
||||
# goes down a fifo that is never read (a second, healthy control client keeps
|
||||
# the pane being read), and a healthy control client viewing a session that
|
||||
# the pane's window has been moved out of.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
DIR=$(mktemp -d)
|
||||
FIFO=$DIR/fifo
|
||||
SERVER=
|
||||
|
||||
mkfifo "$FIFO" || exit 1
|
||||
|
||||
cleanup() {
|
||||
[ -n "$SERVER" ] && kill -9 "$SERVER" 2>/dev/null
|
||||
$TMUX kill-server 2>/dev/null
|
||||
exec 8<&- 2>/dev/null
|
||||
rm -rf "$DIR"
|
||||
}
|
||||
trap cleanup 0 1 15
|
||||
|
||||
alive() {
|
||||
if ! kill -0 "$SERVER" 2>/dev/null; then
|
||||
SERVER=
|
||||
echo "server died after $1"
|
||||
exit 1
|
||||
fi
|
||||
$TMUX has -t rt || exit 1
|
||||
}
|
||||
|
||||
wait_clients() {
|
||||
n=0
|
||||
while [ $n -lt 50 ]; do
|
||||
[ "$($TMUX lsc 2>/dev/null | wc -l)" -ge $1 ] && return
|
||||
sleep 0.1
|
||||
n=$((n + 1))
|
||||
done
|
||||
echo "control clients did not attach"; exit 1
|
||||
}
|
||||
|
||||
# A detached session with a pane that writes a line every 10 milliseconds.
|
||||
$TMUX -f/dev/null new -d -x 80 -y 24 -s rt \
|
||||
'while :; do echo xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx; sleep 0.01; done' ||
|
||||
exit 1
|
||||
SERVER=$($TMUX display -pt rt '#{pid}')
|
||||
|
||||
# One control client with output down the unread fifo and one reading
|
||||
# normally; stdin of both held open by sleep so they stay attached.
|
||||
( sleep 60 ) | $TMUX -f/dev/null -C attach -t rt >"$FIFO" 2>&1 &
|
||||
exec 8<"$FIFO"
|
||||
( sleep 60 ) | $TMUX -f/dev/null -C attach -t rt >/dev/null 2>&1 &
|
||||
wait_clients 2
|
||||
|
||||
# Let the first client fall behind, then respawn the pane. The new process
|
||||
# writes at once and the server must survive reading it.
|
||||
sleep 3
|
||||
$TMUX respawn-pane -k -t rt:0 'echo respawned; sleep 60' || exit 1
|
||||
sleep 1
|
||||
alive "respawn-pane with a lagging control client"
|
||||
|
||||
# Now a pane whose window is moved out of the session both clients view: their
|
||||
# offsets for it stop advancing, so they fall behind until it is respawned.
|
||||
$TMUX neww -d -t rt \
|
||||
'while :; do echo yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy; sleep 0.01; done' ||
|
||||
exit 1
|
||||
sleep 1
|
||||
$TMUX new -d -s other || exit 1
|
||||
$TMUX movew -d -s rt:1 -t other: || exit 1
|
||||
sleep 2
|
||||
$TMUX respawn-pane -k -t other:1 'echo respawned; sleep 60' || exit 1
|
||||
sleep 1
|
||||
alive "respawn-pane on a window moved out of the clients' session"
|
||||
|
||||
exit 0
|
||||
173
regress/sync-output-atomic.sh
Normal file
173
regress/sync-output-atomic.sh
Normal file
@@ -0,0 +1,173 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Application synchronized updates must remain one physical client transaction,
|
||||
# including when BSU, printable cells and ESU arrive in one pane read.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
LC_ALL=C.UTF-8
|
||||
export PATH TERM LC_ALL
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
|
||||
DIR=$(mktemp -d) || exit 1
|
||||
TMUX_TMPDIR=$DIR
|
||||
export TMUX_TMPDIR
|
||||
|
||||
INNER="$TEST_TMUX -Li$$ -f/dev/null"
|
||||
OUTER="$TEST_TMUX -Lo$$ -f/dev/null"
|
||||
CLIENT_BYTES=$DIR/client-bytes
|
||||
CONTROL=$DIR/control
|
||||
EMITTER=$DIR/emitter.pl
|
||||
ASSERT=$DIR/assert-sync-output.pl
|
||||
|
||||
fail()
|
||||
{
|
||||
echo "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
cleanup()
|
||||
{
|
||||
$OUTER kill-server 2>/dev/null
|
||||
$INNER kill-server 2>/dev/null
|
||||
rm -rf "$DIR"
|
||||
}
|
||||
trap cleanup 0 1 15
|
||||
|
||||
wait_for_client()
|
||||
{
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
$INNER list-clients -F '#{client_termfeatures}' 2>/dev/null |
|
||||
grep -q 'sync' && return 0
|
||||
sleep 0.1
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "sync-capable client did not attach"
|
||||
}
|
||||
|
||||
wait_for_marker()
|
||||
{
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
grep -q 'FRAME_000001_ROW_23' "$CLIENT_BYTES" 2>/dev/null && return 0
|
||||
sleep 0.1
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "client did not receive both synchronized frames"
|
||||
}
|
||||
|
||||
wait_for_stable_bytes()
|
||||
{
|
||||
previous=-1
|
||||
stable=0
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
current=$(wc -c <"$CLIENT_BYTES" 2>/dev/null) || current=0
|
||||
if [ "$current" -gt 0 ] && [ "$current" -eq "$previous" ]; then
|
||||
stable=$((stable + 1))
|
||||
[ "$stable" -eq 5 ] && return 0
|
||||
else
|
||||
stable=0
|
||||
fi
|
||||
previous=$current
|
||||
sleep 0.1
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "client byte stream did not become stable"
|
||||
}
|
||||
|
||||
cat >"$EMITTER" <<'PERL'
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
my $control = $ENV{CONTROL};
|
||||
(my $dir = $control) =~ s{/[^/]+$}{};
|
||||
open my $ready, '>', "$dir/ready" or die "$dir/ready: $!\n";
|
||||
close $ready;
|
||||
while (!-e $control) {
|
||||
select undef, undef, undef, 0.01;
|
||||
}
|
||||
for my $frame (0 .. 1) {
|
||||
my @rows;
|
||||
for my $row (0 .. 23) {
|
||||
my $marker = sprintf "FRAME_%06d_ROW_%02d_", $frame, $row;
|
||||
push @rows, substr($marker . ('X' x 79), 0, 79);
|
||||
}
|
||||
my $frame = "\e[?2026h\e[H" . join("\r\n", @rows) .
|
||||
"\e[?2026l";
|
||||
my $written = syswrite STDOUT, $frame;
|
||||
die "short synchronized frame write\n"
|
||||
unless defined $written && $written == length $frame;
|
||||
select undef, undef, undef, 0.2;
|
||||
}
|
||||
select undef, undef, undef, 2;
|
||||
PERL
|
||||
|
||||
$INNER new-session -d -s inner -x 80 -y 24 \
|
||||
"CONTROL='$CONTROL' perl '$EMITTER'" || exit 1
|
||||
$INNER set-option -g status off || exit 1
|
||||
$INNER set-option -g window-size manual || exit 1
|
||||
$INNER set-option -as terminal-features '*:sync' || exit 1
|
||||
|
||||
$OUTER new-session -d -s outer -x 80 -y 24 \
|
||||
"$TEST_TMUX -Li$$ -f/dev/null attach-session -t inner" ||
|
||||
exit 1
|
||||
$OUTER set-option -g status off || exit 1
|
||||
$OUTER set-option -g window-size manual || exit 1
|
||||
wait_for_client
|
||||
|
||||
i=0
|
||||
while [ "$i" -lt 50 ] && [ ! -e "$DIR/ready" ]; do
|
||||
sleep 0.1
|
||||
i=$((i + 1))
|
||||
done
|
||||
[ -e "$DIR/ready" ] || fail "application emitter did not become ready"
|
||||
$OUTER pipe-pane -O -t outer:0.0 "cat >'$CLIENT_BYTES'" || exit 1
|
||||
: >"$CONTROL"
|
||||
wait_for_marker
|
||||
wait_for_stable_bytes
|
||||
$OUTER pipe-pane -t outer:0.0 || exit 1
|
||||
|
||||
cat >"$ASSERT" <<'PERL'
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
my $path = shift;
|
||||
open my $fh, '<:raw', $path or die "$path: $!\n";
|
||||
local $/;
|
||||
my $bytes = <$fh>;
|
||||
my ($on, $off) = ("\e[?2026h", "\e[?2026l");
|
||||
my ($active, %seen);
|
||||
|
||||
for (my $i = 0; $i < length($bytes);) {
|
||||
if (substr($bytes, $i, length($on)) eq $on) {
|
||||
die "duplicate synchronized-output start at byte $i\n" if $active;
|
||||
$active = 1;
|
||||
$i += length($on);
|
||||
next;
|
||||
}
|
||||
if (substr($bytes, $i, length($off)) eq $off) {
|
||||
die "unmatched synchronized-output end at byte $i\n" unless $active;
|
||||
$active = 0;
|
||||
$i += length($off);
|
||||
next;
|
||||
}
|
||||
if (substr($bytes, $i) =~ /\AFRAME_(\d{6})_ROW_(\d{2})_/) {
|
||||
my $marker = "$1:$2";
|
||||
die "marker $marker outside synchronized output\n" unless $active;
|
||||
$seen{$marker}++;
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
die "unterminated synchronized-output transaction\n" if $active;
|
||||
for my $frame (0 .. 1) {
|
||||
for my $row (0 .. 23) {
|
||||
my $marker = sprintf "%06d:%02d", $frame, $row;
|
||||
die "missing marker $marker\n" unless $seen{$marker};
|
||||
}
|
||||
}
|
||||
PERL
|
||||
|
||||
perl "$ASSERT" "$CLIENT_BYTES" || exit 1
|
||||
Reference in New Issue
Block a user