mirror of
https://github.com/tmux/tmux.git
synced 2026-08-27 17:11:35 +00:00
Merge branch 'master' into command_parser
This commit is contained in:
1
.github/workflows/regress.yml
vendored
1
.github/workflows/regress.yml
vendored
@@ -61,6 +61,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).
|
||||
|
||||
@@ -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.75 2026/07/23 09:38:27 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -172,6 +172,7 @@ 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);
|
||||
|
||||
layout_init(w, wp);
|
||||
wp->flags |= PANE_CHANGED;
|
||||
|
||||
@@ -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-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.146 2026/07/21 12:28:43 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] "
|
||||
@@ -201,6 +201,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
|
||||
};
|
||||
|
||||
|
||||
138
configure.ac
138
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,26 @@ 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_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)
|
||||
|
||||
3
file.c
3
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.21 2026/07/26 15:08:15 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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
103
format.c
103
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.409 2026/07/26 15:21:53 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);
|
||||
@@ -4319,7 +4325,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;
|
||||
}
|
||||
@@ -4797,7 +4803,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/
|
||||
@@ -4816,7 +4822,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++;
|
||||
@@ -4838,7 +4844,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];
|
||||
|
||||
@@ -5797,6 +5803,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,
|
||||
@@ -5817,6 +5891,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;
|
||||
@@ -5877,6 +5952,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;
|
||||
@@ -6098,6 +6182,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);
|
||||
|
||||
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);
|
||||
@@ -230,7 +231,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");
|
||||
@@ -321,7 +322,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);
|
||||
@@ -334,7 +335,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)
|
||||
@@ -352,9 +353,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);
|
||||
}
|
||||
|
||||
@@ -362,14 +363,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)
|
||||
@@ -399,8 +400,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);
|
||||
}
|
||||
@@ -420,7 +420,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);
|
||||
@@ -443,7 +443,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;
|
||||
|
||||
@@ -469,3 +469,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.190 2026/07/22 19:23:59 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -45,6 +45,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}" \
|
||||
@@ -549,6 +552,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 } }",
|
||||
|
||||
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>
|
||||
@@ -1430,6 +1430,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,
|
||||
@@ -1450,8 +1459,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",
|
||||
@@ -1935,6 +1944,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;
|
||||
};
|
||||
@@ -434,6 +437,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)
|
||||
{
|
||||
@@ -1361,7 +1383,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)
|
||||
|
||||
13
proc.c
13
proc.c
@@ -31,6 +31,10 @@
|
||||
#include <ncurses.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_JEMALLOC
|
||||
#include <jemalloc/jemalloc.h>
|
||||
#endif
|
||||
|
||||
#include "tmux.h"
|
||||
|
||||
struct tmuxproc {
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
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 0.2'
|
||||
check_ok set-option -t pipe:0 remain-on-exit on
|
||||
check_ok pipe-pane -t pipe:0.0 -I 'sleep 0.6; 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 0.7
|
||||
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 │···········
|
||||
│ │···········
|
||||
│ │···········
|
||||
──────────────────└─────────┘···········
|
||||
────────────────────────────┘···········
|
||||
········································
|
||||
········································
|
||||
········································
|
||||
|
||||
@@ -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
|
||||
@@ -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);
|
||||
|
||||
@@ -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.285 2026/07/26 09:02:08 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -2892,7 +2892,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__);
|
||||
|
||||
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.499 2026/07/22 08:19:14 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -525,6 +525,8 @@ server_client_lost(struct client *c)
|
||||
|
||||
evtimer_del(&c->repeat_timer);
|
||||
evtimer_del(&c->click_timer);
|
||||
if (event_initialized(&c->cycle_timer))
|
||||
evtimer_del(&c->cycle_timer);
|
||||
|
||||
key_bindings_unref_table(c->keytable);
|
||||
|
||||
@@ -1057,6 +1059,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
@@ -371,6 +371,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))
|
||||
|
||||
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. */
|
||||
|
||||
85
tmux.1
85
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.1151 2026/07/28 07:04:18 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: July 28 2026 $
|
||||
.Dt TMUX 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
@@ -3638,7 +3638,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 +3694,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
|
||||
@@ -4690,6 +4693,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
|
||||
@@ -4717,6 +4721,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
|
||||
@@ -5708,8 +5716,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
|
||||
@@ -5775,6 +5788,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
|
||||
@@ -6586,6 +6607,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
|
||||
@@ -6598,6 +6620,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
|
||||
@@ -7012,6 +7038,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}
|
||||
@@ -7176,6 +7216,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"
|
||||
@@ -7241,7 +7301,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"
|
||||
@@ -7283,6 +7347,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"
|
||||
|
||||
23
tmux.h
23
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.1416 2026/07/27 19:15:58 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -1335,6 +1335,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;
|
||||
@@ -1474,7 +1475,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
|
||||
@@ -2219,6 +2221,7 @@ struct client {
|
||||
struct mouse_event click_event;
|
||||
|
||||
struct status_line status;
|
||||
struct event cycle_timer;
|
||||
enum client_theme theme;
|
||||
|
||||
struct input_requests input_requests;
|
||||
@@ -2757,6 +2760,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 *);
|
||||
@@ -2774,6 +2781,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 *);
|
||||
@@ -3729,7 +3739,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 *);
|
||||
@@ -3757,8 +3766,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 *,
|
||||
@@ -3952,6 +3963,8 @@ 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 *);
|
||||
|
||||
@@ -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-keys.c,v 1.210 2026/07/10 13:38:45 nicm Exp $ */
|
||||
/* $OpenBSD: tty-keys.c,v 1.211 2026/07/21 07:12:49 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)
|
||||
|
||||
@@ -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-copy.c,v 1.422 2026/07/20 11:16:33 nicm Exp $ */
|
||||
/* $OpenBSD: window-copy.c,v 1.423 2026/07/21 11:52:13 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -62,6 +62,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);
|
||||
@@ -5027,7 +5028,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 +5037,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 +5069,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 +5112,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 +5124,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 +5182,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 +5308,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 +5339,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 +5369,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 +5621,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))
|
||||
@@ -6799,6 +6827,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 +6898,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)
|
||||
{
|
||||
@@ -692,7 +722,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))
|
||||
@@ -701,13 +731,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;
|
||||
}
|
||||
@@ -1227,6 +1258,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;
|
||||
@@ -1240,6 +1272,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 = " ";
|
||||
@@ -1306,8 +1339,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,
|
||||
@@ -1333,10 +1374,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))
|
||||
|
||||
26
window.c
26
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.368 2026/07/23 09:38:27 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);
|
||||
@@ -472,7 +470,6 @@ window_destroy(struct window *w)
|
||||
event_del(&w->offset_timer);
|
||||
|
||||
options_free(w->options);
|
||||
free(w->fill_character);
|
||||
|
||||
free(w->name);
|
||||
free(w);
|
||||
@@ -571,13 +568,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
|
||||
@@ -2442,25 +2439,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)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user