mirror of
https://github.com/tmux/tmux.git
synced 2026-07-24 09:40:32 +00:00
Merge branch 'master' into command_parser
This commit is contained in:
@@ -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]
|
||||
|
||||
|
||||
62
README.ja
62
README.ja
@@ -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>
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
4
cmd.c
@@ -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;
|
||||
|
||||
31
format.c
31
format.c
@@ -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
|
||||
},
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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) {
|
||||
|
||||
7
layout.c
7
layout.c
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
308
regress/modal-pane.sh
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
108
server-client.c
108
server-client.c
@@ -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
33
spawn.c
@@ -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
14
tmux.1
@@ -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
12
tmux.h
@@ -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 *);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
94
window.c
94
window.c
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user