mirror of
https://github.com/tmux/tmux.git
synced 2026-09-05 13:10:45 +00:00
Merge branch 'master' into local_windows
This commit is contained in:
1
.github/workflows/lock.yml
vendored
1
.github/workflows/lock.yml
vendored
@@ -16,6 +16,7 @@ concurrency:
|
||||
jobs:
|
||||
action:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'tmux/tmux'
|
||||
steps:
|
||||
- uses: dessant/lock-threads@v6
|
||||
with:
|
||||
|
||||
2
.github/workflows/regress.yml
vendored
2
.github/workflows/regress.yml
vendored
@@ -16,6 +16,7 @@ jobs:
|
||||
regress:
|
||||
name: ${{ matrix.name }}
|
||||
runs-on: ${{ matrix.runner }}
|
||||
if: github.repository == 'tmux/tmux'
|
||||
timeout-minutes: 45
|
||||
|
||||
strategy:
|
||||
@@ -61,6 +62,7 @@ jobs:
|
||||
automake \
|
||||
bison \
|
||||
libevent \
|
||||
jemalloc \
|
||||
make \
|
||||
ncurses \
|
||||
utf8proc \
|
||||
|
||||
11
CHANGES
11
CHANGES
@@ -1,5 +1,8 @@
|
||||
CHANGES FROM 3.7b TO 3.8
|
||||
|
||||
* Build with jemalloc on macOS to avoid what appears to be a bug in the system
|
||||
calloc(3) (issue 5385).
|
||||
|
||||
* Many improvements to floating panes:
|
||||
|
||||
- new-pane and split-window can now set the title with -T and border lines
|
||||
@@ -87,6 +90,14 @@ CHANGES FROM 3.7b TO 3.8
|
||||
while scrolling or while hovered and disappears after pane-scrollbars-timeout
|
||||
(Michael Grant).
|
||||
|
||||
* Extend the fill-character option so both inside and outside the window can be
|
||||
changed.
|
||||
|
||||
* Change set-option and set-hooks to use formats and add a -F flag to each.
|
||||
|
||||
* Add a #{A/count:frames} modifier to show a series of frames as an animation
|
||||
in the status line (issue 5412 from Fernando Daciuk).
|
||||
|
||||
* Change command templates so %% is escaped for single quotes while %1 remains
|
||||
unquoted, preventing single quotes in session or target names from
|
||||
terminating quoted command sections (reported by Aliz Hammond).
|
||||
|
||||
16
cfg.c
16
cfg.c
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: cfg.c,v 1.90 2026/07/14 17:17:17 nicm Exp $ */
|
||||
/* $OpenBSD: cfg.c,v 1.91 2026/08/03 13:38:42 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -223,13 +223,15 @@ cfg_print_causes(struct cmdq_item *item)
|
||||
{
|
||||
struct client *c = cmdq_get_client(item);
|
||||
u_int i;
|
||||
char *cause;
|
||||
|
||||
for (i = 0; i < cfg_ncauses; i++) {
|
||||
cause = cfg_causes[i];
|
||||
if (c != NULL && (c->flags & CLIENT_CONTROL))
|
||||
control_write(c, "%%config-error %s", cfg_causes[i]);
|
||||
control_notify_write(c, "%%config-error %s", cause);
|
||||
else
|
||||
cmdq_print(item, "%s", cfg_causes[i]);
|
||||
free(cfg_causes[i]);
|
||||
cmdq_print(item, "%s", cause);
|
||||
free(cause);
|
||||
}
|
||||
|
||||
free(cfg_causes);
|
||||
@@ -244,14 +246,16 @@ cfg_show_causes(struct session *s)
|
||||
struct window_pane *wp;
|
||||
struct window_mode_entry *wme;
|
||||
u_int i;
|
||||
char *cause;
|
||||
|
||||
if (cfg_ncauses == 0)
|
||||
return;
|
||||
|
||||
if (c != NULL && (c->flags & CLIENT_CONTROL)) {
|
||||
for (i = 0; i < cfg_ncauses; i++) {
|
||||
control_write(c, "%%config-error %s", cfg_causes[i]);
|
||||
free(cfg_causes[i]);
|
||||
cause = cfg_causes[i];
|
||||
control_notify_write(c, "%%config-error %s", cause);
|
||||
free(cause);
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
4
client.c
4
client.c
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: client.c,v 1.166 2026/07/10 15:45:11 nicm Exp $ */
|
||||
/* $OpenBSD: client.c,v 1.167 2026/08/17 07:56:56 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -276,7 +276,7 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags,
|
||||
proc_set_signals(client_proc, client_signal);
|
||||
|
||||
/* Save the flags. */
|
||||
client_flags = flags;
|
||||
client_flags = flags|CLIENT_WRITE_ACK;
|
||||
log_debug("flags are %#llx", (unsigned long long)client_flags);
|
||||
|
||||
/* Initialize the client socket and start the server. */
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: cmd-break-pane.c,v 1.74 2026/07/15 13:02:33 nicm Exp $ */
|
||||
/* $OpenBSD: cmd-break-pane.c,v 1.76 2026/08/17 07:04:45 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -190,14 +190,16 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
|
||||
w->name = clean_name(name, 0);
|
||||
options_set_number(w->options, "automatic-rename", 0);
|
||||
}
|
||||
window_set_fill_cells(w);
|
||||
|
||||
if (idx == -1)
|
||||
idx = -1 - options_get_number(dst_s->options, "base-index");
|
||||
wl = session_attach(dst_s, w, idx, &cause); /* can't fail */
|
||||
|
||||
layout_init(w, wp);
|
||||
wp->flags |= PANE_CHANGED;
|
||||
colour_palette_from_option(&wp->palette, wp->options);
|
||||
|
||||
if (idx == -1)
|
||||
idx = -1 - options_get_number(dst_s->options, "base-index");
|
||||
wl = session_attach(dst_s, w, idx, &cause); /* can't fail */
|
||||
window_remove_ref(w, __func__);
|
||||
events_fire_window("window-created", w);
|
||||
window_fire_pane_moved(wp, old_w, old_idx, w, wl->idx);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: cmd-join-pane.c,v 1.73 2026/07/17 15:24:30 nicm Exp $ */
|
||||
/* $OpenBSD: cmd-join-pane.c,v 1.74 2026/08/03 20:29:52 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2011 George Nachman <tmux@georgester.com>
|
||||
@@ -438,25 +438,23 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
|
||||
if (cmd_get_entry(self) == &cmd_move_pane_entry) {
|
||||
if (args_has(args, 'M'))
|
||||
return (cmd_join_pane_mouse_update(item));
|
||||
if (!window_pane_is_floating(dst_wp)) {
|
||||
cmdq_error(item, "pane is not floating");
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
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) {
|
||||
server_unzoom_window(dst_w);
|
||||
return (cmd_join_pane_zindex(item, dst_wl, dst_wp, s));
|
||||
}
|
||||
if (args_has(args, 'X') ||
|
||||
if (args_has(args, 'P') ||
|
||||
args_has(args, 'z') ||
|
||||
args_has(args, 'X') ||
|
||||
args_has(args, 'Y') ||
|
||||
args_has(args, 'U') ||
|
||||
args_has(args, 'D') ||
|
||||
args_has(args, 'L') ||
|
||||
args_has(args, 'R')) {
|
||||
if (!window_pane_is_floating(dst_wp)) {
|
||||
cmdq_error(item, "pane is not floating");
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
server_unzoom_window(dst_w);
|
||||
if ((s = args_get(args, 'P')) != NULL)
|
||||
return (cmd_join_pane_place(item, dst_wl, dst_wp, s));
|
||||
if ((s = args_get(args, 'z')) != NULL)
|
||||
return (cmd_join_pane_zindex(item, dst_wl, dst_wp, s));
|
||||
return (cmd_join_pane_move(item, args, dst_wl, dst_wp));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: cmd-pipe-pane.c,v 1.63 2026/04/28 08:47:55 nicm Exp $ */
|
||||
/* $OpenBSD: cmd-pipe-pane.c,v 1.64 2026/07/27 14:25:46 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -194,9 +194,13 @@ static void
|
||||
cmd_pipe_pane_read_callback(__unused struct bufferevent *bufev, void *data)
|
||||
{
|
||||
struct window_pane *wp = data;
|
||||
struct evbuffer *evb = wp->pipe_event->input;
|
||||
struct evbuffer *evb;
|
||||
size_t available;
|
||||
|
||||
if (wp->pipe_event == NULL)
|
||||
return;
|
||||
evb = wp->pipe_event->input;
|
||||
|
||||
available = EVBUFFER_LENGTH(evb);
|
||||
log_debug("%%%u pipe read %zu", wp->id, available);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: cmd-queue.c,v 1.121 2026/07/14 17:17:17 nicm Exp $ */
|
||||
/* $OpenBSD: cmd-queue.c,v 1.122 2026/08/03 13:38:42 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2013 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -802,7 +802,7 @@ cmdq_guard(struct cmdq_item *item, const char *guard, int flags)
|
||||
u_int number = item->number;
|
||||
|
||||
if (c != NULL && (c->flags & CLIENT_CONTROL))
|
||||
control_write(c, "%%%s %ld %u %d", guard, t, number, flags);
|
||||
control_write_guard(c, guard, t, number, flags);
|
||||
}
|
||||
|
||||
/* Show message from command. */
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: cmd-show-options.c,v 1.73 2026/07/10 13:38:45 nicm Exp $ */
|
||||
/* $OpenBSD: cmd-show-options.c,v 1.76 2026/07/27 19:15:58 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -27,14 +27,23 @@
|
||||
* Show options.
|
||||
*/
|
||||
|
||||
#define SHOW_OPTIONS_TEMPLATE \
|
||||
"#{?option_value_only," \
|
||||
"#{option_value}," \
|
||||
"#{option_name}#{?option_has_array_key," \
|
||||
"[#{option_array_key}],}" \
|
||||
"#{?option_is_parent,*,}" \
|
||||
"#{?option_has_value, " \
|
||||
"#{?option_is_string,#{q/a:option_value},#{option_value}},}}"
|
||||
#define SHOW_HOOKS_MONITOR_TEMPLATE \
|
||||
"#{option_name}:#{hook_monitor_target}:#{hook_monitor_format}"
|
||||
|
||||
static enum cmd_retval cmd_show_options_exec(struct cmd *, struct cmdq_item *);
|
||||
|
||||
static void cmd_show_options_print(struct cmd *, struct cmdq_item *,
|
||||
struct options_entry *, const char *, int);
|
||||
static void cmd_show_hooks_print_monitor(struct cmdq_item *,
|
||||
struct options_entry *);
|
||||
static enum cmd_retval cmd_show_hooks_monitor(struct cmd *, struct cmdq_item *,
|
||||
int, struct options *);
|
||||
static void cmd_show_hooks_print_monitor(struct cmd *,
|
||||
struct cmdq_item *, struct options_entry *);
|
||||
static enum cmd_retval cmd_show_options_all(struct cmd *, struct cmdq_item *,
|
||||
int, struct options *);
|
||||
|
||||
@@ -42,8 +51,8 @@ const struct cmd_entry cmd_show_options_entry = {
|
||||
.name = "show-options",
|
||||
.alias = "show",
|
||||
|
||||
.args = { "AgHpqst:vw", 0, 1, NULL },
|
||||
.usage = "[-AgHpqsvw] " CMD_TARGET_PANE_USAGE " [option]",
|
||||
.args = { "AgF:Hpqst:vw", 0, 1, NULL },
|
||||
.usage = "[-AgHpqsvw] [-F format] " CMD_TARGET_PANE_USAGE " [option]",
|
||||
|
||||
.target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL },
|
||||
|
||||
@@ -55,8 +64,8 @@ const struct cmd_entry cmd_show_window_options_entry = {
|
||||
.name = "show-window-options",
|
||||
.alias = "showw",
|
||||
|
||||
.args = { "gvt:", 0, 1, NULL },
|
||||
.usage = "[-gv] " CMD_TARGET_WINDOW_USAGE " [option]",
|
||||
.args = { "F:gvt:", 0, 1, NULL },
|
||||
.usage = "[-gv] [-F format] " CMD_TARGET_WINDOW_USAGE " [option]",
|
||||
|
||||
.target = { 't', CMD_FIND_WINDOW, CMD_FIND_CANFAIL },
|
||||
|
||||
@@ -68,8 +77,8 @@ const struct cmd_entry cmd_show_hooks_entry = {
|
||||
.name = "show-hooks",
|
||||
.alias = NULL,
|
||||
|
||||
.args = { "Bgpt:w", 0, 1, NULL },
|
||||
.usage = "[-Bgpw] " CMD_TARGET_PANE_USAGE " [hook]",
|
||||
.args = { "BF:gpt:w", 0, 1, NULL },
|
||||
.usage = "[-Bgpw] [-F format] " CMD_TARGET_PANE_USAGE " [hook]",
|
||||
|
||||
.target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL },
|
||||
|
||||
@@ -85,7 +94,7 @@ cmd_show_options_exec(struct cmd *self, struct cmdq_item *item)
|
||||
struct options *oo;
|
||||
char *argument, *name = NULL, *cause;
|
||||
char *array_key = NULL;
|
||||
int window, ambiguous, parent, scope;
|
||||
int window, ambiguous, parent, print_parent, scope;
|
||||
struct options_entry *o;
|
||||
|
||||
window = (cmd_get_entry(self) == &cmd_show_window_options_entry);
|
||||
@@ -101,8 +110,14 @@ cmd_show_options_exec(struct cmd *self, struct cmdq_item *item)
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
if (cmd_get_entry(self) == &cmd_show_hooks_entry &&
|
||||
args_has(args, 'B'))
|
||||
return (cmd_show_hooks_monitor(self, item, scope, oo));
|
||||
args_has(args, 'B')) {
|
||||
o = options_first(oo);
|
||||
while (o != NULL) {
|
||||
cmd_show_hooks_print_monitor(self, item, o);
|
||||
o = options_next(o);
|
||||
}
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
return (cmd_show_options_all(self, item, scope, oo));
|
||||
}
|
||||
argument = format_single_from_target(item, args_string(args, 0));
|
||||
@@ -135,11 +150,17 @@ cmd_show_options_exec(struct cmd *self, struct cmdq_item *item)
|
||||
if (o != NULL) {
|
||||
if (cmd_get_entry(self) == &cmd_show_hooks_entry &&
|
||||
args_has(args, 'B'))
|
||||
cmd_show_hooks_print_monitor(item, o);
|
||||
else
|
||||
cmd_show_options_print(self, item, o, array_key, parent);
|
||||
}
|
||||
else if (*name == '@') {
|
||||
cmd_show_hooks_print_monitor(self, item, o);
|
||||
else {
|
||||
print_parent = parent;
|
||||
if (array_key == NULL && options_is_array(o) &&
|
||||
options_array_first(o) == NULL) {
|
||||
print_parent = 0;
|
||||
}
|
||||
cmd_show_options_print(self, item, o, array_key,
|
||||
print_parent);
|
||||
}
|
||||
} else if (*name == '@') {
|
||||
if (args_has(args, 'q'))
|
||||
goto out;
|
||||
cmdq_error(item, "invalid option: %s", argument);
|
||||
@@ -163,22 +184,24 @@ static void
|
||||
cmd_show_options_print(struct cmd *self, struct cmdq_item *item,
|
||||
struct options_entry *o, const char *array_key, int parent)
|
||||
{
|
||||
struct args *args = cmd_get_args(self);
|
||||
struct options_array_item *a;
|
||||
const char *name = options_name(o);
|
||||
char *value, *tmp = NULL, *escaped;
|
||||
struct args *args = cmd_get_args(self);
|
||||
struct options_array_item *a;
|
||||
struct format_tree *ft;
|
||||
const char *name = options_name(o);
|
||||
const char *template = args_get(args, 'F');
|
||||
char *value, *line;
|
||||
struct timeval tv = { 0 };
|
||||
u_int fire_count;
|
||||
time_t fire_time;
|
||||
int is_hook = 0, is_user = 0;
|
||||
int has_value = 1;
|
||||
const struct options_table_entry *oe = options_table_entry(o);
|
||||
|
||||
if (array_key != NULL) {
|
||||
xasprintf(&tmp, "%s[%s]", name, array_key);
|
||||
name = tmp;
|
||||
} else {
|
||||
if (options_is_array(o)) {
|
||||
a = options_array_first(o);
|
||||
if (a == NULL) {
|
||||
if (!args_has(args, 'v'))
|
||||
cmdq_print(item, "%s", name);
|
||||
return;
|
||||
}
|
||||
if (array_key != NULL)
|
||||
value = options_to_string(o, array_key, 0);
|
||||
else if (options_is_array(o)) {
|
||||
a = options_array_first(o);
|
||||
if (a != NULL) {
|
||||
while (a != NULL) {
|
||||
array_key = options_array_item_key(a);
|
||||
cmd_show_options_print(self, item, o, array_key,
|
||||
@@ -187,54 +210,130 @@ cmd_show_options_print(struct cmd *self, struct cmdq_item *item,
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (template == NULL && args_has(args, 'v'))
|
||||
return;
|
||||
value = xstrdup("");
|
||||
has_value = 0;
|
||||
} else
|
||||
value = options_to_string(o, NULL, 0);
|
||||
|
||||
value = options_to_string(o, array_key, 0);
|
||||
if (args_has(args, 'v'))
|
||||
cmdq_print(item, "%s", value);
|
||||
else if (options_is_string(o)) {
|
||||
escaped = args_escape(value);
|
||||
if (parent)
|
||||
cmdq_print(item, "%s* %s", name, escaped);
|
||||
else
|
||||
cmdq_print(item, "%s %s", name, escaped);
|
||||
free(escaped);
|
||||
if (template == NULL)
|
||||
template = SHOW_OPTIONS_TEMPLATE;
|
||||
|
||||
if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_HOOK))
|
||||
is_hook = 1;
|
||||
else if (oe == NULL)
|
||||
is_user = 1;
|
||||
|
||||
ft = format_create_from_target(item);
|
||||
format_add(ft, "option_name", "%s", name);
|
||||
format_add(ft, "option_value", "%s", value);
|
||||
format_add(ft, "option_value_only", "%d", args_has(args, 'v'));
|
||||
format_add(ft, "option_is_parent", "%d", parent);
|
||||
format_add(ft, "option_is_array", "%d", options_is_array(o));
|
||||
format_add(ft, "option_is_string", "%d", options_is_string(o));
|
||||
format_add(ft, "option_is_hook", "%d", is_hook);
|
||||
format_add(ft, "option_is_user", "%d", is_user);
|
||||
format_add(ft, "option_has_value", "%d", has_value);
|
||||
if (cmd_get_entry(self) == &cmd_show_hooks_entry) {
|
||||
fire_count = options_get_fire_count(o);
|
||||
format_add(ft, "hook_fire_count", "%u", fire_count);
|
||||
|
||||
fire_time = options_get_fire_time(o);
|
||||
if (fire_time != 0) {
|
||||
tv.tv_sec = fire_time;
|
||||
format_add_tv(ft, "hook_fire_time", &tv);
|
||||
}
|
||||
}
|
||||
if (array_key != NULL) {
|
||||
format_add(ft, "option_array_key", "%s", array_key);
|
||||
format_add(ft, "option_has_array_key", "1");
|
||||
} else {
|
||||
if (parent)
|
||||
cmdq_print(item, "%s* %s", name, value);
|
||||
else
|
||||
cmdq_print(item, "%s %s", name, value);
|
||||
format_add(ft, "option_array_key", "%s", "");
|
||||
format_add(ft, "option_has_array_key", "0");
|
||||
}
|
||||
free(value);
|
||||
line = format_expand(ft, template);
|
||||
format_free(ft);
|
||||
|
||||
free(tmp);
|
||||
cmdq_print(item, "%s", line);
|
||||
free(line);
|
||||
free(value);
|
||||
}
|
||||
|
||||
static void
|
||||
cmd_show_hooks_print_monitor(struct cmdq_item *item, struct options_entry *o)
|
||||
cmd_show_hooks_print_monitor(struct cmd *self, struct cmdq_item *item,
|
||||
struct options_entry *o)
|
||||
{
|
||||
char *value;
|
||||
struct args *args = cmd_get_args(self);
|
||||
struct format_tree *ft;
|
||||
enum monitor_type type;
|
||||
const char *template = args_get(args, 'F'), *format;
|
||||
char *value, *target, *line;
|
||||
struct timeval tv = { 0 };
|
||||
u_int fire_count;
|
||||
time_t fire_time;
|
||||
int id;
|
||||
|
||||
value = hooks_monitor_to_string(o);
|
||||
if (value == NULL)
|
||||
return;
|
||||
cmdq_print(item, "%s", value);
|
||||
free(value);
|
||||
}
|
||||
|
||||
/* Show all hook monitors. */
|
||||
static enum cmd_retval
|
||||
cmd_show_hooks_monitor(__unused struct cmd *self, struct cmdq_item *item,
|
||||
__unused int scope, struct options *oo)
|
||||
{
|
||||
struct options_entry *o;
|
||||
|
||||
o = options_first(oo);
|
||||
while (o != NULL) {
|
||||
cmd_show_hooks_print_monitor(item, o);
|
||||
o = options_next(o);
|
||||
if (!hooks_monitor_get(o, &type, &id, &format)) {
|
||||
free(value);
|
||||
return;
|
||||
}
|
||||
return (CMD_RETURN_NORMAL);
|
||||
if (template == NULL)
|
||||
template = SHOW_HOOKS_MONITOR_TEMPLATE;
|
||||
|
||||
switch (type) {
|
||||
case MONITOR_SESSION:
|
||||
target = xstrdup("");
|
||||
break;
|
||||
case MONITOR_PANE:
|
||||
xasprintf(&target, "%%%d", id);
|
||||
break;
|
||||
case MONITOR_ALL_PANES:
|
||||
target = xstrdup("%*");
|
||||
break;
|
||||
case MONITOR_WINDOW:
|
||||
xasprintf(&target, "@%d", id);
|
||||
break;
|
||||
case MONITOR_ALL_WINDOWS:
|
||||
target = xstrdup("@*");
|
||||
break;
|
||||
}
|
||||
|
||||
ft = format_create_from_target(item);
|
||||
format_add(ft, "option_name", "%s", options_name(o));
|
||||
format_add(ft, "option_value", "%s", value);
|
||||
format_add(ft, "option_value_only", "%d", 0);
|
||||
format_add(ft, "option_is_parent", "%d", 0);
|
||||
format_add(ft, "option_is_array", "%d", 0);
|
||||
format_add(ft, "option_is_string", "%d", 1);
|
||||
format_add(ft, "option_is_hook", "%d", 1);
|
||||
format_add(ft, "option_is_user", "%d", 1);
|
||||
format_add(ft, "option_has_value", "%d", 1);
|
||||
format_add(ft, "option_array_key", "%s", "");
|
||||
format_add(ft, "option_has_array_key", "0");
|
||||
|
||||
format_add(ft, "hook_monitor_target", "%s", target);
|
||||
format_add(ft, "hook_monitor_format", "%s", format);
|
||||
|
||||
fire_count = hooks_monitor_get_fire_count(o);
|
||||
format_add(ft, "hook_fire_count", "%u", fire_count);
|
||||
|
||||
fire_time = hooks_monitor_get_fire_time(o);
|
||||
if (fire_time != 0) {
|
||||
tv.tv_sec = fire_time;
|
||||
format_add_tv(ft, "hook_fire_time", &tv);
|
||||
}
|
||||
|
||||
line = format_expand(ft, template);
|
||||
format_free(ft);
|
||||
|
||||
cmdq_print(item, "%s", line);
|
||||
free(line);
|
||||
free(target);
|
||||
free(value);
|
||||
}
|
||||
|
||||
static enum cmd_retval
|
||||
@@ -244,17 +343,29 @@ cmd_show_options_all(struct cmd *self, struct cmdq_item *item, int scope,
|
||||
struct args *args = cmd_get_args(self);
|
||||
const struct options_table_entry *oe;
|
||||
struct options_entry *o;
|
||||
struct options_array_item *a;
|
||||
const char *name, *array_key;
|
||||
int parent;
|
||||
const char *name;
|
||||
int parent, is_user_hook;
|
||||
|
||||
if (cmd_get_entry(self) != &cmd_show_hooks_entry) {
|
||||
o = options_first(oo);
|
||||
while (o != NULL) {
|
||||
if (options_table_entry(o) == NULL)
|
||||
o = options_first(oo);
|
||||
while (o != NULL) {
|
||||
if (options_table_entry(o) == NULL) {
|
||||
name = options_name(o);
|
||||
is_user_hook = 0;
|
||||
if (*name == '@') {
|
||||
if (hooks_is_event(name) ||
|
||||
options_get_monitor_data(o) != NULL) {
|
||||
is_user_hook = 1;
|
||||
}
|
||||
}
|
||||
if (cmd_get_entry(self) != &cmd_show_hooks_entry) {
|
||||
if (!is_user_hook || args_has(args, 'H')) {
|
||||
cmd_show_options_print(self, item, o,
|
||||
NULL, 0);
|
||||
}
|
||||
} else if (is_user_hook)
|
||||
cmd_show_options_print(self, item, o, NULL, 0);
|
||||
o = options_next(o);
|
||||
}
|
||||
o = options_next(o);
|
||||
}
|
||||
for (oe = options_table; oe->name != NULL; oe++) {
|
||||
if (~oe->scope & scope)
|
||||
@@ -278,24 +389,7 @@ cmd_show_options_all(struct cmd *self, struct cmdq_item *item, int scope,
|
||||
} else
|
||||
parent = 0;
|
||||
|
||||
if (!options_is_array(o))
|
||||
cmd_show_options_print(self, item, o, NULL, parent);
|
||||
else if ((a = options_array_first(o)) == NULL) {
|
||||
if (!args_has(args, 'v')) {
|
||||
name = options_name(o);
|
||||
if (parent)
|
||||
cmdq_print(item, "%s*", name);
|
||||
else
|
||||
cmdq_print(item, "%s", name);
|
||||
}
|
||||
} else {
|
||||
while (a != NULL) {
|
||||
array_key = options_array_item_key(a);
|
||||
cmd_show_options_print(self, item, o, array_key,
|
||||
parent);
|
||||
a = options_array_next(a);
|
||||
}
|
||||
}
|
||||
cmd_show_options_print(self, item, o, NULL, parent);
|
||||
}
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: cmd-split-window.c,v 1.145 2026/07/15 13:02:33 nicm Exp $ */
|
||||
/* $OpenBSD: cmd-split-window.c,v 1.148 2026/08/07 08:10:53 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:Op:PR:s:S:t:T:vWx:X:y:Y:Z", 0, -1, NULL },
|
||||
.usage = "[-bdefhIklMOPvWZ] [-B border-lines] "
|
||||
.args = { "bB:Cc:de:EfF:hIkl:LMm:Op:PR:s:S:t:T:vWx:X:y:Y:Z", 0, -1, NULL },
|
||||
.usage = "[-bCdefhIklMOPvWZ] [-B border-lines] "
|
||||
"[-c start-directory] [-e environment] "
|
||||
"[-F format] [-l size] [-m message] [-p percentage] "
|
||||
"[-s style] [-S active-border-style] "
|
||||
@@ -99,6 +99,7 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
|
||||
if (cmd_get_entry(self) == &cmd_new_pane_entry)
|
||||
is_floating = !args_has(args, 'L');
|
||||
else {
|
||||
window_unzoom(w, 1);
|
||||
is_floating = window_pane_is_floating(wp);
|
||||
flags |= SPAWN_SPLIT;
|
||||
}
|
||||
@@ -201,6 +202,8 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
|
||||
*/
|
||||
goto fail;
|
||||
}
|
||||
if (args_has(args, 'C') && args_has(args, 'O'))
|
||||
new_wp->flags |= PANE_CLOSEONCLICK;
|
||||
|
||||
style = args_get(args, 's');
|
||||
if (style != NULL) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: cmd-swap-window.c,v 1.29 2025/10/30 13:52:08 nicm Exp $ */
|
||||
/* $OpenBSD: cmd-swap-window.c,v 1.30 2026/07/27 08:03:01 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -38,7 +38,7 @@ const struct cmd_entry cmd_swap_window_entry = {
|
||||
.source = { 's', CMD_FIND_WINDOW, CMD_FIND_DEFAULT_MARKED },
|
||||
.target = { 't', CMD_FIND_WINDOW, 0 },
|
||||
|
||||
.flags = 0,
|
||||
.flags = CMD_AFTERHOOK,
|
||||
.exec = cmd_swap_window_exec
|
||||
};
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ getpeereid(int s, uid_t *uid, gid_t *gid)
|
||||
{
|
||||
#ifdef HAVE_SO_PEERCRED
|
||||
struct ucred uc;
|
||||
int len = sizeof uc;
|
||||
socklen_t len = sizeof uc;
|
||||
|
||||
if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &uc, &len) == -1)
|
||||
return (-1);
|
||||
|
||||
143
configure.ac
143
configure.ac
@@ -76,9 +76,7 @@ AM_CONDITIONAL(IS_OPTIMIZED, test "x$enable_optimizations" = xyes)
|
||||
# Is this --enable-asan?
|
||||
AC_ARG_ENABLE(
|
||||
asan,
|
||||
AS_HELP_STRING(--enable-asan, enable ASAN build flags),
|
||||
,
|
||||
[case "x$host_os" in *darwin*) enable_asan=yes;; esac]
|
||||
AS_HELP_STRING(--enable-asan, enable ASAN build flags)
|
||||
)
|
||||
AM_CONDITIONAL(IS_ASAN, test "x$enable_asan" = xyes)
|
||||
|
||||
@@ -240,6 +238,7 @@ AC_LIBOBJ(getopt_long)
|
||||
|
||||
# Look for libevent. Try libevent_core or libevent with pkg-config first then
|
||||
# look for the library.
|
||||
libevent_version=off
|
||||
PKG_CHECK_MODULES(
|
||||
LIBEVENT_CORE,
|
||||
[libevent_core >= 2],
|
||||
@@ -248,6 +247,8 @@ PKG_CHECK_MODULES(
|
||||
CPPFLAGS="$AM_CPPFLAGS $SAVED_CPPFLAGS"
|
||||
LIBS="$LIBEVENT_CORE_LIBS $LIBS"
|
||||
found_libevent=yes
|
||||
libevent_version=`$PKG_CONFIG --modversion libevent_core 2>/dev/null`
|
||||
test "x$libevent_version" = x && libevent_version=on
|
||||
],
|
||||
found_libevent=no
|
||||
)
|
||||
@@ -260,6 +261,8 @@ if test x$found_libevent = xno; then
|
||||
CPPFLAGS="$AM_CPPFLAGS $SAVED_CPPFLAGS"
|
||||
LIBS="$LIBEVENT_LIBS $LIBS"
|
||||
found_libevent=yes
|
||||
libevent_version=`$PKG_CONFIG --modversion libevent 2>/dev/null`
|
||||
test "x$libevent_version" = x && libevent_version=on
|
||||
],
|
||||
found_libevent=no
|
||||
)
|
||||
@@ -268,7 +271,8 @@ if test x$found_libevent = xno; then
|
||||
AC_SEARCH_LIBS(
|
||||
event_init,
|
||||
[event_core event event-1.4],
|
||||
found_libevent=yes,
|
||||
[found_libevent=yes
|
||||
libevent_version=on],
|
||||
found_libevent=no
|
||||
)
|
||||
fi
|
||||
@@ -295,6 +299,7 @@ fi
|
||||
|
||||
# Look for ncurses or curses. Try pkg-config first then directly for the
|
||||
# library.
|
||||
ncurses_version=off
|
||||
PKG_CHECK_MODULES(
|
||||
LIBTINFOW,
|
||||
tinfow,
|
||||
@@ -303,6 +308,8 @@ PKG_CHECK_MODULES(
|
||||
CPPFLAGS="$LIBTINFOW_CFLAGS $SAVED_CPPFLAGS"
|
||||
LIBS="$LIBTINFOW_LIBS $LIBS"
|
||||
found_ncurses=yes
|
||||
ncurses_version=`$PKG_CONFIG --modversion tinfow 2>/dev/null`
|
||||
test "x$ncurses_version" = x && ncurses_version=on
|
||||
],
|
||||
found_ncurses=no
|
||||
)
|
||||
@@ -315,6 +322,8 @@ if test "x$found_ncurses" = xno; then
|
||||
CPPFLAGS="$LIBTINFO_CFLAGS $SAVED_CPPFLAGS"
|
||||
LIBS="$LIBTINFO_LIBS $LIBS"
|
||||
found_ncurses=yes
|
||||
ncurses_version=`$PKG_CONFIG --modversion tinfo 2>/dev/null`
|
||||
test "x$ncurses_version" = x && ncurses_version=on
|
||||
],
|
||||
found_ncurses=no
|
||||
)
|
||||
@@ -328,6 +337,8 @@ if test "x$found_ncurses" = xno; then
|
||||
CPPFLAGS="$LIBNCURSESW_CFLAGS $SAVED_CPPFLAGS"
|
||||
LIBS="$LIBNCURSESW_LIBS $LIBS"
|
||||
found_ncurses=yes
|
||||
ncurses_version=`$PKG_CONFIG --modversion ncursesw 2>/dev/null`
|
||||
test "x$ncurses_version" = x && ncurses_version=on
|
||||
],
|
||||
found_ncurses=no
|
||||
)
|
||||
@@ -341,6 +352,8 @@ if test "x$found_ncurses" = xno; then
|
||||
CPPFLAGS="$LIBNCURSES_CFLAGS $SAVED_CPPFLAGS"
|
||||
LIBS="$LIBNCURSES_LIBS $LIBS"
|
||||
found_ncurses=yes
|
||||
ncurses_version=`$PKG_CONFIG --modversion ncurses 2>/dev/null`
|
||||
test "x$ncurses_version" = x && ncurses_version=on
|
||||
],
|
||||
found_ncurses=no
|
||||
)
|
||||
@@ -349,7 +362,8 @@ if test "x$found_ncurses" = xno; then
|
||||
AC_SEARCH_LIBS(
|
||||
setupterm,
|
||||
[tinfow tinfo terminfo ncursesw ncurses],
|
||||
found_ncurses=yes,
|
||||
[found_ncurses=yes
|
||||
ncurses_version=on],
|
||||
found_ncurses=no
|
||||
)
|
||||
if test "x$found_ncurses" = xyes; then
|
||||
@@ -379,6 +393,7 @@ else
|
||||
LIBS="$LIBS -lcurses"
|
||||
CPPFLAGS="$CPPFLAGS -DHAVE_CURSES_H"
|
||||
AC_DEFINE(HAVE_CURSES_H)
|
||||
ncurses_version=on
|
||||
else
|
||||
AC_MSG_ERROR("curses not found")
|
||||
fi
|
||||
@@ -389,6 +404,7 @@ AC_CHECK_FUNCS([ \
|
||||
])
|
||||
|
||||
# Look for utempter.
|
||||
utempter_version=off
|
||||
AC_ARG_ENABLE(
|
||||
utempter,
|
||||
AS_HELP_STRING(--enable-utempter, use utempter if it is installed)
|
||||
@@ -405,17 +421,34 @@ if test "x$enable_utempter" = xyes; then
|
||||
fi
|
||||
if test "x$enable_utempter" = xyes; then
|
||||
AC_DEFINE(HAVE_UTEMPTER)
|
||||
utempter_version=on
|
||||
else
|
||||
AC_MSG_ERROR("utempter not found")
|
||||
fi
|
||||
fi
|
||||
|
||||
# Look for utf8proc.
|
||||
utf8proc_version=off
|
||||
AC_ARG_ENABLE(
|
||||
utf8proc,
|
||||
AS_HELP_STRING(--enable-utf8proc, use utf8proc if it is installed)
|
||||
)
|
||||
try_utf8proc=no
|
||||
require_utf8proc=no
|
||||
if test "x$enable_utf8proc" = xyes; then
|
||||
try_utf8proc=yes
|
||||
elif test "x$enable_utf8proc" = x; then
|
||||
case "$host_os" in
|
||||
*darwin*)
|
||||
try_utf8proc=yes
|
||||
require_utf8proc=yes
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
if test "x$try_utf8proc" = xyes; then
|
||||
SAVED_AM_CPPFLAGS="$AM_CPPFLAGS"
|
||||
SAVED_OLD_CPPFLAGS="$CPPFLAGS"
|
||||
SAVED_LIBS="$LIBS"
|
||||
PKG_CHECK_MODULES(
|
||||
LIBUTF8PROC,
|
||||
libutf8proc,
|
||||
@@ -423,7 +456,10 @@ if test "x$enable_utf8proc" = xyes; then
|
||||
AM_CPPFLAGS="$LIBUTF8PROC_CFLAGS $AM_CPPFLAGS"
|
||||
CPPFLAGS="$LIBUTF8PROC_CFLAGS $SAVED_CPPFLAGS"
|
||||
LIBS="$LIBUTF8PROC_LIBS $LIBS"
|
||||
]
|
||||
utf8proc_version=`$PKG_CONFIG --modversion libutf8proc 2>/dev/null`
|
||||
test "x$utf8proc_version" = x && utf8proc_version=on
|
||||
],
|
||||
[:]
|
||||
)
|
||||
AC_CHECK_HEADER(utf8proc.h, enable_utf8proc=yes, enable_utf8proc=no)
|
||||
if test "x$enable_utf8proc" = xyes; then
|
||||
@@ -436,13 +472,23 @@ if test "x$enable_utf8proc" = xyes; then
|
||||
fi
|
||||
if test "x$enable_utf8proc" = xyes; then
|
||||
AC_DEFINE(HAVE_UTF8PROC)
|
||||
test "x$utf8proc_version" = xoff && utf8proc_version=on
|
||||
else
|
||||
AC_MSG_ERROR("utf8proc not found")
|
||||
AM_CPPFLAGS="$SAVED_AM_CPPFLAGS"
|
||||
CPPFLAGS="$SAVED_OLD_CPPFLAGS"
|
||||
LIBS="$SAVED_LIBS"
|
||||
utf8proc_version=off
|
||||
if test "x$require_utf8proc" = xyes; then
|
||||
enable_utf8proc=
|
||||
else
|
||||
AC_MSG_ERROR("utf8proc not found")
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
AM_CONDITIONAL(HAVE_UTF8PROC, [test "x$enable_utf8proc" = xyes])
|
||||
|
||||
# Check for systemd support.
|
||||
systemd_version=off
|
||||
AC_ARG_ENABLE(
|
||||
systemd,
|
||||
AS_HELP_STRING(--enable-systemd, enable systemd integration)
|
||||
@@ -456,6 +502,8 @@ if test x"$enable_systemd" = xyes; then
|
||||
CPPFLAGS="$AM_CPPFLAGS $SAVED_CPPFLAGS"
|
||||
LIBS="$SYSTEMD_LIBS $LIBS"
|
||||
found_systemd=yes
|
||||
systemd_version=`$PKG_CONFIG --modversion libsystemd 2>/dev/null`
|
||||
test "x$systemd_version" = x && systemd_version=on
|
||||
],
|
||||
found_systemd=no
|
||||
)
|
||||
@@ -580,11 +628,34 @@ if test "x$found_malloc_trim" = xyes; then
|
||||
fi
|
||||
|
||||
# Build against jemalloc if requested.
|
||||
jemalloc_version=off
|
||||
AC_ARG_ENABLE(
|
||||
jemalloc,
|
||||
AS_HELP_STRING(--enable-jemalloc, use jemalloc if it is installed)
|
||||
)
|
||||
try_jemalloc=no
|
||||
require_jemalloc=no
|
||||
if test "x$enable_asan" = xyes && test "x$enable_jemalloc" = xyes; then
|
||||
AC_MSG_ERROR([--enable-asan and --enable-jemalloc cannot be used together])
|
||||
fi
|
||||
if test "x$enable_jemalloc" = xyes; then
|
||||
try_jemalloc=yes
|
||||
elif test "x$enable_jemalloc" = x; then
|
||||
case "$host_os" in
|
||||
*darwin*)
|
||||
if test "x$enable_asan" = xyes; then
|
||||
enable_jemalloc=no
|
||||
else
|
||||
try_jemalloc=yes
|
||||
require_jemalloc=yes
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
if test "x$try_jemalloc" = xyes; then
|
||||
SAVED_AM_CPPFLAGS="$AM_CPPFLAGS"
|
||||
SAVED_OLD_CPPFLAGS="$CPPFLAGS"
|
||||
SAVED_LIBS="$LIBS"
|
||||
PKG_CHECK_MODULES(
|
||||
JEMALLOC,
|
||||
jemalloc,
|
||||
@@ -592,9 +663,26 @@ if test "x$enable_jemalloc" = xyes; then
|
||||
AM_CPPFLAGS="$JEMALLOC_CFLAGS $AM_CPPFLAGS"
|
||||
CPPFLAGS="$AM_CPPFLAGS $SAVED_CPPFLAGS"
|
||||
LIBS="$LIBS $JEMALLOC_LIBS"
|
||||
jemalloc_version=`$PKG_CONFIG --modversion jemalloc 2>/dev/null`
|
||||
test "x$jemalloc_version" = x && jemalloc_version=on
|
||||
],
|
||||
AC_MSG_ERROR("jemalloc not found")
|
||||
enable_jemalloc=no
|
||||
)
|
||||
if test "x$enable_jemalloc" = xno; then
|
||||
AM_CPPFLAGS="$SAVED_AM_CPPFLAGS"
|
||||
CPPFLAGS="$SAVED_OLD_CPPFLAGS"
|
||||
LIBS="$SAVED_LIBS"
|
||||
jemalloc_version=off
|
||||
if test "x$require_jemalloc" = xyes; then
|
||||
enable_jemalloc=
|
||||
else
|
||||
AC_MSG_ERROR("jemalloc not found")
|
||||
fi
|
||||
else
|
||||
enable_jemalloc=yes
|
||||
AC_DEFINE(HAVE_JEMALLOC)
|
||||
test "x$jemalloc_version" = xoff && jemalloc_version=on
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check for CMSG_DATA. On some platforms like HP-UX this requires UNIX 95
|
||||
@@ -941,6 +1029,20 @@ case "$host_os" in
|
||||
AC_MSG_NOTICE([])
|
||||
AC_MSG_ERROR([must give --enable-utf8proc or --disable-utf8proc])
|
||||
fi
|
||||
#
|
||||
# macOS calloc(3) does not appear to always zero memory correctly,
|
||||
# so complain and suggest using jemalloc instead.
|
||||
#
|
||||
if test "x$enable_jemalloc" = x; then
|
||||
AC_MSG_NOTICE([])
|
||||
AC_MSG_NOTICE([ macOS calloc(3) appears not to correctly])
|
||||
AC_MSG_NOTICE([ zero allocations in some circumstances;])
|
||||
AC_MSG_NOTICE([ to avoid this, configuring with])
|
||||
AC_MSG_NOTICE([ --enable-jemalloc is recommended. To build])
|
||||
AC_MSG_NOTICE([ without anyway, use --disable-jemalloc])
|
||||
AC_MSG_NOTICE([])
|
||||
AC_MSG_ERROR([must give --enable-jemalloc or --disable-jemalloc])
|
||||
fi
|
||||
;;
|
||||
*dragonfly*)
|
||||
AC_MSG_RESULT(dragonfly)
|
||||
@@ -1028,6 +1130,31 @@ AC_MSG_CHECKING(lock-command)
|
||||
AC_MSG_RESULT($DEFAULT_LOCK_CMD)
|
||||
AC_SUBST(DEFAULT_LOCK_CMD)
|
||||
|
||||
# Print a summary.
|
||||
AC_MSG_NOTICE([])
|
||||
if test "x$enable_asan" = xyes; then
|
||||
AC_MSG_NOTICE([ASAN: on])
|
||||
else
|
||||
AC_MSG_NOTICE([ASAN: off])
|
||||
fi
|
||||
if test "x$enable_sixel" = xyes; then
|
||||
AC_MSG_NOTICE([SIXEL: on])
|
||||
else
|
||||
AC_MSG_NOTICE([SIXEL: off])
|
||||
fi
|
||||
if test "x$enable_debug" = xyes; then
|
||||
AC_MSG_NOTICE([debug: on])
|
||||
else
|
||||
AC_MSG_NOTICE([debug: off])
|
||||
fi
|
||||
AC_MSG_NOTICE([jemalloc: $jemalloc_version])
|
||||
AC_MSG_NOTICE([libevent: $libevent_version])
|
||||
AC_MSG_NOTICE([ncurses: $ncurses_version])
|
||||
AC_MSG_NOTICE([systemd: $systemd_version])
|
||||
AC_MSG_NOTICE([utempter: $utempter_version])
|
||||
AC_MSG_NOTICE([utf8proc: $utf8proc_version])
|
||||
AC_MSG_NOTICE([])
|
||||
|
||||
# Save our CFLAGS/CPPFLAGS/LDFLAGS for the Makefile and restore the old user
|
||||
# variables.
|
||||
AC_SUBST(AM_CPPFLAGS)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: control-notify.c,v 1.37 2026/07/10 13:38:45 nicm Exp $ */
|
||||
/* $OpenBSD: control-notify.c,v 1.38 2026/08/03 13:38:42 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2012 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -44,7 +44,8 @@ control_pane_mode_changed_cb(__unused const char *name,
|
||||
TAILQ_FOREACH(c, &clients, entry) {
|
||||
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
||||
continue;
|
||||
control_write(c, "%%pane-mode-changed %%%u", wp->id);
|
||||
control_notify_write(c, "%%pane-mode-changed %%%u",
|
||||
wp->id);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -53,8 +54,9 @@ control_pane_mode_changed_cb(__unused const char *name,
|
||||
if (value == NULL)
|
||||
return;
|
||||
TAILQ_FOREACH(c, &clients, entry) {
|
||||
if (CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
||||
control_write(c, "%%pane-mode-changed %s", value);
|
||||
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
||||
continue;
|
||||
control_notify_write(c, "%%pane-mode-changed %s", value);
|
||||
}
|
||||
free(value);
|
||||
}
|
||||
@@ -92,7 +94,7 @@ control_window_layout_changed_cb(__unused const char *name,
|
||||
continue;
|
||||
s = c->session;
|
||||
if (winlink_find_by_window_id(&s->windows, w->id) != NULL)
|
||||
control_write(c, "%s", cp);
|
||||
control_notify_write(c, "%s", cp);
|
||||
}
|
||||
free(cp);
|
||||
}
|
||||
@@ -111,7 +113,7 @@ control_window_pane_changed_cb(__unused const char *name,
|
||||
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
||||
continue;
|
||||
|
||||
control_write(c, "%%window-pane-changed @%u %%%u", w->id,
|
||||
control_notify_write(c, "%%window-pane-changed @%u %%%u", w->id,
|
||||
w->active->id);
|
||||
}
|
||||
}
|
||||
@@ -133,9 +135,11 @@ control_window_unlinked_cb(__unused const char *name, struct event_payload *ep,
|
||||
cs = c->session;
|
||||
|
||||
if (winlink_find_by_window_id(&cs->windows, w->id) != NULL)
|
||||
control_write(c, "%%window-close @%u", w->id);
|
||||
else
|
||||
control_write(c, "%%unlinked-window-close @%u", w->id);
|
||||
control_notify_write(c, "%%window-close @%u", w->id);
|
||||
else {
|
||||
control_notify_write(c, "%%unlinked-window-close @%u",
|
||||
w->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,9 +160,11 @@ control_window_linked_cb(__unused const char *name, struct event_payload *ep,
|
||||
cs = c->session;
|
||||
|
||||
if (winlink_find_by_window_id(&cs->windows, w->id) != NULL)
|
||||
control_write(c, "%%window-add @%u", w->id);
|
||||
else
|
||||
control_write(c, "%%unlinked-window-add @%u", w->id);
|
||||
control_notify_write(c, "%%window-add @%u", w->id);
|
||||
else {
|
||||
control_notify_write(c, "%%unlinked-window-add @%u",
|
||||
w->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,11 +185,11 @@ control_window_renamed_cb(__unused const char *name, struct event_payload *ep,
|
||||
cs = c->session;
|
||||
|
||||
if (winlink_find_by_window_id(&cs->windows, w->id) != NULL) {
|
||||
control_write(c, "%%window-renamed @%u %s", w->id,
|
||||
w->name);
|
||||
} else {
|
||||
control_write(c, "%%unlinked-window-renamed @%u %s",
|
||||
control_notify_write(c, "%%window-renamed @%u %s",
|
||||
w->id, w->name);
|
||||
} else {
|
||||
control_notify_write(c,
|
||||
"%%unlinked-window-renamed @%u %s", w->id, w->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -206,11 +212,12 @@ control_client_session_changed_cb(__unused const char *name,
|
||||
continue;
|
||||
|
||||
if (cc == c) {
|
||||
control_write(c, "%%session-changed $%u %s", s->id,
|
||||
s->name);
|
||||
control_notify_write(c, "%%session-changed $%u %s",
|
||||
s->id, s->name);
|
||||
} else {
|
||||
control_write(c, "%%client-session-changed %s $%u %s",
|
||||
cc->name, s->id, s->name);
|
||||
control_notify_write(c,
|
||||
"%%client-session-changed %s $%u %s", cc->name,
|
||||
s->id, s->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -248,8 +255,9 @@ control_client_detached_cb(__unused const char *name, struct event_payload *ep,
|
||||
if (cc == NULL)
|
||||
return;
|
||||
TAILQ_FOREACH(c, &clients, entry) {
|
||||
if (CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
||||
control_write(c, "%%client-detached %s", cc->name);
|
||||
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
||||
continue;
|
||||
control_notify_write(c, "%%client-detached %s", cc->name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -266,8 +274,8 @@ control_session_renamed_cb(__unused const char *name, struct event_payload *ep,
|
||||
TAILQ_FOREACH(c, &clients, entry) {
|
||||
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
||||
continue;
|
||||
|
||||
control_write(c, "%%session-renamed $%u %s", s->id, s->name);
|
||||
control_notify_write(c, "%%session-renamed $%u %s", s->id,
|
||||
s->name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -281,8 +289,7 @@ control_session_created_cb(__unused const char *name,
|
||||
TAILQ_FOREACH(c, &clients, entry) {
|
||||
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
||||
continue;
|
||||
|
||||
control_write(c, "%%sessions-changed");
|
||||
control_notify_write(c, "%%sessions-changed");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -296,8 +303,7 @@ control_session_closed_cb(__unused const char *name,
|
||||
TAILQ_FOREACH(c, &clients, entry) {
|
||||
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
||||
continue;
|
||||
|
||||
control_write(c, "%%sessions-changed");
|
||||
control_notify_write(c, "%%sessions-changed");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -320,9 +326,8 @@ control_session_window_changed_cb(__unused const char *name,
|
||||
TAILQ_FOREACH(c, &clients, entry) {
|
||||
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
||||
continue;
|
||||
|
||||
control_write(c, "%%session-window-changed $%u @%u", s->id,
|
||||
s->curw->window->id);
|
||||
control_notify_write(c, "%%session-window-changed $%u @%u",
|
||||
s->id, s->curw->window->id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -339,8 +344,7 @@ control_paste_buffer_changed_cb(__unused const char *name,
|
||||
TAILQ_FOREACH(c, &clients, entry) {
|
||||
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
||||
continue;
|
||||
|
||||
control_write(c, "%%paste-buffer-changed %s", pbname);
|
||||
control_notify_write(c, "%%paste-buffer-changed %s", pbname);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -357,8 +361,7 @@ control_paste_buffer_deleted_cb(__unused const char *name,
|
||||
TAILQ_FOREACH(c, &clients, entry) {
|
||||
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
||||
continue;
|
||||
|
||||
control_write(c, "%%paste-buffer-deleted %s", pbname);
|
||||
control_notify_write(c, "%%paste-buffer-deleted %s", pbname);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
176
control.c
176
control.c
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: control.c,v 1.62 2026/07/17 08:37:29 nicm Exp $ */
|
||||
/* $OpenBSD: control.c,v 1.65 2026/08/04 11:18:22 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2012 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -53,6 +53,17 @@ struct control_block {
|
||||
TAILQ_ENTRY(control_block) all_entry;
|
||||
};
|
||||
|
||||
/*
|
||||
* A notification line deferred because it was generated while a command's
|
||||
* %begin/%end guard block was open. Notifications must never appear inside a
|
||||
* guard block, so they are held here and flushed once the block closes.
|
||||
*/
|
||||
struct control_line {
|
||||
char *line;
|
||||
|
||||
TAILQ_ENTRY(control_line) entry;
|
||||
};
|
||||
|
||||
/* Control client pane. */
|
||||
struct control_pane {
|
||||
u_int pane;
|
||||
@@ -102,6 +113,13 @@ struct control_state {
|
||||
struct bufferevent *write_event;
|
||||
|
||||
struct monitor_set *subs;
|
||||
|
||||
/*
|
||||
* Depth of open %begin/%end guard blocks and notifications deferred
|
||||
* until the outermost block closes.
|
||||
*/
|
||||
int guard_depth;
|
||||
TAILQ_HEAD(, control_line) deferred;
|
||||
};
|
||||
|
||||
/* Low and high watermarks. */
|
||||
@@ -352,7 +370,7 @@ control_continue_pane(struct client *c, struct window_pane *wp)
|
||||
cp->flags &= ~CONTROL_PANE_PAUSED;
|
||||
memcpy(&cp->offset, &wp->offset, sizeof cp->offset);
|
||||
memcpy(&cp->queued, &wp->offset, sizeof cp->queued);
|
||||
control_write(c, "%%continue %%%u", wp->id);
|
||||
control_notify_write(c, "%%continue %%%u", wp->id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -366,52 +384,120 @@ control_pause_pane(struct client *c, struct window_pane *wp)
|
||||
if (~cp->flags & CONTROL_PANE_PAUSED) {
|
||||
cp->flags |= CONTROL_PANE_PAUSED;
|
||||
control_discard_pane(c, cp);
|
||||
control_write(c, "%%pause %%%u", wp->id);
|
||||
control_notify_write(c, "%%pause %%%u", wp->id);
|
||||
}
|
||||
}
|
||||
|
||||
/* Write a line. */
|
||||
static void printflike(2, 0)
|
||||
control_vwrite(struct client *c, const char *fmt, va_list ap)
|
||||
{
|
||||
struct control_state *cs = c->control_state;
|
||||
char *s;
|
||||
|
||||
xvasprintf(&s, fmt, ap);
|
||||
log_debug("%s: %s: writing line: %s", __func__, c->name, s);
|
||||
|
||||
bufferevent_write(cs->write_event, s, strlen(s));
|
||||
bufferevent_write(cs->write_event, "\n", 1);
|
||||
|
||||
bufferevent_enable(cs->write_event, EV_WRITE);
|
||||
free(s);
|
||||
}
|
||||
|
||||
/* Write a line. */
|
||||
void
|
||||
control_write(struct client *c, const char *fmt, ...)
|
||||
/* Write an already-formatted line, queueing it behind %output if needed. */
|
||||
static void
|
||||
control_write_line(struct client *c, char *line)
|
||||
{
|
||||
struct control_state *cs = c->control_state;
|
||||
struct control_block *cb;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
|
||||
if (TAILQ_EMPTY(&cs->all_blocks)) {
|
||||
control_vwrite(c, fmt, ap);
|
||||
va_end(ap);
|
||||
log_debug("%s: %s: writing line: %s", __func__, c->name, line);
|
||||
bufferevent_write(cs->write_event, line, strlen(line));
|
||||
bufferevent_write(cs->write_event, "\n", 1);
|
||||
bufferevent_enable(cs->write_event, EV_WRITE);
|
||||
free(line);
|
||||
return;
|
||||
}
|
||||
|
||||
cb = xcalloc(1, sizeof *cb);
|
||||
xvasprintf(&cb->line, fmt, ap);
|
||||
cb->line = line;
|
||||
TAILQ_INSERT_TAIL(&cs->all_blocks, cb, all_entry);
|
||||
cb->t = get_timer();
|
||||
|
||||
log_debug("%s: %s: storing line: %s", __func__, c->name, cb->line);
|
||||
bufferevent_enable(cs->write_event, EV_WRITE);
|
||||
}
|
||||
|
||||
/* Flush notifications that were deferred while a guard block was open. */
|
||||
static void
|
||||
control_flush_deferred(struct client *c)
|
||||
{
|
||||
struct control_state *cs = c->control_state;
|
||||
struct control_line *cl, *cl1;
|
||||
|
||||
TAILQ_FOREACH_SAFE(cl, &cs->deferred, entry, cl1) {
|
||||
TAILQ_REMOVE(&cs->deferred, cl, entry);
|
||||
control_write_line(c, cl->line);
|
||||
free(cl);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Write a line of command output or error text. This is a sink for arbitrary
|
||||
* user-controlled text (command output, capture-pane, error messages), so it
|
||||
* must never try to interpret the content: guard tracking is done only in
|
||||
* control_write_guard.
|
||||
*/
|
||||
void
|
||||
control_write(struct client *c, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char *line;
|
||||
|
||||
va_start(ap, fmt);
|
||||
xvasprintf(&line, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
control_write_line(c, line);
|
||||
}
|
||||
|
||||
/*
|
||||
* Write a %begin, %end or %error guard around a command's output. This is the
|
||||
* only place guard lines are produced, so the block depth is maintained here;
|
||||
* when the outermost block closes any deferred notifications are flushed after
|
||||
* it. "guard" is always one of the fixed strings from cmdq_guard, never user
|
||||
* text.
|
||||
*/
|
||||
void
|
||||
control_write_guard(struct client *c, const char *guard, long t, u_int number,
|
||||
int flags)
|
||||
{
|
||||
struct control_state *cs = c->control_state;
|
||||
char *line;
|
||||
|
||||
if (strcmp(guard, "begin") == 0)
|
||||
cs->guard_depth++;
|
||||
|
||||
xasprintf(&line, "%%%s %ld %u %d", guard, t, number, flags);
|
||||
control_write_line(c, line);
|
||||
|
||||
if (strcmp(guard, "begin") != 0 && cs->guard_depth > 0 &&
|
||||
--cs->guard_depth == 0)
|
||||
control_flush_deferred(c);
|
||||
}
|
||||
|
||||
/*
|
||||
* Write a notification line. Notifications must never appear inside a command's
|
||||
* %begin/%end guard block, so if one is open the line is deferred until it
|
||||
* closes.
|
||||
*/
|
||||
void
|
||||
control_notify_write(struct client *c, const char *fmt, ...)
|
||||
{
|
||||
struct control_state *cs = c->control_state;
|
||||
struct control_line *cl;
|
||||
va_list ap;
|
||||
char *line;
|
||||
|
||||
va_start(ap, fmt);
|
||||
xvasprintf(&line, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (cs->guard_depth == 0) {
|
||||
control_write_line(c, line);
|
||||
return;
|
||||
}
|
||||
|
||||
log_debug("%s: %s: deferring notification: %s", __func__, c->name,
|
||||
line);
|
||||
cl = xcalloc(1, sizeof *cl);
|
||||
cl->line = line;
|
||||
TAILQ_INSERT_TAIL(&cs->deferred, cl, entry);
|
||||
}
|
||||
|
||||
/* Check age for this pane. */
|
||||
@@ -438,7 +524,7 @@ control_check_age(struct client *c, struct window_pane *wp,
|
||||
return (0);
|
||||
cp->flags |= CONTROL_PANE_PAUSED;
|
||||
control_discard_pane(c, cp);
|
||||
control_write(c, "%%pause %%%u", wp->id);
|
||||
control_notify_write(c, "%%pause %%%u", wp->id);
|
||||
} else {
|
||||
if (age < CONTROL_MAXIMUM_AGE)
|
||||
return (0);
|
||||
@@ -805,14 +891,17 @@ control_sub_change(struct monitor_change *change, __unused void *data)
|
||||
|
||||
if (wp != NULL) {
|
||||
w = wp->window;
|
||||
control_write(c, "%%subscription-changed %s $%u @%u %u %%%u : %s",
|
||||
control_notify_write(c,
|
||||
"%%subscription-changed %s $%u @%u %u %%%u : %s",
|
||||
change->name, s->id, w->id, wl->idx, wp->id, change->value);
|
||||
} else if (wl != NULL) {
|
||||
w = wl->window;
|
||||
control_write(c, "%%subscription-changed %s $%u @%u %u - : %s",
|
||||
control_notify_write(c,
|
||||
"%%subscription-changed %s $%u @%u %u - : %s",
|
||||
change->name, s->id, w->id, wl->idx, change->value);
|
||||
} else {
|
||||
control_write(c, "%%subscription-changed %s $%u - - - : %s",
|
||||
control_notify_write(c,
|
||||
"%%subscription-changed %s $%u - - - : %s",
|
||||
change->name, s->id, change->value);
|
||||
}
|
||||
}
|
||||
@@ -835,6 +924,7 @@ control_start(struct client *c)
|
||||
RB_INIT(&cs->windows);
|
||||
TAILQ_INIT(&cs->pending_list);
|
||||
TAILQ_INIT(&cs->all_blocks);
|
||||
TAILQ_INIT(&cs->deferred);
|
||||
cs->subs = monitor_create_client(c, control_sub_change, NULL);
|
||||
|
||||
cs->read_event = bufferevent_new(c->fd, control_read_callback,
|
||||
@@ -878,6 +968,19 @@ control_discard(struct client *c)
|
||||
bufferevent_disable(cs->read_event, EV_READ);
|
||||
}
|
||||
|
||||
/* Discard all tmux-owned queued control blocks and stop writing. */
|
||||
void
|
||||
control_discard_all(struct client *c)
|
||||
{
|
||||
struct control_state *cs = c->control_state;
|
||||
struct control_block *cb, *cb1;
|
||||
|
||||
control_discard(c);
|
||||
TAILQ_FOREACH_SAFE(cb, &cs->all_blocks, all_entry, cb1)
|
||||
control_free_block(cs, cb);
|
||||
bufferevent_disable(cs->write_event, EV_WRITE);
|
||||
}
|
||||
|
||||
/* Stop control mode. */
|
||||
void
|
||||
control_stop(struct client *c)
|
||||
@@ -885,12 +988,19 @@ control_stop(struct client *c)
|
||||
struct control_state *cs = c->control_state;
|
||||
struct control_block *cb, *cb1;
|
||||
struct control_window *cw, *cw1;
|
||||
struct control_line *cl, *cl1;
|
||||
|
||||
if (cs == NULL)
|
||||
return;
|
||||
|
||||
monitor_destroy(cs->subs);
|
||||
|
||||
TAILQ_FOREACH_SAFE(cl, &cs->deferred, entry, cl1) {
|
||||
TAILQ_REMOVE(&cs->deferred, cl, entry);
|
||||
free(cl->line);
|
||||
free(cl);
|
||||
}
|
||||
|
||||
if (~c->flags & CLIENT_CONTROLCONTROL)
|
||||
bufferevent_free(cs->write_event);
|
||||
bufferevent_free(cs->read_event);
|
||||
|
||||
87
file.c
87
file.c
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: file.c,v 1.20 2026/05/17 10:54:01 nicm Exp $ */
|
||||
/* $OpenBSD: file.c,v 1.22 2026/08/17 07:56:56 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -185,6 +185,7 @@ file_can_print(struct client *c)
|
||||
{
|
||||
if (c == NULL ||
|
||||
(c->flags & CLIENT_ATTACHED) ||
|
||||
(c->flags & CLIENT_DEAD) ||
|
||||
(c->flags & CLIENT_CONTROL))
|
||||
return (0);
|
||||
return (1);
|
||||
@@ -501,7 +502,8 @@ file_push(struct client_file *cf)
|
||||
} else if (cf->stream > 2) {
|
||||
close.stream = cf->stream;
|
||||
proc_send(cf->peer, MSG_WRITE_CLOSE, -1, &close, sizeof close);
|
||||
file_fire_done(cf);
|
||||
if (cf->c == NULL || (~cf->c->flags & CLIENT_WRITE_ACK))
|
||||
file_fire_done(cf);
|
||||
}
|
||||
free(msg);
|
||||
}
|
||||
@@ -526,14 +528,48 @@ file_write_left(struct client_files *files)
|
||||
return (waiting != 0);
|
||||
}
|
||||
|
||||
/* Finish writing a client file. */
|
||||
static void
|
||||
file_write_finished(struct client_file *cf)
|
||||
{
|
||||
struct msg_write_done msg;
|
||||
|
||||
if (cf->event != NULL) {
|
||||
bufferevent_free(cf->event);
|
||||
cf->event = NULL;
|
||||
}
|
||||
if (cf->fd != -1) {
|
||||
if (close(cf->fd) != 0 && cf->error == 0)
|
||||
cf->error = errno;
|
||||
cf->fd = -1;
|
||||
}
|
||||
|
||||
msg.stream = cf->stream;
|
||||
msg.error = cf->error;
|
||||
proc_send(cf->peer, MSG_WRITE_DONE, -1, &msg, sizeof msg);
|
||||
|
||||
if (cf->cb != NULL)
|
||||
cf->cb(NULL, NULL, 0, -1, NULL, cf->data);
|
||||
file_free(cf);
|
||||
}
|
||||
|
||||
/* Client file write error callback. */
|
||||
static void
|
||||
file_write_error_callback(__unused struct bufferevent *bev, __unused short what,
|
||||
file_write_error_callback(__unused struct bufferevent *bev, short what,
|
||||
void *arg)
|
||||
{
|
||||
struct client_file *cf = arg;
|
||||
int error;
|
||||
|
||||
if (what & EVBUFFER_ERROR)
|
||||
error = errno;
|
||||
else
|
||||
error = EIO;
|
||||
if (error == 0)
|
||||
error = EIO;
|
||||
|
||||
log_debug("write error file %d", cf->stream);
|
||||
cf->error = error;
|
||||
|
||||
bufferevent_free(cf->event);
|
||||
cf->event = NULL;
|
||||
@@ -541,7 +577,9 @@ file_write_error_callback(__unused struct bufferevent *bev, __unused short what,
|
||||
close(cf->fd);
|
||||
cf->fd = -1;
|
||||
|
||||
if (cf->cb != NULL)
|
||||
if (cf->closed)
|
||||
file_write_finished(cf);
|
||||
else if (cf->cb != NULL)
|
||||
cf->cb(NULL, NULL, 0, -1, NULL, cf->data);
|
||||
}
|
||||
|
||||
@@ -553,15 +591,10 @@ file_write_callback(__unused struct bufferevent *bev, void *arg)
|
||||
|
||||
log_debug("write check file %d", cf->stream);
|
||||
|
||||
if (cf->cb != NULL)
|
||||
if (cf->closed && EVBUFFER_LENGTH(cf->event->output) == 0)
|
||||
file_write_finished(cf);
|
||||
else if (cf->cb != NULL)
|
||||
cf->cb(NULL, NULL, 0, -1, NULL, cf->data);
|
||||
|
||||
if (cf->closed && EVBUFFER_LENGTH(cf->event->output) == 0) {
|
||||
bufferevent_free(cf->event);
|
||||
close(cf->fd);
|
||||
RB_REMOVE(client_files, cf->tree, cf);
|
||||
file_free(cf);
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle a file write open message (client). */
|
||||
@@ -662,14 +695,10 @@ file_write_close(struct client_files *files, struct imsg *imsg)
|
||||
if ((cf = RB_FIND(client_files, files, &find)) == NULL)
|
||||
fatalx("unknown stream number");
|
||||
log_debug("close file %d", cf->stream);
|
||||
cf->closed = 1;
|
||||
|
||||
if (cf->event == NULL || EVBUFFER_LENGTH(cf->event->output) == 0) {
|
||||
if (cf->event != NULL)
|
||||
bufferevent_free(cf->event);
|
||||
if (cf->fd != -1)
|
||||
close(cf->fd);
|
||||
RB_REMOVE(client_files, files, cf);
|
||||
file_free(cf);
|
||||
file_write_finished(cf);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -828,6 +857,28 @@ file_write_ready(struct client_files *files, struct imsg *imsg)
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Handle a write done message (server). */
|
||||
int
|
||||
file_write_done(struct client_files *files, struct imsg *imsg)
|
||||
{
|
||||
struct msg_write_done *msg = imsg->data;
|
||||
size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
|
||||
struct client_file find, *cf;
|
||||
|
||||
if (msglen != sizeof *msg)
|
||||
return (-1);
|
||||
find.stream = msg->stream;
|
||||
if ((cf = RB_FIND(client_files, files, &find)) == NULL)
|
||||
return (0);
|
||||
if (cf->c == NULL || (~cf->c->flags & CLIENT_WRITE_ACK))
|
||||
return (0);
|
||||
|
||||
log_debug("file %d write done", cf->stream);
|
||||
cf->error = msg->error;
|
||||
file_fire_done(cf);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Handle read data message (server). */
|
||||
int
|
||||
file_read_data(struct client_files *files, struct imsg *imsg)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: format-draw.c,v 1.33 2026/06/29 17:08:52 nicm Exp $ */
|
||||
/* $OpenBSD: format-draw.c,v 1.34 2026/07/21 07:20:18 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -958,7 +958,7 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
|
||||
if (srs != NULL) {
|
||||
if (fr != NULL && !format_is_type(fr, &sy)) {
|
||||
if (s[current].cx != fr->start) {
|
||||
fr->end = s[current].cx + 1;
|
||||
fr->end = s[current].cx;
|
||||
TAILQ_INSERT_TAIL(&frs, fr, entry);
|
||||
} else
|
||||
free(fr);
|
||||
|
||||
169
format.c
169
format.c
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: format.c,v 1.407 2026/07/20 07:42:13 nicm Exp $ */
|
||||
/* $OpenBSD: format.c,v 1.412 2026/08/05 07:31:08 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2011 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -128,6 +128,7 @@ format_job_cmp(struct format_job *fj1, struct format_job *fj2)
|
||||
#define FORMAT_OPTIONS 0x40000000
|
||||
#define FORMAT_ENVIRON 0x80000000ULL
|
||||
#define FORMAT_DIFFERENCE 0x100000000ULL
|
||||
#define FORMAT_CYCLE 0x200000000ULL
|
||||
|
||||
/* Limit on recursion. */
|
||||
#define FORMAT_LOOP_LIMIT 100
|
||||
@@ -138,9 +139,13 @@ format_job_cmp(struct format_job *fj1, struct format_job *fj2)
|
||||
/* How often to check the time in long loops. */
|
||||
#define FORMAT_TIME_LOOP_CHECK 10000
|
||||
|
||||
/* Fixed animation period (ms): redraw interval and shortest frame step. */
|
||||
#define FORMAT_CYCLE_PERIOD 100
|
||||
|
||||
/* Format expand flags. */
|
||||
#define FORMAT_EXPAND_TIME 0x1
|
||||
#define FORMAT_EXPAND_NOJOBS 0x2
|
||||
#define FORMAT_EXPAND_NOCYCLE 0x4
|
||||
|
||||
/* Entry in format tree. */
|
||||
struct format_entry {
|
||||
@@ -413,7 +418,8 @@ format_job_get(struct format_expand_state *es, const char *cmd)
|
||||
RB_INSERT(format_job_tree, jobs, fj);
|
||||
}
|
||||
|
||||
format_copy_state(&next, es, FORMAT_EXPAND_NOJOBS);
|
||||
format_copy_state(&next, es, FORMAT_EXPAND_NOJOBS|
|
||||
FORMAT_EXPAND_NOCYCLE);
|
||||
next.flags &= ~FORMAT_EXPAND_TIME;
|
||||
|
||||
expanded = format_expand1(&next, cmd);
|
||||
@@ -2095,6 +2101,60 @@ format_cb_synchronized_output_flag(struct format_tree *ft)
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/* Callback for pane_private_modes. */
|
||||
static void *
|
||||
format_cb_pane_private_modes(struct format_tree *ft)
|
||||
{
|
||||
static const struct {
|
||||
int mode;
|
||||
int number;
|
||||
} table[] = {
|
||||
{ MODE_KCURSOR, 1 }, /* DECCKM */
|
||||
{ MODE_ORIGIN, 6 }, /* DECOM */
|
||||
{ MODE_WRAP, 7 }, /* DECAWM */
|
||||
{ MODE_CURSOR_BLINKING, 12 }, /* cursor blinking */
|
||||
{ MODE_CURSOR, 25 }, /* DECTCEM */
|
||||
{ MODE_MOUSE_STANDARD, 1000 }, /* mouse normal tracking */
|
||||
{ MODE_MOUSE_BUTTON, 1002 }, /* mouse button tracking */
|
||||
{ MODE_MOUSE_ALL, 1003 }, /* mouse any tracking */
|
||||
{ MODE_FOCUSON, 1004 }, /* focus reporting */
|
||||
{ MODE_MOUSE_UTF8, 1005 }, /* mouse: UTF-8 */
|
||||
{ MODE_MOUSE_SGR, 1006 }, /* mouse: SGR */
|
||||
{ MODE_BRACKETPASTE, 2004 }, /* bracketed paste */
|
||||
{ MODE_SYNC, 2026 }, /* synchronized output */
|
||||
{ MODE_THEME_UPDATES, 2031 }, /* theme update notifications */
|
||||
};
|
||||
int mode;
|
||||
char *value = NULL, *tmp;
|
||||
u_int i;
|
||||
|
||||
if (ft->wp == NULL)
|
||||
return (NULL);
|
||||
mode = ft->wp->base.mode;
|
||||
|
||||
for (i = 0; i < nitems(table); i++) {
|
||||
if (~mode & table[i].mode)
|
||||
continue;
|
||||
/*
|
||||
* Only report cursor blinking when set by the application, not
|
||||
* when it comes from the cursor-style option.
|
||||
*/
|
||||
if (table[i].mode == MODE_CURSOR_BLINKING &&
|
||||
(~mode & MODE_CURSOR_BLINKING_SET))
|
||||
continue;
|
||||
if (value == NULL)
|
||||
xasprintf(&value, "%d", table[i].number);
|
||||
else {
|
||||
xasprintf(&tmp, "%s,%d", value, table[i].number);
|
||||
free(value);
|
||||
value = tmp;
|
||||
}
|
||||
}
|
||||
if (value == NULL)
|
||||
return (xstrdup(""));
|
||||
return (value);
|
||||
}
|
||||
|
||||
/* Callback for pane_active. */
|
||||
static void *
|
||||
format_cb_pane_active(struct format_tree *ft)
|
||||
@@ -2622,20 +2682,23 @@ format_cb_pane_unzoomed_width(struct format_tree *ft)
|
||||
{
|
||||
struct window_pane *wp = ft->wp;
|
||||
struct layout_cell *lc;
|
||||
int sb_w, sb_pad;
|
||||
int saved, sb_w, sb_pad;
|
||||
u_int sx;
|
||||
|
||||
if (wp == NULL)
|
||||
return (NULL);
|
||||
|
||||
lc = wp->saved_layout_cell;
|
||||
saved = (lc != NULL);
|
||||
if (lc == NULL)
|
||||
lc = wp->layout_cell;
|
||||
if (lc == NULL)
|
||||
return (NULL);
|
||||
sx = lc->g.sx;
|
||||
|
||||
if (window_pane_scrollbar_reserve(wp)) {
|
||||
if ((saved && !SCREEN_IS_ALTERNATE(&wp->base) &&
|
||||
wp->window->sb == PANE_SCROLLBARS_ALWAYS) ||
|
||||
(!saved && window_pane_scrollbar_reserve(wp))) {
|
||||
sb_w = wp->scrollbar_style.width;
|
||||
sb_pad = wp->scrollbar_style.pad;
|
||||
if (sb_w < 1)
|
||||
@@ -3853,6 +3916,9 @@ static const struct format_table_entry format_table[] = {
|
||||
{ "pane_pipe_pid", FORMAT_TABLE_STRING,
|
||||
format_cb_pane_pipe_pid
|
||||
},
|
||||
{ "pane_private_modes", FORMAT_TABLE_STRING,
|
||||
format_cb_pane_private_modes
|
||||
},
|
||||
{ "pane_right", FORMAT_TABLE_STRING,
|
||||
format_cb_pane_right
|
||||
},
|
||||
@@ -4240,6 +4306,8 @@ format_log_debug_cb(const char *key, const char *value, void *arg)
|
||||
void
|
||||
format_log_debug(struct format_tree *ft, const char *prefix)
|
||||
{
|
||||
if (log_get_level() == 0)
|
||||
return;
|
||||
format_each(ft, format_log_debug_cb, (void *)prefix);
|
||||
}
|
||||
|
||||
@@ -4368,7 +4436,7 @@ format_quote_shell(const char *s)
|
||||
|
||||
at = out = xmalloc(strlen(s) * 2 + 1);
|
||||
for (cp = s; *cp != '\0'; cp++) {
|
||||
if (strchr("|&;<>()$`\\\"'*?[# =%", *cp) != NULL)
|
||||
if (strchr("|&;<>()$`\\\"'*?[# =%\n\t", *cp) != NULL)
|
||||
*at++ = '\\';
|
||||
*at++ = *cp;
|
||||
}
|
||||
@@ -4846,7 +4914,7 @@ format_build_modifiers(struct format_expand_state *es, const char **s,
|
||||
|
||||
/*
|
||||
* Modifiers are a ; separated list of the forms:
|
||||
* l,m,C,a,b,c,d,I,n,t,w,q,E,T,S,W,P,O,V,R,<,>
|
||||
* l,m,C,a,b,c,d,I,n,t,w,q,E,T,S,W,P,O,V,R,A,<,>
|
||||
* =a
|
||||
* =/a
|
||||
* =/a/
|
||||
@@ -4865,7 +4933,7 @@ format_build_modifiers(struct format_expand_state *es, const char **s,
|
||||
break;
|
||||
|
||||
/* Check single character modifiers with no arguments. */
|
||||
if (strchr("labdnwETSWPOVL!<>", cp[0]) != NULL &&
|
||||
if (strchr("labdnwETSWPOVL!<>A", cp[0]) != NULL &&
|
||||
format_is_end(cp[1])) {
|
||||
format_add_modifier(&list, count, cp, 1, NULL, 0);
|
||||
cp++;
|
||||
@@ -4887,7 +4955,7 @@ format_build_modifiers(struct format_expand_state *es, const char **s,
|
||||
}
|
||||
|
||||
/* Now try single character with arguments. */
|
||||
if (strchr("ImCLNPSOVst=pReqWc", cp[0]) == NULL)
|
||||
if (strchr("ImCLNPSOVst=pReqWcA", cp[0]) == NULL)
|
||||
break;
|
||||
c = cp[0];
|
||||
|
||||
@@ -5850,6 +5918,74 @@ fail:
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/* Callback for the cycle timer; redraw the status line. */
|
||||
static void
|
||||
format_cycle_callback(__unused int fd, __unused short events, void *arg)
|
||||
{
|
||||
struct client *c = arg;
|
||||
|
||||
if (c->message_string == NULL && c->prompt == NULL)
|
||||
c->flags |= CLIENT_REDRAWSTATUS;
|
||||
}
|
||||
|
||||
/* Arm the cycle timer to redraw the status line if it is not already. */
|
||||
static void
|
||||
format_cycle_start_timer(struct client *c)
|
||||
{
|
||||
struct timeval tv;
|
||||
|
||||
tv.tv_sec = FORMAT_CYCLE_PERIOD / 1000;
|
||||
tv.tv_usec = (FORMAT_CYCLE_PERIOD % 1000) * 1000L;
|
||||
|
||||
if (!event_initialized(&c->cycle_timer))
|
||||
evtimer_set(&c->cycle_timer, format_cycle_callback, c);
|
||||
if (!evtimer_pending(&c->cycle_timer, NULL))
|
||||
evtimer_add(&c->cycle_timer, &tv);
|
||||
}
|
||||
|
||||
/* Expand the "A" animation modifier; see the manual for the syntax. */
|
||||
static char *
|
||||
format_cycle(struct format_expand_state *es, const char *frames, u_int count)
|
||||
{
|
||||
struct format_tree *ft = es->ft;
|
||||
const char *start, *end, *cp;
|
||||
u_int n, index, i;
|
||||
|
||||
/*
|
||||
* A cycle is only expanded in a status format, and never in the
|
||||
* command or output of #() where it would change on every frame and
|
||||
* make the job run again.
|
||||
*/
|
||||
if (!(ft->flags & FORMAT_STATUS) || (es->flags & FORMAT_EXPAND_NOCYCLE))
|
||||
return (xstrdup(""));
|
||||
if (*frames == '\0')
|
||||
return (xstrdup(""));
|
||||
|
||||
/* Count the comma-separated frames (there is at least one). */
|
||||
n = 1;
|
||||
for (cp = frames; *cp != '\0'; cp++) {
|
||||
if (*cp == ',')
|
||||
n++;
|
||||
}
|
||||
index = (es->start_time / (count * FORMAT_CYCLE_PERIOD)) % n;
|
||||
|
||||
/*
|
||||
* Redraw the status line so the frames advance on their own; a
|
||||
* single frame never changes so there is nothing to redraw for.
|
||||
*/
|
||||
if (n > 1 && ft->client != NULL)
|
||||
format_cycle_start_timer(ft->client);
|
||||
|
||||
/* Walk to the chosen frame and return a copy of it. */
|
||||
start = frames;
|
||||
for (i = 0; i < index; i++)
|
||||
start = strchr(start, ',') + 1;
|
||||
end = strchr(start, ',');
|
||||
if (end == NULL)
|
||||
end = start + strlen(start);
|
||||
return (xstrndup(start, end - start));
|
||||
}
|
||||
|
||||
/* Replace a key. */
|
||||
static int
|
||||
format_replace(struct format_expand_state *es, const char *key, size_t keylen,
|
||||
@@ -5870,6 +6006,7 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
|
||||
struct format_modifier *list, *cmp = NULL, *search = NULL;
|
||||
struct format_modifier **sub = NULL, *mexp = NULL, *fm;
|
||||
struct format_modifier *bool_op_n = NULL;
|
||||
u_int cycle_count = 1;
|
||||
u_int i, count, nsub = 0, nrep, check = 0;
|
||||
const char *loop_flags = "";
|
||||
struct format_expand_state next;
|
||||
@@ -5930,6 +6067,15 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
|
||||
if (errstr != NULL)
|
||||
width = 0;
|
||||
break;
|
||||
case 'A':
|
||||
modifiers |= FORMAT_CYCLE;
|
||||
if (fm->argc < 1)
|
||||
break;
|
||||
cycle_count = strtonum(fm->argv[0], 1, 100,
|
||||
&errstr);
|
||||
if (errstr != NULL)
|
||||
cycle_count = 1;
|
||||
break;
|
||||
case 'w':
|
||||
modifiers |= FORMAT_WIDTH;
|
||||
break;
|
||||
@@ -6151,6 +6297,13 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Is this an animation cycle? */
|
||||
if (modifiers & FORMAT_CYCLE) {
|
||||
value = format_cycle(es, copy, cycle_count);
|
||||
format_log(es, "cycle '%s' is: %s", copy, value);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Is this a literal string? */
|
||||
if (modifiers & FORMAT_LITERAL) {
|
||||
format_log(es, "literal string is '%s'", copy);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: grid-reader.c,v 1.10 2026/05/17 13:12:21 nicm Exp $ */
|
||||
/* $OpenBSD: grid-reader.c,v 1.11 2026/07/29 17:42:56 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2020 Anindya Mukherjee <anindya49@hotmail.com>
|
||||
@@ -54,11 +54,8 @@ grid_reader_cursor_right(struct grid_reader *gr, int wrap, int all, int onemore)
|
||||
px = gr->gd->sx;
|
||||
else if (onemore)
|
||||
px = grid_reader_line_length(gr);
|
||||
else {
|
||||
px = grid_reader_line_length(gr);
|
||||
if (px != 0)
|
||||
px--;
|
||||
}
|
||||
else
|
||||
px = grid_line_limit(gr->gd, gr->cy);
|
||||
|
||||
if (wrap && gr->cx >= px && gr->cy < gr->gd->hsize + gr->gd->sy - 1) {
|
||||
grid_reader_cursor_start_of_line(gr, 0);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: grid-view.c,v 1.38 2026/01/22 08:55:01 nicm Exp $ */
|
||||
/* $OpenBSD: grid-view.c,v 1.39 2026/08/03 12:58:53 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -47,9 +47,9 @@ grid_view_set_cell(struct grid *gd, u_int px, u_int py,
|
||||
|
||||
/* Set padding. */
|
||||
void
|
||||
grid_view_set_padding(struct grid *gd, u_int px, u_int py)
|
||||
grid_view_set_padding(struct grid *gd, u_int px, u_int py, int bg)
|
||||
{
|
||||
grid_set_padding(gd, grid_view_x(gd, px), grid_view_y(gd, py));
|
||||
grid_set_padding(gd, grid_view_x(gd, px), grid_view_y(gd, py), bg);
|
||||
}
|
||||
|
||||
/* Set cells. */
|
||||
|
||||
30
grid.c
30
grid.c
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: grid.c,v 1.154 2026/07/20 11:16:33 nicm Exp $ */
|
||||
/* $OpenBSD: grid.c,v 1.156 2026/08/03 12:58:53 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -682,9 +682,13 @@ grid_set_cell(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc)
|
||||
|
||||
/* Set padding at position. */
|
||||
void
|
||||
grid_set_padding(struct grid *gd, u_int px, u_int py)
|
||||
grid_set_padding(struct grid *gd, u_int px, u_int py, int bg)
|
||||
{
|
||||
grid_set_cell(gd, px, py, &grid_padding_cell);
|
||||
struct grid_cell gc;
|
||||
|
||||
memcpy(&gc, &grid_padding_cell, sizeof gc);
|
||||
gc.bg = bg;
|
||||
grid_set_cell(gd, px, py, &gc);
|
||||
}
|
||||
|
||||
/* Set cells at position. */
|
||||
@@ -1681,6 +1685,26 @@ grid_line_length(struct grid *gd, u_int py)
|
||||
return (px);
|
||||
}
|
||||
|
||||
/* Get last position on line, not including padding. */
|
||||
u_int
|
||||
grid_line_limit(struct grid *gd, u_int py)
|
||||
{
|
||||
struct grid_cell gc;
|
||||
u_int px;
|
||||
|
||||
px = grid_line_length(gd, py);
|
||||
if (px == 0)
|
||||
return (0);
|
||||
px--;
|
||||
while (px > 0) {
|
||||
grid_get_cell(gd, px, py, &gc);
|
||||
if (~gc.flags & GRID_FLAG_PADDING)
|
||||
break;
|
||||
px--;
|
||||
}
|
||||
return (px);
|
||||
}
|
||||
|
||||
/* Check if character is in set. */
|
||||
int
|
||||
grid_in_set(struct grid *gd, u_int px, u_int py, const char *set)
|
||||
|
||||
63
hooks.c
63
hooks.c
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: hooks.c,v 1.14 2026/07/13 22:03:08 nicm Exp $ */
|
||||
/* $OpenBSD: hooks.c,v 1.16 2026/07/27 19:15:58 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -25,7 +25,7 @@
|
||||
#include "tmux.h"
|
||||
|
||||
/* Hook monitor state owned by an option entry. */
|
||||
struct hook_monitor {
|
||||
struct hooks_monitor {
|
||||
struct options *oo;
|
||||
|
||||
struct monitor_set *set;
|
||||
@@ -143,6 +143,7 @@ hooks_insert(struct cmdq_item *item, struct hooks_data *hd)
|
||||
log_debug("%s: hook %s not found", __func__, hd->name);
|
||||
return;
|
||||
}
|
||||
options_hook_fired(o);
|
||||
|
||||
if (item == NULL)
|
||||
state = cmdq_new_state(&fs, NULL, CMDQ_STATE_NOHOOKS);
|
||||
@@ -233,7 +234,7 @@ hooks_event_cb(const char *name, struct event_payload *ep,
|
||||
{
|
||||
struct cmdq_item *item;
|
||||
|
||||
if (event_payload_get_pointer(ep, "_hook_monitor") != NULL)
|
||||
if (event_payload_get_pointer(ep, "_hooks_monitor") != NULL)
|
||||
return;
|
||||
|
||||
item = event_payload_get_pointer(ep, "_cmdq_item");
|
||||
@@ -324,7 +325,7 @@ hooks_run(struct cmdq_item *item, const char *name)
|
||||
void
|
||||
hooks_monitor_free(void *data)
|
||||
{
|
||||
struct hook_monitor *hm = data;
|
||||
struct hooks_monitor *hm = data;
|
||||
|
||||
events_remove_sink(hm->sink);
|
||||
monitor_destroy(hm->set);
|
||||
@@ -337,7 +338,7 @@ void
|
||||
hooks_monitor_remove(struct options *oo, const char *name)
|
||||
{
|
||||
struct options_entry *o;
|
||||
struct hook_monitor *hm;
|
||||
struct hooks_monitor *hm;
|
||||
|
||||
o = options_get_only(oo, name);
|
||||
if (o == NULL)
|
||||
@@ -355,9 +356,9 @@ static void
|
||||
hooks_monitor_hook_cb(const char *name, struct event_payload *ep,
|
||||
void *sink_data)
|
||||
{
|
||||
struct hook_monitor *hm = sink_data;
|
||||
struct hooks_monitor *hm = sink_data;
|
||||
|
||||
if (event_payload_get_pointer(ep, "_hook_monitor") == hm)
|
||||
if (event_payload_get_pointer(ep, "_hooks_monitor") == hm)
|
||||
hooks_insert_event(cmdq_running(NULL), name, ep, hm->oo, 1);
|
||||
}
|
||||
|
||||
@@ -365,14 +366,14 @@ hooks_monitor_hook_cb(const char *name, struct event_payload *ep,
|
||||
static void
|
||||
hooks_monitor_cb(struct monitor_change *change, void *data)
|
||||
{
|
||||
struct hook_monitor *hm = data;
|
||||
struct hooks_monitor *hm = data;
|
||||
struct event_payload *ep;
|
||||
struct winlink *wl = change->wl;
|
||||
struct window_pane *wp = change->wp;
|
||||
struct cmd_find_state fs;
|
||||
|
||||
ep = event_payload_create();
|
||||
event_payload_set_pointer(ep, "_hook_monitor", data, NULL, NULL);
|
||||
event_payload_set_pointer(ep, "_hooks_monitor", data, NULL, NULL);
|
||||
|
||||
cmd_find_clear_state(&fs, 0);
|
||||
if (wl != NULL && wp != NULL && wp->window == wl->window)
|
||||
@@ -402,8 +403,7 @@ hooks_monitor_cb(struct monitor_change *change, void *data)
|
||||
event_payload_set_session(ep, "session", change->s);
|
||||
if (wl != NULL) {
|
||||
if (change->s == NULL)
|
||||
event_payload_set_session(ep, "session",
|
||||
wl->session);
|
||||
event_payload_set_session(ep, "session", wl->session);
|
||||
event_payload_set_window(ep, "window", wl->window);
|
||||
event_payload_set_int(ep, "window_index", wl->idx);
|
||||
}
|
||||
@@ -423,7 +423,7 @@ hooks_monitor_add(__unused struct cmdq_item *item, struct options *oo,
|
||||
int flags, struct cmd_find_state *fs, struct session *s)
|
||||
{
|
||||
struct options_entry *o;
|
||||
struct hook_monitor *hm;
|
||||
struct hooks_monitor *hm;
|
||||
|
||||
hooks_monitor_remove(oo, name);
|
||||
o = options_get_only(oo, name);
|
||||
@@ -446,7 +446,7 @@ hooks_monitor_add(__unused struct cmdq_item *item, struct options *oo,
|
||||
char *
|
||||
hooks_monitor_to_string(struct options_entry *o)
|
||||
{
|
||||
struct hook_monitor *hm = options_get_monitor_data(o);
|
||||
struct hooks_monitor *hm = options_get_monitor_data(o);
|
||||
const char *name = options_name(o);
|
||||
char *s;
|
||||
|
||||
@@ -472,3 +472,40 @@ hooks_monitor_to_string(struct options_entry *o)
|
||||
}
|
||||
return (s);
|
||||
}
|
||||
|
||||
/* Get the parts of a hook monitor. */
|
||||
int
|
||||
hooks_monitor_get(struct options_entry *o, enum monitor_type *type, int *id,
|
||||
const char **format)
|
||||
{
|
||||
struct hooks_monitor *hm = options_get_monitor_data(o);
|
||||
|
||||
if (hm == NULL)
|
||||
return (0);
|
||||
*type = hm->type;
|
||||
*id = hm->id;
|
||||
*format = hm->format;
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* Get hook monitor firing count. */
|
||||
u_int
|
||||
hooks_monitor_get_fire_count(struct options_entry *o)
|
||||
{
|
||||
struct hooks_monitor *hm = options_get_monitor_data(o);
|
||||
|
||||
if (hm == NULL)
|
||||
return (0);
|
||||
return (monitor_get_fire_count(hm->set, options_name(o)));
|
||||
}
|
||||
|
||||
/* Get hook monitor firing time. */
|
||||
time_t
|
||||
hooks_monitor_get_fire_time(struct options_entry *o)
|
||||
{
|
||||
struct hooks_monitor *hm = options_get_monitor_data(o);
|
||||
|
||||
if (hm == NULL)
|
||||
return (0);
|
||||
return (monitor_get_fire_time(hm->set, options_name(o)));
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: key-bindings.c,v 1.189 2026/07/15 10:38:31 nicm Exp $ */
|
||||
/* $OpenBSD: key-bindings.c,v 1.192 2026/08/06 09:05:04 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -47,6 +47,9 @@
|
||||
" ''" \
|
||||
" 'New After' 'w' {new-window -a}" \
|
||||
" 'New At End' 'W' {new-window}"
|
||||
#define DEFAULT_EMPTY_MENU \
|
||||
" 'New Pane' 'p' {new-pane; join-pane}" \
|
||||
" 'New Window' 'w' {new-window}"
|
||||
#define DEFAULT_PANE_MENU \
|
||||
" '#{?#{m/r:(copy|view)-mode,#{pane_mode}},Go To Top,}' '<' {send -X history-top}" \
|
||||
" '#{?#{m/r:(copy|view)-mode,#{pane_mode}},Go To Bottom,}' '>' {send -X history-bottom}" \
|
||||
@@ -420,6 +423,7 @@ key_bindings_init(void)
|
||||
"bind -N 'Spread panes out evenly' E { select-layout -E }",
|
||||
"bind -N 'Switch to the last client' L { switch-client -l }",
|
||||
"bind -N 'Clear the marked pane' M { select-pane -M }",
|
||||
"bind -N 'Change the pane title' T { command-prompt -I'#T' { select-pane -T '%%' } }",
|
||||
"bind -N 'Enter copy mode' [ { copy-mode }",
|
||||
"bind -N 'Paste the most recent paste buffer' ] { paste-buffer -p }",
|
||||
"bind -N 'Create a new window' c { new-window }",
|
||||
@@ -553,6 +557,8 @@ key_bindings_init(void)
|
||||
/* Mouse button 3 down on pane. */
|
||||
"bind -n MouseDown3Pane { if -Ft= '#{||:#{mouse_any_flag},#{&&:#{pane_in_mode},#{?#{m/r:(copy|view)-mode,#{pane_mode}},0,1}}}' { select-pane -t=; send -M } { display-menu -t= -xM -yM -T '#[align=centre]#{pane_index} (#{pane_id})' " DEFAULT_PANE_MENU " } }",
|
||||
"bind -n M-MouseDown3Pane { display-menu -t= -xM -yM -T '#[align=centre]#{pane_index} (#{pane_id})' " DEFAULT_PANE_MENU " }",
|
||||
"bind -n MouseDown3Empty { display-menu -t= -xM -yM -T '#[align=centre]#{window_index}:#{window_name}' " DEFAULT_EMPTY_MENU " }",
|
||||
"bind -n M-MouseDown3Empty { display-menu -t= -xM -yM -T '#[align=centre]#{window_index}:#{window_name}' " DEFAULT_EMPTY_MENU " }",
|
||||
|
||||
/* Mouse on scrollbar. */
|
||||
"bind -n MouseDown1ScrollbarUp { if -Ft= '#{pane_in_mode}' { send -X page-up } {copy-mode -u } }",
|
||||
@@ -592,7 +598,7 @@ key_bindings_init(void)
|
||||
"bind -Tcopy-mode g { command-prompt -P -p'(goto line)' { send -X goto-line -- '%%' } }",
|
||||
"bind -Tcopy-mode n { send -X search-again }",
|
||||
"bind -Tcopy-mode q { send -X cancel }",
|
||||
"bind -Tcopy-mode r { send -X refresh-toggle }",
|
||||
"bind -Tcopy-mode r { send -X refresh-now }",
|
||||
"bind -Tcopy-mode t { command-prompt -P -1p'(jump to forward)' { send -X jump-to-forward -- '%%' } }",
|
||||
"bind -Tcopy-mode Home { send -X start-of-line }",
|
||||
"bind -Tcopy-mode End { send -X end-of-line }",
|
||||
@@ -702,7 +708,7 @@ key_bindings_init(void)
|
||||
"bind -Tcopy-mode-vi n { send -X search-again }",
|
||||
"bind -Tcopy-mode-vi o { send -X other-end }",
|
||||
"bind -Tcopy-mode-vi q { send -X cancel }",
|
||||
"bind -Tcopy-mode-vi r { send -X refresh-toggle }",
|
||||
"bind -Tcopy-mode-vi r { send -X refresh-now }",
|
||||
"bind -Tcopy-mode-vi t { command-prompt -P -1p'(jump to forward)' { send -X jump-to-forward -- '%%' } }",
|
||||
"bind -Tcopy-mode-vi v { send -X rectangle-toggle }",
|
||||
"bind -Tcopy-mode-vi w { send -X next-word }",
|
||||
|
||||
6
menu.c
6
menu.c
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: menu.c,v 1.69 2026/07/14 19:07:03 nicm Exp $ */
|
||||
/* $OpenBSD: menu.c,v 1.70 2026/08/17 07:33:55 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -337,6 +337,7 @@ menu_key(struct client *c, struct menu_data *md, struct key_event *event)
|
||||
struct cmdq_state *state;
|
||||
enum cmd_parse_status status;
|
||||
char *error;
|
||||
key_code key;
|
||||
|
||||
if (KEYC_IS_MOUSE(event->key)) {
|
||||
/*
|
||||
@@ -387,7 +388,8 @@ menu_key(struct client *c, struct menu_data *md, struct key_event *event)
|
||||
name = menu->items[i].name;
|
||||
if (name == NULL || *name == '-')
|
||||
continue;
|
||||
if ((event->key & ~KEYC_MASK_FLAGS) == menu->items[i].key) {
|
||||
key = (event->key & ~KEYC_MASK_FLAGS);
|
||||
if (key == (menu->items[i].key & ~KEYC_MASK_FLAGS)) {
|
||||
md->choice = i;
|
||||
goto chosen;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: mode-tree.c,v 1.100 2026/07/14 19:07:03 nicm Exp $ */
|
||||
/* $OpenBSD: mode-tree.c,v 1.101 2026/08/05 07:50:21 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2017 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -47,7 +47,7 @@ enum mode_tree_preview {
|
||||
"#[acs]x" MODE_TREE_PREFIX_STYLE " }," \
|
||||
"#{mode_tree_repeat}}" \
|
||||
"#{?mode_tree_branch," \
|
||||
"#[acs]#{?mode_tree_last,mq,tq}" MODE_TREE_PREFIX_STYLE "> ,}" \
|
||||
"#[acs]#{?mode_tree_last,mq,tq}+" MODE_TREE_PREFIX_STYLE " ,}" \
|
||||
"#{?mode_tree_has_children," \
|
||||
"#{?mode_tree_expanded,#[fg=themered]-" MODE_TREE_PREFIX_STYLE " ," \
|
||||
"#[fg=themegreen]+" MODE_TREE_PREFIX_STYLE " }," \
|
||||
|
||||
30
monitor.c
30
monitor.c
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: monitor.c,v 1.6 2026/07/10 15:20:06 nicm Exp $ */
|
||||
/* $OpenBSD: monitor.c,v 1.7 2026/07/27 19:15:58 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -59,6 +59,9 @@ struct monitor_item {
|
||||
struct monitor_panes panes;
|
||||
struct monitor_windows windows;
|
||||
|
||||
u_int fire_count;
|
||||
time_t fire_time;
|
||||
|
||||
RB_ENTRY(monitor_item) entry;
|
||||
};
|
||||
RB_HEAD(monitor_items, monitor_item);
|
||||
@@ -180,6 +183,9 @@ monitor_report(struct monitor_set *ms, struct monitor_item *me,
|
||||
|
||||
log_debug("%s: %s changed to %s", __func__, me->name, value);
|
||||
|
||||
me->fire_count++;
|
||||
me->fire_time = current_time;
|
||||
|
||||
change.name = me->name;
|
||||
change.value = value;
|
||||
change.last = last;
|
||||
@@ -659,3 +665,25 @@ monitor_remove(struct monitor_set *ms, const char *name)
|
||||
if (RB_EMPTY(&ms->items) && evtimer_initialized(&ms->timer))
|
||||
evtimer_del(&ms->timer);
|
||||
}
|
||||
|
||||
/* Get subscription firing count. */
|
||||
u_int
|
||||
monitor_get_fire_count(struct monitor_set *ms, const char *name)
|
||||
{
|
||||
struct monitor_item *me, find = { .name = (char *)name };
|
||||
|
||||
if ((me = RB_FIND(monitor_items, &ms->items, &find)) == NULL)
|
||||
return (0);
|
||||
return (me->fire_count);
|
||||
}
|
||||
|
||||
/* Get subscription firing time. */
|
||||
time_t
|
||||
monitor_get_fire_time(struct monitor_set *ms, const char *name)
|
||||
{
|
||||
struct monitor_item *me, find = { .name = (char *)name };
|
||||
|
||||
if ((me = RB_FIND(monitor_items, &ms->items, &find)) == NULL)
|
||||
return (0);
|
||||
return (me->fire_time);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: options-table.c,v 1.239 2026/07/19 17:36:38 nicm Exp $ */
|
||||
/* $OpenBSD: options-table.c,v 1.242 2026/07/27 08:03:01 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2011 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -1431,6 +1431,15 @@ const struct options_table_entry options_table[] = {
|
||||
.text = "Style of current line number in copy mode."
|
||||
},
|
||||
|
||||
{ .name = "copy-mode-current-line-style",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_WINDOW,
|
||||
.default_str = "default",
|
||||
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||
.separator = ",",
|
||||
.text = "Style of the line containing the cursor in copy mode."
|
||||
},
|
||||
|
||||
{ .name = "copy-mode-line-number-style",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_WINDOW,
|
||||
@@ -1451,8 +1460,8 @@ const struct options_table_entry options_table[] = {
|
||||
{ .name = "fill-character",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_WINDOW,
|
||||
.default_str = "",
|
||||
.text = "Character used to fill unused parts of window."
|
||||
.default_str = "#{?is_inside,#[bg=themedarkgrey] ,#[fg=themelightgrey]#[acs]~}",
|
||||
.text = "Format used to fill unused parts of window."
|
||||
},
|
||||
|
||||
{ .name = "main-pane-height",
|
||||
@@ -1936,6 +1945,7 @@ const struct options_table_entry options_table[] = {
|
||||
OPTIONS_TABLE_AFTER_HOOK("show-messages"),
|
||||
OPTIONS_TABLE_AFTER_HOOK("show-options"),
|
||||
OPTIONS_TABLE_AFTER_HOOK("split-window"),
|
||||
OPTIONS_TABLE_AFTER_HOOK("swap-window"),
|
||||
OPTIONS_TABLE_AFTER_HOOK("unbind-key"),
|
||||
OPTIONS_TABLE_HOOK("alert-activity", "",
|
||||
"Run when a window has activity."),
|
||||
|
||||
26
options.c
26
options.c
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: options.c,v 1.90 2026/07/10 13:38:45 nicm Exp $ */
|
||||
/* $OpenBSD: options.c,v 1.92 2026/07/27 19:15:58 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -107,7 +107,10 @@ struct options_entry {
|
||||
|
||||
int cached;
|
||||
struct style style;
|
||||
|
||||
void *monitor_data;
|
||||
u_int fire_count;
|
||||
time_t fire_time;
|
||||
|
||||
RB_ENTRY(options_entry) entry;
|
||||
};
|
||||
@@ -440,6 +443,25 @@ options_set_monitor_data(struct options_entry *o, void *data)
|
||||
o->monitor_data = data;
|
||||
}
|
||||
|
||||
void
|
||||
options_hook_fired(struct options_entry *o)
|
||||
{
|
||||
o->fire_count++;
|
||||
o->fire_time = current_time;
|
||||
}
|
||||
|
||||
u_int
|
||||
options_get_fire_count(struct options_entry *o)
|
||||
{
|
||||
return (o->fire_count);
|
||||
}
|
||||
|
||||
time_t
|
||||
options_get_fire_time(struct options_entry *o)
|
||||
{
|
||||
return (o->fire_time);
|
||||
}
|
||||
|
||||
const struct options_table_entry *
|
||||
options_table_entry(struct options_entry *o)
|
||||
{
|
||||
@@ -1372,7 +1394,7 @@ options_push_changes(const char *name)
|
||||
}
|
||||
if (strcmp(name, "fill-character") == 0) {
|
||||
RB_FOREACH(w, windows, &windows)
|
||||
window_set_fill_character(w);
|
||||
window_set_fill_cells(w);
|
||||
}
|
||||
if (strcmp(name, "key-table") == 0) {
|
||||
TAILQ_FOREACH(loop, &clients, entry)
|
||||
|
||||
19
proc.c
19
proc.c
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: proc.c,v 1.31 2026/06/08 21:38:19 nicm Exp $ */
|
||||
/* $OpenBSD: proc.c,v 1.32 2026/08/04 13:16:03 claudio Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2015 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -31,6 +31,10 @@
|
||||
#include <ncurses.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_JEMALLOC
|
||||
#include <jemalloc/jemalloc.h>
|
||||
#endif
|
||||
|
||||
#include "tmux.h"
|
||||
|
||||
struct tmuxproc {
|
||||
@@ -75,7 +79,7 @@ static void
|
||||
proc_event_cb(__unused int fd, short events, void *arg)
|
||||
{
|
||||
struct tmuxpeer *peer = arg;
|
||||
ssize_t n;
|
||||
int n;
|
||||
struct imsg imsg;
|
||||
|
||||
if (!(peer->flags & PEER_BAD) && (events & EV_READ)) {
|
||||
@@ -84,7 +88,7 @@ proc_event_cb(__unused int fd, short events, void *arg)
|
||||
return;
|
||||
}
|
||||
for (;;) {
|
||||
if ((n = imsg_get(&peer->ibuf, &imsg)) == -1) {
|
||||
if ((n = imsgbuf_get(&peer->ibuf, &imsg)) == -1) {
|
||||
peer->dispatchcb(NULL, peer->arg);
|
||||
return;
|
||||
}
|
||||
@@ -181,6 +185,10 @@ proc_start(const char *name)
|
||||
{
|
||||
struct tmuxproc *tp;
|
||||
struct utsname u;
|
||||
#ifdef HAVE_JEMALLOC
|
||||
const char *version;
|
||||
size_t size = sizeof version;
|
||||
#endif
|
||||
|
||||
log_open(name);
|
||||
setproctitle("%s (%s)", name, socket_path);
|
||||
@@ -195,6 +203,11 @@ proc_start(const char *name)
|
||||
#ifdef HAVE_UTF8PROC
|
||||
log_debug("using utf8proc %s", utf8proc_version());
|
||||
#endif
|
||||
#ifdef HAVE_JEMALLOC
|
||||
if (mallctl("version", &version, &size, NULL, 0) != 0)
|
||||
version = "(unknown version)";
|
||||
log_debug("using jemalloc %s", version);
|
||||
#endif
|
||||
#ifdef NCURSES_VERSION
|
||||
log_debug("using ncurses %s %06u", NCURSES_VERSION, NCURSES_VERSION_PATCH);
|
||||
#endif
|
||||
|
||||
4
prompt.c
4
prompt.c
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: prompt.c,v 1.5 2026/07/13 10:29:17 nicm Exp $ */
|
||||
/* $OpenBSD: prompt.c,v 1.6 2026/08/17 06:45:16 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -1334,7 +1334,7 @@ process_key:
|
||||
if (pr->index != size) {
|
||||
memmove(pr->buffer + pr->index,
|
||||
pr->buffer + pr->index + 1,
|
||||
(size + 1 - pr->index) *
|
||||
(size - pr->index) *
|
||||
sizeof *pr->buffer);
|
||||
goto changed;
|
||||
}
|
||||
|
||||
77
regress/control-client-exit-stalled.sh
Executable file
77
regress/control-client-exit-stalled.sh
Executable file
@@ -0,0 +1,77 @@
|
||||
#!/bin/sh
|
||||
|
||||
# A control client whose terminal has stopped accepting output (for example
|
||||
# its ssh connection or terminal emulator died) never drains the output that
|
||||
# server_client_check_exit waits on before sending MSG_EXIT, and may then
|
||||
# never close its socket either. This must not prevent the server from
|
||||
# exiting: after kill-server the server previously stayed alive forever with
|
||||
# server_exit set, closing every new connection immediately, so every new
|
||||
# client failed with "server exited unexpectedly" until the server was killed
|
||||
# by hand. The server now bounds the exit handshake: it discards output it
|
||||
# can never deliver and drops the client if it still does not close.
|
||||
#
|
||||
# The stalled terminal is simulated by a fifo whose read end is held open but
|
||||
# never read.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
DIR=$(mktemp -d)
|
||||
FIFO=$DIR/fifo
|
||||
SERVER=
|
||||
|
||||
mkfifo "$FIFO" || exit 1
|
||||
|
||||
cleanup() {
|
||||
[ -n "$SERVER" ] && kill -9 "$SERVER" 2>/dev/null
|
||||
$TMUX kill-server 2>/dev/null
|
||||
exec 8<&- 2>/dev/null
|
||||
rm -rf "$DIR"
|
||||
}
|
||||
trap cleanup 0 1 15
|
||||
|
||||
# A detached session whose pane floods printable output forever.
|
||||
$TMUX -f/dev/null new -d -x 80 -y 24 -s rt 'cat /dev/zero | tr "\000" x' || exit 1
|
||||
SERVER=$($TMUX display -pt rt '#{pid}')
|
||||
|
||||
# Attach a control client with output down the fifo; stdin held open by sleep
|
||||
# so it stays attached. The fifo fills and is never read, so a backlog forms
|
||||
# in the server that can never be delivered.
|
||||
( sleep 60 ) | $TMUX -f/dev/null -C attach -t rt >"$FIFO" 2>&1 &
|
||||
exec 8<"$FIFO"
|
||||
|
||||
n=0
|
||||
while [ $n -lt 50 ]; do
|
||||
$TMUX lsc -F '#{client_name}' 2>/dev/null | grep -q . && break
|
||||
sleep 0.1
|
||||
n=$((n + 1))
|
||||
done
|
||||
$TMUX lsc -F '#{client_name}' 2>/dev/null | grep -q . ||
|
||||
{ echo "control client did not attach"; exit 1; }
|
||||
|
||||
# Let the pane flood fill the fifo so the client's output is stuck.
|
||||
sleep 2
|
||||
|
||||
# Ask the server to exit. This sets CLIENT_EXIT on the stalled client, whose
|
||||
# output can never drain.
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
# The server must exit within the exit timeout (10 seconds) plus slack,
|
||||
# rather than staying in limbo forever rejecting new clients.
|
||||
n=0
|
||||
while [ $n -lt 100 ]; do
|
||||
if ! kill -0 "$SERVER" 2>/dev/null; then
|
||||
SERVER=
|
||||
exit 0
|
||||
fi
|
||||
sleep 0.2
|
||||
n=$((n + 1))
|
||||
done
|
||||
|
||||
echo "server did not exit after kill-server; new clients see:"
|
||||
$TMUX ls 2>&1
|
||||
exit 1
|
||||
158
regress/control-notify-guard.sh
Executable file
158
regress/control-notify-guard.sh
Executable file
@@ -0,0 +1,158 @@
|
||||
#!/bin/sh
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
LC_ALL=C.UTF-8
|
||||
LANG=C.UTF-8
|
||||
export TERM LC_ALL LANG
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
|
||||
TMPDIR=$(mktemp -d)
|
||||
IN="$TMPDIR/in"
|
||||
OUT="$TMPDIR/out"
|
||||
BADCFG="$TMPDIR/bad.conf"
|
||||
PID=
|
||||
|
||||
cleanup()
|
||||
{
|
||||
exec 3>&- 2>/dev/null
|
||||
[ -n "$PID" ] && kill "$PID" 2>/dev/null
|
||||
$TMUX kill-server 2>/dev/null
|
||||
rm -rf "$TMPDIR"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
fail()
|
||||
{
|
||||
echo "$1" >&2
|
||||
[ -s "$OUT" ] && cat "$OUT" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
wait_for()
|
||||
{
|
||||
pattern=$1
|
||||
timeout=${2:-60}
|
||||
i=0
|
||||
|
||||
while [ "$i" -lt "$timeout" ]; do
|
||||
grep -F -- "$pattern" "$OUT" >/dev/null 2>&1 && return 0
|
||||
sleep 0.1
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "missing: $pattern"
|
||||
}
|
||||
|
||||
wait_for_count()
|
||||
{
|
||||
pattern=$1
|
||||
expected=$2
|
||||
timeout=${3:-60}
|
||||
i=0
|
||||
|
||||
while [ "$i" -lt "$timeout" ]; do
|
||||
count=$(grep -F -c -- "$pattern" "$OUT" 2>/dev/null)
|
||||
[ "$count" -ge "$expected" ] && return 0
|
||||
sleep 0.1
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "missing count $expected for: $pattern"
|
||||
}
|
||||
|
||||
send()
|
||||
{
|
||||
printf '%s\n' "$1" >&3 || fail "failed to send: $1"
|
||||
}
|
||||
|
||||
# A notification is a standalone protocol message. It must never be part of
|
||||
# the response between the real %begin and matching %end or %error for a
|
||||
# command. Ignore guard-looking command output while a real guard is open.
|
||||
check_guards()
|
||||
{
|
||||
awk '
|
||||
function notification(line) {
|
||||
return line ~ /^%(sessions-changed|session-changed |unlinked-window-add )/ ||
|
||||
line ~ /^%(pause |continue |config-error )/
|
||||
}
|
||||
!open && $0 ~ /^%begin [0-9]+ [0-9]+ [0-9]+$/ {
|
||||
open = 1
|
||||
time = $2
|
||||
number = $3
|
||||
flags = $4
|
||||
next
|
||||
}
|
||||
open && ($0 == "%end " time " " number " " flags ||
|
||||
$0 == "%error " time " " number " " flags) {
|
||||
open = 0
|
||||
next
|
||||
}
|
||||
notification($0) {
|
||||
seen[$1]++
|
||||
if (open) {
|
||||
print "notification inside guard: " $0 > "/dev/stderr"
|
||||
bad = 1
|
||||
}
|
||||
}
|
||||
END {
|
||||
if (open) {
|
||||
print "unterminated command guard" > "/dev/stderr"
|
||||
bad = 1
|
||||
}
|
||||
if (seen["%sessions-changed"] < 2 ||
|
||||
seen["%session-changed"] < 1 ||
|
||||
seen["%unlinked-window-add"] < 2 ||
|
||||
seen["%pause"] < 1 || seen["%continue"] < 1 ||
|
||||
seen["%config-error"] < 1) {
|
||||
print "missing expected notification" > "/dev/stderr"
|
||||
bad = 1
|
||||
}
|
||||
exit bad
|
||||
}' "$OUT" || fail "bad control protocol notification ordering"
|
||||
}
|
||||
|
||||
$TMUX kill-server 2>/dev/null
|
||||
$TMUX new-session -d -s guard -x 80 -y 24 || exit 1
|
||||
pane=$($TMUX display-message -p -t guard:0.0 '#{pane_id}') || exit 1
|
||||
|
||||
mkfifo "$IN" || exit 1
|
||||
: >"$OUT"
|
||||
$TMUX -C attach-session -t guard <"$IN" >"$OUT" 2>&1 &
|
||||
PID=$!
|
||||
exec 3>"$IN"
|
||||
|
||||
# Attaching generates a session notification synchronously from inside the
|
||||
# attach command. It must follow the closing guard.
|
||||
wait_for '%session-changed '
|
||||
|
||||
# Guard-looking output is arbitrary command output, not protocol state. Make
|
||||
# new-session both generate notifications and print an unmatched fake %begin;
|
||||
# it must not leave those notifications permanently deferred.
|
||||
send "set-option -g @fake-begin '%begin 1 2 3'"
|
||||
send "new-session -d -P -F '#{@fake-begin}' -s fake-begin"
|
||||
wait_for_count '%sessions-changed' 1
|
||||
|
||||
# Likewise, fake %end output from a command that generates multiple
|
||||
# notifications must not flush them before the command actually ends.
|
||||
send "set-option -g @fake-end '%end 1 2 3'"
|
||||
send "new-session -d -P -F '#{@fake-end}' -s fake-end"
|
||||
wait_for_count '%sessions-changed' 2
|
||||
|
||||
# Exercise notification paths outside control-notify.c.
|
||||
send "refresh-client -A '$pane:pause'"
|
||||
wait_for "%pause $pane"
|
||||
send "refresh-client -A '$pane:continue'"
|
||||
wait_for "%continue $pane"
|
||||
|
||||
printf '%s\n' 'not-a-command' >"$BADCFG"
|
||||
send "source-file '$BADCFG'"
|
||||
wait_for '%config-error '
|
||||
|
||||
# A final command guarantees all earlier command guards have completed before
|
||||
# the transcript is checked.
|
||||
send "display-message -p 'guard-check-done'"
|
||||
wait_for 'guard-check-done'
|
||||
check_guards
|
||||
|
||||
exit 0
|
||||
@@ -114,5 +114,30 @@ $TMUX send-keys -X next-space-end
|
||||
$TMUX send-keys -X copy-selection
|
||||
[ "$($TMUX show-buffer)" = "500xyz" ] || exit 1
|
||||
|
||||
# Test that vi cursor movement does not stop on the padding cell of a wide
|
||||
# character at the end of a line.
|
||||
$TMUX kill-server 2>/dev/null
|
||||
sleep 1
|
||||
$TMUX new -d -x20 -y5 \
|
||||
"printf 'abc中\nxyz\n'; exec cat" || exit 1
|
||||
$TMUX set-window-option -g mode-keys vi
|
||||
$TMUX copy-mode
|
||||
$TMUX send-keys -X history-top
|
||||
$TMUX send-keys -X start-of-line
|
||||
$TMUX send-keys -X cursor-right
|
||||
$TMUX send-keys -X cursor-right
|
||||
$TMUX send-keys -X cursor-right
|
||||
[ "$($TMUX display -p '#{copy_cursor_x},#{copy_cursor_y}')" = "3,0" ] ||
|
||||
exit 1
|
||||
$TMUX send-keys -X cursor-right
|
||||
[ "$($TMUX display -p '#{copy_cursor_x},#{copy_cursor_y}')" = "0,1" ] ||
|
||||
exit 1
|
||||
$TMUX send-keys -X cursor-left
|
||||
[ "$($TMUX display -p '#{copy_cursor_x},#{copy_cursor_y}')" = "3,0" ] ||
|
||||
exit 1
|
||||
$TMUX send-keys -X cursor-left
|
||||
[ "$($TMUX display -p '#{copy_cursor_x},#{copy_cursor_y}')" = "2,0" ] ||
|
||||
exit 1
|
||||
|
||||
$TMUX kill-server 2>/dev/null
|
||||
exit 0
|
||||
|
||||
@@ -280,6 +280,7 @@ test_user_hook()
|
||||
settle
|
||||
assert_equals "$($TMUX show-option -qv @cm_hook)" \
|
||||
"display-message hook" "add user hook"
|
||||
$TMUX set-hook -E @cm_hook || fail "fire user hook failed"
|
||||
|
||||
send q
|
||||
wait_mode 0
|
||||
@@ -287,7 +288,10 @@ test_user_hook()
|
||||
repeat_key j 3
|
||||
send Right j
|
||||
settle
|
||||
assert_contains "$(capture)" "@cm_hook" "user hook shown as hook"
|
||||
screen=$(capture)
|
||||
assert_contains "$screen" "@cm_hook" "user hook shown as hook"
|
||||
assert_contains "$screen" "This hook has been fired 1 times, last " \
|
||||
"user hook fire count and time"
|
||||
|
||||
send u y
|
||||
settle
|
||||
@@ -302,10 +306,17 @@ test_hook_array()
|
||||
|
||||
$TMUX set-hook -g 'after-new-session[0]' 'display-message old' ||
|
||||
fail "set hook array failed"
|
||||
$TMUX set-hook -g -R after-new-session || fail "fire hook array failed"
|
||||
|
||||
open_customize '#{==:#{option_name},after-new-session}'
|
||||
repeat_key j 3
|
||||
send Right j Right j
|
||||
send Right j
|
||||
settle
|
||||
screen=$(capture)
|
||||
assert_contains "$screen" "This hook has been fired " \
|
||||
"array hook fire count"
|
||||
assert_contains "$screen" ", last " "array hook fire time"
|
||||
send Right j
|
||||
settle
|
||||
|
||||
send a
|
||||
|
||||
109
regress/format-animation.sh
Normal file
109
regress/format-animation.sh
Normal file
@@ -0,0 +1,109 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Exercise the A format modifier (format_cycle in format.c). It is expanded
|
||||
# only in a status format and arms the client cycle timer to redraw the status
|
||||
# line as the frames advance, so the only place its behaviour shows is a
|
||||
# rendered status line.
|
||||
#
|
||||
# The status line is rendered by an inner tmux attached inside an outer tmux
|
||||
# pane; capturing the outer pane shows what the inner tmux drew. The inner
|
||||
# window runs sleep and every other status line is blanked, so the capture
|
||||
# contains the frame and nothing else.
|
||||
#
|
||||
# Each frame lasts 700 milliseconds and the capture is repeated once a second,
|
||||
# so consecutive samples always land on a different frame: no exact period is
|
||||
# assumed and nothing in the test asks for a redraw, so seeing the frame change
|
||||
# means the status line animated on its own.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
LC_ALL=C.UTF-8
|
||||
export TERM LC_ALL
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
|
||||
|
||||
TMP=$(mktemp)
|
||||
trap "rm -f $TMP; $TMUX kill-server 2>/dev/null; $TMUX2 kill-server 2>/dev/null" \
|
||||
0 1 15
|
||||
|
||||
fail() {
|
||||
echo "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Capture the outer pane and return the text the inner tmux drew.
|
||||
capture() {
|
||||
$TMUX capturep -p >$TMP || fail "capture failed"
|
||||
tr -d '[:space:]' <$TMP
|
||||
}
|
||||
|
||||
$TMUX kill-server 2>/dev/null
|
||||
$TMUX2 kill-server 2>/dev/null
|
||||
|
||||
# Inner: a window which draws nothing and a status line which is only the
|
||||
# animation, so the outer capture is the current frame on its own.
|
||||
$TMUX2 new -d -x30 -y8 "sh -c 'exec sleep 100'" || exit 1
|
||||
$TMUX2 set -g window-size latest || exit 1
|
||||
i=1
|
||||
while [ $i -le 4 ]; do
|
||||
$TMUX2 set -g status-format[$i] "" || exit 1
|
||||
i=$((i + 1))
|
||||
done
|
||||
$TMUX2 set -g status-format[0] "#{A/7:AAAA,BBBB,CCCC}" || exit 1
|
||||
|
||||
$TMUX new -d -x30 -y8 || exit 1
|
||||
$TMUX set -g status off || exit 1
|
||||
$TMUX set -g window-size manual || exit 1
|
||||
$TMUX set -g default-terminal "tmux-256color" || exit 1
|
||||
$TMUX send -l "$TMUX2 attach" || exit 1
|
||||
$TMUX send Enter || exit 1
|
||||
sleep 1
|
||||
|
||||
# Sample the frames. Every sample must be one of the frames and at least two
|
||||
# different ones must be seen; status-interval is 15 seconds so only the cycle
|
||||
# timer can have redrawn the status line.
|
||||
seen=""
|
||||
i=0
|
||||
while [ $i -lt 4 ]; do
|
||||
[ $i -eq 0 ] || sleep 1
|
||||
frame=$(capture)
|
||||
case "$frame" in
|
||||
AAAA|BBBB|CCCC) ;;
|
||||
*) fail "status line is '$frame', not a frame" ;;
|
||||
esac
|
||||
case " $seen " in
|
||||
*" $frame "*) ;;
|
||||
*) seen="$seen $frame" ;;
|
||||
esac
|
||||
i=$((i + 1))
|
||||
done
|
||||
set -- $seen
|
||||
[ $# -ge 2 ] || fail "status line did not animate, only saw$seen"
|
||||
|
||||
# Outside a status format there is no animation at all.
|
||||
out=$($TMUX2 display-message -p "#{A:ZZZZ,YYYY}") || exit 1
|
||||
[ -z "$out" ] || fail "display-message gave '$out', want empty"
|
||||
out=$($TMUX2 list-panes -F "#{A:ZZZZ,YYYY}") || exit 1
|
||||
[ -z "$out" ] || fail "list-panes gave '$out', want empty"
|
||||
|
||||
# Nor inside #(), where the frames would change the command on every frame.
|
||||
$TMUX2 set -g status-format[0] '#(echo "[#{A:ZZZZ,YYYY}]")' || exit 1
|
||||
sleep 1
|
||||
out=$(capture)
|
||||
case "$out" in
|
||||
*ZZZZ*|*YYYY*) fail "job command saw a frame: '$out'" ;;
|
||||
*"[]"*) ;;
|
||||
*) fail "job output is '$out', want []" ;;
|
||||
esac
|
||||
|
||||
# Empty frames and a missing or bad count must not upset the server.
|
||||
for f in '#{A:}' '#{A/0:a,b}' '#{A/x:a,b}' '#{A:,,,}'; do
|
||||
$TMUX2 set -g status-format[0] "$f" || fail "setting $f failed"
|
||||
$TMUX2 has-session >/dev/null 2>&1 || fail "server lost with $f"
|
||||
done
|
||||
sleep 1
|
||||
$TMUX2 has-session >/dev/null 2>&1 || fail "server lost after empty frames"
|
||||
|
||||
exit 0
|
||||
@@ -85,6 +85,24 @@ echo "$shown" | grep -q '^session-created\[0\]' ||
|
||||
fail "missing first array item: $shown"
|
||||
echo "$shown" | grep -q '^session-created\[1\]' ||
|
||||
fail "missing second array item: $shown"
|
||||
shown=$($TMUX show-hooks -gF '#{option_name}:#{hook_fire_count}:#{t/p:hook_fire_time}' \
|
||||
session-created) ||
|
||||
fail "show-hooks -gF failed"
|
||||
echo "$shown" | grep -q '^session-created:[1-9][0-9]*:[^-][^ ]*$' ||
|
||||
fail "missing hook fire formats: $shown"
|
||||
|
||||
# User hooks are options, but show-hooks should list only registered @ hooks
|
||||
# and not ordinary user options.
|
||||
$TMUX set -g @not-a-hook value || fail "set @not-a-hook failed"
|
||||
$TMUX set-hook -g @user-hook 'lsk' ||
|
||||
fail "set-hook @user-hook failed"
|
||||
shown=$($TMUX show-hooks -g) ||
|
||||
fail "show-hooks -g all failed"
|
||||
echo "$shown" | grep -q '^@user-hook lsk$' ||
|
||||
fail "missing user hook: $shown"
|
||||
if echo "$shown" | grep -q '^@not-a-hook '; then
|
||||
fail "show-hooks listed user option: $shown"
|
||||
fi
|
||||
|
||||
# Unsetting removes the whole hook.
|
||||
$TMUX set-hook -gu session-created || fail "set-hook -gu failed"
|
||||
|
||||
@@ -174,3 +174,17 @@ check_raw_matches()
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
check_raw_no_matches()
|
||||
{
|
||||
name=$1
|
||||
shift
|
||||
|
||||
capture_raw "$name" >"$TMP"
|
||||
for unexpected in "$@"; do
|
||||
if grep -Eq "$unexpected" "$TMP"; then
|
||||
printf "%s\n" "$unexpected" >"$EXP"
|
||||
fail "$name raw unexpected"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
@@ -22,6 +22,16 @@ check_capture rename 'X'
|
||||
start_pane apc-title 20 3 '\033_test-title\033\\X\n'
|
||||
check_capture apc-title 'X'
|
||||
|
||||
cmd='i=0; while [ "$i" -lt 12 ]; do '
|
||||
cmd="${cmd}printf '\\033[22;0t'; i=\$((i + 1)); done; "
|
||||
cmd="${cmd}printf X; sleep 2"
|
||||
start_cmd title-stack 20 3 "$cmd"
|
||||
check_capture title-stack 'X'
|
||||
$TMUX respawn-pane -k -t title-stack: \
|
||||
"printf '\\033[22;0tY'; sleep 2" || exit 1
|
||||
sleep 0.3
|
||||
check_capture title-stack 'Y'
|
||||
|
||||
$TMUX kill-server 2>/dev/null
|
||||
sleep 0.1
|
||||
$TMUX new-session -d -x 20 -y 3 -s osc52 "sleep 2" || exit 1
|
||||
|
||||
@@ -41,5 +41,9 @@ start_pane combining-left 10 3 '\314\201A\n'
|
||||
check_capture combining-left 'A'
|
||||
check_cursor combining-left '0,1'
|
||||
|
||||
start_pane combining-overflow 10 3 'u\314\245\314\245\314\245\314\245\314\245\314\245\314\245\314\245\314\245\314\245\314\245\314\245\314\245\314\245\314\245\314\245\n'
|
||||
check_raw_matches combining-overflow '^ C 0,0 data=\(1,31,u'
|
||||
check_raw_no_matches combining-overflow 'data=\(0,'
|
||||
|
||||
$TMUX kill-server 2>/dev/null
|
||||
exit $exit_status
|
||||
|
||||
66
regress/menu-mouse.sh
Normal file
66
regress/menu-mouse.sh
Normal file
@@ -0,0 +1,66 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Check that mouse selection in an active menu uses the correct coordinates
|
||||
# when the status line is at the top.
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
# click COL ROW
|
||||
#
|
||||
# Write an SGR mouse press then release at a 1-based position to the outer pane
|
||||
# holding the inner client.
|
||||
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
|
||||
}
|
||||
|
||||
cleanup
|
||||
|
||||
$TMUX new-session -d -s inner -x 80 -y 24 'sleep 100' || exit 1
|
||||
$TMUX set -g mouse on || exit 1
|
||||
$TMUX set -g status-position top || exit 1
|
||||
$TMUX set -g @menu-choice '' || 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."
|
||||
|
||||
$TMUX display-menu -M -x 5 -y 7 \
|
||||
"First item" f "set -g @menu-choice first" \
|
||||
"Second item" s "set -g @menu-choice second" || exit 1
|
||||
sleep 1
|
||||
|
||||
# -y is the bottom of the menu, so with four menu lines this puts the menu at
|
||||
# window y=3. The first item is then at window y=4. With one status line at the
|
||||
# top, this is terminal row 6 in SGR's 1-based coordinates.
|
||||
click 8 6
|
||||
|
||||
choice=$($TMUX show -gv @menu-choice 2>/dev/null)
|
||||
[ "$choice" = "first" ] || fail "got '$choice', expected 'first'"
|
||||
|
||||
cleanup
|
||||
exit 0
|
||||
@@ -269,6 +269,22 @@ test_customize_mode()
|
||||
assert_alive "customize-mode exit"
|
||||
}
|
||||
|
||||
test_customize_break_pane()
|
||||
{
|
||||
start_client option-break
|
||||
side=$($TMUX split-window -d -P -F '#{pane_id}' \
|
||||
-t option-break:0 'cat') || fail "customize split failed"
|
||||
|
||||
$TMUX customize-mode -t "$side" || fail "customize-mode failed"
|
||||
wait_mode "$side" 1
|
||||
$TMUX break-pane -d -s "$side" || fail "customize break-pane failed"
|
||||
|
||||
assert_alive "customize-mode break-pane"
|
||||
wait_mode "$side" 1
|
||||
$TMUX send-keys -t "$side" q || fail "customize-mode quit failed"
|
||||
wait_mode "$side" 0
|
||||
}
|
||||
|
||||
test_copy_mode()
|
||||
{
|
||||
start_client copy-a 'i=0; while [ $i -lt 200 ]; do echo "copy mutation line $i"; i=$((i + 1)); done; cat'
|
||||
@@ -302,6 +318,7 @@ test_choose_tree
|
||||
test_choose_buffer
|
||||
test_choose_client
|
||||
test_customize_mode
|
||||
test_customize_break_pane
|
||||
test_copy_mode
|
||||
cleanup
|
||||
exit 0
|
||||
|
||||
@@ -49,11 +49,33 @@ drag()
|
||||
sleep 1
|
||||
}
|
||||
|
||||
# right_click COL ROW KEY
|
||||
#
|
||||
# Open the right-click menu at a 1-based position and choose KEY.
|
||||
right_click()
|
||||
{
|
||||
col="$1"
|
||||
row="$2"
|
||||
key="$3"
|
||||
|
||||
seq=$(printf '\033[<2;%s;%sM' "$col" "$row")
|
||||
$TMUX2 send-keys -t "$OUTER" -l "$seq" 2>/dev/null
|
||||
sleep 0.2
|
||||
$TMUX2 send-keys -t "$OUTER" "$key" 2>/dev/null
|
||||
sleep 1
|
||||
}
|
||||
|
||||
cleanup
|
||||
|
||||
$TMUX new-session -d -s inner -x 80 -y 24 || exit 1
|
||||
$TMUX set -g mouse on
|
||||
$TMUX set -g default-command 'sleep 100'
|
||||
keys=$($TMUX list-keys -T root -F '#{key_command}' MouseDown3Empty) ||
|
||||
fail "list MouseDown3Empty failed"
|
||||
case "$keys" in
|
||||
*"New Pane"*"New Window"*) ;;
|
||||
*) fail "missing empty-area menu binding: $keys" ;;
|
||||
esac
|
||||
|
||||
$TMUX2 new-session -d -x 80 -y 24 "$TMUX attach -t inner" || exit 1
|
||||
sleep 1
|
||||
@@ -80,6 +102,17 @@ $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"
|
||||
|
||||
# Right-click in empty window space and choose New Pane. This creates a new
|
||||
# floating pane and tiles it.
|
||||
right_click 20 8 p
|
||||
|
||||
id=$($TMUX list-panes -F '#{?pane_active,#{pane_id},}' | tail -n 1)
|
||||
[ -n "$id" ] || fail "no pane created from empty-area menu"
|
||||
[ "$id" != "$BASE" ] || fail "empty-area menu did not create a new pane"
|
||||
|
||||
must_equal "$($TMUX display-message -p -t "$id" '#{pane_floating_flag}')" 0
|
||||
$TMUX kill-pane -t "$id" || fail "kill empty-area menu pane failed"
|
||||
|
||||
# Drag in empty window space with no tiled pane underneath.
|
||||
drag 40 10 50 15
|
||||
|
||||
@@ -92,6 +125,12 @@ 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-pane -PF '#{pane_id}' 'sleep 100') ||
|
||||
fail "new pane from only floating panes failed"
|
||||
$TMUX join-pane -t "$TILED" ||
|
||||
fail "tile new pane from only floating panes failed"
|
||||
must_equal "$($TMUX display-message -p -t "$TILED" '#{pane_floating_flag}')" 0
|
||||
|
||||
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"
|
||||
|
||||
@@ -140,6 +140,24 @@ update-environment[notify] EEE"
|
||||
# sorts by ascending numeric key and keeps the gap at [1].
|
||||
check_ok set -g status-format ""
|
||||
check_array "-g status-format" "status-format"
|
||||
out=$($TMUX show -gF \
|
||||
'#{option_name}:#{option_has_value}:#{option_value}:#{option_is_array}:#{option_has_array_key}:#{option_array_key}' \
|
||||
status-format 2>&1)
|
||||
[ "$out" = "status-format:0::1:0:" ] || {
|
||||
echo "show -F empty status-format failed."
|
||||
echo "Expected: 'status-format:0::1:0:'"
|
||||
echo "But got: '$out'"
|
||||
exit 1
|
||||
}
|
||||
out=$($TMUX show -gvF \
|
||||
'#{option_name}:#{option_value_only}:#{option_has_value}:#{option_value}' \
|
||||
status-format 2>&1)
|
||||
[ "$out" = "status-format:1:0:" ] || {
|
||||
echo "show -vF empty status-format failed."
|
||||
echo "Expected: 'status-format:1:0:'"
|
||||
echo "But got: '$out'"
|
||||
exit 1
|
||||
}
|
||||
check_ok set -g status-format[5] "five"
|
||||
check_ok set -g status-format[0] "zero"
|
||||
check_ok set -g status-format[2] "two"
|
||||
@@ -154,6 +172,21 @@ status-format[5] five
|
||||
status-format[foo-bar] foo-bar
|
||||
status-format[xterm-256color] xterm
|
||||
status-format[zoom] zoom"
|
||||
out=$($TMUX show -gF \
|
||||
'#{option_name}:#{option_array_key}:#{option_is_array}:#{option_has_value}:#{option_has_array_key}:#{option_value}' \
|
||||
status-format 2>&1)
|
||||
[ "$out" = "$(printf '%s' 'status-format:0:1:1:1:zero
|
||||
status-format:1:1:1:1:one
|
||||
status-format:2:1:1:1:two
|
||||
status-format:5:1:1:1:five
|
||||
status-format:foo-bar:1:1:1:foo-bar
|
||||
status-format:xterm-256color:1:1:1:xterm
|
||||
status-format:zoom:1:1:1:zoom')" ] || {
|
||||
echo "show -F status-format failed."
|
||||
echo "Expected formatted array output"
|
||||
echo "But got:"; printf '%s\n' "$out"
|
||||
exit 1
|
||||
}
|
||||
check_value "-gv status-format[01]" "one"
|
||||
check_ok set -gu status-format[zoom]
|
||||
check_value "-gv status-format[zoom]" ""
|
||||
|
||||
@@ -168,6 +168,30 @@ check_ok set -g @str "foo"
|
||||
check_ok set -ga @str "bar"
|
||||
check_value "-gv @str" "foobar"
|
||||
|
||||
# --- show -F custom format ------------------------------------------------
|
||||
#
|
||||
# show-options and show-hooks use a format for their output, with the default
|
||||
# format preserving the traditional output.
|
||||
check_ok set -g @str "two words"
|
||||
check_value "-g @str" '@str "two words"'
|
||||
out=$($TMUX show -gF \
|
||||
'#{option_name}=#{option_value}=#{option_is_user}=#{option_is_string}=#{option_value_only}' \
|
||||
@str 2>&1)
|
||||
[ "$out" = "@str=two words=1=1=0" ] || {
|
||||
echo "show -F @str failed."
|
||||
echo "Expected: '@str=two words=1=1=0'"
|
||||
echo "But got: '$out'"
|
||||
exit 1
|
||||
}
|
||||
out=$($TMUX show -gvF '#{option_name}=#{option_value}=#{option_value_only}' \
|
||||
@str 2>&1)
|
||||
[ "$out" = "@str=two words=1" ] || {
|
||||
echo "show -vF @str failed."
|
||||
echo "Expected: '@str=two words=1'"
|
||||
echo "But got: '$out'"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# --- -F expands at set time -----------------------------------------------
|
||||
#
|
||||
# With -F the value is expanded as a format once, at set time; without -F it is
|
||||
|
||||
@@ -192,13 +192,13 @@ check_fail "invalid window name: $(printf 'a\377b')" \
|
||||
break-pane -d -n "$(printf 'a\377b')" -s "$p1" -t P:
|
||||
|
||||
# join-pane can move a pane from one window to another without destroying
|
||||
# the source window if other panes remain. (On this branch move-pane is
|
||||
# reserved for floating panes, covered by floating-pane-geometry.sh.)
|
||||
# the source window if other panes remain. move-pane does the same when no
|
||||
# floating-pane movement flags are given.
|
||||
check_ok new-window -d -t P:5 -n other
|
||||
check_ok join-pane -d -s "$p1" -t P:5.0
|
||||
check_fmt 'P:5' '#{window_panes}' '2'
|
||||
check_fmt 'P:0' '#{window_panes}' '3'
|
||||
check_ok join-pane -d -v -s "$p1" -t "$p2"
|
||||
check_ok move-pane -d -v -s "$p1" -t "$p2"
|
||||
check_fmt 'P:0' '#{window_panes}' '4'
|
||||
check_fmt 'P:5' '#{window_panes}' '1'
|
||||
|
||||
|
||||
52
regress/pipe-pane.sh
Normal file
52
regress/pipe-pane.sh
Normal file
@@ -0,0 +1,52 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Tests of pipe-pane behaviour.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
LANG=C.UTF-8
|
||||
LC_ALL=C.UTF-8
|
||||
export TERM LANG LC_ALL
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
fail()
|
||||
{
|
||||
echo "$1"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
exit 1
|
||||
}
|
||||
|
||||
check_ok()
|
||||
{
|
||||
if ! $TMUX "$@"; then
|
||||
fail "Command failed: $*"
|
||||
fi
|
||||
}
|
||||
|
||||
check_alive()
|
||||
{
|
||||
if [ "$($TMUX display-message -p alive 2>&1)" != "alive" ]; then
|
||||
fail "Server died"
|
||||
fi
|
||||
}
|
||||
|
||||
# A pipe-pane -I child may write after the pane process has exited. With
|
||||
# remain-on-exit, the pane stays around but its bufferevent has been freed.
|
||||
check_ok new-session -d -s pipe -x 80 -y 24 'sleep 1'
|
||||
check_ok set-option -t pipe:0 remain-on-exit on
|
||||
check_ok pipe-pane -t pipe:0.0 -I 'sleep 2; printf x'
|
||||
|
||||
i=0
|
||||
while [ "$($TMUX display-message -p -t pipe:0.0 '#{pane_dead}')" != "1" ]; do
|
||||
i=$((i + 1))
|
||||
[ "$i" -gt 50 ] && fail "Pane did not die"
|
||||
sleep 0.1
|
||||
done
|
||||
|
||||
sleep 2
|
||||
check_alive
|
||||
|
||||
$TMUX kill-server 2>/dev/null
|
||||
@@ -11,7 +11,7 @@ TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
TMP=$(mktemp)
|
||||
trap "rm -f $TMP" 0 1 15
|
||||
trap "$TMUX kill-server 2>/dev/null; rm -f $TMP" 0 1 15
|
||||
|
||||
$TMUX -f/dev/null new -d "$TMUX run 'echo foo' >$TMP; sleep 10" || exit 1
|
||||
sleep 1 && [ "$(cat $TMP)" = "foo" ] || exit 1
|
||||
@@ -20,6 +20,15 @@ $TMUX -f/dev/null new -d "$TMUX run -t: 'echo foo' >$TMP; sleep 10" || exit 1
|
||||
sleep 1 && [ "$(cat $TMP)" = "" ] || exit 1
|
||||
[ "$($TMUX display -p '#{pane_mode}')" = "view-mode" ] || exit 1
|
||||
|
||||
$TMUX -f/dev/null new -d -s t1 'sleep 10' || exit 1
|
||||
$TMUX -f/dev/null run -d 1 'echo delayed' >$TMP 2>&1 &
|
||||
pid=$!
|
||||
sleep 0.2
|
||||
kill -9 "$pid" 2>/dev/null
|
||||
wait "$pid" 2>/dev/null
|
||||
sleep 2
|
||||
$TMUX has-session -t t1 || exit 1
|
||||
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
exit 0
|
||||
|
||||
63
regress/screen-redraw-fill-character.sh
Normal file
63
regress/screen-redraw-fill-character.sh
Normal file
@@ -0,0 +1,63 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Exercise fill-character as a format. The window is smaller than the attached
|
||||
# client so OUTSIDE spans exist, then the only tiled pane is removed so EMPTY
|
||||
# spans exist inside the window around a floating pane.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
LC_ALL=C.UTF-8
|
||||
export TERM LC_ALL
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
|
||||
|
||||
TMP=$(mktemp)
|
||||
trap "rm -f $TMP; $TMUX kill-server 2>/dev/null; $TMUX2 kill-server 2>/dev/null" \
|
||||
0 1 15
|
||||
|
||||
fail() {
|
||||
echo "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
must_equal() {
|
||||
if [ "$1" != "$2" ]; then
|
||||
fail "expected '$2', got '$1'"
|
||||
fi
|
||||
}
|
||||
|
||||
$TMUX kill-server 2>/dev/null
|
||||
$TMUX2 kill-server 2>/dev/null
|
||||
|
||||
$TMUX2 new -d -x40 -y12 "sh -c 'printf base; exec sleep 100'" || exit 1
|
||||
$TMUX2 set -g status off || exit 1
|
||||
$TMUX2 set -g window-size manual || exit 1
|
||||
$TMUX2 setw fill-character '#{?is_inside,I,#{?is_outside,O,X}}' || exit 1
|
||||
$TMUX2 resizew -x28 -y8 || exit 1
|
||||
$TMUX2 new-pane -x12 -y4 -X8 -Y2 "sh -c 'printf FLOAT; exec sleep 100'" || exit 1
|
||||
tiled=$($TMUX2 list-panes -F '#{pane_floating_flag} #{pane_id}' | \
|
||||
awk '$1==0{print $2; exit}') || exit 1
|
||||
$TMUX2 kill-pane -t "$tiled" || exit 1
|
||||
|
||||
$TMUX new -d -x40 -y12 || exit 1
|
||||
$TMUX set -g status off || exit 1
|
||||
$TMUX set -g window-size manual || exit 1
|
||||
$TMUX set -g default-terminal "tmux-256color" || exit 1
|
||||
$TMUX send -l "$TMUX2 attach" || exit 1
|
||||
$TMUX send Enter || exit 1
|
||||
sleep 1
|
||||
|
||||
$TMUX capturep -p >$TMP || exit 1
|
||||
|
||||
must_equal "$(sed -n '1p' $TMP | cut -c1-28)" \
|
||||
"IIIIIIIIIIIIIIIIIIIIIIIIIIII"
|
||||
must_equal "$(sed -n '1p' $TMP | cut -c29-40)" "OOOOOOOOOOOO"
|
||||
must_equal "$(sed -n '9p' $TMP)" "OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO"
|
||||
|
||||
if grep -q X "$TMP"; then
|
||||
fail "fill-character used neither inside nor outside"
|
||||
fi
|
||||
|
||||
exit 0
|
||||
@@ -125,6 +125,13 @@ new_scene 28 8
|
||||
$TMUX2 new-pane -x16 -y5 -X18 -Y4 "sh -c 'printf OUT; exec sleep 100'" || exit 1
|
||||
compare floating-outside
|
||||
|
||||
# Floating pane whose right border is exactly on the window right edge. The
|
||||
# floating pane is clipped so the tiled pane's right border remains unbroken.
|
||||
new_scene 28 8
|
||||
$TMUX2 respawnp -k "sh -c 'i=0; while [ \$i -lt 8 ]; do printf \"%02d:abcdefghijklmnopq\n\" \$i; i=\$((i + 1)); done; exec sleep 100'" || exit 1
|
||||
$TMUX2 new-pane -x10 -y5 -X19 -Y2 "sh -c 'printf EDGE; exec sleep 100'" || exit 1
|
||||
compare floating-clip-window-edge
|
||||
|
||||
# Floating pane clipped at the top-left corner (negative offsets).
|
||||
new_scene 40 12
|
||||
$TMUX2 new-pane -x18 -y6 -X-4 -Y-2 "sh -c 'printf TL; exec sleep 100'" || exit 1
|
||||
@@ -208,9 +215,9 @@ $TMUX2 new-pane -x20 -y6 -X8 -Y7 "sh -c 'printf OVERST; exec sleep 100'" || exit
|
||||
compare floating-over-status
|
||||
|
||||
# A window left with only a floating pane: killing the single tiled pane removes
|
||||
# it from the layout, so the area it occupied is no longer owned by any pane and
|
||||
# is drawn as EMPTY cells (middle dots) around the float. This is the only way to
|
||||
# produce a REDRAW_SPAN_EMPTY span.
|
||||
# it from the layout, so the area it occupied is no longer owned by any pane.
|
||||
# Use a visible fill character for the EMPTY cells around the float.
|
||||
$TMUX2 set -g fill-character '#[acs]~' || exit 1
|
||||
new_scene 40 12
|
||||
$TMUX2 new-pane -x20 -y6 -X8 -Y3 "sh -c 'printf FLOAT; exec sleep 100'" || exit 1
|
||||
tiled=$($TMUX2 list-panes -F '#{pane_floating_flag} #{pane_id}' | \
|
||||
@@ -218,4 +225,11 @@ tiled=$($TMUX2 list-panes -F '#{pane_floating_flag} #{pane_id}' | \
|
||||
$TMUX2 kill-pane -t "$tiled" || exit 1
|
||||
compare floating-empty
|
||||
|
||||
# Same after pane-border-lines is set to none: empty window background is not a
|
||||
# pane border and should still use the dotted window fill.
|
||||
new_scene 40 12
|
||||
$TMUX2 breakp -W || exit 1
|
||||
$TMUX2 set pane-border-lines none || exit 1
|
||||
compare floating-empty-noborder
|
||||
|
||||
exit 0
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
01:abcdefghijklmnopq │···········
|
||||
02:abcdefghijklmnopq │···········
|
||||
03:abcdefghijklmnop┌────────│···········
|
||||
04:abcdefghijklmnop│EDGE │···········
|
||||
05:abcdefghijklmnop│ │···········
|
||||
06:abcdefghijklmnop│ │···········
|
||||
07:abcdefghijklmnop└────────│···········
|
||||
│···········
|
||||
────────────────────────────┘···········
|
||||
········································
|
||||
········································
|
||||
········································
|
||||
12
regress/screen-redraw-results/floating-empty-noborder.result
Normal file
12
regress/screen-redraw-results/floating-empty-noborder.result
Normal file
@@ -0,0 +1,12 @@
|
||||
········································
|
||||
········································
|
||||
····base ················
|
||||
···· ················
|
||||
···· ················
|
||||
········································
|
||||
········································
|
||||
········································
|
||||
········································
|
||||
········································
|
||||
········································
|
||||
|
||||
@@ -6,7 +6,7 @@ base │···········
|
||||
│OUT │···········
|
||||
│ │···········
|
||||
│ │···········
|
||||
──────────────────└─────────┘···········
|
||||
────────────────────────────┘···········
|
||||
········································
|
||||
········································
|
||||
········································
|
||||
|
||||
49
regress/server-socket-error.sh
Executable file
49
regress/server-socket-error.sh
Executable file
@@ -0,0 +1,49 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Server socket creation failures must produce a nonzero client exit status.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
export TERM
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
|
||||
# Root bypasses directory mode bits, so it cannot trigger this failure.
|
||||
[ "$(id -u)" -eq 0 ] && exit 0
|
||||
|
||||
TMP=$(mktemp -d) || exit 1
|
||||
SOCKET="$TMP/ro/socket"
|
||||
|
||||
cleanup()
|
||||
{
|
||||
chmod 700 "$TMP/ro" 2>/dev/null
|
||||
rm -rf "$TMP"
|
||||
}
|
||||
|
||||
fail()
|
||||
{
|
||||
echo "$1" >&2
|
||||
cleanup
|
||||
exit 1
|
||||
}
|
||||
|
||||
mkdir "$TMP/ro" || exit 1
|
||||
chmod 500 "$TMP/ro" || exit 1
|
||||
trap cleanup 0 1 15
|
||||
|
||||
check_failure()
|
||||
{
|
||||
output=$($TEST_TMUX -S "$SOCKET" -f/dev/null "$@" 2>&1)
|
||||
retval=$?
|
||||
|
||||
[ "$retval" -ne 0 ] || fail "tmux $* exited zero: $output"
|
||||
case "$output" in
|
||||
"error creating $SOCKET ("*) ;;
|
||||
*) fail "tmux $* produced unexpected error: $output" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
check_failure start-server
|
||||
check_failure new-session -d
|
||||
|
||||
exit 0
|
||||
@@ -67,10 +67,25 @@ shown=$($TMUX show-hooks -g -B @session-name) ||
|
||||
fail "show-hooks -B failed"
|
||||
[ "$shown" = '@session-name::#{session_name}' ] ||
|
||||
fail "unexpected show-hooks -B output: $shown"
|
||||
shown=$($TMUX show-hooks -g -BF \
|
||||
'#{option_name}:#{hook_monitor_target}:#{hook_monitor_format}:#{option_value}:#{option_is_hook}:#{option_is_user}' \
|
||||
@session-name) ||
|
||||
fail "show-hooks -BF failed"
|
||||
[ "$shown" = '@session-name::#{session_name}:@session-name::#{session_name}:1:1' ] ||
|
||||
fail "unexpected show-hooks -BF output: $shown"
|
||||
shown=$($TMUX show-hooks -g) ||
|
||||
fail "show-hooks -g failed"
|
||||
echo "$shown" | grep -q '^@session-name ' ||
|
||||
fail "show-hooks -g did not show monitor hook: $shown"
|
||||
assert_unchanged @seen 0
|
||||
|
||||
$TMUX rename-session two || fail "rename-session two failed"
|
||||
wait_for @seen '@session-name:two'
|
||||
shown=$($TMUX show-hooks -g -BF '#{hook_fire_count}:#{t/p:hook_fire_time}' \
|
||||
@session-name) ||
|
||||
fail "show-hooks -BF fire failed"
|
||||
echo "$shown" | grep -q '^1:[^-][^ ]*$' ||
|
||||
fail "unexpected show-hooks -BF fire output: $shown"
|
||||
|
||||
$TMUX set -g @seen-last 0 || fail "set @seen-last failed"
|
||||
$TMUX set-hook -g -B '@session-name::#{session_name}' \
|
||||
@@ -112,6 +127,11 @@ $TMUX set -g @pane-seen 0 || fail "set @pane-seen failed"
|
||||
$TMUX set-hook -g -B "@pane:%$pane_number:#{pane_width}" \
|
||||
'set -g @pane-seen "#{hook_session}:#{hook_window}:#{hook_window_index}:#{hook_pane}:#{hook_value}"' ||
|
||||
fail "set-hook -B pane selector failed"
|
||||
shown=$($TMUX show-hooks -g -BF \
|
||||
'#{option_name}:#{hook_monitor_target}:#{hook_monitor_format}' @pane) ||
|
||||
fail "show-hooks -BF pane failed"
|
||||
[ "$shown" = "@pane:%$pane_number:#{pane_width}" ] ||
|
||||
fail "unexpected show-hooks -BF pane output: $shown"
|
||||
assert_unchanged @pane-seen 0
|
||||
$TMUX set-hook -g -B "@pane:%$pane_number:#{@pane-value}" \
|
||||
'set -g @pane-seen "#{hook_session}:#{hook_window}:#{hook_window_index}:#{hook_pane}:#{hook_value}"' ||
|
||||
|
||||
136
regress/show-options-output.sh
Normal file
136
regress/show-options-output.sh
Normal file
@@ -0,0 +1,136 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Tests of the default output from show-options and show-hooks. This is
|
||||
# intended to guard the output compatibility when the implementation is changed
|
||||
# to use formats internally, so it deliberately does not use show -F.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
check_value()
|
||||
{
|
||||
out=$($TMUX show $1 2>&1)
|
||||
if [ "$out" != "$(printf '%s' "$2")" ]; then
|
||||
echo "show $1 failed."
|
||||
echo "Expected:"; printf '%s\n' "$2"
|
||||
echo "But got:"; printf '%s\n' "$out"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
check_hooks()
|
||||
{
|
||||
out=$($TMUX show-hooks $1 2>&1)
|
||||
if [ "$out" != "$(printf '%s' "$2")" ]; then
|
||||
echo "show-hooks $1 failed."
|
||||
echo "Expected:"; printf '%s\n' "$2"
|
||||
echo "But got:"; printf '%s\n' "$out"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
check_ok()
|
||||
{
|
||||
if ! $TMUX "$@"; then
|
||||
echo "Command failed (expected success): $*"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
assert_alive()
|
||||
{
|
||||
if [ "$($TMUX display-message -p alive)" != "alive" ]; then
|
||||
echo "Server died: $1"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
$TMUX new-session -d -s main -x 80 -y 24 || exit 1
|
||||
|
||||
# Scalar options: strings are escaped, non-strings are not, and -v suppresses
|
||||
# the option name.
|
||||
check_ok set -g @words "two words"
|
||||
check_value "-g @words" '@words "two words"'
|
||||
check_value "-gv @words" 'two words'
|
||||
check_ok set -g display-time 1234
|
||||
check_value "-g display-time" 'display-time 1234'
|
||||
check_value "-gv display-time" '1234'
|
||||
|
||||
# Inherited options shown with -A are marked with an asterisk.
|
||||
check_ok set -g status-left "GLOBAL"
|
||||
check_ok set -u status-left
|
||||
out=$($TMUX show -A 2>/dev/null | grep '^status-left\*')
|
||||
if [ "$out" != "status-left* GLOBAL" ]; then
|
||||
echo "show -A did not mark inherited status-left."
|
||||
echo "But got:"; printf '%s\n' "$out"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Arrays: an empty array prints just the name unless -v is used; populated
|
||||
# arrays include every key, including key 0.
|
||||
check_ok set -g status-format ""
|
||||
check_value "-g status-format" 'status-format'
|
||||
check_value "-gv status-format" ''
|
||||
out=$($TMUX show -A 2>/dev/null | grep '^status-format\*')
|
||||
if [ "$out" != "status-format*" ]; then
|
||||
echo "show -A did not mark inherited empty status-format."
|
||||
echo "But got:"; printf '%s\n' "$out"
|
||||
exit 1
|
||||
fi
|
||||
check_value "-A status-format" 'status-format'
|
||||
check_ok set -g update-environment "AAA BBB,CCC"
|
||||
check_value "-g update-environment" 'update-environment[0] AAA
|
||||
update-environment[1] BBB
|
||||
update-environment[2] CCC'
|
||||
check_value "-gv update-environment[0]" 'AAA'
|
||||
|
||||
# Hooks use the same array output style, and monitor hooks have their own
|
||||
# default output form.
|
||||
check_ok set-hook -g window-renamed[first] "display-message renamed"
|
||||
check_hooks "-g window-renamed" 'window-renamed[first] display-message renamed'
|
||||
check_ok set-hook -g -B '@monitor:%*:#{pane_width}'
|
||||
check_hooks "-g -B @monitor" '@monitor:%*:#{pane_width}'
|
||||
check_ok set-hook -g @user-hook "display-message user"
|
||||
check_hooks "-g @user-hook" '@user-hook "display-message user"'
|
||||
out=$($TMUX show -g 2>&1)
|
||||
echo "$out" | grep -q "^@monitor ''$" && {
|
||||
echo "show -g showed monitor hook without -H."
|
||||
echo "But got:"; printf '%s\n' "$out"
|
||||
exit 1
|
||||
}
|
||||
echo "$out" | grep -q '^@user-hook "display-message user"$' && {
|
||||
echo "show -g showed user hook without -H."
|
||||
echo "But got:"; printf '%s\n' "$out"
|
||||
exit 1
|
||||
}
|
||||
out=$($TMUX show -gH 2>&1)
|
||||
echo "$out" | grep -q "^@monitor ''$" || {
|
||||
echo "show -gH did not show monitor hook."
|
||||
echo "But got:"; printf '%s\n' "$out"
|
||||
exit 1
|
||||
}
|
||||
echo "$out" | grep -q '^@user-hook "display-message user"$' || {
|
||||
echo "show -gH did not show user hook."
|
||||
echo "But got:"; printf '%s\n' "$out"
|
||||
exit 1
|
||||
}
|
||||
out=$($TMUX show-hooks -g 2>&1)
|
||||
echo "$out" | grep -q "^@monitor ''$" || {
|
||||
echo "show-hooks -g did not show monitor hook."
|
||||
echo "But got:"; printf '%s\n' "$out"
|
||||
exit 1
|
||||
}
|
||||
echo "$out" | grep -q '^@user-hook "display-message user"$' || {
|
||||
echo "show-hooks -g did not show user hook."
|
||||
echo "But got:"; printf '%s\n' "$out"
|
||||
exit 1
|
||||
}
|
||||
|
||||
assert_alive "after show-options output tests"
|
||||
|
||||
$TMUX kill-server 2>/dev/null
|
||||
exit 0
|
||||
43
regress/tab-cell-background.sh
Executable file
43
regress/tab-cell-background.sh
Executable file
@@ -0,0 +1,43 @@
|
||||
#!/bin/sh
|
||||
|
||||
# When a write breaks up a wide character or a tab, the columns it covered must
|
||||
# keep the background colour, both in the grid and on the terminal. The second
|
||||
# server is attached to the first so that what is actually sent is checked, not
|
||||
# just what tmux has stored.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
|
||||
$TMUX2 kill-server 2>/dev/null
|
||||
|
||||
TMP=$(mktemp)
|
||||
trap "rm -f $TMP" 0 1 15
|
||||
|
||||
# 1. Write inside a tab. 2. Overwrite the right half of a wide character.
|
||||
# 3. Two tabs of different colours, the first partly deleted so that padding
|
||||
# from both ends up next to each other, then written over.
|
||||
# The sleep is so that the client is attached before anything is written and
|
||||
# the changes are sent as they happen rather than as one redraw.
|
||||
$TMUX2 -f/dev/null new -d -x24 -y3 "
|
||||
sleep 2
|
||||
printf '\033[44m\033[K\t\033[4GX'
|
||||
printf '\033[2;1H\033[44m\033[K\344\270\226\033[2GX'
|
||||
printf '\033[3;1H\033[42m\033[K\t\033[43m\033[K\t\033[0m\033[4G\033[8P\rx'
|
||||
cat" || exit 1
|
||||
$TMUX2 set -g status off || exit 1
|
||||
|
||||
$TMUX -f/dev/null new -x24 -y4 -d "$TMUX2 attach" || exit 1
|
||||
sleep 4
|
||||
|
||||
$TMUX capturep -pe -S0 -E2 >$TMP || exit 1
|
||||
$TMUX kill-server 2>/dev/null
|
||||
$TMUX2 kill-server 2>/dev/null
|
||||
|
||||
printf '\033[44m X\n X\n\033[49mx\033[42m \033[43m \033[49m\n' |
|
||||
cmp - $TMP || exit 1
|
||||
|
||||
exit 0
|
||||
@@ -34,6 +34,18 @@ captureen() {
|
||||
$TMUX capturep -peNS0 -E- >$TMP || exit 1
|
||||
}
|
||||
|
||||
timed() {
|
||||
if command -v timeout >/dev/null 2>&1; then
|
||||
timeout 5 "$@"
|
||||
else
|
||||
"$@"
|
||||
fi
|
||||
}
|
||||
|
||||
capture_timed() {
|
||||
timed $TMUX capturep -pS0 -E- >$TMP || exit 1
|
||||
}
|
||||
|
||||
check_line() {
|
||||
line=$1
|
||||
want=$2
|
||||
@@ -81,6 +93,8 @@ $TMUX2 neww -d \
|
||||
"printf '\033(0x\033(B'; exec sleep 100" || exit 1
|
||||
$TMUX2 neww -d \
|
||||
"awk 'BEGIN { for (i = 0; i < 1100; i++) printf \"a\" }'; exec sleep 100" || exit 1
|
||||
$TMUX2 neww -d \
|
||||
"printf 'u\314\245\314\245\314\245\314\245\314\245\314\245\314\245\314\245\314\245\314\245\314\245\314\245\314\245\314\245\314\245\314\245'; exec sleep 100" || exit 1
|
||||
$TMUX2 selectw -t:0 || exit 1
|
||||
|
||||
$TMUX -f/dev/null new -d -x20 -y6 || exit 1
|
||||
@@ -245,4 +259,12 @@ capture
|
||||
len=$(sed -n 1p $TMP | wc -c)
|
||||
[ "$len" -ge 1100 ] || fail "long same-style line was truncated"
|
||||
|
||||
# Too many combining marks on one base character must not leave a standalone
|
||||
# width-zero cell that can make tty_draw_line loop forever on redraw.
|
||||
$TMUX resizew -x20 -y6 || exit 1
|
||||
timed $TMUX2 selectw -t:13 || fail "zero-width overflow select hung"
|
||||
sleep 1
|
||||
capture_timed
|
||||
check_grep '^u'
|
||||
|
||||
exit 0
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: screen-redraw.c,v 1.153 2026/07/17 12:42:51 nicm Exp $ */
|
||||
/* $OpenBSD: screen-redraw.c,v 1.157 2026/07/24 08:49:23 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -320,7 +320,7 @@ redraw_reset_cell(struct redraw_build_ctx *bctx, u_int x, u_int y)
|
||||
struct window *w = bctx->w;
|
||||
|
||||
memset(bc, 0, sizeof *bc);
|
||||
if (bctx->ox + x <= w->sx && bctx->oy + y <= w->sy)
|
||||
if (bctx->ox + x < w->sx && bctx->oy + y < w->sy)
|
||||
bc->data.type = REDRAW_SPAN_EMPTY;
|
||||
else
|
||||
bc->data.type = REDRAW_SPAN_OUTSIDE;
|
||||
@@ -547,7 +547,8 @@ redraw_mark_border_cell(struct redraw_build_ctx *bctx, int wx, int wy,
|
||||
* merged.
|
||||
*/
|
||||
if (!floating) {
|
||||
if (bc->data.type == REDRAW_SPAN_EMPTY)
|
||||
if (bc->data.type == REDRAW_SPAN_EMPTY ||
|
||||
bc->data.type == REDRAW_SPAN_OUTSIDE)
|
||||
reset = 1;
|
||||
else if (bc->data.type != REDRAW_SPAN_BORDER)
|
||||
return;
|
||||
@@ -696,20 +697,22 @@ redraw_mark_pane_borders(struct redraw_build_ctx *bctx, struct window_pane *wp,
|
||||
bottom = wp->yoff + wp->sy;
|
||||
|
||||
mark_left = (left >= 0);
|
||||
mark_right = (right <= (int)bctx->w->sx);
|
||||
mark_top = (top >= 0);
|
||||
mark_bottom = (bottom <= (int)bctx->w->sy);
|
||||
|
||||
if (floating) {
|
||||
mark_right = (right < (int)bctx->w->sx);
|
||||
mark_bottom = (bottom < (int)bctx->w->sy);
|
||||
if (left < 0)
|
||||
left = 0;
|
||||
if (right > (int)bctx->w->sx)
|
||||
if (right >= (int)bctx->w->sx)
|
||||
right = (int)bctx->w->sx - 1;
|
||||
if (top < 0)
|
||||
top = 0;
|
||||
if (bottom > (int)bctx->w->sy)
|
||||
if (bottom >= (int)bctx->w->sy)
|
||||
bottom = (int)bctx->w->sy - 1;
|
||||
} else {
|
||||
mark_right = (right <= (int)bctx->w->sx);
|
||||
mark_bottom = (bottom <= (int)bctx->w->sy);
|
||||
if (pane_status == PANE_STATUS_TOP)
|
||||
mark_bottom = 0;
|
||||
else if (pane_status == PANE_STATUS_BOTTOM)
|
||||
@@ -1230,7 +1233,15 @@ redraw_draw_border_span(struct redraw_draw_ctx *dctx,
|
||||
|
||||
if (wp == NULL) {
|
||||
redraw_get_default_border_style(dctx, &gc, &pane_lines);
|
||||
window_get_border_cell(w, NULL, pane_lines, cell_type, &gc);
|
||||
if (span->data.type == REDRAW_SPAN_OUTSIDE)
|
||||
window_get_fill_cell(w, 0, &gc);
|
||||
else if (span->data.type == REDRAW_SPAN_EMPTY)
|
||||
window_get_fill_cell(w, 1, &gc);
|
||||
else {
|
||||
if (span->data.type != REDRAW_SPAN_BORDER)
|
||||
pane_lines = PANE_LINES_SINGLE;
|
||||
window_get_border_cell(NULL, pane_lines, cell_type, &gc);
|
||||
}
|
||||
} else {
|
||||
window_pane_get_border_style(wp, c, &gc);
|
||||
window_pane_get_border_cell(wp, cell_type, &gc);
|
||||
|
||||
114
screen-write.c
114
screen-write.c
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: screen-write.c,v 1.284 2026/07/20 11:16:33 nicm Exp $ */
|
||||
/* $OpenBSD: screen-write.c,v 1.286 2026/08/03 12:58:53 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -2480,15 +2480,62 @@ screen_write_collect_insert_clear(struct screen_write_ctx *ctx, u_int px,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear a cell that is being broken up by a write over part of it, keeping the
|
||||
* background so the columns it covered do not lose their colour.
|
||||
*/
|
||||
static void
|
||||
screen_write_clear_cell(struct grid *gd, u_int px, u_int py)
|
||||
{
|
||||
struct grid_cell gc;
|
||||
int bg;
|
||||
|
||||
grid_view_get_cell(gd, px, py, &gc);
|
||||
bg = gc.bg;
|
||||
|
||||
memcpy(&gc, &grid_default_cell, sizeof gc);
|
||||
gc.bg = bg;
|
||||
grid_view_set_cell(gd, px, py, &gc);
|
||||
}
|
||||
|
||||
/*
|
||||
* Insert clears for a range of cells already cleared in the grid. Adjacent
|
||||
* cells may have come from different characters and so have different
|
||||
* backgrounds, so this is done in runs of the same colour.
|
||||
*/
|
||||
static void
|
||||
screen_write_insert_clears(struct screen_write_ctx *ctx, u_int px, u_int nx)
|
||||
{
|
||||
struct screen *s = ctx->s;
|
||||
struct grid_cell gc;
|
||||
u_int xx, start = px, n;
|
||||
int bg = 8;
|
||||
|
||||
for (xx = px; xx < px + nx; xx++) {
|
||||
grid_view_get_cell(s->grid, xx, s->cy, &gc);
|
||||
if (xx == start)
|
||||
bg = gc.bg;
|
||||
else if (gc.bg != bg) {
|
||||
n = xx - start;
|
||||
log_debug("%s: from %u, size %u", __func__, start, n);
|
||||
screen_write_collect_insert_clear(ctx, start, n, bg);
|
||||
start = xx;
|
||||
bg = gc.bg;
|
||||
}
|
||||
}
|
||||
log_debug("%s: from %u, size %u", __func__, start, xx - start);
|
||||
screen_write_collect_insert_clear(ctx, start, xx - start, bg);
|
||||
}
|
||||
|
||||
/* Finish and store collected cells. */
|
||||
void
|
||||
screen_write_collect_end(struct screen_write_ctx *ctx)
|
||||
{
|
||||
struct screen *s = ctx->s;
|
||||
struct screen_write_citem *ci = ctx->item, *bci = NULL, *aci;
|
||||
struct screen_write_citem *ci = ctx->item;
|
||||
struct screen_write_cline *cl = &s->write_list[s->cy];
|
||||
struct grid_cell gc;
|
||||
u_int xx;
|
||||
u_int xx, bx = 0, bnx = 0;
|
||||
|
||||
if (ci->used == 0)
|
||||
return;
|
||||
@@ -2504,8 +2551,7 @@ screen_write_collect_end(struct screen_write_ctx *ctx)
|
||||
grid_view_get_cell(s->grid, xx, s->cy, &gc);
|
||||
if (~gc.flags & GRID_FLAG_PADDING)
|
||||
break;
|
||||
grid_view_set_cell(s->grid, xx, s->cy,
|
||||
&grid_default_cell);
|
||||
screen_write_clear_cell(s->grid, xx, s->cy);
|
||||
log_debug("%s: padding erased (before) at %u (cx %u)",
|
||||
__func__, xx, s->cx);
|
||||
}
|
||||
@@ -2514,20 +2560,12 @@ screen_write_collect_end(struct screen_write_ctx *ctx)
|
||||
grid_view_get_cell(s->grid, 0, s->cy, &gc);
|
||||
if (gc.data.width > 1 ||
|
||||
(gc.flags & GRID_FLAG_PADDING)) {
|
||||
grid_view_set_cell(s->grid, xx, s->cy,
|
||||
&grid_default_cell);
|
||||
screen_write_clear_cell(s->grid, xx, s->cy);
|
||||
log_debug("%s: padding erased (before) at %u "
|
||||
"(cx %u)", __func__, xx, s->cx);
|
||||
}
|
||||
}
|
||||
if (xx != s->cx) {
|
||||
bci = ctx->item;
|
||||
bci->type = CLEAR;
|
||||
bci->x = xx;
|
||||
bci->bg = 8;
|
||||
bci->used = s->cx - xx;
|
||||
log_debug("%s: padding erased (before): from %u, "
|
||||
"size %u", __func__, bci->x, bci->used);
|
||||
bx = xx;
|
||||
bnx = s->cx - xx;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2538,28 +2576,20 @@ screen_write_collect_end(struct screen_write_ctx *ctx)
|
||||
|
||||
grid_view_set_cells(s->grid, s->cx, s->cy, &ci->gc, cl->data + ci->x,
|
||||
ci->used);
|
||||
if (bci != NULL)
|
||||
screen_write_collect_insert(ctx, bci);
|
||||
if (bnx != 0)
|
||||
screen_write_insert_clears(ctx, bx, bnx);
|
||||
screen_write_set_cursor(ctx, s->cx + ci->used, -1);
|
||||
|
||||
for (xx = s->cx; xx < screen_size_x(s); xx++) {
|
||||
grid_view_get_cell(s->grid, xx, s->cy, &gc);
|
||||
if (~gc.flags & GRID_FLAG_PADDING)
|
||||
break;
|
||||
grid_view_set_cell(s->grid, xx, s->cy, &grid_default_cell);
|
||||
log_debug("%s: padding erased (after) at %u (cx %u)",
|
||||
__func__, xx, s->cx);
|
||||
}
|
||||
if (xx != s->cx) {
|
||||
aci = ctx->item;
|
||||
aci->type = CLEAR;
|
||||
aci->x = s->cx;
|
||||
aci->bg = 8;
|
||||
aci->used = xx - s->cx;
|
||||
log_debug("%s: padding erased (after): from %u, size %u",
|
||||
__func__, aci->x, aci->used);
|
||||
screen_write_collect_insert(ctx, aci);
|
||||
screen_write_clear_cell(s->grid, xx, s->cy);
|
||||
log_debug("%s: padding erased (after) at %u (cx %u)", __func__,
|
||||
xx, s->cx);
|
||||
}
|
||||
if (xx != s->cx)
|
||||
screen_write_insert_clears(ctx, s->cx, xx - s->cx);
|
||||
}
|
||||
|
||||
/* Write cell data, collecting if necessary. */
|
||||
@@ -2687,7 +2717,7 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
|
||||
*/
|
||||
for (xx = s->cx + 1; xx < s->cx + width; xx++) {
|
||||
log_debug("%s: new padding at %u,%u", __func__, xx, s->cy);
|
||||
grid_view_set_padding(gd, xx, s->cy);
|
||||
grid_view_set_padding(gd, xx, s->cy, gc->bg);
|
||||
skip = 0;
|
||||
}
|
||||
|
||||
@@ -2875,7 +2905,7 @@ screen_write_combine(struct screen_write_ctx *ctx, const struct grid_cell *gc)
|
||||
|
||||
/* Check if this combined character would be too long. */
|
||||
if (last.data.size + ud->size > sizeof last.data.data)
|
||||
return (0);
|
||||
return (zero_width);
|
||||
|
||||
/* Combining; flush any pending output. */
|
||||
screen_write_collect_flush(ctx, 0, __func__);
|
||||
@@ -2899,7 +2929,7 @@ screen_write_combine(struct screen_write_ctx *ctx, const struct grid_cell *gc)
|
||||
/* Set the new cell. */
|
||||
grid_view_set_cell(gd, cx - n, cy, &last);
|
||||
if (force_wide)
|
||||
grid_view_set_padding(gd, cx - 1, cy);
|
||||
grid_view_set_padding(gd, cx - 1, cy, last.bg);
|
||||
|
||||
/*
|
||||
* Check if all of this character is visible. No character will be
|
||||
@@ -2968,12 +2998,12 @@ screen_write_overwrite(struct screen_write_ctx *ctx, struct grid_cell *gc,
|
||||
if (~tmp_gc.flags & GRID_FLAG_PADDING)
|
||||
break;
|
||||
log_debug("%s: padding at %u,%u", __func__, xx, s->cy);
|
||||
grid_view_set_cell(gd, xx, s->cy, &grid_default_cell);
|
||||
screen_write_clear_cell(gd, xx, s->cy);
|
||||
}
|
||||
|
||||
/* Overwrite the character at the start of this padding. */
|
||||
log_debug("%s: character at %u,%u", __func__, xx, s->cy);
|
||||
grid_view_set_cell(gd, xx, s->cy, &grid_default_cell);
|
||||
screen_write_clear_cell(gd, xx, s->cy);
|
||||
done = 1;
|
||||
}
|
||||
|
||||
@@ -2991,17 +3021,7 @@ screen_write_overwrite(struct screen_write_ctx *ctx, struct grid_cell *gc,
|
||||
break;
|
||||
log_debug("%s: overwrite at %u,%u", __func__, xx,
|
||||
s->cy);
|
||||
if (gc->flags & GRID_FLAG_TAB) {
|
||||
memcpy(&tmp_gc, gc, sizeof tmp_gc);
|
||||
memset(tmp_gc.data.data, 0,
|
||||
sizeof tmp_gc.data.data);
|
||||
*tmp_gc.data.data = ' ';
|
||||
tmp_gc.data.width = tmp_gc.data.size =
|
||||
tmp_gc.data.have = 1;
|
||||
grid_view_set_cell(gd, xx, s->cy, &tmp_gc);
|
||||
} else
|
||||
grid_view_set_cell(gd, xx, s->cy,
|
||||
&grid_default_cell);
|
||||
screen_write_clear_cell(gd, xx, s->cy);
|
||||
done = 1;
|
||||
}
|
||||
}
|
||||
|
||||
4
screen.c
4
screen.c
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: screen.c,v 1.105 2026/06/29 18:17:28 nicm Exp $ */
|
||||
/* $OpenBSD: screen.c,v 1.107 2026/07/26 09:20:54 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -69,6 +69,7 @@ screen_free_titles(struct screen *s)
|
||||
|
||||
free(s->titles);
|
||||
s->titles = NULL;
|
||||
s->ntitles = 0;
|
||||
}
|
||||
|
||||
/* Create a new screen. */
|
||||
@@ -80,6 +81,7 @@ screen_init(struct screen *s, u_int sx, u_int sy, u_int hlimit)
|
||||
|
||||
s->title = xstrdup("");
|
||||
s->titles = NULL;
|
||||
s->ntitles = 0;
|
||||
s->path = NULL;
|
||||
|
||||
s->cstyle = SCREEN_CURSOR_DEFAULT;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: server-client.c,v 1.497 2026/07/17 12:42:51 nicm Exp $ */
|
||||
/* $OpenBSD: server-client.c,v 1.503 2026/08/17 07:56:56 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -36,7 +36,8 @@ static void server_client_check_window_resize(struct window *);
|
||||
static key_code server_client_check_mouse(struct client *, struct key_event *);
|
||||
static void server_client_repeat_timer(int, short, void *);
|
||||
static void server_client_click_timer(int, short, void *);
|
||||
static void server_client_check_exit(struct client *);
|
||||
static void server_client_check_exit(struct client *, int);
|
||||
static void server_client_exit_timer(int, short, void *);
|
||||
static void server_client_check_redraw(struct client *);
|
||||
static void server_client_check_modes(struct client *);
|
||||
static void server_client_set_title(struct client *);
|
||||
@@ -309,6 +310,7 @@ server_client_create(int fd)
|
||||
|
||||
evtimer_set(&c->repeat_timer, server_client_repeat_timer, c);
|
||||
evtimer_set(&c->click_timer, server_client_click_timer, c);
|
||||
evtimer_set(&c->exit_timer, server_client_exit_timer, c);
|
||||
|
||||
c->click_wp = -1;
|
||||
|
||||
@@ -527,6 +529,9 @@ server_client_lost(struct client *c)
|
||||
|
||||
evtimer_del(&c->repeat_timer);
|
||||
evtimer_del(&c->click_timer);
|
||||
evtimer_del(&c->exit_timer);
|
||||
if (event_initialized(&c->cycle_timer))
|
||||
evtimer_del(&c->cycle_timer);
|
||||
|
||||
key_bindings_unref_table(c->keytable);
|
||||
|
||||
@@ -1059,6 +1064,11 @@ have_event:
|
||||
c->tty.mouse_scrolling_flag = 0;
|
||||
c->tty.mouse_slider_mpos = -1;
|
||||
c->tty.mouse_last_pane = -1;
|
||||
if ((w->modal->flags & PANE_CLOSEONCLICK) &&
|
||||
(type == KEYC_TYPE_MOUSEDOWN ||
|
||||
type == KEYC_TYPE_SECONDCLICK ||
|
||||
type == KEYC_TYPE_TRIPLECLICK))
|
||||
server_kill_pane(w->modal);
|
||||
return (KEYC_UNKNOWN);
|
||||
}
|
||||
}
|
||||
@@ -1677,6 +1687,9 @@ server_client_handle_menu_key(struct client *c, struct key_event *event)
|
||||
memcpy(&new_event, event, sizeof new_event);
|
||||
if (KEYC_IS_MOUSE(event->key)) {
|
||||
m = &new_event.m;
|
||||
m->statusat = status_at_line(c);
|
||||
m->statuslines = status_line_size(c);
|
||||
|
||||
tty_window_offset(&c->tty, &ox, &oy, &sx, &sy);
|
||||
m->x += ox;
|
||||
if (m->statusat == 0) {
|
||||
@@ -1684,8 +1697,7 @@ server_client_handle_menu_key(struct client *c, struct key_event *event)
|
||||
m->x = m->y = UINT_MAX;
|
||||
else
|
||||
m->y = m->y - m->statuslines + oy;
|
||||
} else if (m->statusat > 0 &&
|
||||
m->y >= (u_int)m->statusat)
|
||||
} else if (m->statusat > 0 && m->y >= (u_int)m->statusat)
|
||||
m->x = m->y = UINT_MAX;
|
||||
else
|
||||
m->y += oy;
|
||||
@@ -1844,7 +1856,7 @@ server_client_loop(void)
|
||||
|
||||
/* Check clients. */
|
||||
TAILQ_FOREACH(c, &clients, entry) {
|
||||
server_client_check_exit(c);
|
||||
server_client_check_exit(c, 0);
|
||||
if (c->session != NULL && c->session->curw != NULL) {
|
||||
server_client_check_modes(c);
|
||||
server_client_check_redraw(c);
|
||||
@@ -2280,9 +2292,37 @@ server_client_click_timer(__unused int fd, __unused short events, void *data)
|
||||
c->flags &= ~(CLIENT_DOUBLECLICK|CLIENT_TRIPLECLICK);
|
||||
}
|
||||
|
||||
/* Check if client should be exited. */
|
||||
/* Start client exit timer. */
|
||||
static void
|
||||
server_client_check_exit(struct client *c)
|
||||
server_client_start_exit_timer(struct client *c)
|
||||
{
|
||||
struct timeval tv = { .tv_sec = 10 };
|
||||
|
||||
if (!evtimer_pending(&c->exit_timer, NULL))
|
||||
evtimer_add(&c->exit_timer, &tv);
|
||||
}
|
||||
|
||||
/* Exit timer has expired: stop waiting for the client. */
|
||||
static void
|
||||
server_client_exit_timer(__unused int fd, __unused short events, void *data)
|
||||
{
|
||||
struct client *c = data;
|
||||
|
||||
if (c->flags & (CLIENT_DEAD|CLIENT_SUSPENDED))
|
||||
return;
|
||||
|
||||
if (c->flags & CLIENT_EXITED) {
|
||||
log_debug("%s: %s took too long to exit", __func__, c->name);
|
||||
server_client_lost(c);
|
||||
} else if (c->flags & CLIENT_EXIT) {
|
||||
log_debug("%s: %s took too long to flush", __func__, c->name);
|
||||
server_client_check_exit(c, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if client should be exited, abandoning buffered output if forced. */
|
||||
static void
|
||||
server_client_check_exit(struct client *c, int force)
|
||||
{
|
||||
struct client_file *cf;
|
||||
const char *name = c->exit_session;
|
||||
@@ -2295,16 +2335,29 @@ server_client_check_exit(struct client *c)
|
||||
return;
|
||||
|
||||
if (c->flags & CLIENT_CONTROL) {
|
||||
control_discard(c);
|
||||
if (!control_all_done(c))
|
||||
return;
|
||||
if (force)
|
||||
control_discard_all(c);
|
||||
else {
|
||||
control_discard(c);
|
||||
if (!control_all_done(c)) {
|
||||
server_client_start_exit_timer(c);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
RB_FOREACH(cf, client_files, &c->files) {
|
||||
if (EVBUFFER_LENGTH(cf->buffer) != 0)
|
||||
return;
|
||||
if (!force) {
|
||||
RB_FOREACH(cf, client_files, &c->files) {
|
||||
if (EVBUFFER_LENGTH(cf->buffer) != 0) {
|
||||
server_client_start_exit_timer(c);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
c->flags |= CLIENT_EXITED;
|
||||
|
||||
evtimer_del(&c->exit_timer);
|
||||
server_client_start_exit_timer(c);
|
||||
|
||||
switch (c->exit_type) {
|
||||
case CLIENT_EXIT_RETURN:
|
||||
if (c->exit_message != NULL)
|
||||
@@ -2649,6 +2702,10 @@ server_client_dispatch(struct imsg *imsg, void *arg)
|
||||
if (file_write_ready(&c->files, imsg) != 0)
|
||||
goto bad;
|
||||
break;
|
||||
case MSG_WRITE_DONE:
|
||||
if (file_write_done(&c->files, imsg) != 0)
|
||||
goto bad;
|
||||
break;
|
||||
case MSG_READ:
|
||||
if (file_read_data(&c->files, imsg) != 0)
|
||||
goto bad;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: server-fn.c,v 1.148 2026/07/14 19:07:03 nicm Exp $ */
|
||||
/* $OpenBSD: server-fn.c,v 1.149 2026/07/27 14:25:46 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -368,6 +368,12 @@ server_destroy_pane(struct window_pane *wp, int notify)
|
||||
close(wp->fd);
|
||||
wp->fd = -1;
|
||||
}
|
||||
if (wp->pipe_fd != -1) {
|
||||
bufferevent_free(wp->pipe_event);
|
||||
wp->pipe_event = NULL;
|
||||
close(wp->pipe_fd);
|
||||
wp->pipe_fd = -1;
|
||||
}
|
||||
|
||||
remain_on_exit = options_get_number(wp->options, "remain-on-exit");
|
||||
if (remain_on_exit != 0 && (~wp->flags & PANE_STATUSREADY))
|
||||
|
||||
4
server.c
4
server.c
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: server.c,v 1.214 2026/07/10 13:38:45 nicm Exp $ */
|
||||
/* $OpenBSD: server.c,v 1.215 2026/08/03 10:07:57 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -240,6 +240,7 @@ server_start(struct tmuxproc *client, uint64_t flags, struct event_base *base,
|
||||
if (cause != NULL) {
|
||||
if (c != NULL) {
|
||||
c->exit_message = cause;
|
||||
c->retval = 1;
|
||||
c->flags |= CLIENT_EXIT;
|
||||
} else {
|
||||
fprintf(stderr, "%s\n", cause);
|
||||
@@ -398,6 +399,7 @@ server_accept(int fd, short events, __unused void *data)
|
||||
c = server_client_create(newfd);
|
||||
if (!server_acl_join(c)) {
|
||||
c->exit_message = xstrdup("access not allowed");
|
||||
c->retval = 1;
|
||||
c->flags |= CLIENT_EXIT;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: session.c,v 1.106 2026/07/13 13:01:14 nicm Exp $ */
|
||||
/* $OpenBSD: session.c,v 1.107 2026/08/05 07:35:35 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -710,8 +710,10 @@ session_group_synchronize1(struct session *target, struct session *s)
|
||||
/* Fix up the current window. */
|
||||
if (s->curw != NULL)
|
||||
s->curw = winlink_find_by_index(&s->windows, s->curw->idx);
|
||||
else
|
||||
else if (target->curw != NULL)
|
||||
s->curw = winlink_find_by_index(&s->windows, target->curw->idx);
|
||||
if (s->curw == NULL)
|
||||
s->curw = RB_MIN(winlinks, &s->windows);
|
||||
|
||||
/* Fix up the last window stack. */
|
||||
memcpy(&old_lastw, &s->lastw, sizeof old_lastw);
|
||||
|
||||
3
spawn.c
3
spawn.c
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: spawn.c,v 1.49 2026/07/15 13:02:33 nicm Exp $ */
|
||||
/* $OpenBSD: spawn.c,v 1.50 2026/07/23 09:38:27 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -222,6 +222,7 @@ spawn_window(struct spawn_context *sc, char **cause)
|
||||
w->name = xstrdup(sc->name);
|
||||
options_set_number(w->options, "automatic-rename", 0);
|
||||
}
|
||||
window_set_fill_cells(w);
|
||||
}
|
||||
|
||||
/* Switch to the new window if required. */
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: tmux-protocol.h,v 1.2 2023/01/06 07:09:27 nicm Exp $ */
|
||||
/* $OpenBSD: tmux-protocol.h,v 1.3 2026/08/17 07:56:56 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2021 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -67,7 +67,8 @@ enum msgtype {
|
||||
MSG_WRITE,
|
||||
MSG_WRITE_READY,
|
||||
MSG_WRITE_CLOSE,
|
||||
MSG_READ_CANCEL
|
||||
MSG_READ_CANCEL,
|
||||
MSG_WRITE_DONE
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -116,4 +117,9 @@ struct msg_write_close {
|
||||
int stream;
|
||||
};
|
||||
|
||||
struct msg_write_done {
|
||||
int stream;
|
||||
int error;
|
||||
};
|
||||
|
||||
#endif /* TMUX_PROTOCOL_H */
|
||||
|
||||
96
tmux.1
96
tmux.1
@@ -1,4 +1,4 @@
|
||||
.\" $OpenBSD: tmux.1,v 1.1144 2026/07/19 17:36:38 nicm Exp $
|
||||
.\" $OpenBSD: tmux.1,v 1.1155 2026/08/06 09:05:04 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 19 2026 $
|
||||
.Dd $Mdocdate: August 6 2026 $
|
||||
.Dt TMUX 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
@@ -356,6 +356,8 @@ Force redraw of the attached client.
|
||||
Select a new session for the attached client interactively.
|
||||
.It t
|
||||
Show the time.
|
||||
.It T
|
||||
Change the current pane title.
|
||||
.It w
|
||||
Choose the current window interactively.
|
||||
.It x
|
||||
@@ -2319,10 +2321,14 @@ the bottom and is paused while a selection is in progress.
|
||||
.Xc
|
||||
Turn off automatic refresh of the content from the pane.
|
||||
.It Xo
|
||||
.Ic refresh\-toggle
|
||||
.Ic refresh\-now
|
||||
(vi: r)
|
||||
(emacs: r)
|
||||
.Xc
|
||||
Refresh the content from the pane once.
|
||||
.It Xo
|
||||
.Ic refresh\-toggle
|
||||
.Xc
|
||||
Toggle automatic refresh of the content from the pane.
|
||||
.It Xo
|
||||
.Ic scroll\-bottom
|
||||
@@ -3638,7 +3644,7 @@ but a different format may be specified with
|
||||
.Fl F .
|
||||
.Tg newp
|
||||
.It Xo Ic new\-pane
|
||||
.Op Fl bdefhIkLMOPvWZ
|
||||
.Op Fl bCdefhIkLMOPvWZ
|
||||
.Op Fl B Ar border\-lines
|
||||
.Op Fl c Ar start\-directory
|
||||
.Op Fl e Ar environment
|
||||
@@ -3694,6 +3700,9 @@ 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.
|
||||
With
|
||||
.Fl C ,
|
||||
the modal pane is closed when the mouse is clicked outside it.
|
||||
.Pp
|
||||
The
|
||||
.Fl L
|
||||
@@ -4698,6 +4707,7 @@ the result would be the default background and a blue foreground.
|
||||
.Tg show
|
||||
.It Xo Ic show\-options
|
||||
.Op Fl AgHpqsvw
|
||||
.Op Fl F Ar format
|
||||
.Op Fl t Ar target\-pane
|
||||
.Op Ar option
|
||||
.Xc
|
||||
@@ -4725,6 +4735,10 @@ Global session or window options are listed if
|
||||
is used.
|
||||
.Fl v
|
||||
shows only the option value, not the name.
|
||||
With
|
||||
.Fl F ,
|
||||
.Ar format
|
||||
is used for each option.
|
||||
If
|
||||
.Fl q
|
||||
is set, no error will be returned if
|
||||
@@ -5716,8 +5730,13 @@ Set the time in milliseconds for which the indicators shown by the
|
||||
.Ic display\-panes
|
||||
command appear.
|
||||
.Pp
|
||||
.It Ic fill\-character Ar character
|
||||
Set the character used to fill areas of the terminal unused by a window.
|
||||
.It Ic fill\-character Ar format
|
||||
Set the format used to fill areas of the terminal unused by a pane.
|
||||
The format is expanded once for areas inside the window with
|
||||
.Ql is_inside
|
||||
set, and once for areas outside the window with
|
||||
.Ql is_outside
|
||||
set.
|
||||
.Pp
|
||||
.It Ic main\-pane\-height Ar height
|
||||
.It Ic main\-pane\-width Ar width
|
||||
@@ -5783,6 +5802,14 @@ see the
|
||||
.Sx STYLES
|
||||
section.
|
||||
.Pp
|
||||
.It Ic copy\-mode\-current\-line\-style Ar style
|
||||
Set the style of the line containing the cursor in copy mode.
|
||||
For how to specify
|
||||
.Ar style ,
|
||||
see the
|
||||
.Sx STYLES
|
||||
section.
|
||||
.Pp
|
||||
.It Ic copy\-mode\-line\-number\-style Ar style
|
||||
Set style of line numbers in copy mode.
|
||||
For how to specify
|
||||
@@ -6594,6 +6621,7 @@ run
|
||||
immediately.
|
||||
.It Xo Ic show\-hooks
|
||||
.Op Fl Bgpw
|
||||
.Op Fl F Ar format
|
||||
.Op Fl t Ar target\-pane
|
||||
.Op Ar hook
|
||||
.Xc
|
||||
@@ -6606,6 +6634,10 @@ With
|
||||
shows the subscriptions installed with
|
||||
.Em set\-hook
|
||||
.Fl B .
|
||||
With
|
||||
.Fl F ,
|
||||
.Ar format
|
||||
is used for each hook.
|
||||
.El
|
||||
.Sh MOUSE SUPPORT
|
||||
If the
|
||||
@@ -7020,6 +7052,20 @@ escape
|
||||
command arguments; with
|
||||
.Ql s
|
||||
use single quotes.
|
||||
.Pp
|
||||
Some variables such as
|
||||
.Ic pane_title
|
||||
and
|
||||
.Ic pane_path
|
||||
are set by escape sequences received from pane and may contain arbitrary
|
||||
content including shell metacharacters.
|
||||
.Ql q:\&
|
||||
should be used to escape these with commands passed to
|
||||
.Ic run\-shell ,
|
||||
.Ic if\-shell ,
|
||||
or
|
||||
.Ic pipe\-pane .
|
||||
.Pp
|
||||
.Ql E:\&
|
||||
will expand the format twice, for example
|
||||
.Ql #{E:status\-left}
|
||||
@@ -7184,6 +7230,26 @@ For example
|
||||
will be replaced by
|
||||
.Ql #{?pane_in_mode,yes,no} .
|
||||
.Pp
|
||||
The
|
||||
.Ql A
|
||||
modifier shows one of a list of frames in turn:
|
||||
.Ql #{A/count:frame,frame,...}
|
||||
or
|
||||
.Ql #{A:frame,frame,...} .
|
||||
Each frame is shown for
|
||||
.Ar count
|
||||
periods of 100 milliseconds before the next, wrapping back to the first after
|
||||
the last.
|
||||
If no count is given, one is used.
|
||||
The
|
||||
.Ql A
|
||||
modifier is expanded only in the status line and
|
||||
.Ic pane\-border\-format ;
|
||||
if present, the status line or pane borders are redrawn every 100 milliseconds.
|
||||
For example,
|
||||
.Ql #{A/2:|,/,\-,\e}
|
||||
shows the four frames in turn, changing every 200 milliseconds.
|
||||
.Pp
|
||||
The following variables are available, where appropriate:
|
||||
.Bl -column "XXXXXXXXXXXXXXXXXXX" "XXXXX"
|
||||
.It Sy "Variable name" Ta Sy "Alias" Ta Sy "Replaced with"
|
||||
@@ -7252,7 +7318,11 @@ The following variables are available, where appropriate:
|
||||
.It Li "history_size" Ta "" Ta "Size of history in lines"
|
||||
.It Li "hook" Ta "" Ta "Name of running hook, if any"
|
||||
.It Li "hook_client" Ta "" Ta "Name of client where hook was run, if any"
|
||||
.It Li "hook_fire_count" Ta "" Ta "Number of times hook has fired"
|
||||
.It Li "hook_fire_time" Ta "" Ta "Time hook last fired"
|
||||
.It Li "hook_last" Ta "" Ta "Previous value for a monitor hook"
|
||||
.It Li "hook_monitor_format" Ta "" Ta "Format for a monitor hook"
|
||||
.It Li "hook_monitor_target" Ta "" Ta "Target for a monitor hook"
|
||||
.It Li "hook_pane" Ta "" Ta "ID of pane where hook was run, if any"
|
||||
.It Li "hook_session" Ta "" Ta "ID of session where hook was run, if any"
|
||||
.It Li "hook_session_name" Ta "" Ta "Name of session where hook was run, if any"
|
||||
@@ -7294,6 +7364,17 @@ The following variables are available, where appropriate:
|
||||
.It Li "next_session_id" Ta "" Ta "Unique session ID for next new session"
|
||||
.It Li "next_window_active" Ta "" Ta "1 if next window in W: loop is active"
|
||||
.It Li "next_window_index" Ta "" Ta "Index of next window in W: loop"
|
||||
.It Li "option_array_key" Ta "" Ta "Array key, if option is an array item"
|
||||
.It Li "option_has_array_key" Ta "" Ta "1 if option array key is present"
|
||||
.It Li "option_has_value" Ta "" Ta "1 if option value is present"
|
||||
.It Li "option_is_array" Ta "" Ta "1 if option is an array"
|
||||
.It Li "option_is_hook" Ta "" Ta "1 if option is a hook"
|
||||
.It Li "option_is_parent" Ta "" Ta "1 if option is inherited"
|
||||
.It Li "option_is_string" Ta "" Ta "1 if option is a string"
|
||||
.It Li "option_is_user" Ta "" Ta "1 if option is a user option"
|
||||
.It Li "option_name" Ta "" Ta "Name of option"
|
||||
.It Li "option_value" Ta "" Ta "Value of option"
|
||||
.It Li "option_value_only" Ta "" Ta "1 if only option values will be shown"
|
||||
.It Li "origin_flag" Ta "" Ta "Pane origin flag"
|
||||
.It Li "pane_active" Ta "" Ta "1 if active pane"
|
||||
.It Li "pane_at_bottom" Ta "" Ta "1 if pane is at the bottom of window"
|
||||
@@ -7335,6 +7416,7 @@ The following variables are available, where appropriate:
|
||||
.It Li "pane_pid" Ta "" Ta "PID of first process in pane"
|
||||
.It Li "pane_pipe" Ta "" Ta "1 if pane is being piped"
|
||||
.It Li "pane_pipe_pid" Ta "" Ta "PID of pipe process, if any"
|
||||
.It Li "pane_private_modes" Ta "" Ta "List of enabled DECSET private modes"
|
||||
.It Li "pane_pb_state" Ta "" Ta "Pane progress bar state, one of hidden, normal, error, indeterminate, paused (can be set by application)"
|
||||
.It Li "pane_pb_progress" Ta "" Ta "Pane progress bar progress percentage (can be set by application)"
|
||||
.It Li "pane_right" Ta "" Ta "Right of pane"
|
||||
@@ -8690,7 +8772,7 @@ values are given, they are available as
|
||||
and so on.
|
||||
For example:
|
||||
.Bd -literal -offset indent
|
||||
run-shell 'myscript.sh #1 #2' foo bar
|
||||
run-shell 'myscript.sh #{1} #{2}' foo bar
|
||||
.Ed
|
||||
.Pp
|
||||
With
|
||||
|
||||
39
tmux.h
39
tmux.h
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: tmux.h,v 1.1411 2026/07/20 11:16:33 nicm Exp $ */
|
||||
/* $OpenBSD: tmux.h,v 1.1423 2026/08/17 07:56:56 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -1194,6 +1194,8 @@ struct window_mode {
|
||||
int flags;
|
||||
#define WINDOW_MODE_HIDE_PANE_STATUS 0x1
|
||||
#define WINDOW_MODE_NO_STACK 0x2
|
||||
#define WINDOW_MODE_FILL_WINDOW 0x4
|
||||
#define WINDOW_MODE_HIDE_SCROLLBARS 0x8
|
||||
|
||||
struct screen *(*init)(struct window_mode_entry *,
|
||||
struct cmdq_item *, struct cmd_find_state *,
|
||||
@@ -1332,6 +1334,7 @@ struct window_pane {
|
||||
#define PANE_DESTROYED 0x10000
|
||||
#define PANE_CMDRUNNING 0x20000
|
||||
#define PANE_ACTIVITY 0x40000
|
||||
#define PANE_CLOSEONCLICK 0x80000
|
||||
|
||||
bitstr_t *sync_dirty;
|
||||
u_int sync_dirty_size;
|
||||
@@ -1471,7 +1474,8 @@ struct window {
|
||||
int sb;
|
||||
int sb_pos;
|
||||
|
||||
struct utf8_data *fill_character;
|
||||
struct grid_cell inside_cell;
|
||||
struct grid_cell outside_cell;
|
||||
int flags;
|
||||
#define WINDOW_BELL 0x1
|
||||
#define WINDOW_ACTIVITY 0x2
|
||||
@@ -1727,6 +1731,7 @@ struct tty_term {
|
||||
#define TERM_RGBCOLOURS 0x10
|
||||
#define TERM_VT100LIKE 0x20
|
||||
#define TERM_SIXEL 0x40
|
||||
#define TERM_INVALIDMS 0x80
|
||||
int flags;
|
||||
|
||||
LIST_ENTRY(tty_term) entry;
|
||||
@@ -2239,10 +2244,13 @@ struct client {
|
||||
struct event click_timer;
|
||||
int click_loc;
|
||||
int click_wp;
|
||||
|
||||
struct event exit_timer;
|
||||
u_int click_button;
|
||||
struct mouse_event click_event;
|
||||
|
||||
struct status_line status;
|
||||
struct event cycle_timer;
|
||||
enum client_theme theme;
|
||||
|
||||
struct input_requests input_requests;
|
||||
@@ -2285,7 +2293,7 @@ struct client {
|
||||
/* 0x800000000ULL unused */
|
||||
#define CLIENT_BRACKETPASTING 0x1000000000ULL
|
||||
#define CLIENT_ASSUMEPASTING 0x2000000000ULL
|
||||
/* 0x4000000000ULL unused */
|
||||
#define CLIENT_WRITE_ACK 0x4000000000ULL
|
||||
#define CLIENT_NO_DETACH_ON_DESTROY 0x8000000000ULL
|
||||
#define CLIENT_ALLREDRAWFLAGS \
|
||||
(CLIENT_REDRAWWINDOW| \
|
||||
@@ -2807,6 +2815,10 @@ void hooks_monitor_add(struct cmdq_item *, struct options *,
|
||||
void hooks_monitor_remove(struct options *, const char *);
|
||||
void hooks_monitor_free(void *);
|
||||
char *hooks_monitor_to_string(struct options_entry *);
|
||||
int hooks_monitor_get(struct options_entry *, enum monitor_type *, int *,
|
||||
const char **);
|
||||
u_int hooks_monitor_get_fire_count(struct options_entry *);
|
||||
time_t hooks_monitor_get_fire_time(struct options_entry *);
|
||||
|
||||
/* options.c */
|
||||
struct options *options_create(struct options *);
|
||||
@@ -2824,6 +2836,9 @@ const char *options_name(struct options_entry *);
|
||||
struct options *options_owner(struct options_entry *);
|
||||
void *options_get_monitor_data(struct options_entry *);
|
||||
void options_set_monitor_data(struct options_entry *, void *);
|
||||
void options_hook_fired(struct options_entry *);
|
||||
u_int options_get_fire_count(struct options_entry *);
|
||||
time_t options_get_fire_time(struct options_entry *);
|
||||
const struct options_table_entry *options_table_entry(struct options_entry *);
|
||||
struct options_entry *options_get_only(struct options *, const char *);
|
||||
struct options_entry *options_get(struct options *, const char *);
|
||||
@@ -3291,6 +3306,7 @@ void file_write_close(struct client_files *, struct imsg *);
|
||||
void file_read_open(struct client_files *, struct tmuxpeer *, struct imsg *,
|
||||
int, int, client_file_cb, void *);
|
||||
int file_write_ready(struct client_files *, struct imsg *);
|
||||
int file_write_done(struct client_files *, struct imsg *);
|
||||
int file_read_data(struct client_files *, struct imsg *);
|
||||
int file_read_done(struct client_files *, struct imsg *);
|
||||
void file_read_cancel(struct client_files *, struct imsg *);
|
||||
@@ -3507,7 +3523,7 @@ void grid_clear_history(struct grid *);
|
||||
const struct grid_line *grid_peek_line(struct grid *, u_int);
|
||||
void grid_get_cell(struct grid *, u_int, u_int, struct grid_cell *);
|
||||
void grid_set_cell(struct grid *, u_int, u_int, const struct grid_cell *);
|
||||
void grid_set_padding(struct grid *, u_int, u_int);
|
||||
void grid_set_padding(struct grid *, u_int, u_int, int);
|
||||
void grid_set_cells(struct grid *, u_int, u_int, const struct grid_cell *,
|
||||
const char *, size_t);
|
||||
struct grid_line *grid_get_line(struct grid *, u_int);
|
||||
@@ -3524,6 +3540,7 @@ void grid_reflow(struct grid *, u_int);
|
||||
void grid_wrap_position(struct grid *, u_int, u_int, u_int *, u_int *);
|
||||
void grid_unwrap_position(struct grid *, u_int *, u_int *, u_int, u_int);
|
||||
u_int grid_line_length(struct grid *, u_int);
|
||||
u_int grid_line_limit(struct grid *, u_int);
|
||||
int grid_in_set(struct grid *, u_int, u_int, const char *);
|
||||
|
||||
/* grid-reader.c */
|
||||
@@ -3551,7 +3568,7 @@ void grid_reader_cursor_back_to_indentation(struct grid_reader *);
|
||||
void grid_view_get_cell(struct grid *, u_int, u_int, struct grid_cell *);
|
||||
void grid_view_set_cell(struct grid *, u_int, u_int,
|
||||
const struct grid_cell *);
|
||||
void grid_view_set_padding(struct grid *, u_int, u_int);
|
||||
void grid_view_set_padding(struct grid *, u_int, u_int, int);
|
||||
void grid_view_set_cells(struct grid *, u_int, u_int,
|
||||
const struct grid_cell *, const char *, size_t);
|
||||
void grid_view_clear_history(struct grid *, u_int);
|
||||
@@ -3804,7 +3821,6 @@ void *window_pane_get_new_data(struct window_pane *,
|
||||
struct window_pane_offset *, size_t *);
|
||||
void window_pane_update_used_data(struct window_pane *,
|
||||
struct window_pane_offset *, size_t);
|
||||
void window_set_fill_character(struct window *);
|
||||
void window_pane_default_cursor(struct window_pane *);
|
||||
int window_pane_mode(struct window_pane *);
|
||||
int window_pane_show_scrollbar(struct window_pane *);
|
||||
@@ -3832,8 +3848,10 @@ struct style_range *window_pane_status_get_range(struct window_pane *, u_int,
|
||||
int window_pane_is_floating(struct window_pane *);
|
||||
|
||||
/* window-border.c */
|
||||
void window_get_border_cell(struct window *, struct window_pane *,
|
||||
enum pane_lines, int, struct grid_cell *);
|
||||
void window_set_fill_cells(struct window *);
|
||||
void window_get_border_cell(struct window_pane *, enum pane_lines,
|
||||
int, struct grid_cell *);
|
||||
void window_get_fill_cell(struct window *, int, struct grid_cell *);
|
||||
void window_pane_get_border_cell(struct window_pane *, int,
|
||||
struct grid_cell *);
|
||||
void window_pane_get_border_style(struct window_pane *,
|
||||
@@ -4027,9 +4045,12 @@ int monitor_parse(const char *, char **, enum monitor_type *, int *,
|
||||
void monitor_add(struct monitor_set *, const char *, enum monitor_type, int,
|
||||
const char *, int);
|
||||
void monitor_remove(struct monitor_set *, const char *);
|
||||
u_int monitor_get_fire_count(struct monitor_set *, const char *);
|
||||
time_t monitor_get_fire_time(struct monitor_set *, const char *);
|
||||
|
||||
/* control.c */
|
||||
void control_discard(struct client *);
|
||||
void control_discard_all(struct client *);
|
||||
void control_start(struct client *);
|
||||
void control_ready(struct client *);
|
||||
void control_stop(struct client *);
|
||||
@@ -4045,6 +4066,8 @@ struct window_pane_offset *control_pane_offset(struct client *,
|
||||
struct window_pane *, int *);
|
||||
void control_reset_offsets(struct client *);
|
||||
void printflike(2, 3) control_write(struct client *, const char *, ...);
|
||||
void printflike(2, 3) control_notify_write(struct client *, const char *, ...);
|
||||
void control_write_guard(struct client *, const char *, long, u_int, int);
|
||||
void control_write_output(struct client *, struct window_pane *);
|
||||
int control_all_done(struct client *);
|
||||
void control_add_sub(struct client *, const char *, enum monitor_type, int,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: tty-draw.c,v 1.14 2026/06/19 10:38:29 nicm Exp $ */
|
||||
/* $OpenBSD: tty-draw.c,v 1.15 2026/07/26 09:02:08 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -100,6 +100,8 @@ tty_draw_line_get_empty(const struct grid_cell *gc,
|
||||
empty = nx;
|
||||
else if (gc->flags & GRID_FLAG_PADDING)
|
||||
empty = 1;
|
||||
else if (gc->data.width == 0)
|
||||
empty = 1;
|
||||
else if (gc->flags & GRID_FLAG_SELECTED)
|
||||
empty = 0;
|
||||
else if (gc->bg == last->bg && gc->attr == 0 && gc->link == 0) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: tty-features.c,v 1.40 2026/07/01 06:17:58 nicm Exp $ */
|
||||
/* $OpenBSD: tty-features.c,v 1.41 2026/08/17 07:52:16 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2020 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -596,6 +596,18 @@ tty_default_features(int *feat, const char *name, u_int version)
|
||||
"usstyle,"
|
||||
"progressbar"
|
||||
},
|
||||
{ .name = "Rio",
|
||||
.features = TTY_FEATURES_BASE_MODERN_XTERM ","
|
||||
"ccolour,"
|
||||
"cstyle,"
|
||||
"focus,"
|
||||
"overline,"
|
||||
"hyperlinks,"
|
||||
"osc7,"
|
||||
"sync,"
|
||||
"usstyle,"
|
||||
"progressbar"
|
||||
},
|
||||
{ .name = "XTerm",
|
||||
/*
|
||||
* xterm also supports DECSLRM and DECFRA, but they can be
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: tty-keys.c,v 1.210 2026/07/10 13:38:45 nicm Exp $ */
|
||||
/* $OpenBSD: tty-keys.c,v 1.212 2026/08/17 07:52:16 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -977,8 +977,9 @@ partial_key:
|
||||
if (delay < 500)
|
||||
delay = 500;
|
||||
}
|
||||
if ((tty->flags & (TTY_WAITFG|TTY_WAITBG) ||
|
||||
(tty->flags & TTY_ALL_REQUEST_FLAGS) != TTY_ALL_REQUEST_FLAGS) ||
|
||||
if (tty->flags & (TTY_WAITFG|TTY_WAITBG) ||
|
||||
tty->flags & (TTY_OSC52QUERY|TTY_WINSIZEQUERY) ||
|
||||
(tty->flags & TTY_ALL_REQUEST_FLAGS) != TTY_ALL_REQUEST_FLAGS ||
|
||||
!TAILQ_EMPTY(&c->input_requests)) {
|
||||
log_debug("%s: increasing delay (active query)", c->name);
|
||||
if (delay < 500)
|
||||
@@ -1666,6 +1667,8 @@ tty_keys_extended_device_attributes(struct tty *tty, const char *buf,
|
||||
tty_default_features(features, "WezTerm", 0);
|
||||
else if (strncmp(tmp, "ghostty ", 8) == 0)
|
||||
tty_default_features(features, "ghostty", 0);
|
||||
else if (strncmp(tmp, "Rio ", 4) == 0)
|
||||
tty_default_features(features, "Rio", 0);
|
||||
log_debug("%s: received extended DA %.*s", c->name, (int)*size, buf);
|
||||
|
||||
free(c->term_type);
|
||||
|
||||
23
tty-term.c
23
tty-term.c
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: tty-term.c,v 1.106 2026/06/13 09:17:29 nicm Exp $ */
|
||||
/* $OpenBSD: tty-term.c,v 1.107 2026/08/05 08:54:56 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "tmux.h"
|
||||
|
||||
static char *tty_term_strip(const char *);
|
||||
static void tty_term_validate(struct tty_term *);
|
||||
|
||||
struct tty_terms tty_terms = LIST_HEAD_INITIALIZER(tty_terms);
|
||||
|
||||
@@ -519,6 +520,26 @@ tty_term_apply_overrides(struct tty_term *term)
|
||||
acs = "a#j+k+l+m+n+o-p-q-r-s-t+u+v+w+x|y<z>~.";
|
||||
for (; acs[0] != '\0' && acs[1] != '\0'; acs += 2)
|
||||
term->acs[(u_char) acs[0]][0] = acs[1];
|
||||
|
||||
tty_term_validate(term);
|
||||
}
|
||||
|
||||
static void
|
||||
tty_term_validate(struct tty_term *term)
|
||||
{
|
||||
struct tty_code *code = &term->codes[TTYC_MS];
|
||||
|
||||
if (code->type != TTYCODE_STRING)
|
||||
return;
|
||||
if (*tty_term_string_ss(term, TTYC_MS, "c", "?") != '\0') {
|
||||
term->flags &= ~TERM_INVALIDMS;
|
||||
return;
|
||||
}
|
||||
|
||||
log_debug("removing invalid Ms capability");
|
||||
term->flags |= TERM_INVALIDMS;
|
||||
free(code->value.string);
|
||||
code->type = TTYCODE_NONE;
|
||||
}
|
||||
|
||||
struct tty_term *
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: window-border.c,v 1.2 2026/07/17 12:42:51 nicm Exp $ */
|
||||
/* $OpenBSD: window-border.c,v 1.3 2026/07/23 09:38:27 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -23,18 +23,82 @@
|
||||
|
||||
#include "tmux.h"
|
||||
|
||||
/* Set window fill cell. */
|
||||
static void
|
||||
window_set_fill_cell(struct window *w, int inside, struct grid_cell *gc)
|
||||
{
|
||||
struct format_tree *ft;
|
||||
struct screen s;
|
||||
struct screen_write_ctx ctx;
|
||||
struct grid_cell new_gc;
|
||||
const char *value;
|
||||
char *expanded;
|
||||
|
||||
memcpy(gc, &grid_default_cell, sizeof *gc);
|
||||
gc->attr |= GRID_ATTR_CHARSET;
|
||||
utf8_set(&gc->data, CELL_BORDERS[CELL_NONE]);
|
||||
|
||||
ft = format_create(NULL, NULL, FORMAT_WINDOW|w->id, FORMAT_NOJOBS);
|
||||
format_defaults(ft, NULL, NULL, NULL, w->active);
|
||||
format_add(ft, "is_inside", "%d", inside);
|
||||
format_add(ft, "is_outside", "%d", !inside);
|
||||
|
||||
value = options_get_string(w->options, "fill-character");
|
||||
expanded = format_expand(ft, value);
|
||||
format_free(ft);
|
||||
|
||||
screen_init(&s, 1, 1, 0);
|
||||
screen_write_start(&ctx, &s);
|
||||
format_draw(&ctx, &grid_default_cell, 1, expanded, NULL, 0);
|
||||
screen_write_stop(&ctx);
|
||||
free(expanded);
|
||||
|
||||
grid_view_get_cell(s.grid, 0, 0, &new_gc);
|
||||
if (new_gc.data.width == 1)
|
||||
memcpy(gc, &new_gc, sizeof *gc);
|
||||
screen_free(&s);
|
||||
}
|
||||
|
||||
/* Set window fill cells. */
|
||||
void
|
||||
window_set_fill_cells(struct window *w)
|
||||
{
|
||||
window_set_fill_cell(w, 1, &w->inside_cell);
|
||||
window_set_fill_cell(w, 0, &w->outside_cell);
|
||||
}
|
||||
|
||||
/* Merge a window fill cell over an existing style. */
|
||||
static void
|
||||
window_copy_fill_cell(struct grid_cell *gc, const struct grid_cell *fill)
|
||||
{
|
||||
utf8_copy(&gc->data, &fill->data);
|
||||
gc->attr |= fill->attr;
|
||||
gc->flags |= fill->flags;
|
||||
if (fill->fg != 8)
|
||||
gc->fg = fill->fg;
|
||||
if (fill->bg != 8)
|
||||
gc->bg = fill->bg;
|
||||
if (fill->us != 8)
|
||||
gc->us = fill->us;
|
||||
}
|
||||
|
||||
/* Get window fill cell. */
|
||||
void
|
||||
window_get_fill_cell(struct window *w, int inside, struct grid_cell *gc)
|
||||
{
|
||||
if (inside)
|
||||
window_copy_fill_cell(gc, &w->inside_cell);
|
||||
else
|
||||
window_copy_fill_cell(gc, &w->outside_cell);
|
||||
}
|
||||
|
||||
/* Get border cell. */
|
||||
void
|
||||
window_get_border_cell(struct window *w, struct window_pane *wp,
|
||||
enum pane_lines pane_lines, int cell_type, struct grid_cell *gc)
|
||||
window_get_border_cell(struct window_pane *wp, enum pane_lines pane_lines,
|
||||
int cell_type, struct grid_cell *gc)
|
||||
{
|
||||
u_int idx;
|
||||
|
||||
if (cell_type == CELL_NONE && w->fill_character != NULL) {
|
||||
utf8_copy(&gc->data, &w->fill_character[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (pane_lines) {
|
||||
case PANE_LINES_NUMBER:
|
||||
if (cell_type == CELL_NONE) {
|
||||
@@ -79,7 +143,7 @@ window_pane_get_border_cell(struct window_pane *wp, int cell_type,
|
||||
{
|
||||
enum pane_lines pane_lines = window_pane_get_pane_lines(wp);
|
||||
|
||||
window_get_border_cell(wp->window, wp, pane_lines, cell_type, gc);
|
||||
window_get_border_cell(wp, pane_lines, cell_type, gc);
|
||||
}
|
||||
|
||||
/* Get pane border style. */
|
||||
@@ -147,7 +211,7 @@ window_make_pane_status(struct window_pane *wp, struct client *c, u_int width,
|
||||
pane_lines = window_pane_get_pane_lines(wp);
|
||||
for (i = 0; i < width; i++) {
|
||||
cell_type = redraw_get_status_border_cell_type(&span, i);
|
||||
window_get_border_cell(wp->window, wp, pane_lines, cell_type, &gc);
|
||||
window_get_border_cell(wp, pane_lines, cell_type, &gc);
|
||||
screen_write_cell(&ctx, &gc);
|
||||
}
|
||||
gc.attr &= ~GRID_ATTR_CHARSET;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: window-client.c,v 1.47 2026/07/14 17:17:18 nicm Exp $ */
|
||||
/* $OpenBSD: window-client.c,v 1.48 2026/08/05 08:54:56 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2017 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -113,14 +113,16 @@ static const char *window_client_info_lines[] = {
|
||||
"#{?#{I/c:kmous},,#[align=right]unavailable: [kmous] missing}",
|
||||
|
||||
"#[fg=themelightgrey]set-clipboard #[#{E:tree-mode-border-style},acs]x#[default] "
|
||||
"#{?#{!=:#{set-clipboard},off},#{?#{I/f:clipboard},,"
|
||||
"#{?#{!=:#{set-clipboard},off},#{?#{I/c:Ms},,"
|
||||
"#[fg=themered]}#{set-clipboard},#[fg=themelightgrey]off} "
|
||||
"#{?#{I/f:clipboard},,#[align=right]unavailable: [Ms] missing}",
|
||||
"#{?#{I/c:Ms},,#[align=right]unavailable: [Ms] "
|
||||
"#{?clipboard_invalid,invalid,missing}}",
|
||||
|
||||
"#[fg=themelightgrey]get-clipboard #[#{E:tree-mode-border-style},acs]x#[default] "
|
||||
"#{?#{!=:#{get-clipboard},off},#{?#{I/f:clipboard},,"
|
||||
"#{?#{!=:#{get-clipboard},off},#{?#{I/c:Ms},,"
|
||||
"#[fg=themered]}#{get-clipboard},#[fg=themelightgrey]off} "
|
||||
"#{?#{I/f:clipboard},,#[align=right]unavailable: [Ms] missing}",
|
||||
"#{?#{I/c:Ms},,#[align=right]unavailable: [Ms] "
|
||||
"#{?clipboard_invalid,invalid,missing}}",
|
||||
|
||||
"#[fg=themelightgrey]focus-events #[#{E:tree-mode-border-style},acs]x#[default] "
|
||||
"#{?focus-events,#{?#{I/f:focus},,#[fg=themered]}on,#[fg=themelightgrey]off} "
|
||||
@@ -274,6 +276,10 @@ window_client_draw_info(__unused void *modedata, void *itemdata,
|
||||
|
||||
w = active_get_effective_window(c, c->session);
|
||||
ft = format_create_defaults(NULL, c, NULL, NULL, NULL);
|
||||
if (c->tty.term->flags & TERM_INVALIDMS)
|
||||
format_add(ft, "clipboard_invalid", "1");
|
||||
else
|
||||
format_add(ft, "clipboard_invalid", "0");
|
||||
|
||||
screen_write_cursormove(ctx, cx, cy, 0);
|
||||
for (i = 0; i < nitems(window_client_info_lines); i++) {
|
||||
|
||||
105
window-copy.c
105
window-copy.c
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: window-copy.c,v 1.422 2026/07/20 11:16:33 nicm Exp $ */
|
||||
/* $OpenBSD: window-copy.c,v 1.425 2026/08/05 12:23:25 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -54,6 +54,7 @@ static void window_copy_redraw_screen(struct window_mode_entry *);
|
||||
static void window_copy_do_refresh(struct window_mode_entry *, int);
|
||||
static void window_copy_refresh_timer(int, short, void *);
|
||||
static void window_copy_refresh_arm(struct window_mode_entry *);
|
||||
static int window_copy_refresh_allowed(struct window_mode_entry *);
|
||||
static void window_copy_refresh_start(struct window_mode_entry *);
|
||||
static void window_copy_refresh_stop(struct window_mode_entry *);
|
||||
static void window_copy_style_changed(struct window_mode_entry *);
|
||||
@@ -62,6 +63,7 @@ static void window_copy_set_line_numbers1(struct window_mode_entry *, int,
|
||||
static int window_copy_line_number_mode(struct window_mode_entry *);
|
||||
static int window_copy_line_number_is_absolute(struct window_mode_entry *);
|
||||
static int window_copy_line_numbers_active(struct window_mode_entry *);
|
||||
static int window_copy_cursor_line_active(struct window_mode_entry *);
|
||||
static u_int window_copy_line_number_width(struct window_mode_entry *);
|
||||
static u_int window_copy_cursor_offset(struct window_mode_entry *, u_int,
|
||||
u_int);
|
||||
@@ -3035,6 +3037,20 @@ window_copy_refresh_arm(struct window_mode_entry *wme)
|
||||
evtimer_add(&data->refresh_timer, &tv);
|
||||
}
|
||||
|
||||
static int
|
||||
window_copy_refresh_allowed(struct window_mode_entry *wme)
|
||||
{
|
||||
struct window_copy_mode_data *data = wme->data;
|
||||
|
||||
/*
|
||||
* Do not refresh a view of another pane (copy-mode -s): the source may
|
||||
* disappear and changes are not tracked on this pane.
|
||||
*/
|
||||
if (data->viewmode || wme->swp != wme->wp)
|
||||
return (0);
|
||||
return (1);
|
||||
}
|
||||
|
||||
static void
|
||||
window_copy_refresh_timer(__unused int fd, __unused short events, void *arg)
|
||||
{
|
||||
@@ -3069,11 +3085,7 @@ window_copy_refresh_start(struct window_mode_entry *wme)
|
||||
{
|
||||
struct window_copy_mode_data *data = wme->data;
|
||||
|
||||
/*
|
||||
* Do not refresh a view of another pane (copy-mode -s): the source may
|
||||
* disappear and changes are not tracked on this pane.
|
||||
*/
|
||||
if (data->viewmode || wme->swp != wme->wp || data->refresh_active)
|
||||
if (!window_copy_refresh_allowed(wme) || data->refresh_active)
|
||||
return;
|
||||
data->refresh_active = 1;
|
||||
window_copy_refresh_arm(wme);
|
||||
@@ -3088,6 +3100,25 @@ window_copy_refresh_stop(struct window_mode_entry *wme)
|
||||
evtimer_del(&data->refresh_timer);
|
||||
}
|
||||
|
||||
static enum window_copy_cmd_action
|
||||
window_copy_cmd_refresh_now(struct window_copy_cmd_state *cs)
|
||||
{
|
||||
struct window_mode_entry *wme = cs->wme;
|
||||
struct window_copy_mode_data *data = wme->data;
|
||||
struct window_pane *wp = wme->wp;
|
||||
int follow;
|
||||
|
||||
if (!window_copy_refresh_allowed(wme))
|
||||
return (WINDOW_COPY_CMD_NOTHING);
|
||||
|
||||
follow = (data->oy == 0 &&
|
||||
data->cy == screen_size_y(&data->screen) - 1);
|
||||
window_copy_do_refresh(wme, follow);
|
||||
wp->flags &= ~PANE_UNSEENCHANGES;
|
||||
|
||||
return (WINDOW_COPY_CMD_REDRAW);
|
||||
}
|
||||
|
||||
static enum window_copy_cmd_action
|
||||
window_copy_cmd_refresh_on(struct window_copy_cmd_state *cs)
|
||||
{
|
||||
@@ -3630,6 +3661,12 @@ static const struct {
|
||||
.clear = WINDOW_COPY_CMD_CLEAR_NEVER,
|
||||
.f = window_copy_cmd_refresh_off
|
||||
},
|
||||
{ .command = "refresh-now",
|
||||
.args = { "", 0, 0, NULL },
|
||||
.flags = WINDOW_COPY_CMD_FLAG_READONLY,
|
||||
.clear = WINDOW_COPY_CMD_CLEAR_NEVER,
|
||||
.f = window_copy_cmd_refresh_now
|
||||
},
|
||||
{ .command = "refresh-toggle",
|
||||
.args = { "", 0, 0, NULL },
|
||||
.flags = WINDOW_COPY_CMD_FLAG_READONLY,
|
||||
@@ -5027,7 +5064,8 @@ window_copy_match_at_cursor(struct window_copy_mode_data *data)
|
||||
static void
|
||||
window_copy_update_style(struct window_mode_entry *wme, u_int fx, u_int fy,
|
||||
struct grid_cell *gc, const struct grid_cell *mgc,
|
||||
const struct grid_cell *cgc, const struct grid_cell *mkgc)
|
||||
const struct grid_cell *cgc, const struct grid_cell *mkgc,
|
||||
const struct grid_cell *clgc)
|
||||
{
|
||||
struct window_pane *wp = wme->wp;
|
||||
struct window_copy_mode_data *data = wme->data;
|
||||
@@ -5035,6 +5073,15 @@ window_copy_update_style(struct window_mode_entry *wme, u_int fx, u_int fy,
|
||||
int inv = 0, found = 0;
|
||||
int keys;
|
||||
|
||||
cy = screen_hsize(data->backing) - data->oy + data->cy;
|
||||
if (fy == cy) {
|
||||
if (clgc->fg != 8)
|
||||
gc->fg = clgc->fg;
|
||||
if (clgc->bg != 8)
|
||||
gc->bg = clgc->bg;
|
||||
gc->attr |= clgc->attr;
|
||||
}
|
||||
|
||||
if (data->showmark && fy == data->my) {
|
||||
gc->attr = mkgc->attr;
|
||||
if (fx == data->mx)
|
||||
@@ -5058,7 +5105,6 @@ window_copy_update_style(struct window_mode_entry *wme, u_int fx, u_int fy,
|
||||
if (mark == 0)
|
||||
return;
|
||||
|
||||
cy = screen_hsize(data->backing) - data->oy + data->cy;
|
||||
if (window_copy_search_mark_at(data, data->cx, cy, &cursor) == 0) {
|
||||
keys = options_get_number(wp->window->options, "mode-keys");
|
||||
if (cursor != 0 &&
|
||||
@@ -5102,7 +5148,7 @@ static void
|
||||
window_copy_write_one(struct window_mode_entry *wme,
|
||||
struct screen_write_ctx *ctx, u_int px, u_int py, u_int fy, u_int nx,
|
||||
const struct grid_cell *mgc, const struct grid_cell *cgc,
|
||||
const struct grid_cell *mkgc)
|
||||
const struct grid_cell *mkgc, const struct grid_cell *clgc)
|
||||
{
|
||||
struct window_copy_mode_data *data = wme->data;
|
||||
struct grid *gd = data->backing->grid;
|
||||
@@ -5114,7 +5160,7 @@ window_copy_write_one(struct window_mode_entry *wme,
|
||||
grid_get_cell(gd, fx, fy, &gc);
|
||||
if (fx + gc.data.width <= nx) {
|
||||
window_copy_update_style(wme, fx, fy, &gc, mgc, cgc,
|
||||
mkgc);
|
||||
mkgc, clgc);
|
||||
if (gc.flags & GRID_FLAG_PADDING) {
|
||||
if (ctx->s->cy == py && ctx->s->cx <= px + fx) {
|
||||
gc.flags &= ~GRID_FLAG_PADDING;
|
||||
@@ -5172,6 +5218,16 @@ window_copy_line_numbers_active(struct window_mode_entry *wme)
|
||||
WINDOW_COPY_LINE_NUMBERS_OFF);
|
||||
}
|
||||
|
||||
static int
|
||||
window_copy_cursor_line_active(struct window_mode_entry *wme)
|
||||
{
|
||||
struct options *oo = wme->wp->window->options;
|
||||
const char *s;
|
||||
|
||||
s = options_get_string(oo, "copy-mode-current-line-style");
|
||||
return (strcmp(s, "default") != 0);
|
||||
}
|
||||
|
||||
static u_int
|
||||
window_copy_line_number_width(struct window_mode_entry *wme)
|
||||
{
|
||||
@@ -5288,7 +5344,8 @@ window_copy_write_line(struct window_mode_entry *wme,
|
||||
struct window_copy_mode_data *data = wme->data;
|
||||
struct screen *s = &data->screen;
|
||||
struct options *oo = wp->window->options;
|
||||
struct grid_cell gc, mgc, cgc, mkgc, ln_gc, cur_ln_gc;
|
||||
struct grid_cell gc, mgc, cgc, mkgc, clgc, ln_gc;
|
||||
struct grid_cell cur_ln_gc;
|
||||
u_int sx = screen_size_x(s);
|
||||
u_int hsize = screen_hsize(data->backing);
|
||||
u_int width;
|
||||
@@ -5318,6 +5375,8 @@ window_copy_write_line(struct window_mode_entry *wme,
|
||||
cgc.flags |= GRID_FLAG_NOPALETTE;
|
||||
style_apply(&mkgc, oo, "copy-mode-mark-style", ft);
|
||||
mkgc.flags |= GRID_FLAG_NOPALETTE;
|
||||
style_apply(&clgc, oo, "copy-mode-current-line-style", ft);
|
||||
clgc.flags |= GRID_FLAG_NOPALETTE;
|
||||
if (width != 0) {
|
||||
style_apply(&ln_gc, oo, "copy-mode-line-number-style", ft);
|
||||
ln_gc.flags |= GRID_FLAG_NOPALETTE;
|
||||
@@ -5346,7 +5405,7 @@ window_copy_write_line(struct window_mode_entry *wme,
|
||||
}
|
||||
|
||||
window_copy_write_one(wme, ctx, width, py, hsize - data->oy + py,
|
||||
content_sx, &mgc, &cgc, &mkgc);
|
||||
content_sx, &mgc, &cgc, &mkgc, &clgc);
|
||||
|
||||
if (py == 0 && s->rupper < s->rlower && !data->hide_position) {
|
||||
value = options_get_string(oo, "copy-mode-position-format");
|
||||
@@ -5598,6 +5657,11 @@ window_copy_update_cursor(struct window_mode_entry *wme, u_int cx, u_int cy)
|
||||
screen_write_stop(&ctx);
|
||||
return;
|
||||
}
|
||||
if (old_cy != data->cy && window_copy_cursor_line_active(wme)) {
|
||||
window_copy_redraw_lines(wme, old_cy, 1);
|
||||
window_copy_redraw_lines(wme, data->cy, 1);
|
||||
return;
|
||||
}
|
||||
if (old_cx == screen_size_x(s))
|
||||
window_copy_redraw_lines(wme, old_cy, 1);
|
||||
if (data->cx == screen_size_x(s))
|
||||
@@ -6187,16 +6251,13 @@ static u_int
|
||||
window_copy_cursor_limit(struct window_mode_entry *wme, u_int py,
|
||||
int allow_onemore)
|
||||
{
|
||||
struct window_copy_mode_data *data = wme->data;
|
||||
struct options *oo = wme->wp->window->options;
|
||||
u_int len;
|
||||
|
||||
len = window_copy_find_length(wme, py);
|
||||
if (allow_onemore ||
|
||||
options_get_number(oo, "mode-keys") != MODEKEY_VI)
|
||||
return (len);
|
||||
if (len == 0)
|
||||
return (0);
|
||||
return (len - 1);
|
||||
return (window_copy_find_length(wme, py));
|
||||
return (grid_line_limit(data->backing->grid, py));
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -6799,6 +6860,10 @@ window_copy_scroll_up(struct window_mode_entry *wme, u_int ny)
|
||||
if (data->searchmark != NULL && !data->timeout)
|
||||
window_copy_search_marks(wme, NULL, data->searchregex, 1);
|
||||
window_copy_update_selection_view(wme, 0, 0);
|
||||
if (window_copy_cursor_line_active(wme)) {
|
||||
window_copy_redraw_screen(wme);
|
||||
return;
|
||||
}
|
||||
if (window_copy_line_numbers_active(wme)) {
|
||||
if (window_copy_line_number_mode(wme) !=
|
||||
WINDOW_COPY_LINE_NUMBERS_ABSOLUTE) {
|
||||
@@ -6866,6 +6931,10 @@ window_copy_scroll_down(struct window_mode_entry *wme, u_int ny)
|
||||
if (data->searchmark != NULL && !data->timeout)
|
||||
window_copy_search_marks(wme, NULL, data->searchregex, 1);
|
||||
window_copy_update_selection_view(wme, 0, 0);
|
||||
if (window_copy_cursor_line_active(wme)) {
|
||||
window_copy_redraw_screen(wme);
|
||||
return;
|
||||
}
|
||||
if (window_copy_line_numbers_active(wme)) {
|
||||
if (window_copy_line_number_mode(wme) !=
|
||||
WINDOW_COPY_LINE_NUMBERS_ABSOLUTE) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: window-customize.c,v 1.35 2026/07/15 12:45:39 nicm Exp $ */
|
||||
/* $OpenBSD: window-customize.c,v 1.36 2026/07/28 10:35:31 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2020 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -276,6 +276,36 @@ window_customize_scope_text(enum window_customize_scope scope,
|
||||
return (s);
|
||||
}
|
||||
|
||||
static int
|
||||
window_customize_write_hook_fire(struct screen_write_ctx *ctx, u_int cx,
|
||||
u_int sx, u_int sy, struct options_entry *o)
|
||||
{
|
||||
char *fire_time_string;
|
||||
u_int fire_count;
|
||||
time_t fire_time;
|
||||
|
||||
if (options_get_monitor_data(o) != NULL) {
|
||||
fire_count = hooks_monitor_get_fire_count(o);
|
||||
fire_time = hooks_monitor_get_fire_time(o);
|
||||
} else {
|
||||
fire_count = options_get_fire_count(o);
|
||||
fire_time = options_get_fire_time(o);
|
||||
}
|
||||
if (fire_time != 0) {
|
||||
fire_time_string = format_pretty_time(fire_time, 0);
|
||||
if (!screen_write_text(ctx, cx, sx, sy, 0, &grid_default_cell,
|
||||
"This hook has been fired %u times, last %s.", fire_count,
|
||||
fire_time_string)) {
|
||||
free(fire_time_string);
|
||||
return (0);
|
||||
}
|
||||
free(fire_time_string);
|
||||
return (1);
|
||||
}
|
||||
return (screen_write_text(ctx, cx, sx, sy, 0, &grid_default_cell,
|
||||
"This hook has been fired %u times.", fire_count));
|
||||
}
|
||||
|
||||
static struct window_customize_itemdata *
|
||||
window_customize_add_item(struct window_customize_modedata *data)
|
||||
{
|
||||
@@ -654,7 +684,7 @@ window_customize_build_option(struct window_customize_modedata *data,
|
||||
char *text, *expanded, *value;
|
||||
int global = 0, array = 0;
|
||||
int is_hook = 0, is_monitor = 0;
|
||||
int is_user_hook = 0;
|
||||
int is_user_hook = 0, is_any_hook;
|
||||
uint64_t tag;
|
||||
|
||||
if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_HOOK))
|
||||
@@ -663,13 +693,14 @@ window_customize_build_option(struct window_customize_modedata *data,
|
||||
is_monitor = 1;
|
||||
if (*name == '@' && hooks_is_event(name))
|
||||
is_user_hook = 1;
|
||||
is_any_hook = (is_hook || is_monitor || is_user_hook);
|
||||
switch (type) {
|
||||
case WINDOW_CUSTOMIZE_OPTIONS:
|
||||
if (is_hook || is_monitor || is_user_hook)
|
||||
if (is_any_hook)
|
||||
return (0);
|
||||
break;
|
||||
case WINDOW_CUSTOMIZE_HOOKS:
|
||||
if (!is_hook && !is_monitor && !is_user_hook)
|
||||
if (!is_any_hook)
|
||||
return (0);
|
||||
break;
|
||||
}
|
||||
@@ -1188,6 +1219,7 @@ window_customize_draw_option(struct window_customize_modedata *data,
|
||||
struct cmd_find_state fs;
|
||||
struct format_tree *ft;
|
||||
int is_hook, is_monitor, is_user_hook;
|
||||
int is_any_hook;
|
||||
|
||||
if (!window_customize_check_item(data, item, &fs))
|
||||
return;
|
||||
@@ -1201,6 +1233,7 @@ window_customize_draw_option(struct window_customize_modedata *data,
|
||||
is_hook = (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_HOOK));
|
||||
is_monitor = (options_get_monitor_data(o) != NULL);
|
||||
is_user_hook = (*name == '@' && hooks_is_event(name));
|
||||
is_any_hook = (is_hook || is_monitor || is_user_hook);
|
||||
|
||||
if (oe != NULL && oe->unit != NULL) {
|
||||
space = " ";
|
||||
@@ -1267,8 +1300,16 @@ monitor:
|
||||
}
|
||||
if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY)) {
|
||||
if (is_hook) {
|
||||
if (array_key == NULL)
|
||||
if (array_key == NULL) {
|
||||
if (!screen_write_text(ctx, cx, sx,
|
||||
sy - (s->cy - cy), 0, &grid_default_cell,
|
||||
"This is an array hook."))
|
||||
goto out;
|
||||
if (!window_customize_write_hook_fire(ctx, cx,
|
||||
sx, sy - (s->cy - cy), o))
|
||||
goto out;
|
||||
goto out;
|
||||
}
|
||||
} else if (array_key != NULL) {
|
||||
if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy),
|
||||
0, &grid_default_cell,
|
||||
@@ -1294,10 +1335,13 @@ monitor:
|
||||
default_value = NULL;
|
||||
}
|
||||
}
|
||||
if (is_hook || is_monitor || is_user_hook) {
|
||||
if (is_any_hook) {
|
||||
if (!window_customize_write_value(ctx, cx, sx, sy - (s->cy - cy),
|
||||
0, "Hook command: ", "%s%s%s", value, space, unit))
|
||||
goto out;
|
||||
if (!window_customize_write_hook_fire(ctx, cx, sx,
|
||||
sy - (s->cy - cy), o))
|
||||
goto out;
|
||||
} else {
|
||||
if (!window_customize_write_value(ctx, cx, sx, sy - (s->cy - cy),
|
||||
0, "Option value: ", "%s%s%s", value, space, unit))
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: window-panes.c,v 1.3 2026/07/19 19:53:11 nicm Exp $ */
|
||||
/* $OpenBSD: window-panes.c,v 1.4 2026/07/29 14:06:32 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -35,7 +35,8 @@ static void window_panes_key(struct window_mode_entry *,
|
||||
|
||||
const struct window_mode window_panes_mode = {
|
||||
.name = "panes-mode",
|
||||
.flags = WINDOW_MODE_HIDE_PANE_STATUS|WINDOW_MODE_NO_STACK,
|
||||
.flags = WINDOW_MODE_HIDE_PANE_STATUS|WINDOW_MODE_NO_STACK|
|
||||
WINDOW_MODE_FILL_WINDOW|WINDOW_MODE_HIDE_SCROLLBARS,
|
||||
|
||||
.init = window_panes_init,
|
||||
.free = window_panes_free,
|
||||
|
||||
42
window.c
42
window.c
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: window.c,v 1.366 2026/07/19 19:53:11 nicm Exp $ */
|
||||
/* $OpenBSD: window.c,v 1.369 2026/07/29 14:06:32 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -437,8 +437,6 @@ window_create(u_int sx, u_int sy, u_int xpixel, u_int ypixel)
|
||||
w->id = next_window_id++;
|
||||
RB_INSERT(windows, &windows, w);
|
||||
|
||||
window_set_fill_character(w);
|
||||
|
||||
if (gettimeofday(&w->creation_time, NULL) != 0)
|
||||
fatal("gettimeofday failed");
|
||||
window_update_activity(w);
|
||||
@@ -473,7 +471,6 @@ window_destroy(struct window *w)
|
||||
event_del(&w->offset_timer);
|
||||
|
||||
options_free(w->options);
|
||||
free(w->fill_character);
|
||||
|
||||
free(w->name);
|
||||
free(w);
|
||||
@@ -572,13 +569,13 @@ window_resize(struct window *w, u_int sx, u_int sy, int xpixel, int ypixel)
|
||||
w->sy = sy;
|
||||
if (w->menu != NULL) {
|
||||
menu_resize(w->menu, w);
|
||||
redraw_invalidate_scene(w);
|
||||
server_redraw_window(w);
|
||||
}
|
||||
if (xpixel != -1)
|
||||
w->xpixel = xpixel;
|
||||
if (ypixel != -1)
|
||||
w->ypixel = ypixel;
|
||||
redraw_invalidate_scene(w);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -2446,25 +2443,6 @@ window_pane_update_used_data(struct window_pane *wp,
|
||||
wpo->used += size;
|
||||
}
|
||||
|
||||
void
|
||||
window_set_fill_character(struct window *w)
|
||||
{
|
||||
const char *value;
|
||||
struct utf8_data *ud;
|
||||
|
||||
free(w->fill_character);
|
||||
w->fill_character = NULL;
|
||||
|
||||
value = options_get_string(w->options, "fill-character");
|
||||
if (*value != '\0' && utf8_isvalid(value)) {
|
||||
ud = utf8_fromcstr(value);
|
||||
if (ud != NULL && ud[0].width == 1)
|
||||
w->fill_character = ud;
|
||||
else
|
||||
free(ud);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
window_pane_default_cursor(struct window_pane *wp)
|
||||
{
|
||||
@@ -2486,11 +2464,21 @@ window_pane_mode(struct window_pane *wp)
|
||||
int
|
||||
window_pane_show_scrollbar(struct window_pane *wp)
|
||||
{
|
||||
struct window *w = wp->window;
|
||||
struct window_mode_entry *wme;
|
||||
|
||||
if (SCREEN_IS_ALTERNATE(&wp->base))
|
||||
return (0);
|
||||
if (wp->window->sb == PANE_SCROLLBARS_ALWAYS ||
|
||||
wp->window->sb == PANE_SCROLLBARS_AUTOHIDE ||
|
||||
(wp->window->sb == PANE_SCROLLBARS_MODAL &&
|
||||
if ((w->flags & WINDOW_ZOOMED) && w->active != NULL) {
|
||||
wme = TAILQ_FIRST(&w->active->modes);
|
||||
if (wme != NULL &&
|
||||
(wme->mode->flags & WINDOW_MODE_HIDE_SCROLLBARS)) {
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
if (w->sb == PANE_SCROLLBARS_ALWAYS ||
|
||||
w->sb == PANE_SCROLLBARS_AUTOHIDE ||
|
||||
(w->sb == PANE_SCROLLBARS_MODAL &&
|
||||
window_pane_mode(wp) != WINDOW_PANE_NO_MODE))
|
||||
return (1);
|
||||
return (0);
|
||||
|
||||
Reference in New Issue
Block a user