Merge branch 'master' into command_parser

This commit is contained in:
Your Name
2026-07-16 21:26:42 +01:00
22 changed files with 650 additions and 139 deletions

View File

@@ -4,7 +4,7 @@ CLEANFILES = tmux.1.mdoc tmux.1.man cmd-parse.c
# Distribution tarball options.
EXTRA_DIST = \
CHANGES README README.ja COPYING example_tmux.conf \
CHANGES README COPYING example_tmux.conf \
osdep-*.c mdoc2man.awk tmux.1
dist_EXTRA_tmux_SOURCES = compat/*.[ch]

View File

@@ -1,62 +0,0 @@
tmuxへようこそ!
tmuxはターミナルマルチプレクサーです。複数のターミナルを一つのスクリーン内に作成し、操作することができます。
バックグラウンドで処理を実行中に一度スクリーンから離れて後から復帰することも可能です。
OpenBSD、FreeBSD、NetBSD、Linux、macOS、Solarisで実行できます。
tmuxはlibevent 2.x.に依存します。 下記からダウンロードしてください。
http://libevent.org
また、ncursesも必要です。こちらからどうぞ。
http://invisible-island.net/ncurses/
tarballでのtmuxのビルドとインストール方法。
$ ./configure && make
$ sudo make install
tmuxはutmp(5)をアップデートするためにutempterを使うことができます。もしインストール済みであればオプション「--enable-utempter」をつけて実行してください。
リポジトリから最新バージョンを手に入れるためには下記を実行。
$ git clone https://github.com/tmux/tmux.git
$ cd tmux
$ sh autogen.sh
$ ./configure && make
(ビルドのためにはlibevent、ncurses libraries、headersに加えて、C compiler、make、autoconf、automake、pkg-configが必要です。)
詳しい情報はhttp://git-scm.comをご覧ください。修正はメール<tmux-users@googlegroups.com>宛、もしくはhttps://github.com/tmux/tmux/issuesにて受け付けています。
tmuxのドキュメントについてはtmux.1マニュアルをご覧ください。こちらのコマンドで参照可能です。
$ nroff -mdoc tmux.1|less
サンプル設定は本リポジトリのexample_tmux.confに
また、bash-completionファイルは下記にあります。
https://github.com/scop/bash-completion/blob/main/completions/tmux
「-v」や「-vv」を指定することでデバッグモードでの起動が可能です。カレントディレクトリにサーバーやクライアントのログファイルが生成されます。
議論やバグレポート用のメーリングリストにはこちらから参加可能です。
https://groups.google.com/forum/#!forum/tmux-users
gitコミットについての連絡先
https://groups.google.com/forum/#!forum/tmux-git
購読は<tmux-users+subscribe@googlegroups.com>までメールをお願いします。
バグレポートや機能追加(特にコードへの貢献)は大歓迎です。こちらにご連絡ください。
tmux-users@googlegroups.com
本ファイル、CHANGES、 FAQ、SYNCING.mdそしてTODOはISC licenseで保護されています。
その他のファイルのライセンスや著作権については、ファイルの上部に明記されています。
-- Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-break-pane.c,v 1.73 2026/07/13 13:01:14 nicm Exp $ */
/* $OpenBSD: cmd-break-pane.c,v 1.74 2026/07/15 13:02:33 nicm Exp $ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -103,6 +103,11 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
int idx = target->idx, before, old_idx = wl->idx;
const char *template, *name = args_get(args, 'n');
if (wp == w->modal) {
cmdq_error(item, "pane is modal");
return (CMD_RETURN_ERROR);
}
if (args_has(args, 'W'))
return (cmd_break_pane_float(item, args, w, wp));

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-join-pane.c,v 1.71 2026/07/13 13:01:14 nicm Exp $ */
/* $OpenBSD: cmd-join-pane.c,v 1.72 2026/07/15 13:02:33 nicm Exp $ */
/*
* Copyright (c) 2011 George Nachman <tmux@georgester.com>
@@ -424,7 +424,6 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
dst_wp = target->wp;
dst_w = dst_wl->window;
dst_idx = dst_wl->idx;
server_unzoom_window(dst_w);
if (cmd_get_entry(self) == &cmd_move_pane_entry) {
if (args_has(args, 'M'))
@@ -433,22 +432,35 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
cmdq_error(item, "pane is not floating");
return (CMD_RETURN_ERROR);
}
if ((s = args_get(args, 'P')) != NULL)
if ((s = args_get(args, 'P')) != NULL) {
server_unzoom_window(dst_w);
return (cmd_join_pane_place(item, dst_wl, dst_wp, s));
if ((s = args_get(args, 'z')) != NULL)
}
if ((s = args_get(args, 'z')) != NULL) {
server_unzoom_window(dst_w);
return (cmd_join_pane_zindex(item, dst_wl, dst_wp, s));
}
if (args_has(args, 'X') ||
args_has(args, 'Y') ||
args_has(args, 'U') ||
args_has(args, 'D') ||
args_has(args, 'L') ||
args_has(args, 'R'))
args_has(args, 'R')) {
server_unzoom_window(dst_w);
return (cmd_join_pane_move(item, args, dst_wl, dst_wp));
}
}
src_wl = source->wl;
src_wp = source->wp;
src_w = src_wl->window;
if (src_wp == src_w->modal || dst_wp == dst_w->modal) {
cmdq_error(item, "pane is modal");
return (CMD_RETURN_ERROR);
}
server_unzoom_window(dst_w);
server_unzoom_window(src_w);
if (src_wp == dst_wp) {

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-split-window.c,v 1.144 2026/07/14 15:06:54 nicm Exp $ */
/* $OpenBSD: cmd-split-window.c,v 1.145 2026/07/15 13:02:33 nicm Exp $ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -40,8 +40,8 @@ const struct cmd_entry cmd_new_pane_entry = {
.name = "new-pane",
.alias = "newp",
.args = { "bB:c:de:EfF:hIkl:LMm:p:PR:s:S:t:T:vWx:X:y:Y:Z", 0, -1, NULL },
.usage = "[-bdefhIklMPvWZ] [-B border-lines] "
.args = { "bB:c:de:EfF:hIkl:LMm:Op:PR:s:S:t:T:vWx:X:y:Y:Z", 0, -1, NULL },
.usage = "[-bdefhIklMOPvWZ] [-B border-lines] "
"[-c start-directory] [-e environment] "
"[-F format] [-l size] [-m message] [-p percentage] "
"[-s style] [-S active-border-style] "
@@ -103,6 +103,17 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
flags |= SPAWN_SPLIT;
}
if (args_has(args, 'O')) {
if (!is_floating) {
cmdq_error(item, "modal pane must be floating");
return (CMD_RETURN_ERROR);
}
if (w->modal != NULL) {
cmdq_error(item, "window already has a modal pane");
return (CMD_RETURN_ERROR);
}
}
if (args_has(args, 'M') && is_floating) {
if (event == NULL || !event->m.valid || tc == NULL)
return (CMD_RETURN_NORMAL);
@@ -120,6 +131,8 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
flags |= SPAWN_DETACHED;
if (args_has(args, 'Z'))
flags |= SPAWN_ZOOM;
if (args_has(args, 'O'))
flags |= SPAWN_MODAL;
input = args_has(args, 'I');
if (input || (count == 1 && *args_string(args, 0) == '\0'))
@@ -255,7 +268,7 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
if (~flags & SPAWN_DETACHED)
cmd_find_from_winlink_pane(current, wl, new_wp, 0);
if (~flags & SPAWN_FLOATING) {
if ((~flags & SPAWN_FLOATING) && !args_has(args, 'O')) {
window_pop_zoom(wp->window);
server_redraw_window(wp->window);
}
@@ -306,7 +319,8 @@ fail:
if (!is_floating)
layout_close_pane(new_wp);
window_remove_pane(wp->window, new_wp);
}
} else if (args_has(args, 'O'))
window_pop_modal_zoom(wp->window);
if (sc.argv != NULL)
cmd_free_argv(sc.argc, sc.argv);
environ_free(sc.environ);

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-swap-pane.c,v 1.54 2026/07/13 13:01:14 nicm Exp $ */
/* $OpenBSD: cmd-swap-pane.c,v 1.55 2026/07/15 13:02:33 nicm Exp $ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -77,6 +77,11 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item)
src_wp = source->wp;
src_idx = source->wl->idx;
if (src_wp == src_w->modal || dst_wp == dst_w->modal) {
cmdq_error(item, "pane is modal");
return (CMD_RETURN_ERROR);
}
if (window_push_zoom(dst_w, 0, args_has(args, 'Z')))
server_redraw_window(dst_w);

4
cmd.c
View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd.c,v 1.187 2026/07/13 09:37:39 nicm Exp $ */
/* $OpenBSD: cmd.c,v 1.188 2026/07/15 13:02:33 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -633,6 +633,8 @@ cmd_mouse_pane(struct mouse_event *m, struct session **sp,
if (!window_has_pane(wl->window, wp))
return (NULL);
}
if (wl->window->modal != NULL && wp != wl->window->modal)
return (NULL);
if (wlp != NULL)
*wlp = wl;

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: format.c,v 1.404 2026/07/14 15:06:54 nicm Exp $ */
/* $OpenBSD: format.c,v 1.405 2026/07/15 13:02:33 nicm Exp $ */
/*
* Copyright (c) 2011 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -1088,6 +1088,20 @@ format_cb_pane_floating_flag(struct format_tree *ft)
return (NULL);
}
/* Callback for pane_modal_flag. */
static void *
format_cb_pane_modal_flag(struct format_tree *ft)
{
struct window_pane *wp = ft->wp;
if (wp != NULL) {
if (wp == wp->window->modal)
return (xstrdup("1"));
return (xstrdup("0"));
}
return (NULL);
}
/* Callback for pane_bg. */
static void *
format_cb_pane_bg(struct format_tree *ft)
@@ -3102,6 +3116,15 @@ format_cb_window_marked_flag(struct format_tree *ft)
return (NULL);
}
/* Callback for window_modal_pane. */
static void *
format_cb_window_modal_pane(struct format_tree *ft)
{
if (ft->w != NULL && ft->w->modal != NULL)
return (format_printf("%%%u", ft->w->modal->id));
return (NULL);
}
/* Callback for window_name. */
static void *
format_cb_window_name(struct format_tree *ft)
@@ -3684,6 +3707,9 @@ static const struct format_table_entry format_table[] = {
{ "pane_marked_set", FORMAT_TABLE_STRING,
format_cb_pane_marked_set
},
{ "pane_modal_flag", FORMAT_TABLE_STRING,
format_cb_pane_modal_flag
},
{ "pane_mode", FORMAT_TABLE_STRING,
format_cb_pane_mode
},
@@ -3942,6 +3968,9 @@ static const struct format_table_entry format_table[] = {
{ "window_marked_flag", FORMAT_TABLE_STRING,
format_cb_window_marked_flag
},
{ "window_modal_pane", FORMAT_TABLE_STRING,
format_cb_window_modal_pane
},
{ "window_name", FORMAT_TABLE_STRING,
format_cb_window_name
},

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: key-bindings.c,v 1.188 2026/07/13 16:30:28 nicm Exp $ */
/* $OpenBSD: key-bindings.c,v 1.189 2026/07/15 10:38:31 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -501,6 +501,7 @@ key_bindings_init(void)
/* Mouse button 1 drag on pane. */
"bind -n MouseDrag1Pane { if -F '#{||:#{pane_in_mode},#{mouse_any_flag}}' { send -M } { copy-mode -M } }",
"bind -n C-MouseDrag1Pane { new-pane -M }",
"bind -n C-MouseDrag1Empty { new-pane -M }",
"bind -n M-MouseDrag1Pane { move-pane -M }",
/* Mouse wheel up on pane. */

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: layout-custom.c,v 1.37 2026/07/10 13:38:45 nicm Exp $ */
/* $OpenBSD: layout-custom.c,v 1.38 2026/07/16 12:36:58 nicm Exp $ */
/*
* Copyright (c) 2010 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -177,7 +177,7 @@ layout_parse(struct window *w, const char *layout, char **cause)
struct window_pane *wp;
u_int npanes, ncells, sx = 0, sy = 0;
u_short csum;
int n;
int n = 0;
/* Check validity. */
if (sscanf(layout, "%hx,%n", &csum, &n) != 1 || n != 5) {

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: layout.c,v 1.95 2026/07/14 17:17:17 nicm Exp $ */
/* $OpenBSD: layout.c,v 1.96 2026/07/15 13:02:33 nicm Exp $ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -1677,7 +1677,10 @@ layout_get_floating_cell(struct cmdq_item *item, struct args *args,
return (NULL);
}
window_push_zoom(wp->window, 1, (flags & SPAWN_ZOOM));
if (flags & SPAWN_MODAL)
window_push_modal_zoom(w);
else
window_push_zoom(wp->window, 1, (flags & SPAWN_ZOOM));
lcnew = layout_floating_pane(w, wp, &fg);
return (lcnew);
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: options-table.c,v 1.237 2026/07/14 17:17:17 nicm Exp $ */
/* $OpenBSD: options-table.c,v 1.238 2026/07/15 13:02:33 nicm Exp $ */
/*
* Copyright (c) 2011 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -1530,9 +1530,10 @@ const struct options_table_entry options_table[] = {
{ .name = "pane-active-border-style",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
.default_str = "fg=#{?pane_marked,thememagenta,"
.default_str = "fg=#{?pane_modal_flag,themeblue,"
"#{?pane_marked,thememagenta,"
"#{?synchronize-panes,themered,"
"#{?pane_in_mode,themeyellow,themegreen}}}",
"#{?pane_in_mode,themeyellow,themegreen}}}}",
.flags = OPTIONS_TABLE_IS_STYLE,
.separator = ",",
.text = "Style of the active pane border."

308
regress/modal-pane.sh Normal file
View File

@@ -0,0 +1,308 @@
#!/bin/sh
# Tests for modal floating panes.
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
}
must_equal()
{
got=$1
want=$2
[ "$got" = "$want" ] || fail "got '$got', expected '$want'"
}
check_ok()
{
$TMUX "$@" || fail "command failed: $*"
}
check_fail()
{
exp="$1"
shift
out=$($TMUX "$@" 2>&1)
if [ $? -eq 0 ]; then
fail "command succeeded (expected failure): $*"
fi
must_equal "$out" "$exp"
}
fmt()
{
$TMUX display-message -p -t "$1" "$2"
}
click()
{
col="$1"
row="$2"
seq=$(printf '\033[<0;%s;%sM\033[<0;%s;%sm' \
"$col" "$row" "$col" "$row")
$TMUX2 send-keys -t "$OUTER" -l "$seq" 2>/dev/null
sleep 1
}
move_mouse()
{
col="$1"
row="$2"
seq=$(printf '\033[<35;%s;%sM' "$col" "$row")
$TMUX2 send-keys -t "$OUTER" -l "$seq" 2>/dev/null
sleep 1
}
ctrl_drag()
{
scol="$1"
srow="$2"
ecol="$3"
erow="$4"
seq=$(printf '\033[<16;%s;%sM' "$scol" "$srow")
$TMUX2 send-keys -t "$OUTER" -l "$seq" 2>/dev/null
sleep 0.2
seq=$(printf '\033[<48;%s;%sM' "$ecol" "$erow")
$TMUX2 send-keys -t "$OUTER" -l "$seq" 2>/dev/null
sleep 0.2
seq=$(printf '\033[<16;%s;%sm' "$ecol" "$erow")
$TMUX2 send-keys -t "$OUTER" -l "$seq" 2>/dev/null
sleep 1
}
drag()
{
scol="$1"
srow="$2"
ecol="$3"
erow="$4"
seq=$(printf '\033[<0;%s;%sM' "$scol" "$srow")
$TMUX2 send-keys -t "$OUTER" -l "$seq" 2>/dev/null
sleep 0.2
seq=$(printf '\033[<32;%s;%sM' "$ecol" "$erow")
$TMUX2 send-keys -t "$OUTER" -l "$seq" 2>/dev/null
sleep 0.2
seq=$(printf '\033[<0;%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'
sleep 1
p0=$(fmt modal:0 '#{pane_id}')
check_ok split-window -h -t "$p0" 'cat'
sleep 1
p1=$(fmt modal:0 '#{pane_id}')
check_ok select-pane -t "$p0"
check_ok resize-pane -Z -t "$p0"
must_equal "$(fmt modal:0 '#{window_zoomed_flag}')" 1
modal=$($TMUX new-pane -OPF '#{pane_id}' -t "$p1" \
-x 20 -y 5 -X 20 -Y 10 'cat') ||
fail "new-pane -O failed"
sleep 1
must_equal "$(fmt "$modal" '#{pane_floating_flag}:#{pane_modal_flag}:#{pane_active}')" 1:1:1
case "$(fmt "$modal" '#{pane_flags}')" in
*O*) ;;
*) fail "modal pane flags do not include O" ;;
esac
case "$(fmt modal:0 '#{window_flags}')" in
*O*) ;;
*) fail "modal window flags do not include O" ;;
esac
must_equal "$(fmt modal:0 '#{window_modal_pane}')" "$modal"
must_equal "$(fmt modal:0 '#{window_zoomed_flag}')" 0
check_fail "window already has a modal pane" \
new-pane -O -x 10 -y 4 'cat'
check_fail "modal pane must be floating" \
new-pane -O -L 'cat'
check_fail "pane is modal" \
break-pane -s "$modal"
check_fail "pane is modal" \
join-pane -s "$modal" -t "$p0"
check_fail "pane is modal" \
join-pane -s "$p0" -t "$modal"
check_fail "pane is modal" \
swap-pane -s "$modal" -t "$p0"
check_fail "pane is modal" \
swap-pane -s "$p0" -t "$modal"
check_ok select-pane -t "$p1"
must_equal "$(fmt modal:0 '#{pane_id}')" "$modal"
check_ok last-pane -t modal:0
must_equal "$(fmt modal:0 '#{pane_id}')" "$modal"
under=$($TMUX split-window -PF '#{pane_id}' -t "$p0" 'cat') ||
fail "split-window under modal failed"
sleep 1
must_equal "$(fmt modal:0 '#{pane_id}')" "$modal"
must_equal "$(fmt "$under" '#{pane_active}')" 0
float=$($TMUX new-pane -PF '#{pane_id}' -x 10 -y 4 -X 5 -Y 3 'cat') ||
fail "new floating pane under modal failed"
sleep 1
must_equal "$(fmt modal:0 '#{pane_id}')" "$modal"
must_equal "$(fmt "$float" '#{pane_active}')" 0
check_ok new-window -d -t modal: -n other 'cat'
check_ok select-window -t modal:other
other=$(fmt modal:other '#{pane_id}')
must_equal "$(fmt modal:other '#{pane_id}')" "$other"
check_ok select-window -t modal:0
must_equal "$(fmt modal:0 '#{pane_id}')" "$modal"
check_ok send-keys -t modal:0 'modal-key' Enter
sleep 1
case "$($TMUX capture-pane -pt "$modal")" in
*modal-key*) ;;
*) fail "keyboard input did not reach modal pane" ;;
esac
case "$($TMUX capture-pane -pt "$p0")" in
*modal-key*) fail "keyboard input reached pane below modal" ;;
esac
$TMUX set -g mouse on
$TMUX set -g focus-follows-mouse on
$TMUX set -g @modal-mouse ''
$TMUX bind -n MouseDown1Pane run-shell \
"$TMUX set -g @modal-mouse '#{mouse_pane}'"
$TMUX bind x set -g @modal-prefix yes
$TMUX2 new-session -d -x 80 -y 24 "$TMUX attach -t modal" ||
fail "outer session failed"
sleep 1
OUTER=$($TMUX2 list-panes -F '#{pane_id}' | head -1)
[ -n "$OUTER" ] || fail "no outer pane"
click 1 1
must_equal "$($TMUX show -gv @modal-mouse)" ''
must_equal "$(fmt modal:0 '#{pane_id}')" "$modal"
panes=$(fmt modal:0 '#{window_panes}')
ctrl_drag 1 1 8 3
must_equal "$(fmt modal:0 '#{window_panes}')" "$panes"
must_equal "$(fmt modal:0 '#{pane_id}')" "$modal"
move_mouse 1 1
must_equal "$(fmt modal:0 '#{pane_id}')" "$modal"
left=$(fmt "$modal" '#{pane_left}')
top=$(fmt "$modal" '#{pane_top}')
click $((left + 1)) $((top + 1))
must_equal "$($TMUX show -gv @modal-mouse)" "$modal"
must_equal "$(fmt modal:0 '#{pane_id}')" "$modal"
width=$(fmt "$modal" '#{pane_width}')
right=$((left + width + 1))
drag "$right" $((top + 1)) $((right + 5)) $((top + 1))
new_width=$(fmt "$modal" '#{pane_width}')
[ "$new_width" -gt "$width" ] ||
fail "modal pane did not grow after right-border drag"
$TMUX2 send-keys -t "$OUTER" C-b x
sleep 1
must_equal "$($TMUX show -gv @modal-prefix)" yes
$TMUX set-buffer -b modal-edit-test 'test'
check_ok choose-buffer -t "$modal"
panes=$(fmt modal:0 '#{window_panes}')
$TMUX2 send-keys -t "$OUTER" e
sleep 1
must_equal "$(fmt modal:0 '#{window_panes}')" "$panes"
must_equal "$(fmt modal:0 '#{window_modal_pane}')" "$modal"
$TMUX2 send-keys -t "$OUTER" q
sleep 1
check_ok kill-pane -t "$modal"
sleep 1
must_equal "$(fmt modal:0 '#{window_modal_pane}')" ''
case "$(fmt modal:0 '#{window_flags}')" in
*O*) fail "modal window flag remained after modal pane closed" ;;
esac
must_equal "$(fmt modal:0 '#{pane_id}')" "$p0"
must_equal "$(fmt modal:0 '#{window_zoomed_flag}')" 1
check_ok resize-pane -Z -t "$p0"
must_equal "$(fmt modal:0 '#{window_zoomed_flag}')" 0
$TMUX set -g editor 'sh -c "sleep 10" sh'
check_ok resize-pane -Z -t "$p0"
check_ok choose-buffer -t "$p0"
panes=$(fmt modal:0 '#{window_panes}')
$TMUX2 send-keys -t "$OUTER" e
sleep 1
editor=$(fmt modal:0 '#{window_modal_pane}')
[ -n "$editor" ] || fail "buffer editor did not open as modal pane"
must_equal "$(fmt modal:0 '#{window_panes}')" $((panes + 1))
must_equal "$(fmt "$editor" '#{pane_modal_flag}:#{pane_active}')" 1:1
check_ok kill-pane -t "$editor"
sleep 1
must_equal "$(fmt modal:0 '#{window_modal_pane}')" ''
must_equal "$(fmt modal:0 '#{pane_id}')" "$p0"
must_equal "$(fmt modal:0 '#{window_zoomed_flag}')" 1
check_ok resize-pane -Z -t "$p0"
must_equal "$(fmt modal:0 '#{window_zoomed_flag}')" 0
detached=$($TMUX new-pane -OdPF '#{pane_id}' -x 20 -y 5 -X 20 -Y 10 \
'cat') || fail "new detached modal failed"
sleep 1
must_equal "$(fmt "$detached" '#{pane_modal_flag}:#{pane_active}')" 1:1
must_equal "$(fmt modal:0 '#{window_modal_pane}')" "$detached"
check_ok kill-pane -t "$detached"
sleep 1
must_equal "$(fmt modal:0 '#{window_modal_pane}')" ''
must_equal "$(fmt modal:0 '#{pane_id}')" "$p0"
$TMUX set -g @modal-custom old
check_ok customize-mode -t "$p0" \
-f '#{==:#{option_name},@modal-custom}'
panes=$(fmt modal:0 '#{window_panes}')
$TMUX2 send-keys -t "$OUTER" j Right j e
sleep 1
editor=$(fmt modal:0 '#{window_modal_pane}')
[ -n "$editor" ] || fail "customize editor did not open as modal pane"
must_equal "$(fmt modal:0 '#{window_panes}')" $((panes + 1))
must_equal "$(fmt "$editor" '#{pane_modal_flag}:#{pane_active}')" 1:1
check_ok kill-pane -t "$editor"
sleep 1
must_equal "$(fmt modal:0 '#{window_modal_pane}')" ''
$TMUX2 send-keys -t "$OUTER" q
sleep 1
modal=$($TMUX new-pane -OkPF '#{pane_id}' -x 20 -y 5 -X 20 -Y 10 'printf done') ||
fail "new retained modal failed"
sleep 2
must_equal "$(fmt "$modal" '#{pane_dead}:#{pane_modal_flag}:#{pane_active}')" 1:1:1
check_ok respawn-pane -k -t "$modal" 'cat'
sleep 1
must_equal "$(fmt "$modal" '#{pane_dead}:#{pane_modal_flag}:#{pane_active}')" 0:1:1
check_ok select-pane -t "$p1"
must_equal "$(fmt modal:0 '#{pane_id}')" "$modal"
check_ok kill-pane -t "$modal"
sleep 1
must_equal "$(fmt modal:0 '#{window_modal_pane}')" ''
must_equal "$(fmt modal:0 '#{pane_id}')" "$p0"
cleanup
exit 0

View File

@@ -75,7 +75,26 @@ must_equal "$($TMUX display-message -p -t "$id" '#{pane_top}')" 1
must_equal "$($TMUX display-message -p -t "$id" '#{pane_width}')" 6
must_equal "$($TMUX display-message -p -t "$id" '#{pane_height}')" 3
$TMUX new-pane -d -M -L -t "$BASE" 'sleep 100' || fail "new-pane -M -L failed"
$TMUX kill-pane -t "$id" || fail "kill floating pane failed"
$TMUX break-pane -W -s "$BASE" || fail "break base pane failed"
$TMUX resize-pane -t "$BASE" -x10 -y4 || fail "resize floating base failed"
$TMUX move-pane -t "$BASE" -P top-left || fail "move floating base failed"
# Drag in empty window space with no tiled pane underneath.
drag 40 10 50 15
id=$($TMUX list-panes -F '#{?pane_active,#{pane_id},}' | tail -n 1)
[ -n "$id" ] || fail "no floating pane created from empty area"
[ "$id" != "$BASE" ] || fail "empty-area drag did not create a new pane"
must_equal "$($TMUX display-message -p -t "$id" '#{pane_left}')" 40
must_equal "$($TMUX display-message -p -t "$id" '#{pane_top}')" 10
must_equal "$($TMUX display-message -p -t "$id" '#{pane_width}')" 9
must_equal "$($TMUX display-message -p -t "$id" '#{pane_height}')" 4
TILED=$($TMUX new-window -dPF '#{pane_id}' 'sleep 100') ||
fail "new tiled window failed"
$TMUX new-pane -d -M -L -t "$TILED" 'sleep 100' || fail "new-pane -M -L failed"
cleanup
exit 0

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: screen-redraw.c,v 1.151 2026/07/14 19:07:03 nicm Exp $ */
/* $OpenBSD: screen-redraw.c,v 1.152 2026/07/16 10:52:32 nicm Exp $ */
/*
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -1858,9 +1858,12 @@ redraw_screen(struct client *c)
{
int flags = 0;
if (c->flags & CLIENT_REDRAWWINDOW)
redraw_draw(c, NULL, REDRAW_ALL);
else {
if (c->flags & CLIENT_REDRAWWINDOW) {
if (c->flags & CLIENT_REDRAWOVERLAY)
redraw_draw(c, NULL, REDRAW_ALL);
else
redraw_draw(c, NULL, REDRAW_ALL & ~REDRAW_OVERLAY);
} else {
if (c->flags & CLIENT_REDRAWBORDERS)
flags |= (REDRAW_PANE_BORDER|REDRAW_PANE_STATUS);
if (c->flags & CLIENT_REDRAWSTATUS)

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: server-client.c,v 1.491 2026/07/14 19:07:03 nicm Exp $ */
/* $OpenBSD: server-client.c,v 1.494 2026/07/15 14:14:50 nicm Exp $ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -871,6 +871,7 @@ server_client_check_mouse(struct client *c, struct key_event *event)
u_int x, y, sx, sy, px, py, n, sl_mpos = 0;
u_int b, bn;
int ignore = 0;
int modal_drag = 0;
key_code key;
struct timeval tv;
struct style_range *sr;
@@ -1029,46 +1030,73 @@ have_event:
* Not on status line. Adjust position and check for border, pane, or
* scrollbar.
*/
if (loc == KEYC_MOUSE_LOCATION_NOWHERE) {
if (c->tty.mouse_scrolling_flag) {
if (lwp != NULL) {
loc = KEYC_MOUSE_LOCATION_SCROLLBAR_SLIDER;
m->wp = lwp->id;
m->w = lwp->window->id;
}
} else {
px = x;
if (m->statusat == 0 && y >= m->statuslines)
py = y - m->statuslines;
else if (m->statusat > 0 && y >= (u_int)m->statusat)
py = m->statusat - 1;
else
py = y;
if (loc == KEYC_MOUSE_LOCATION_NOWHERE && c->tty.mouse_scrolling_flag) {
if (lwp != NULL) {
loc = KEYC_MOUSE_LOCATION_SCROLLBAR_SLIDER;
m->wp = lwp->id;
m->w = lwp->window->id;
}
} else if (loc == KEYC_MOUSE_LOCATION_NOWHERE) {
px = x;
if (m->statusat == 0 && y >= m->statuslines)
py = y - m->statuslines;
else if (m->statusat > 0 && y >= (u_int)m->statusat)
py = m->statusat - 1;
else
py = y;
tty_window_offset(&c->tty, &m->ox, &m->oy, &sx, &sy);
log_debug("mouse window @%u at %u,%u (%ux%u)",
w->id, m->ox, m->oy, sx, sy);
if (px > sx || py > sy) {
tty_window_offset(&c->tty, &m->ox, &m->oy, &sx, &sy);
log_debug("mouse window @%u at %u,%u (%ux%u)", w->id, m->ox,
m->oy, sx, sy);
if (px > sx || py > sy) {
server_client_update_scrollbar_hover(c, type, -1, -1);
return (KEYC_UNKNOWN);
}
px = px + m->ox;
py = py + m->oy;
if (w->modal != NULL &&
!window_pane_contains(w->modal, px, py)) {
if (lwp == w->modal &&
c->tty.mouse_drag_flag != 0 &&
(type == KEYC_TYPE_MOUSEDRAG ||
type == KEYC_TYPE_MOUSEUP)) {
modal_drag = 1;
wp = lwp;
loc = KEYC_MOUSE_LOCATION_PANE;
m->wp = wp->id;
m->w = wp->window->id;
} else {
server_client_update_scrollbar_hover(c, type,
-1, -1);
c->tty.mouse_drag_update = NULL;
c->tty.mouse_drag_release = NULL;
c->tty.mouse_drag_flag = 0;
c->tty.mouse_scrolling_flag = 0;
c->tty.mouse_slider_mpos = -1;
c->tty.mouse_last_pane = -1;
return (KEYC_UNKNOWN);
}
px = px + m->ox;
py = py + m->oy;
server_client_update_scrollbar_hover(c, type, px, py);
}
server_client_update_scrollbar_hover(c, type, px, py);
if (type == KEYC_TYPE_MOUSEDRAG && lwp != NULL) {
/* Use pane from last mouse event. */
wp = lwp;
} else {
/* Try inside the pane. */
wp = window_get_active_at(w, px, py);
if (modal_drag) {
/* Keep the drag with the modal pane. */
} else if (type == KEYC_TYPE_MOUSEDRAG && lwp != NULL) {
/* Use pane from last mouse event. */
wp = lwp;
} else {
/* Try inside the pane. */
wp = window_get_active_at(w, px, py);
}
if (wp == NULL) {
loc = KEYC_MOUSE_LOCATION_EMPTY;
m->w = w->id;
log_debug("mouse %u,%u on empty area", x, y);
} else {
if (!modal_drag) {
loc = server_client_check_mouse_in_pane(wp, px,
py, &sl_mpos);
}
if (wp == NULL)
return (KEYC_UNKNOWN);
loc = server_client_check_mouse_in_pane(wp, px, py,
&sl_mpos);
if (loc == KEYC_MOUSE_LOCATION_PANE) {
log_debug("mouse %u,%u on pane %%%u", x, y,
wp->id);
@@ -3058,16 +3086,20 @@ struct window_pane *
server_client_get_pane(struct client *c)
{
struct session *s = c->session;
struct window *w;
struct client_window *cw;
if (s == NULL)
return (NULL);
w = s->curw->window;
if (w->modal != NULL)
return (w->modal);
if (~c->flags & CLIENT_ACTIVEPANE)
return (s->curw->window->active);
cw = server_client_get_client_window(c, s->curw->window->id);
return (w->active);
cw = server_client_get_client_window(c, w->id);
if (cw == NULL)
return (s->curw->window->active);
return (w->active);
return (cw->pane);
}
@@ -3080,6 +3112,8 @@ server_client_set_pane(struct client *c, struct window_pane *wp)
if (s == NULL)
return;
if (wp->window->modal != NULL && wp != wp->window->modal)
return;
cw = server_client_add_client_window(c, s->curw->window->id);
cw->pane = wp;

33
spawn.c
View File

@@ -1,4 +1,4 @@
/* $OpenBSD: spawn.c,v 1.48 2026/07/13 13:01:14 nicm Exp $ */
/* $OpenBSD: spawn.c,v 1.49 2026/07/15 13:02:33 nicm Exp $ */
/*
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -271,6 +271,17 @@ spawn_pane(struct spawn_context *sc, char **cause)
spawn_log(__func__, sc);
if (sc->flags & SPAWN_MODAL) {
if (~sc->flags & SPAWN_FLOATING) {
xasprintf(cause, "modal pane must be floating");
return (NULL);
}
if (w->modal != NULL) {
xasprintf(cause, "window already has a modal pane");
return (NULL);
}
}
/*
* Work out the current working directory. If respawning, use
* the pane's stored one unless specified.
@@ -565,7 +576,16 @@ complete:
if (sc->flags & SPAWN_RESPAWN)
return (new_wp);
if ((~sc->flags & SPAWN_DETACHED) || w->active == NULL) {
if (sc->flags & SPAWN_MODAL) {
w->modal_last = w->active;
w->modal = new_wp;
window_redraw_active_switch(w, new_wp);
if (sc->flags & SPAWN_NONOTIFY)
window_set_active_pane(w, new_wp, 0);
else
window_set_active_pane(w, new_wp, 1);
} else if (((~sc->flags & SPAWN_DETACHED) || w->active == NULL) &&
w->modal == NULL) {
if (sc->flags & SPAWN_NONOTIFY)
window_set_active_pane(w, new_wp, 0);
else
@@ -681,6 +701,9 @@ spawn_editor(struct client *c, const char *buf, size_t len,
const char *editor;
int fd;
if (w->modal != NULL)
return (NULL);
editor = options_get_string(global_options, "editor");
fd = mkstemp(path);
if (fd == -1)
@@ -707,9 +730,10 @@ spawn_editor(struct client *c, const char *buf, size_t len,
lg.sy = w->sy * 9 / 10;
lg.xoff = w->sx / 2 - lg.sx / 2;
lg.yoff = w->sy / 2 - lg.sy / 2;
window_push_zoom(w, 1, 0);
window_push_modal_zoom(w);
lc = layout_floating_pane(w, NULL, &lg);
if (lc == NULL) {
window_pop_modal_zoom(w);
spawn_editor_free(es);
return (NULL);
}
@@ -726,13 +750,14 @@ spawn_editor(struct client *c, const char *buf, size_t len,
sc.environ = env;
sc.idx = -1;
sc.cwd = _PATH_TMP;
sc.flags = SPAWN_FLOATING;
sc.flags = SPAWN_FLOATING|SPAWN_MODAL;
wp = spawn_pane(&sc, &cause);
free(cmd);
environ_free(env);
if (wp == NULL) {
free(cause);
window_pop_modal_zoom(w);
spawn_editor_free(es);
return (NULL);
}

14
tmux.1
View File

@@ -1,4 +1,4 @@
.\" $OpenBSD: tmux.1,v 1.1140 2026/07/14 17:17:18 nicm Exp $
.\" $OpenBSD: tmux.1,v 1.1142 2026/07/15 13:02:33 nicm Exp $
.\"
.\" Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
.\"
@@ -14,7 +14,7 @@
.\" IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
.\" OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
.Dd $Mdocdate: July 14 2026 $
.Dd $Mdocdate: July 15 2026 $
.Dt TMUX 1
.Os
.Sh NAME
@@ -3647,7 +3647,7 @@ but a different format may be specified with
.Fl F .
.Tg newp
.It Xo Ic new\-pane
.Op Fl bdefhIkLMPvWZ
.Op Fl bdefhIkLMOPvWZ
.Op Fl B Ar border\-lines
.Op Fl c Ar start\-directory
.Op Fl e Ar environment
@@ -3698,6 +3698,11 @@ may be used when bound to a mouse drag key; the new floating pane is resized to
follow the mouse drag.
It has no effect with
.Fl L .
.Fl O
creates a modal pane.
A modal pane is always the active pane and prevents interaction with any other
panes while it is active.
A window can only have one modal pane and it must be a floating pane.
.Pp
The
.Fl L
@@ -6622,6 +6627,7 @@ and a location suffix, one of the following:
.It Li "ScrollbarSlider" Ta "the scrollbar slider"
.It Li "ScrollbarUp" Ta "above the scrollbar slider"
.It Li "ScrollbarDown" Ta "below the scrollbar slider"
.It Li "Empty" Ta "empty space in the window"
.It Li "ControlN" Ta "on control range N"
.El
.Pp
@@ -7321,6 +7327,7 @@ The following variables are available, where appropriate:
.It Li "pane_left" Ta "" Ta "Left of pane"
.It Li "pane_marked" Ta "" Ta "1 if this is the marked pane"
.It Li "pane_marked_set" Ta "" Ta "1 if a marked pane is set"
.It Li "pane_modal_flag" Ta "" Ta "1 if pane is modal"
.It Li "pane_mode" Ta "" Ta "Name of pane mode, if any"
.It Li "pane_path" Ta "" Ta "Path of pane (can be set by application)"
.It Li "pane_pid" Ta "" Ta "PID of first process in pane"
@@ -7421,6 +7428,7 @@ The following variables are available, where appropriate:
.It Li "window_manual_height" Ta "" Ta "Manual height of window, if set"
.It Li "window_manual_width" Ta "" Ta "Manual width of window, if set"
.It Li "window_marked_flag" Ta "" Ta "1 if window contains the marked pane"
.It Li "window_modal_pane" Ta "" Ta "Modal pane in window, if any"
.It Li "window_name" Ta "#W" Ta "Name of window"
.It Li "window_offset_x" Ta "" Ta "X offset into window if larger than client"
.It Li "window_offset_y" Ta "" Ta "Y offset into window if larger than client"

12
tmux.h
View File

@@ -1,4 +1,4 @@
/* $OpenBSD: tmux.h,v 1.1404 2026/07/14 19:07:03 nicm Exp $ */
/* $OpenBSD: tmux.h,v 1.1406 2026/07/15 13:02:33 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -193,6 +193,7 @@ enum key_code_mouse_location {
KEYC_MOUSE_LOCATION_SCROLLBAR_UP,
KEYC_MOUSE_LOCATION_SCROLLBAR_SLIDER,
KEYC_MOUSE_LOCATION_SCROLLBAR_DOWN,
KEYC_MOUSE_LOCATION_EMPTY,
KEYC_MOUSE_LOCATION_CONTROL0, /* keep order */
KEYC_MOUSE_LOCATION_CONTROL1,
KEYC_MOUSE_LOCATION_CONTROL2,
@@ -249,6 +250,7 @@ enum key_code_mouse_location {
KEYC_MOUSE_KEY(KEYC_ ## t, KEYC_TYPE_ ## t, SCROLLBAR_UP), \
KEYC_MOUSE_KEY(KEYC_ ## t, KEYC_TYPE_ ## t, SCROLLBAR_SLIDER), \
KEYC_MOUSE_KEY(KEYC_ ## t, KEYC_TYPE_ ## t, SCROLLBAR_DOWN), \
KEYC_MOUSE_KEY(KEYC_ ## t, KEYC_TYPE_ ## t, EMPTY), \
KEYC_MOUSE_KEY(KEYC_ ## t, KEYC_TYPE_ ## t, CONTROL0), \
KEYC_MOUSE_KEY(KEYC_ ## t, KEYC_TYPE_ ## t, CONTROL1), \
KEYC_MOUSE_KEY(KEYC_ ## t, KEYC_TYPE_ ## t, CONTROL2), \
@@ -283,6 +285,7 @@ enum key_code_mouse_location {
{ #s "ScrollbarUp", KEYC_ ## name ## _SCROLLBAR_UP }, \
{ #s "ScrollbarSlider", KEYC_ ## name ## _SCROLLBAR_SLIDER }, \
{ #s "ScrollbarDown", KEYC_ ## name ## _SCROLLBAR_DOWN }, \
{ #s "Empty", KEYC_ ## name ## _EMPTY }, \
{ #s "Border", KEYC_ ## name ## _BORDER }, \
{ #s "Control0", KEYC_ ## name ## _CONTROL0 }, \
{ #s "Control1", KEYC_ ## name ## _CONTROL1 }, \
@@ -1415,6 +1418,8 @@ struct window {
struct timeval creation_time;
struct window_pane *active;
struct window_pane *modal;
struct window_pane *modal_last;
struct window_panes last_panes;
struct window_panes z_index;
struct window_panes panes;
@@ -1456,6 +1461,7 @@ struct window {
#define WINDOW_ZOOMED 0x8
#define WINDOW_WASZOOMED 0x10
#define WINDOW_RESIZE 0x20
#define WINDOW_WASMODALZOOMED 0x40
#define WINDOW_ALERTFLAGS (WINDOW_BELL|WINDOW_ACTIVITY|WINDOW_SILENCE)
int alerts_queued;
@@ -2486,6 +2492,7 @@ struct spawn_context {
#define SPAWN_FLOATING 0x100
#define SPAWN_HORIZONTAL 0x200
#define SPAWN_SPLIT 0x400
#define SPAWN_MODAL 0x800
};
/* Paste buffer. */
@@ -3636,6 +3643,7 @@ struct window_pane *window_get_active_at(struct window *, u_int, u_int);
struct window_pane *window_find_string(struct window *, const char *);
int window_has_floating_panes(struct window *);
int window_has_pane(struct window *, struct window_pane *);
int window_pane_contains(struct window_pane *, u_int, u_int);
int window_set_active_pane(struct window *, struct window_pane *,
int);
void window_fire_pane_moved(struct window_pane *, struct window *,
@@ -3650,6 +3658,8 @@ void window_resize(struct window *, u_int, u_int, int, int);
void window_pane_send_resize(struct window_pane *, u_int, u_int);
int window_zoom(struct window_pane *);
int window_unzoom(struct window *, int);
void window_push_modal_zoom(struct window *);
int window_pop_modal_zoom(struct window *);
int window_push_zoom(struct window *, int, int);
int window_pop_zoom(struct window *);
void window_lost_pane(struct window *, struct window_pane *);

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: window-buffer.c,v 1.50 2026/07/14 17:17:18 nicm Exp $ */
/* $OpenBSD: window-buffer.c,v 1.51 2026/07/15 12:45:39 nicm Exp $ */
/*
* Copyright (c) 2017 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -525,6 +525,8 @@ window_buffer_draw_waiting(struct window_buffer_modedata *data)
screen_write_start(&ctx, s);
screen_write_cursormove(&ctx, x, y, 0);
screen_write_box(&ctx, box_w, box_h, BOX_LINES_DEFAULT, &gc, NULL);
screen_write_cursormove(&ctx, x + 1, y + 1, 0);
screen_write_clearcharacter(&ctx, box_w - 2, gc.bg);
screen_write_cursormove(&ctx, text_x, y + 1, 0);
screen_write_nputs(&ctx, box_w - 2, &gc, "%s", text);
screen_write_stop(&ctx);

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: window-customize.c,v 1.34 2026/07/14 17:17:18 nicm Exp $ */
/* $OpenBSD: window-customize.c,v 1.35 2026/07/15 12:45:39 nicm Exp $ */
/*
* Copyright (c) 2020 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -397,6 +397,8 @@ window_customize_draw_waiting(struct window_customize_modedata *data)
screen_write_start(&ctx, s);
screen_write_cursormove(&ctx, x, y, 0);
screen_write_box(&ctx, box_w, box_h, BOX_LINES_DEFAULT, &gc, NULL);
screen_write_cursormove(&ctx, x + 1, y + 1, 0);
screen_write_clearcharacter(&ctx, box_w - 2, gc.bg);
screen_write_cursormove(&ctx, text_x, y + 1, 0);
screen_write_nputs(&ctx, box_w - 2, &gc, "%s", text);
screen_write_stop(&ctx);

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: window.c,v 1.363 2026/07/14 19:07:03 nicm Exp $ */
/* $OpenBSD: window.c,v 1.364 2026/07/15 13:02:33 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -633,6 +633,35 @@ window_has_pane(struct window *w, struct window_pane *wp)
return (0);
}
int
window_pane_contains(struct window_pane *wp, u_int x, u_int y)
{
int xoff, yoff;
u_int sx, sy;
if (!window_pane_is_visible(wp))
return (0);
window_pane_full_size_offset(wp, &xoff, &yoff, &sx, &sy);
if (!window_pane_is_floating(wp)) {
if ((int)x < xoff || x > xoff + sx)
return (0);
if ((int)y < yoff || y > yoff + sy)
return (0);
} else if (window_pane_get_pane_lines(wp) == PANE_LINES_NONE) {
if ((int)x < xoff || (int)x >= xoff + (int)sx)
return (0);
if ((int)y < yoff || (int)y >= yoff + (int)sy)
return (0);
} else {
if ((int)x < xoff - 1 || x > xoff + sx)
return (0);
if ((int)y < yoff - 1 || y > yoff + sy)
return (0);
}
return (1);
}
void
window_update_focus(struct window *w)
{
@@ -690,6 +719,8 @@ window_set_active_pane(struct window *w, struct window_pane *wp, int notify)
if (wp == w->active)
return (0);
if (w->modal != NULL && wp != w->modal)
return (0);
if (w->flags & WINDOW_ZOOMED)
window_unzoom(w, 1);
lastwp = w->active;
@@ -728,6 +759,8 @@ window_redraw_active_switch(struct window *w, struct window_pane *wp)
struct grid_cell *gc1, *gc2;
int c1, c2;
if (w->modal != NULL && wp != w->modal)
return;
if (wp == w->active)
return;
@@ -780,6 +813,12 @@ window_get_active_at(struct window *w, u_int x, u_int y)
pane_status = window_get_pane_status(w);
if (w->modal != NULL) {
if (window_pane_contains(w->modal, x, y))
return (w->modal);
return (NULL);
}
if (pane_status == PANE_STATUS_TOP) {
/*
* Prefer a pane's top border status line over the pane above's
@@ -936,6 +975,29 @@ window_unzoom(struct window *w, int notify)
return (0);
}
void
window_push_modal_zoom(struct window *w)
{
if (w->flags & WINDOW_ZOOMED)
w->flags |= WINDOW_WASMODALZOOMED;
else
w->flags &= ~WINDOW_WASMODALZOOMED;
window_unzoom(w, 1);
}
int
window_pop_modal_zoom(struct window *w)
{
struct window_pane *wp = w->active;
if (~w->flags & WINDOW_WASMODALZOOMED)
return (0);
w->flags &= ~WINDOW_WASMODALZOOMED;
if (wp != NULL && window_has_pane(w, wp))
return (window_zoom(wp) == 0);
return (0);
}
int
window_push_zoom(struct window *w, int always, int flag)
{
@@ -986,6 +1048,8 @@ window_add_pane(struct window *w, struct window_pane *other, u_int hlimit,
}
if (~flags & SPAWN_FLOATING)
TAILQ_INSERT_TAIL(&w->z_index, wp, zentry);
else if (w->modal != NULL)
TAILQ_INSERT_AFTER(&w->z_index, w->modal, wp, zentry);
else {
TAILQ_INSERT_HEAD(&w->z_index, wp, zentry);
}
@@ -996,14 +1060,29 @@ window_add_pane(struct window *w, struct window_pane *other, u_int hlimit,
void
window_lost_pane(struct window *w, struct window_pane *wp)
{
struct window_pane *lastwp;
log_debug("%s: @%u pane %%%u", __func__, w->id, wp->id);
if (wp == marked_pane.wp)
server_clear_marked();
if (wp == w->modal_last)
w->modal_last = NULL;
if (w->modal_last == NULL)
w->flags &= ~WINDOW_WASMODALZOOMED;
window_pane_stack_remove(&w->last_panes, wp);
if (wp == w->active) {
w->active = TAILQ_FIRST(&w->last_panes);
lastwp = NULL;
if (wp == w->modal) {
lastwp = w->modal_last;
w->modal = NULL;
w->modal_last = NULL;
}
if (lastwp != NULL && window_has_pane(w, lastwp))
w->active = lastwp;
else
w->active = TAILQ_FIRST(&w->last_panes);
if (w->active == NULL) {
w->active = TAILQ_PREV(wp, window_panes, entry);
if (w->active == NULL)
@@ -1015,6 +1094,9 @@ window_lost_pane(struct window *w, struct window_pane *wp)
window_fire_pane_changed(w, w->active, wp);
window_update_focus(w);
}
} else if (wp == w->modal) {
w->modal = w->modal_last = NULL;
w->flags &= ~WINDOW_WASMODALZOOMED;
}
redraw_invalidate_scene(w);
}
@@ -1022,9 +1104,13 @@ window_lost_pane(struct window *w, struct window_pane *wp)
void
window_remove_pane(struct window *w, struct window_pane *wp)
{
int pop = (wp == w->modal);
window_lost_pane(w, wp);
TAILQ_REMOVE(&w->panes, wp, entry);
TAILQ_REMOVE(&w->z_index, wp, zentry);
if (pop && window_pop_modal_zoom(w))
server_redraw_window(w);
redraw_invalidate_scene(w);
window_pane_destroy(wp);
}
@@ -1157,6 +1243,8 @@ window_printable_flags(struct winlink *wl, int escape)
flags[pos++] = '-';
if (server_check_marked() && wl == marked_pane.wl)
flags[pos++] = 'M';
if (wl->window->modal != NULL)
flags[pos++] = 'O';
if (wl->window->flags & WINDOW_ZOOMED)
flags[pos++] = 'Z';
flags[pos] = '\0';
@@ -1178,6 +1266,8 @@ window_pane_printable_flags(struct window_pane *wp)
flags[pos++] = 'Z';
if (window_pane_is_floating(wp))
flags[pos++] = 'F';
if (wp == w->modal)
flags[pos++] = 'O';
flags[pos] = '\0';
return (flags);
}