Files
tmux/regress/image-scroll-uses-region.sh
Michael Grant cfd1b81dc7 Implement generalised image support
Support Kitty and SIXEL input and output with grid-resident image layers, history and copy-mode integration, text fallback rendering, and damage-rectangle redraws.

Consolidate the image work through 250f96b4b onto the redraw-damage-rectangles-new base, excluding the superseded redraw implementation and its historical merges. The original history is preserved in backup/4902-image-support-before-master-rebase-20261003.
2026-10-07 18:22:35 +02:00

143 lines
6.0 KiB
Bash
Executable File

#!/bin/sh
# Regression test: with imagescroll granted, does tmux actually use a native
# scrolling-region command (DECSTBM, and DECSLRM if the pane isn't full
# width) to perform the scroll, rather than tty_redraw_region()'s fallback
# full manual repaint? This directly checks the concern raised in PR review
# that tmux "isn't using scrolling regions" for an image-bearing pane -
# imagescroll only makes sense to grant a terminal if tmux is actually
# relying on a real native scroll to carry the image along, not silently
# falling back to redrawing every line by hand while still skipping the
# image retransmission (which would just lose the image for nothing).
#
# tty_redraw_region() (tty.c) logs one of two lines whenever it runs at
# all - "%s large region redraw" or "%s small region redraw" - so their
# total absence after the scroll proves the native path
# (tty_cmd_linefeed()/scrollup()/scrolldown()/reverseindex()) was taken
# instead. Combined with image-sixel-region-scroll.sh's own check (the
# image is not retransmitted), this proves imagescroll's trust is actually
# backed by a real native scroll, not a coincidence.
#
# Phase 2 is the deliberate opposite case, and is what PR review's concern
# actually described: pane-scrollbars makes this pane less than full width,
# and without margins granted, tty_cmd_*()'s own
# "(!tty_full_width(tty, ctx) && !tty_use_margin(tty))" check has no native
# scrolling-region option left and must fall back - proving phase 1 wasn't
# trivially passing, and that redraw_image_scroll_result()'s failure path
# correctly also stops imagescroll's trust from applying when that happens
# (the image must be retransmitted here, exactly as
# image-sixel-region-scroll.sh's own phase 2 already separately proves).
#
# A real attached client is needed for any of this redraw code to run at
# all, so this uses the same nested inner/outer tmux pattern as
# image-sixel-region-scroll.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$$ -vv -f/dev/null"
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
cleanup()
{
$TMUX kill-server >/dev/null 2>&1
$TMUX2 kill-server >/dev/null 2>&1
}
fail()
{
echo "$*" >&2
cleanup
exit 1
}
cleanup
DIR=$(mktemp -d) || exit 1
cd "$DIR" || exit 1
trap "cleanup; cd /; rm -rf $DIR" 0 1 15
WIDTH=40
HEIGHT=20
HEADER='\033Pq"1;1;26;26#0;2;100;100;100#0!26~-!26~-!26~-!26~-!26B\033\\'
$TMUX new-session -d -s inner -x $WIDTH -y $HEIGHT \
"printf '$HEADER'; exec sh" || exit 1
sleep 0.3
[ "$($TMUX display-message -p '#{image_support}')" = 0 ] && exit 0
$TMUX set -g pane-scrollbars on || exit 1
$TMUX set -as terminal-features ',*:sixel' || exit 1
$TMUX set -as terminal-features ',*:imagescroll' || exit 1
$TMUX2 new-session -d -x $WIDTH -y $HEIGHT || exit 1
OUTER=$($TMUX2 list-panes -F '#{pane_id}' | head -1)
[ -n "$OUTER" ] || fail "No outer pane."
$TMUX2 set -as terminal-features ',*:sixel@' || fail "disable outer sixel failed"
TMP=$(mktemp)
$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"
sleep 1
grep -qa '"1;1;26;26' "$TMP" || fail "sanity: image never reached the client"
LOG=$(ls tmux-server*.log 2>/dev/null | head -1)
[ -n "$LOG" ] || fail "sanity: no server -vv log was produced"
run_scroll()
{
label=$1
BEFORE=$(wc -l <"$LOG")
: >"$TMP"
$TMUX send-keys -t inner -l "yes | head -n 10" || fail "$label: send scroll failed"
$TMUX send-keys -t inner Enter || fail "$label: send enter failed"
sleep 1
n_dcs=$(grep -ac "$(printf '\033P')" "$TMP")
n_redraw=$(tail -n +$((BEFORE + 1)) "$LOG" | grep -c "region redraw")
}
# --- Phase 1: margins granted - real native scrolling region expected,
# image never retransmitted. ---
$TMUX set -as terminal-features ',*:margins' || fail "grant margins failed"
sleep 0.3
$TMUX display-message -p '#{client_termfeatures}' |
grep -q imagescroll || fail "sanity: imagescroll was not granted to the client"
run_scroll "phase 1 (margins granted)"
[ "$n_dcs" -eq 0 ] ||
fail "phase 1: image was retransmitted ($n_dcs times) - imagescroll's trust did not engage, so this run cannot test what it is meant to"
[ "$n_redraw" -eq 0 ] ||
fail "phase 1: tty_redraw_region() fired ($n_redraw times) while scrolling an image-bearing pane with margins+imagescroll granted - tmux fell back to a full manual repaint instead of a real native scroll, so trusting the terminal to have moved the image was never justified"
# --- Phase 2: margins revoked - the pane still isn't full width
# (pane-scrollbars is on), so this is the scenario PR review's "not using
# scrolling regions" concern actually described: no native scrolling-region
# option is available, tty_redraw_region() must fire, and
# redraw_image_scroll_result()'s failure path must in turn stop
# imagescroll's trust from applying, so the image is retransmitted instead
# of silently lost. Resend the image first - phase 1's scrolling already
# pushed the original one (26 pixels tall, a couple of rows at most) out of
# the pane, so without this there would be nothing left to retransmit
# either way, and the check below would pass for the wrong reason. ---
$TMUX set -as terminal-features ',*:margins@' || fail "revoke margins failed"
sleep 0.3
: >"$TMP"
$TMUX send-keys -t inner -l "printf '$HEADER'" || fail "resend image failed"
$TMUX send-keys -t inner Enter || fail "send enter failed"
sleep 1.5
grep -qa '"1;1;26;26' "$TMP" || fail "sanity: image did not reappear before phase 2"
run_scroll "phase 2 (margins revoked)"
[ "$n_redraw" -gt 0 ] ||
fail "sanity: without margins, scrolling a scrollbar-enabled image-bearing pane never fell back to tty_redraw_region() - this scenario no longer exercises the bug phase 1 checks for"
[ "$n_dcs" -gt 0 ] ||
fail "phase 2: image was not retransmitted after tty_redraw_region() fired - imagescroll's trust incorrectly stayed engaged despite the fallback, which would silently lose the image"
exit 0