diff --git a/regress/window-copy-scrollbar-drag-focus-style.sh b/regress/window-copy-scrollbar-drag-focus-style.sh new file mode 100755 index 000000000..77f478955 --- /dev/null +++ b/regress/window-copy-scrollbar-drag-focus-style.sh @@ -0,0 +1,123 @@ +#!/bin/sh + +# Dragging an inactive pane's scrollbar slider changes which pane is +# active (window_copy_scroll(), window-copy.c). Every other place in the +# codebase that changes the active pane pairs it with +# window_redraw_active_switch() (or falls back to a full window redraw) so +# the old/new active panes' window-active-style/window-style colours get +# repainted - window_copy_scroll() was the one caller that did neither, so +# a scrollbar-slider drag changed focus but left both panes' body colours +# stale. This used to be masked by window_set_active_pane() itself doing +# an unconditional full redraw on every active-pane change, until that was +# narrowed to borders/status-only for the common case. +# +# This creates two tiled panes with clearly distinguishable +# window-active-style/window-style backgrounds, puts the *inactive* one in +# copy mode, drags its scrollbar slider, and checks - via an attached +# client's own received bytes, since window-style is applied during +# redraw composition rather than stored in the grid, so capture-pane +# alone would not reflect it - that the newly active pane's marker text +# immediately shows the active-style colour, not the stale one. + +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 +INNER="$TEST_TMUX -Lsbfocus-inner-$$ -f/dev/null" +OUTER="$TEST_TMUX -Lsbfocus-outer-$$ -f/dev/null" +CAPTURE=$DIR/capture +ESC=$(printf '\033') + +fail() +{ + echo "$*" >&2 + [ -s "$CAPTURE" ] && cat -A "$CAPTURE" >&2 + exit 1 +} + +cleanup() +{ + $OUTER kill-server 2>/dev/null + $INNER kill-server 2>/dev/null + rm -rf "$DIR" +} +trap cleanup 0 1 15 + +get_mark_color() +{ + $OUTER capture-pane -e -p -t outer:0.0 >"$CAPTURE" 2>/dev/null || true + LC_ALL=C grep -a "MARK1" "$CAPTURE" | + LC_ALL=C grep -aoE "${ESC}\[4[12]m" | tail -1 +} + +wait_mark_color() +{ + want=$1 + i=0 + while [ "$i" -lt 50 ]; do + [ "$(get_mark_color)" = "$want" ] && return 0 + sleep 0.1 + i=$((i + 1)) + done + fail "MARK1's pane never showed the expected background (wanted '$want', got '$(get_mark_color)')" +} + +mouse() +{ + sequence=$(printf '\033[<%s;%s;%s%s' "$1" "$2" "$3" "$4") + $OUTER send-keys -t outer:0.0 -l "$sequence" || exit 1 + sleep 0.1 +} + +$INNER new-session -d -s inner -x 40 -y 10 'sleep 100' || exit 1 +$INNER set-option -g status off || exit 1 +$INNER set-option -g window-size manual || exit 1 +$INNER set-option -g mouse on || exit 1 +$INNER set-option -g pane-scrollbars on || exit 1 +$INNER setw pane-scrollbars-position right || exit 1 +$INNER setw pane-scrollbars-style 'width=1,pad=0' || exit 1 +$INNER set-option -g window-active-style 'bg=red' || exit 1 +$INNER set-option -g window-style 'bg=green' || exit 1 +$INNER split-window -h -t inner -d 'printf MARK1; sleep 100' || exit 1 + +PANE1=$($INNER list-panes -t inner -F '#{pane_id}' | sed -n 2p) +[ "$($INNER display-message -p -t "$PANE1" '#{pane_active}')" = 0 ] || + fail "sanity: MARK1's pane is already active before the drag" + +# Put the inactive pane in copy mode without switching focus (copy-mode -t +# targets a pane without making it active). +$INNER copy-mode -t "$PANE1" || exit 1 + +$OUTER new-session -d -s outer -x 40 -y 10 'sleep 100' || exit 1 +$OUTER set-option -g status off || exit 1 +$OUTER set-option -g window-size manual || exit 1 +$OUTER set-option -g default-terminal screen-256color || exit 1 +$OUTER respawn-pane -k -t outer:0.0 \ + "$TEST_TMUX -Lsbfocus-inner-$$ -f/dev/null attach-session -t inner" || + exit 1 + +wait_mark_color "${ESC}[42m" + +XOFF=$($INNER display-message -p -t "$PANE1" '#{pane_left}') +YOFF=$($INNER display-message -p -t "$PANE1" '#{pane_top}') +SX=$($INNER display-message -p -t "$PANE1" '#{pane_width}') +SBCOL=$((XOFF + SX + 1)) +SBROW=$((YOFF + 1)) + +# With no scrollback beyond the pane's own content, the slider fills the +# whole scrollbar track, so any point on it (here, its very first row) is +# on the slider. +mouse 0 "$SBCOL" "$SBROW" M +mouse 32 "$SBCOL" "$((SBROW + 1))" M +mouse 0 "$SBCOL" "$((SBROW + 1))" m + +[ "$($INNER display-message -p -t "$PANE1" '#{pane_active}')" = 1 ] || + fail "sanity: dragging the scrollbar slider did not change focus" + +wait_mark_color "${ESC}[41m" + +exit 0 diff --git a/window-copy.c b/window-copy.c index fd42e91bd..8bfe5c07c 100644 --- a/window-copy.c +++ b/window-copy.c @@ -768,6 +768,7 @@ window_copy_scroll(struct window_pane *wp, int sl_mpos, u_int my, struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes); if (wme != NULL) { + window_redraw_active_switch(wp->window, wp); window_set_active_pane(wp->window, wp, 0); window_copy_scroll1(wme, wp, sl_mpos, my, tty_oy, scroll_exit); }