diff --git a/regress/mouse-raise.sh b/regress/mouse-raise.sh new file mode 100644 index 000000000..64fbdeb8f --- /dev/null +++ b/regress/mouse-raise.sh @@ -0,0 +1,70 @@ +#!/bin/sh + +# Moving the mouse onto a pane with focus-follows-mouse makes it active without +# raising it. + +PATH=/bin:/usr/bin +TERM=screen + +[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) +TMUX="$TEST_TMUX -LtestA$$ -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 +} + +# check PANE FORMAT EXPECTED +check() +{ + got=$($TMUX display-message -p -t "$1" "$2") + [ "$got" = "$3" ] || fail "$1 $2: got '$got', expected '$3'" +} + +# mouse SEQUENCE: send the escape sequence to the inner client. +mouse() +{ + $TMUX2 send-keys -t "$OUTER" -l "$(printf "$1")" || fail "send-keys failed" + sleep 1 +} + +cleanup + +$TMUX new-session -d -s inner -x 80 -y 24 'sleep 100' || exit 1 +$TMUX set -g status off || exit 1 +$TMUX set -g mouse on || exit 1 +$TMUX set -g focus-follows-mouse on || exit 1 +$TMUX set -g pane-border-status top || exit 1 +A=$($TMUX display-message -p '#{pane_id}') || exit 1 +X=$($TMUX new-pane -dPF '#{pane_id}' -x 20 -y 6 -X 5 -Y 8 'sleep 100') || exit 1 +Y=$($TMUX new-pane -dPF '#{pane_id}' -x 20 -y 6 -X 45 -Y 8 'sleep 100') || exit 1 + +$TMUX2 new-session -d -x 80 -y 24 "$TMUX attach -t inner" || exit 1 +sleep 1 +OUTER=$($TMUX2 list-panes -F '#{pane_id}' | head -1) +[ -n "$OUTER" ] || fail "No outer pane." + +# Moving onto a floating pane that is behind another one focuses it but does not +# raise it, whatever pane-raise-on-focus says. +check "$A" '#{pane_active}' 1 +check "$Y" '#{pane_z}' 0 +mouse '\033[<35;12;12M\033[<35;14;13M' +check "$X" '#{pane_active}' 1 +check "$Y" '#{pane_z}' 0 + +# Clicking inside a pane still raises a floating pane, as before. +mouse '\033[<0;54;12M\033[<0;54;12m' +check "$Y" '#{pane_active}:#{pane_z}' '1:0' +mouse '\033[<0;14;12M\033[<0;14;12m' +check "$X" '#{pane_active}:#{pane_z}' '1:0' + +cleanup +exit 0 diff --git a/server-client.c b/server-client.c index 7037f2e64..58bf276eb 100644 --- a/server-client.c +++ b/server-client.c @@ -1061,7 +1061,7 @@ have_event: wp != w->active && options_get_number(s->options, "focus-follows-mouse")) { window_redraw_active_switch(w, wp); - window_set_active_pane(w, wp, 1); + window_focus_pane(w, wp, 1); server_redraw_window_borders(w); server_status_window(w); } diff --git a/tmux.1 b/tmux.1 index 9492ea8a6..dbbb17e0b 100644 --- a/tmux.1 +++ b/tmux.1 @@ -6060,6 +6060,11 @@ raises none. A floating or zoomed pane that is covered by a zoomed pane is always raised when it is made active; a tiled pane that is covered unzooms every pane instead. +Moving the mouse onto a pane with +.Ic focus\-follows\-mouse +makes it active without raising it, so +.Ic all +is usually wanted with that option. .Pp .It Xo Ic pane\-scrollbars .Op Ic off | modal | on | auto\-hide diff --git a/tmux.h b/tmux.h index bf484217b..083fe203e 100644 --- a/tmux.h +++ b/tmux.h @@ -3708,6 +3708,7 @@ int window_has_pane(struct window *, struct window_pane *); int window_pane_contains(struct window_pane *, u_int, u_int); int window_pane_floating_overlaps(struct window_pane *, struct window_pane *); +int window_focus_pane(struct window *, struct window_pane *, int); int window_set_active_pane(struct window *, struct window_pane *, int); void window_fire_pane_moved(struct window_pane *, struct window *, diff --git a/window.c b/window.c index dca83b7cd..e55fd4dd0 100644 --- a/window.c +++ b/window.c @@ -73,6 +73,8 @@ static struct window_pane *window_pane_create(struct window *, u_int, u_int, static void window_pane_destroy(struct window_pane *); static void window_pane_free(struct window_pane *); static void window_pane_scrollbar_timer(int, short, void *); +static int window_activate_pane(struct window *, struct window_pane *, int, + int); static void window_pane_full_size_offset(struct window_pane *, int *, int *, u_int *, u_int *); @@ -742,6 +744,20 @@ window_pane_update_focus(struct window_pane *wp) int window_set_active_pane(struct window *w, struct window_pane *wp, int notify) +{ + return (window_activate_pane(w, wp, notify, 1)); +} + +/* Make a pane active without raising it, as when the mouse moves onto it. */ +int +window_focus_pane(struct window *w, struct window_pane *wp, int notify) +{ + return (window_activate_pane(w, wp, notify, 0)); +} + +static int +window_activate_pane(struct window *w, struct window_pane *wp, int notify, + int raise) { struct window_pane *lastwp; @@ -761,7 +777,7 @@ window_set_active_pane(struct window *w, struct window_pane *wp, int notify) window_raise_pane(wp); else window_unzoom(w, 1); - } else { + } else if (raise) { /* Raise the pane if the option says to. */ switch (options_get_number(w->options, "pane-raise-on-focus")) { case PANE_RAISE_FLOATING: