summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicholas Marriott <nicm@cvs.openbsd.org>2020-05-16 16:02:25 +0000
committerNicholas Marriott <nicm@cvs.openbsd.org>2020-05-16 16:02:25 +0000
commit9c0c7cc2a4495d9a666bc12b72f8ab5eea3fe1ce (patch)
treeb70b768f1250608a42b0511429b63e0e5ba8b45c
parentef694b32fd2bc6693d9baff4d2af211a11714165 (diff)
Add a customize mode where keys and options may be browsed and changed,
includes adding a brief description of each option. Bound to "C" by default.
-rw-r--r--usr.bin/tmux/Makefile3
-rw-r--r--usr.bin/tmux/cmd-choose-tree.c19
-rw-r--r--usr.bin/tmux/cmd-list-keys.c10
-rw-r--r--usr.bin/tmux/cmd-run-shell.c5
-rw-r--r--usr.bin/tmux/cmd-set-option.c218
-rw-r--r--usr.bin/tmux/cmd-show-messages.c6
-rw-r--r--usr.bin/tmux/cmd-show-options.c10
-rw-r--r--usr.bin/tmux/cmd.c4
-rw-r--r--usr.bin/tmux/format-draw.c4
-rw-r--r--usr.bin/tmux/format.c4
-rw-r--r--usr.bin/tmux/key-bindings.c62
-rw-r--r--usr.bin/tmux/mode-tree.c99
-rw-r--r--usr.bin/tmux/options-table.c360
-rw-r--r--usr.bin/tmux/options.c254
-rw-r--r--usr.bin/tmux/screen-write.c94
-rw-r--r--usr.bin/tmux/style.c9
-rw-r--r--usr.bin/tmux/tmux.149
-rw-r--r--usr.bin/tmux/tmux.h35
-rw-r--r--usr.bin/tmux/window-buffer.c6
-rw-r--r--usr.bin/tmux/window-client.c4
-rw-r--r--usr.bin/tmux/window-customize.c1430
-rw-r--r--usr.bin/tmux/window-tree.c4
22 files changed, 2305 insertions, 384 deletions
diff --git a/usr.bin/tmux/Makefile b/usr.bin/tmux/Makefile
index 9c238b3b207..d8ae81bdb36 100644
--- a/usr.bin/tmux/Makefile
+++ b/usr.bin/tmux/Makefile
@@ -1,4 +1,4 @@
-# $OpenBSD: Makefile,v 1.104 2020/04/20 13:25:36 nicm Exp $
+# $OpenBSD: Makefile,v 1.105 2020/05/16 16:02:24 nicm Exp $
PROG= tmux
SRCS= alerts.c \
@@ -120,6 +120,7 @@ SRCS= alerts.c \
window-client.c \
window-clock.c \
window-copy.c \
+ window-customize.c \
window-tree.c \
window.c \
xmalloc.c \
diff --git a/usr.bin/tmux/cmd-choose-tree.c b/usr.bin/tmux/cmd-choose-tree.c
index e9c9115bdda..61f9d68f5d0 100644
--- a/usr.bin/tmux/cmd-choose-tree.c
+++ b/usr.bin/tmux/cmd-choose-tree.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: cmd-choose-tree.c,v 1.46 2020/04/13 10:59:58 nicm Exp $ */
+/* $OpenBSD: cmd-choose-tree.c,v 1.47 2020/05/16 16:02:24 nicm Exp $ */
/*
* Copyright (c) 2012 Thomas Adam <thomas@xteddy.org>
@@ -68,6 +68,19 @@ const struct cmd_entry cmd_choose_buffer_entry = {
.exec = cmd_choose_tree_exec
};
+const struct cmd_entry cmd_customize_mode_entry = {
+ .name = "customize-mode",
+ .alias = NULL,
+
+ .args = { "F:f:Nt:Z", 0, 0 },
+ .usage = "[-NZ] [-F format] [-f filter] " CMD_TARGET_PANE_USAGE,
+
+ .target = { 't', CMD_FIND_PANE, 0 },
+
+ .flags = 0,
+ .exec = cmd_choose_tree_exec
+};
+
static enum cmd_retval
cmd_choose_tree_exec(struct cmd *self, struct cmdq_item *item)
{
@@ -84,7 +97,9 @@ cmd_choose_tree_exec(struct cmd *self, struct cmdq_item *item)
if (server_client_how_many() == 0)
return (CMD_RETURN_NORMAL);
mode = &window_client_mode;
- } else
+ } else if (cmd_get_entry(self) == &cmd_customize_mode_entry)
+ mode = &window_customize_mode;
+ else
mode = &window_tree_mode;
window_pane_set_mode(wp, NULL, mode, target, args);
diff --git a/usr.bin/tmux/cmd-list-keys.c b/usr.bin/tmux/cmd-list-keys.c
index d61d8c682fc..361ce6de6ed 100644
--- a/usr.bin/tmux/cmd-list-keys.c
+++ b/usr.bin/tmux/cmd-list-keys.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: cmd-list-keys.c,v 1.57 2020/05/16 15:54:20 nicm Exp $ */
+/* $OpenBSD: cmd-list-keys.c,v 1.58 2020/05/16 16:02:24 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -68,7 +68,8 @@ cmd_list_keys_get_width(const char *tablename, key_code only)
while (bd != NULL) {
if ((only != KEYC_UNKNOWN && bd->key != only) ||
KEYC_IS_MOUSE(bd->key) ||
- bd->note == NULL) {
+ bd->note == NULL ||
+ *bd->note == '\0') {
bd = key_bindings_next(table, bd);
continue;
}
@@ -99,14 +100,15 @@ cmd_list_keys_print_notes(struct cmdq_item *item, struct args *args,
while (bd != NULL) {
if ((only != KEYC_UNKNOWN && bd->key != only) ||
KEYC_IS_MOUSE(bd->key) ||
- (bd->note == NULL && !args_has(args, 'a'))) {
+ ((bd->note == NULL || *bd->note == '\0') &&
+ !args_has(args, 'a'))) {
bd = key_bindings_next(table, bd);
continue;
}
found = 1;
key = key_string_lookup_key(bd->key);
- if (bd->note == NULL)
+ if (bd->note == NULL || *bd->note == '\0')
note = cmd_list_print(bd->cmdlist, 1);
else
note = xstrdup(bd->note);
diff --git a/usr.bin/tmux/cmd-run-shell.c b/usr.bin/tmux/cmd-run-shell.c
index a28199b788a..ef3dba86e12 100644
--- a/usr.bin/tmux/cmd-run-shell.c
+++ b/usr.bin/tmux/cmd-run-shell.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: cmd-run-shell.c,v 1.67 2020/04/13 20:51:57 nicm Exp $ */
+/* $OpenBSD: cmd-run-shell.c,v 1.68 2020/05/16 16:02:24 nicm Exp $ */
/*
* Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>
@@ -190,7 +190,8 @@ cmd_run_shell_callback(struct job *job)
retcode = WTERMSIG(status);
xasprintf(&msg, "'%s' terminated by signal %d", cmd, retcode);
retcode += 128;
- }
+ } else
+ retcode = 0;
if (msg != NULL)
cmd_run_shell_print(job, msg);
free(msg);
diff --git a/usr.bin/tmux/cmd-set-option.c b/usr.bin/tmux/cmd-set-option.c
index 69f1a3c55c2..d2013192d3c 100644
--- a/usr.bin/tmux/cmd-set-option.c
+++ b/usr.bin/tmux/cmd-set-option.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: cmd-set-option.c,v 1.134 2020/05/16 15:01:31 nicm Exp $ */
+/* $OpenBSD: cmd-set-option.c,v 1.135 2020/05/16 16:02:24 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -30,15 +30,6 @@
static enum cmd_retval cmd_set_option_exec(struct cmd *, struct cmdq_item *);
-static int cmd_set_option_set(struct cmd *, struct cmdq_item *,
- struct options *, struct options_entry *, const char *);
-static int cmd_set_option_flag(struct cmdq_item *,
- const struct options_table_entry *, struct options *,
- const char *);
-static int cmd_set_option_choice(struct cmdq_item *,
- const struct options_table_entry *, struct options *,
- const char *);
-
const struct cmd_entry cmd_set_option_entry = {
.name = "set-option",
.alias = "set",
@@ -84,10 +75,6 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
struct args *args = cmd_get_args(self);
int append = args_has(args, 'a');
struct cmd_find_state *target = cmdq_get_target(item);
- struct client *loop;
- struct session *s = target->s;
- struct window *w;
- struct window_pane *wp;
struct options *oo;
struct options_entry *parent, *o;
char *name, *argument, *value = NULL, *cause;
@@ -138,7 +125,7 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
parent = options_get(oo, name);
/* Check that array options and indexes match up. */
- if (idx != -1 && (*name == '@' || !options_isarray(parent))) {
+ if (idx != -1 && (*name == '@' || !options_is_array(parent))) {
cmdq_error(item, "not an array: %s", argument);
goto fail;
}
@@ -185,10 +172,15 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
goto fail;
}
options_set_string(oo, name, append, "%s", value);
- } else if (idx == -1 && !options_isarray(parent)) {
- error = cmd_set_option_set(self, item, oo, parent, value);
- if (error != 0)
+ } else if (idx == -1 && !options_is_array(parent)) {
+ error = options_from_string(oo, options_table_entry(parent),
+ options_table_entry(parent)->name, value,
+ args_has(args, 'a'), &cause);
+ if (error != 0) {
+ cmdq_error(item, "%s", cause);
+ free(cause);
goto fail;
+ }
} else {
if (value == NULL) {
cmdq_error(item, "empty value");
@@ -212,51 +204,7 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
}
}
- /* Update timers and so on for various options. */
- if (strcmp(name, "automatic-rename") == 0) {
- RB_FOREACH(w, windows, &windows) {
- if (w->active == NULL)
- continue;
- if (options_get_number(w->options, "automatic-rename"))
- w->active->flags |= PANE_CHANGED;
- }
- }
- if (strcmp(name, "key-table") == 0) {
- TAILQ_FOREACH(loop, &clients, entry)
- server_client_set_key_table(loop, NULL);
- }
- if (strcmp(name, "user-keys") == 0) {
- TAILQ_FOREACH(loop, &clients, entry) {
- if (loop->tty.flags & TTY_OPENED)
- tty_keys_build(&loop->tty);
- }
- }
- if (strcmp(name, "status") == 0 ||
- strcmp(name, "status-interval") == 0)
- status_timer_start_all();
- if (strcmp(name, "monitor-silence") == 0)
- alerts_reset_all();
- if (strcmp(name, "window-style") == 0 ||
- strcmp(name, "window-active-style") == 0) {
- RB_FOREACH(wp, window_pane_tree, &all_window_panes)
- wp->flags |= PANE_STYLECHANGED;
- }
- if (strcmp(name, "pane-border-status") == 0) {
- RB_FOREACH(w, windows, &windows)
- layout_fix_panes(w);
- }
- RB_FOREACH(s, sessions, &sessions)
- status_update_cache(s);
-
- /*
- * Update sizes and redraw. May not always be necessary but do it
- * anyway.
- */
- recalculate_sizes();
- TAILQ_FOREACH(loop, &clients, entry) {
- if (loop->session != NULL)
- server_redraw_client(loop);
- }
+ options_push_changes(name);
out:
free(argument);
@@ -270,147 +218,3 @@ fail:
free(name);
return (CMD_RETURN_ERROR);
}
-
-static int
-cmd_set_option_check_string(const struct options_table_entry *oe,
- const char *value, char **cause)
-{
- struct style sy;
-
- if (strcmp(oe->name, "default-shell") == 0 && !checkshell(value)) {
- xasprintf(cause, "not a suitable shell: %s", value);
- return (-1);
- }
- if (oe->pattern != NULL && fnmatch(oe->pattern, value, 0) != 0) {
- xasprintf(cause, "value is invalid: %s", value);
- return (-1);
- }
- if ((oe->flags & OPTIONS_TABLE_IS_STYLE) &&
- strstr(value, "#{") == NULL &&
- style_parse(&sy, &grid_default_cell, value) != 0) {
- xasprintf(cause, "invalid style: %s", value);
- return (-1);
- }
- return (0);
-}
-
-static int
-cmd_set_option_set(struct cmd *self, struct cmdq_item *item, struct options *oo,
- struct options_entry *parent, const char *value)
-{
- const struct options_table_entry *oe;
- struct args *args = cmd_get_args(self);
- int append = args_has(args, 'a');
- long long number;
- const char *errstr, *new;
- char *old, *cause;
- key_code key;
-
- oe = options_table_entry(parent);
- if (value == NULL &&
- oe->type != OPTIONS_TABLE_FLAG &&
- oe->type != OPTIONS_TABLE_CHOICE) {
- cmdq_error(item, "empty value");
- return (-1);
- }
-
- switch (oe->type) {
- case OPTIONS_TABLE_STRING:
- old = xstrdup(options_get_string(oo, oe->name));
- options_set_string(oo, oe->name, append, "%s", value);
- new = options_get_string(oo, oe->name);
- if (cmd_set_option_check_string(oe, new, &cause) != 0) {
- cmdq_error(item, "%s", cause);
- free(cause);
-
- options_set_string(oo, oe->name, 0, "%s", old);
- free(old);
- return (-1);
- }
- free(old);
- return (0);
- case OPTIONS_TABLE_NUMBER:
- number = strtonum(value, oe->minimum, oe->maximum, &errstr);
- if (errstr != NULL) {
- cmdq_error(item, "value is %s: %s", errstr, value);
- return (-1);
- }
- options_set_number(oo, oe->name, number);
- return (0);
- case OPTIONS_TABLE_KEY:
- key = key_string_lookup_string(value);
- if (key == KEYC_UNKNOWN) {
- cmdq_error(item, "bad key: %s", value);
- return (-1);
- }
- options_set_number(oo, oe->name, key);
- return (0);
- case OPTIONS_TABLE_COLOUR:
- if ((number = colour_fromstring(value)) == -1) {
- cmdq_error(item, "bad colour: %s", value);
- return (-1);
- }
- options_set_number(oo, oe->name, number);
- return (0);
- case OPTIONS_TABLE_FLAG:
- return (cmd_set_option_flag(item, oe, oo, value));
- case OPTIONS_TABLE_CHOICE:
- return (cmd_set_option_choice(item, oe, oo, value));
- case OPTIONS_TABLE_COMMAND:
- break;
- }
- return (-1);
-}
-
-static int
-cmd_set_option_flag(struct cmdq_item *item,
- const struct options_table_entry *oe, struct options *oo,
- const char *value)
-{
- int flag;
-
- if (value == NULL || *value == '\0')
- flag = !options_get_number(oo, oe->name);
- else if (strcmp(value, "1") == 0 ||
- strcasecmp(value, "on") == 0 ||
- strcasecmp(value, "yes") == 0)
- flag = 1;
- else if (strcmp(value, "0") == 0 ||
- strcasecmp(value, "off") == 0 ||
- strcasecmp(value, "no") == 0)
- flag = 0;
- else {
- cmdq_error(item, "bad value: %s", value);
- return (-1);
- }
- options_set_number(oo, oe->name, flag);
- return (0);
-}
-
-static int
-cmd_set_option_choice(struct cmdq_item *item,
- const struct options_table_entry *oe, struct options *oo,
- const char *value)
-{
- const char **cp;
- int n, choice = -1;
-
- if (value == NULL) {
- choice = options_get_number(oo, oe->name);
- if (choice < 2)
- choice = !choice;
- } else {
- n = 0;
- for (cp = oe->choices; *cp != NULL; cp++) {
- if (strcmp(*cp, value) == 0)
- choice = n;
- n++;
- }
- if (choice == -1) {
- cmdq_error(item, "unknown value: %s", value);
- return (-1);
- }
- }
- options_set_number(oo, oe->name, choice);
- return (0);
-}
diff --git a/usr.bin/tmux/cmd-show-messages.c b/usr.bin/tmux/cmd-show-messages.c
index 9dbb39fa248..9d1c25659db 100644
--- a/usr.bin/tmux/cmd-show-messages.c
+++ b/usr.bin/tmux/cmd-show-messages.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: cmd-show-messages.c,v 1.33 2020/05/16 15:47:22 nicm Exp $ */
+/* $OpenBSD: cmd-show-messages.c,v 1.34 2020/05/16 16:02:24 nicm Exp $ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -18,6 +18,7 @@
#include <sys/types.h>
+#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
@@ -29,6 +30,9 @@
* Show client message log.
*/
+#define SHOW_MESSAGES_TEMPLATE \
+ "#{t/p:message_time}: #{message_text}"
+
static enum cmd_retval cmd_show_messages_exec(struct cmd *,
struct cmdq_item *);
diff --git a/usr.bin/tmux/cmd-show-options.c b/usr.bin/tmux/cmd-show-options.c
index 9ac68028567..9d39fdd3351 100644
--- a/usr.bin/tmux/cmd-show-options.c
+++ b/usr.bin/tmux/cmd-show-options.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: cmd-show-options.c,v 1.63 2020/04/13 20:54:15 nicm Exp $ */
+/* $OpenBSD: cmd-show-options.c,v 1.64 2020/05/16 16:02:24 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -151,7 +151,7 @@ cmd_show_options_print(struct cmd *self, struct cmdq_item *item,
xasprintf(&tmp, "%s[%d]", name, idx);
name = tmp;
} else {
- if (options_isarray(o)) {
+ if (options_is_array(o)) {
a = options_array_first(o);
if (a == NULL) {
if (!args_has(args, 'v'))
@@ -168,10 +168,10 @@ cmd_show_options_print(struct cmd *self, struct cmdq_item *item,
}
}
- value = options_tostring(o, idx, 0);
+ value = options_to_string(o, idx, 0);
if (args_has(args, 'v'))
cmdq_print(item, "%s", value);
- else if (options_isstring(o)) {
+ else if (options_is_string(o)) {
escaped = args_escape(value);
if (parent)
cmdq_print(item, "%s* %s", name, escaped);
@@ -229,7 +229,7 @@ cmd_show_options_all(struct cmd *self, struct cmdq_item *item, int scope,
} else
parent = 0;
- if (!options_isarray(o))
+ if (!options_is_array(o))
cmd_show_options_print(self, item, o, -1, parent);
else if ((a = options_array_first(o)) == NULL) {
if (!args_has(args, 'v')) {
diff --git a/usr.bin/tmux/cmd.c b/usr.bin/tmux/cmd.c
index b4f0d863398..7e6f7fc1f86 100644
--- a/usr.bin/tmux/cmd.c
+++ b/usr.bin/tmux/cmd.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: cmd.c,v 1.160 2020/04/13 16:19:37 nicm Exp $ */
+/* $OpenBSD: cmd.c,v 1.161 2020/05/16 16:02:24 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -40,6 +40,7 @@ extern const struct cmd_entry cmd_clock_mode_entry;
extern const struct cmd_entry cmd_command_prompt_entry;
extern const struct cmd_entry cmd_confirm_before_entry;
extern const struct cmd_entry cmd_copy_mode_entry;
+extern const struct cmd_entry cmd_customize_mode_entry;
extern const struct cmd_entry cmd_delete_buffer_entry;
extern const struct cmd_entry cmd_detach_client_entry;
extern const struct cmd_entry cmd_display_menu_entry;
@@ -130,6 +131,7 @@ const struct cmd_entry *cmd_table[] = {
&cmd_command_prompt_entry,
&cmd_confirm_before_entry,
&cmd_copy_mode_entry,
+ &cmd_customize_mode_entry,
&cmd_delete_buffer_entry,
&cmd_detach_client_entry,
&cmd_display_menu_entry,
diff --git a/usr.bin/tmux/format-draw.c b/usr.bin/tmux/format-draw.c
index 9f8b08862b7..471094b264f 100644
--- a/usr.bin/tmux/format-draw.c
+++ b/usr.bin/tmux/format-draw.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: format-draw.c,v 1.18 2020/05/16 15:34:08 nicm Exp $ */
+/* $OpenBSD: format-draw.c,v 1.19 2020/05/16 16:02:24 nicm Exp $ */
/*
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -547,7 +547,7 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
*/
cp = expanded;
while (*cp != '\0') {
- if (cp[0] != '#' || cp[1] != '[') {
+ if (cp[0] != '#' || cp[1] != '[' || sy.ignore) {
/* See if this is a UTF-8 character. */
if ((more = utf8_open(ud, *cp)) == UTF8_MORE) {
while (*++cp != '\0' && more == UTF8_MORE)
diff --git a/usr.bin/tmux/format.c b/usr.bin/tmux/format.c
index 08ea5dbca7b..1b33b71f9bd 100644
--- a/usr.bin/tmux/format.c
+++ b/usr.bin/tmux/format.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: format.c,v 1.254 2020/05/16 15:48:35 nicm Exp $ */
+/* $OpenBSD: format.c,v 1.255 2020/05/16 16:02:24 nicm Exp $ */
/*
* Copyright (c) 2011 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -1392,7 +1392,7 @@ format_find(struct format_tree *ft, const char *key, int modifiers,
if (o == NULL)
o = options_parse_get(global_s_options, key, &idx, 0);
if (o != NULL) {
- found = options_tostring(o, idx, 1);
+ found = options_to_string(o, idx, 1);
goto found;
}
diff --git a/usr.bin/tmux/key-bindings.c b/usr.bin/tmux/key-bindings.c
index 5f0f9902f3f..19bc55a578a 100644
--- a/usr.bin/tmux/key-bindings.c
+++ b/usr.bin/tmux/key-bindings.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: key-bindings.c,v 1.124 2020/05/16 15:16:36 nicm Exp $ */
+/* $OpenBSD: key-bindings.c,v 1.125 2020/05/16 16:02:24 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -89,9 +89,8 @@ key_bindings_cmp(struct key_binding *bd1, struct key_binding *bd2)
}
static void
-key_bindings_free(struct key_table *table, struct key_binding *bd)
+key_bindings_free(struct key_binding *bd)
{
- RB_REMOVE(key_bindings, &table->key_bindings, bd);
cmd_list_free(bd->cmdlist);
free((void *)bd->note);
free(bd);
@@ -110,6 +109,7 @@ key_bindings_get_table(const char *name, int create)
table = xmalloc(sizeof *table);
table->name = xstrdup(name);
RB_INIT(&table->key_bindings);
+ RB_INIT(&table->default_key_bindings);
table->references = 1; /* one reference in key_tables */
RB_INSERT(key_tables, &key_tables, table);
@@ -138,8 +138,14 @@ key_bindings_unref_table(struct key_table *table)
if (--table->references != 0)
return;
- RB_FOREACH_SAFE(bd, key_bindings, &table->key_bindings, bd1)
- key_bindings_free(table, bd);
+ RB_FOREACH_SAFE(bd, key_bindings, &table->key_bindings, bd1) {
+ RB_REMOVE(key_bindings, &table->key_bindings, bd);
+ key_bindings_free(bd);
+ }
+ RB_FOREACH_SAFE(bd, key_bindings, &table->default_key_bindings, bd1) {
+ RB_REMOVE(key_bindings, &table->default_key_bindings, bd);
+ key_bindings_free(bd);
+ }
free((void *)table->name);
free(table);
@@ -155,6 +161,15 @@ key_bindings_get(struct key_table *table, key_code key)
}
struct key_binding *
+key_bindings_get_default(struct key_table *table, key_code key)
+{
+ struct key_binding bd;
+
+ bd.key = key;
+ return (RB_FIND(key_bindings, &table->default_key_bindings, &bd));
+}
+
+struct key_binding *
key_bindings_first(struct key_table *table)
{
return (RB_MIN(key_bindings, &table->key_bindings));
@@ -176,8 +191,10 @@ key_bindings_add(const char *name, key_code key, const char *note, int repeat,
table = key_bindings_get_table(name, 1);
bd = key_bindings_get(table, key & ~KEYC_XTERM);
- if (bd != NULL)
- key_bindings_free(table, bd);
+ if (bd != NULL) {
+ RB_REMOVE(key_bindings, &table->key_bindings, bd);
+ key_bindings_free(bd);
+ }
bd = xcalloc(1, sizeof *bd);
bd->key = key;
@@ -203,9 +220,12 @@ key_bindings_remove(const char *name, key_code key)
bd = key_bindings_get(table, key & ~KEYC_XTERM);
if (bd == NULL)
return;
- key_bindings_free(table, bd);
- if (RB_EMPTY(&table->key_bindings)) {
+ RB_REMOVE(key_bindings, &table->key_bindings, bd);
+ key_bindings_free(bd);
+
+ if (RB_EMPTY(&table->key_bindings) &&
+ RB_EMPTY(&table->default_key_bindings)) {
RB_REMOVE(key_tables, &key_tables, table);
key_bindings_unref_table(table);
}
@@ -228,6 +248,28 @@ key_bindings_remove_table(const char *name)
}
}
+static enum cmd_retval
+key_bindings_init_done(__unused struct cmdq_item *item, __unused void *data)
+{
+ struct key_table *table;
+ struct key_binding *bd, *new_bd;
+
+ RB_FOREACH(table, key_tables, &key_tables) {
+ RB_FOREACH(bd, key_bindings, &table->key_bindings) {
+ new_bd = xcalloc(1, sizeof *bd);
+ new_bd->key = bd->key;
+ if (bd->note != NULL)
+ new_bd->note = xstrdup(bd->note);
+ new_bd->flags = bd->flags;
+ new_bd->cmdlist = bd->cmdlist;
+ new_bd->cmdlist->references++;
+ RB_INSERT(key_bindings, &table->default_key_bindings,
+ new_bd);
+ }
+ }
+ return (CMD_RETURN_NORMAL);
+}
+
void
key_bindings_init(void)
{
@@ -278,6 +320,7 @@ key_bindings_init(void)
"bind -N 'Toggle the marked pane' m select-pane -m",
"bind -N 'Select the next window' n next-window",
"bind -N 'Select the next pane' o select-pane -t:.+",
+ "bind -N 'Customize options' C customize-mode -Z",
"bind -N 'Select the previous pane' p previous-window",
"bind -N 'Display pane numbers' q display-panes",
"bind -N 'Redraw the current client' r refresh-client",
@@ -524,6 +567,7 @@ key_bindings_init(void)
cmdq_append(NULL, cmdq_get_command(pr->cmdlist, NULL));
cmd_list_free(pr->cmdlist);
}
+ cmdq_append(NULL, cmdq_get_callback(key_bindings_init_done, NULL));
}
static enum cmd_retval
diff --git a/usr.bin/tmux/mode-tree.c b/usr.bin/tmux/mode-tree.c
index 1169238398e..740305bb5a9 100644
--- a/usr.bin/tmux/mode-tree.c
+++ b/usr.bin/tmux/mode-tree.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: mode-tree.c,v 1.45 2020/05/16 15:54:20 nicm Exp $ */
+/* $OpenBSD: mode-tree.c,v 1.46 2020/05/16 16:02:24 nicm Exp $ */
/*
* Copyright (c) 2017 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -45,6 +45,7 @@ struct mode_tree_data {
mode_tree_draw_cb drawcb;
mode_tree_search_cb searchcb;
mode_tree_menu_cb menucb;
+ mode_tree_height_cb heightcb;
struct mode_tree_list children;
struct mode_tree_list saved;
@@ -79,6 +80,7 @@ struct mode_tree_item {
int expanded;
int tagged;
+ int draw_as_parent;
struct mode_tree_list children;
TAILQ_ENTRY(mode_tree_item) entry;
@@ -210,7 +212,7 @@ mode_tree_clear_tagged(struct mode_tree_list *mtl)
}
}
-static void
+void
mode_tree_up(struct mode_tree_data *mtd, int wrap)
{
if (mtd->current == 0) {
@@ -247,6 +249,12 @@ mode_tree_get_current(struct mode_tree_data *mtd)
return (mtd->line_list[mtd->current].item->itemdata);
}
+const char *
+mode_tree_get_current_name(struct mode_tree_data *mtd)
+{
+ return (mtd->line_list[mtd->current].item->name);
+}
+
void
mode_tree_expand_current(struct mode_tree_data *mtd)
{
@@ -256,6 +264,15 @@ mode_tree_expand_current(struct mode_tree_data *mtd)
}
}
+void
+mode_tree_collapse_current(struct mode_tree_data *mtd)
+{
+ if (mtd->line_list[mtd->current].item->expanded) {
+ mtd->line_list[mtd->current].item->expanded = 0;
+ mode_tree_build(mtd);
+ }
+}
+
static int
mode_tree_get_tag(struct mode_tree_data *mtd, uint64_t tag, u_int *found)
{
@@ -343,7 +360,8 @@ mode_tree_each_tagged(struct mode_tree_data *mtd, mode_tree_each_cb cb,
struct mode_tree_data *
mode_tree_start(struct window_pane *wp, struct args *args,
mode_tree_build_cb buildcb, mode_tree_draw_cb drawcb,
- mode_tree_search_cb searchcb, mode_tree_menu_cb menucb, void *modedata,
+ mode_tree_search_cb searchcb, mode_tree_menu_cb menucb,
+ mode_tree_height_cb heightcb, void *modedata,
const struct menu_item *menu, const char **sort_list, u_int sort_size,
struct screen **s)
{
@@ -381,6 +399,7 @@ mode_tree_start(struct window_pane *wp, struct args *args,
mtd->drawcb = drawcb;
mtd->searchcb = searchcb;
mtd->menucb = menucb;
+ mtd->heightcb = heightcb;
TAILQ_INIT(&mtd->children);
@@ -404,6 +423,27 @@ mode_tree_zoom(struct mode_tree_data *mtd, struct args *args)
mtd->zoomed = -1;
}
+static void
+mode_tree_set_height(struct mode_tree_data *mtd)
+{
+ struct screen *s = &mtd->screen;
+ u_int height;
+
+ if (mtd->heightcb != NULL) {
+ height = mtd->heightcb(mtd, screen_size_y(s));
+ if (height < screen_size_y(s))
+ mtd->height = screen_size_y(s) - height;
+ } else {
+ mtd->height = (screen_size_y(s) / 3) * 2;
+ if (mtd->height > mtd->line_size)
+ mtd->height = screen_size_y(s) / 2;
+ }
+ if (mtd->height < 10)
+ mtd->height = screen_size_y(s);
+ if (screen_size_y(s) - mtd->height < 2)
+ mtd->height = screen_size_y(s);
+}
+
void
mode_tree_build(struct mode_tree_data *mtd)
{
@@ -434,15 +474,9 @@ mode_tree_build(struct mode_tree_data *mtd)
mode_tree_set_current(mtd, tag);
mtd->width = screen_size_x(s);
- if (mtd->preview) {
- mtd->height = (screen_size_y(s) / 3) * 2;
- if (mtd->height > mtd->line_size)
- mtd->height = screen_size_y(s) / 2;
- if (mtd->height < 10)
- mtd->height = screen_size_y(s);
- if (screen_size_y(s) - mtd->height < 2)
- mtd->height = screen_size_y(s);
- } else
+ if (mtd->preview)
+ mode_tree_set_height(mtd);
+ else
mtd->height = screen_size_y(s);
mode_tree_check_selected(mtd);
}
@@ -494,7 +528,7 @@ mode_tree_add(struct mode_tree_data *mtd, struct mode_tree_item *parent,
struct mode_tree_item *mti, *saved;
log_debug("%s: %llu, %s %s", __func__, (unsigned long long)tag,
- name, text);
+ name, (text == NULL ? "" : text));
mti = xcalloc(1, sizeof *mti);
mti->parent = parent;
@@ -502,7 +536,8 @@ mode_tree_add(struct mode_tree_data *mtd, struct mode_tree_item *parent,
mti->tag = tag;
mti->name = xstrdup(name);
- mti->text = xstrdup(text);
+ if (text != NULL)
+ mti->text = xstrdup(text);
saved = mode_tree_find_item(&mtd->saved, tag);
if (saved != NULL) {
@@ -525,6 +560,12 @@ mode_tree_add(struct mode_tree_data *mtd, struct mode_tree_item *parent,
}
void
+mode_tree_draw_as_parent(struct mode_tree_item *mti)
+{
+ mti->draw_as_parent = 1;
+}
+
+void
mode_tree_remove(struct mode_tree_data *mtd, struct mode_tree_item *mti)
{
struct mode_tree_item *parent = mti->parent;
@@ -621,8 +662,8 @@ mode_tree_draw(struct mode_tree_data *mtd)
tag = "*";
else
tag = "";
- xasprintf(&text, "%-*s%s%s%s: ", keylen, key, start, mti->name,
- tag);
+ xasprintf(&text, "%-*s%s%s%s%s", keylen, key, start, mti->name,
+ tag, (mti->text != NULL) ? ": " : "" );
width = utf8_cstrwidth(text);
if (width > w)
width = w;
@@ -636,11 +677,17 @@ mode_tree_draw(struct mode_tree_data *mtd)
if (i != mtd->current) {
screen_write_clearendofline(&ctx, 8);
screen_write_nputs(&ctx, w, &gc0, "%s", text);
- format_draw(&ctx, &gc0, w - width, mti->text, NULL);
+ if (mti->text != NULL) {
+ format_draw(&ctx, &gc0, w - width, mti->text,
+ NULL);
+ }
} else {
screen_write_clearendofline(&ctx, gc.bg);
screen_write_nputs(&ctx, w, &gc, "%s", text);
- format_draw(&ctx, &gc, w - width, mti->text, NULL);
+ if (mti->text != NULL) {
+ format_draw(&ctx, &gc, w - width, mti->text,
+ NULL);
+ }
}
free(text);
@@ -658,13 +705,18 @@ mode_tree_draw(struct mode_tree_data *mtd)
line = &mtd->line_list[mtd->current];
mti = line->item;
+ if (mti->draw_as_parent)
+ mti = mti->parent;
screen_write_cursormove(&ctx, 0, h, 0);
screen_write_box(&ctx, w, sy - h);
- xasprintf(&text, " %s (sort: %s%s)", mti->name,
- mtd->sort_list[mtd->sort_crit.field],
- mtd->sort_crit.reversed ? ", reversed" : "");
+ if (mtd->sort_list != NULL) {
+ xasprintf(&text, " %s (sort: %s%s)", mti->name,
+ mtd->sort_list[mtd->sort_crit.field],
+ mtd->sort_crit.reversed ? ", reversed" : "");
+ } else
+ xasprintf(&text, " %s", mti->name);
if (w - 2 >= strlen(text)) {
screen_write_cursormove(&ctx, 1, h, 0);
screen_write_puts(&ctx, &gc0, "%s", text);
@@ -680,7 +732,8 @@ mode_tree_draw(struct mode_tree_data *mtd)
else
screen_write_puts(&ctx, &gc0, "active");
screen_write_puts(&ctx, &gc0, ") ");
- }
+ } else
+ screen_write_puts(&ctx, &gc0, " ");
}
free(text);
@@ -1027,7 +1080,7 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
break;
case 'O':
mtd->sort_crit.field++;
- if (mtd->sort_crit.field == mtd->sort_size)
+ if (mtd->sort_crit.field >= mtd->sort_size)
mtd->sort_crit.field = 0;
mode_tree_build(mtd);
break;
diff --git a/usr.bin/tmux/options-table.c b/usr.bin/tmux/options-table.c
index 0a55720e0f6..67a59a249f5 100644
--- a/usr.bin/tmux/options-table.c
+++ b/usr.bin/tmux/options-table.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: options-table.c,v 1.126 2020/05/16 15:47:22 nicm Exp $ */
+/* $OpenBSD: options-table.c,v 1.127 2020/05/16 16:02:24 nicm Exp $ */
/*
* Copyright (c) 2011 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -103,9 +103,9 @@ static const char *options_table_window_size_list[] = {
"," \
"#[range=window|#{window_index} list=focus " \
"#{?#{!=:#{window-status-current-style},default}," \
- "#{window-status-current-style}," \
- "#{window-status-style}" \
- "}" \
+ "#{window-status-current-style}," \
+ "#{window-status-style}" \
+ "}" \
"#{?#{&&:#{window_last_flag}," \
"#{!=:#{window-status-last-style},default}}, " \
"#{window-status-last-style}," \
@@ -175,6 +175,7 @@ const struct options_table_entry options_table[] = {
.type = OPTIONS_TABLE_KEY,
.scope = OPTIONS_TABLE_SERVER,
.default_num = '\177',
+ .text = "The key to send for backspace."
},
{ .name = "buffer-limit",
@@ -182,7 +183,9 @@ const struct options_table_entry options_table[] = {
.scope = OPTIONS_TABLE_SERVER,
.minimum = 1,
.maximum = INT_MAX,
- .default_num = 50
+ .default_num = 50,
+ .text = "The maximum number of automatic buffers. "
+ "When this is reached, the oldest buffer is deleted."
},
{ .name = "command-alias",
@@ -195,25 +198,31 @@ const struct options_table_entry options_table[] = {
"info=show-messages -JT,"
"choose-window=choose-tree -w,"
"choose-session=choose-tree -s",
- .separator = ","
+ .separator = ",",
+ .text = "Array of command aliases. "
+ "Each entry is an alias and a command separated by '='."
},
{ .name = "copy-command",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SERVER,
- .default_str = ""
+ .default_str = "",
+ .text = "Shell command run when text is copied. "
+ "If empty, no command is run."
},
{ .name = "default-terminal",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SERVER,
- .default_str = "screen"
+ .default_str = "screen",
+ .text = "Default for the 'TERM' environment variable."
},
{ .name = "editor",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SERVER,
- .default_str = _PATH_VI
+ .default_str = _PATH_VI,
+ .text = "Editor run to edit files."
},
{ .name = "escape-time",
@@ -221,31 +230,38 @@ const struct options_table_entry options_table[] = {
.scope = OPTIONS_TABLE_SERVER,
.minimum = 0,
.maximum = INT_MAX,
- .default_num = 500
+ .default_num = 500,
+ .text = "Time to wait before assuming a key is Escape."
},
{ .name = "exit-empty",
.type = OPTIONS_TABLE_FLAG,
.scope = OPTIONS_TABLE_SERVER,
- .default_num = 1
+ .default_num = 1,
+ .text = "Whether the server should exit if there are no sessions."
},
{ .name = "exit-unattached",
.type = OPTIONS_TABLE_FLAG,
.scope = OPTIONS_TABLE_SERVER,
- .default_num = 0
+ .default_num = 0,
+ .text = "Whether the server should exit if there are no attached "
+ "clients."
},
{ .name = "focus-events",
.type = OPTIONS_TABLE_FLAG,
.scope = OPTIONS_TABLE_SERVER,
- .default_num = 0
+ .default_num = 0,
+ .text = "Whether to send focus events to applications."
},
{ .name = "history-file",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SERVER,
- .default_str = ""
+ .default_str = "",
+ .text = "Location of the command prompt history file. "
+ "Empty does not write a history file."
},
{ .name = "message-limit",
@@ -253,14 +269,18 @@ const struct options_table_entry options_table[] = {
.scope = OPTIONS_TABLE_SERVER,
.minimum = 0,
.maximum = INT_MAX,
- .default_num = 1000
+ .default_num = 1000,
+ .text = "Maximum number of server messages to keep."
},
{ .name = "set-clipboard",
.type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_SERVER,
.choices = options_table_set_clipboard_list,
- .default_num = 1
+ .default_num = 1,
+ .text = "Whether to attempt to set the system clipboard ('on' or "
+ "'external') and whether to allow applications to create "
+ "paste buffers with an escape sequence ('on' only)."
},
{ .name = "terminal-overrides",
@@ -268,7 +288,8 @@ const struct options_table_entry options_table[] = {
.scope = OPTIONS_TABLE_SERVER,
.flags = OPTIONS_TABLE_IS_ARRAY,
.default_str = "",
- .separator = ","
+ .separator = ",",
+ .text = "List of terminal capabilities overrides."
},
{ .name = "terminal-features",
@@ -276,8 +297,10 @@ const struct options_table_entry options_table[] = {
.scope = OPTIONS_TABLE_SERVER,
.flags = OPTIONS_TABLE_IS_ARRAY,
.default_str = "xterm*:clipboard:ccolour:cstyle:title,"
- "screen*:title",
- .separator = ","
+ "screen*:title",
+ .separator = ",",
+ .text = "List of terminal features, used if they cannot be "
+ "automatically detected."
},
{ .name = "user-keys",
@@ -285,7 +308,10 @@ const struct options_table_entry options_table[] = {
.scope = OPTIONS_TABLE_SERVER,
.flags = OPTIONS_TABLE_IS_ARRAY,
.default_str = "",
- .separator = ","
+ .separator = ",",
+ .text = "User key assignments. "
+ "Each sequence in the list is translated into a key: "
+ "'User0', 'User1' and so on."
},
/* Session options. */
@@ -293,7 +319,8 @@ const struct options_table_entry options_table[] = {
.type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_SESSION,
.choices = options_table_bell_action_list,
- .default_num = ALERT_OTHER
+ .default_num = ALERT_OTHER,
+ .text = "Action to take on an activity alert."
},
{ .name = "assume-paste-time",
@@ -302,6 +329,9 @@ const struct options_table_entry options_table[] = {
.minimum = 0,
.maximum = INT_MAX,
.default_num = 1,
+ .unit = "milliseconds",
+ .text = "Maximum time between input to assume it pasting rather "
+ "than typing."
},
{ .name = "base-index",
@@ -309,57 +339,69 @@ const struct options_table_entry options_table[] = {
.scope = OPTIONS_TABLE_SESSION,
.minimum = 0,
.maximum = INT_MAX,
- .default_num = 0
+ .default_num = 0,
+ .text = "Default index of the first window in each session."
},
{ .name = "bell-action",
.type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_SESSION,
.choices = options_table_bell_action_list,
- .default_num = ALERT_ANY
+ .default_num = ALERT_ANY,
+ .text = "Action to take on a bell alert."
},
{ .name = "default-command",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SESSION,
- .default_str = ""
+ .default_str = "",
+ .text = "Default command to run in new panes. If empty, a shell is "
+ "started."
},
{ .name = "default-shell",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SESSION,
- .default_str = _PATH_BSHELL
+ .default_str = _PATH_BSHELL,
+ .text = "Location of default shell."
},
{ .name = "default-size",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SESSION,
.pattern = "[0-9]*x[0-9]*",
- .default_str = "80x24"
+ .default_str = "80x24",
+ .text = "Initial size of new sessions."
},
{ .name = "destroy-unattached",
.type = OPTIONS_TABLE_FLAG,
.scope = OPTIONS_TABLE_SESSION,
- .default_num = 0
+ .default_num = 0,
+ .text = "Whether to destroy sessions when they have no attached "
+ "clients."
},
{ .name = "detach-on-destroy",
.type = OPTIONS_TABLE_FLAG,
.scope = OPTIONS_TABLE_SESSION,
- .default_num = 1
+ .default_num = 1,
+ .text = "Whether to detach when a session is destroyed, or switch "
+ "the client to another session if any exist."
},
{ .name = "display-panes-active-colour",
.type = OPTIONS_TABLE_COLOUR,
.scope = OPTIONS_TABLE_SESSION,
- .default_num = 1
+ .default_num = 1,
+ .text = "Colour of the active pane for 'display-panes'."
},
{ .name = "display-panes-colour",
.type = OPTIONS_TABLE_COLOUR,
.scope = OPTIONS_TABLE_SESSION,
- .default_num = 4
+ .default_num = 4,
+ .text = "Colour of not active panes for 'display-panes'."
},
{ .name = "display-panes-time",
@@ -367,7 +409,9 @@ const struct options_table_entry options_table[] = {
.scope = OPTIONS_TABLE_SESSION,
.minimum = 1,
.maximum = INT_MAX,
- .default_num = 1000
+ .default_num = 1000,
+ .unit = "milliseconds",
+ .text = "Time for which 'display-panes' should show pane numbers."
},
{ .name = "display-time",
@@ -375,7 +419,9 @@ const struct options_table_entry options_table[] = {
.scope = OPTIONS_TABLE_SESSION,
.minimum = 0,
.maximum = INT_MAX,
- .default_num = 750
+ .default_num = 750,
+ .unit = "milliseconds",
+ .text = "Time for which status line messages should appear."
},
{ .name = "history-limit",
@@ -383,13 +429,19 @@ const struct options_table_entry options_table[] = {
.scope = OPTIONS_TABLE_SESSION,
.minimum = 0,
.maximum = INT_MAX,
- .default_num = 2000
+ .default_num = 2000,
+ .unit = "lines",
+ .text = "Maximum number of lines to keep in the history for each "
+ "pane. "
+ "If changed, the new value applies only to new panes."
},
{ .name = "key-table",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SESSION,
- .default_str = "root"
+ .default_str = "root",
+ .text = "Default key table. "
+ "Key presses are first looked up in this table."
},
{ .name = "lock-after-time",
@@ -397,13 +449,16 @@ const struct options_table_entry options_table[] = {
.scope = OPTIONS_TABLE_SESSION,
.minimum = 0,
.maximum = INT_MAX,
- .default_num = 0
+ .default_num = 0,
+ .unit = "seconds",
+ .text = "Time after which a client is locked if not used."
},
{ .name = "lock-command",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SESSION,
- .default_str = "lock -np"
+ .default_str = "lock -np",
+ .text = "Shell command to run to lock a client."
},
{ .name = "message-command-style",
@@ -411,7 +466,9 @@ const struct options_table_entry options_table[] = {
.scope = OPTIONS_TABLE_SESSION,
.default_str = "bg=black,fg=yellow",
.flags = OPTIONS_TABLE_IS_STYLE,
- .separator = ","
+ .separator = ",",
+ .text = "Style of the command prompt when in command mode, if "
+ "'mode-keys' is set to 'vi'."
},
{ .name = "message-style",
@@ -419,31 +476,39 @@ const struct options_table_entry options_table[] = {
.scope = OPTIONS_TABLE_SESSION,
.default_str = "bg=yellow,fg=black",
.flags = OPTIONS_TABLE_IS_STYLE,
- .separator = ","
+ .separator = ",",
+ .text = "Style of the command prompt."
},
{ .name = "mouse",
.type = OPTIONS_TABLE_FLAG,
.scope = OPTIONS_TABLE_SESSION,
- .default_num = 0
+ .default_num = 0,
+ .text = "Whether the mouse is recognised and mouse key bindings are "
+ "executed. "
+ "Applications inside panes can use the mouse even when 'off'."
},
{ .name = "prefix",
.type = OPTIONS_TABLE_KEY,
.scope = OPTIONS_TABLE_SESSION,
.default_num = '\002',
+ .text = "The prefix key."
},
{ .name = "prefix2",
.type = OPTIONS_TABLE_KEY,
.scope = OPTIONS_TABLE_SESSION,
.default_num = KEYC_NONE,
+ .text = "A second prefix key."
},
{ .name = "renumber-windows",
.type = OPTIONS_TABLE_FLAG,
.scope = OPTIONS_TABLE_SESSION,
- .default_num = 0
+ .default_num = 0,
+ .text = "Whether windows are automatically renumbered rather than "
+ "leaving gaps."
},
{ .name = "repeat-time",
@@ -451,45 +516,56 @@ const struct options_table_entry options_table[] = {
.scope = OPTIONS_TABLE_SESSION,
.minimum = 0,
.maximum = SHRT_MAX,
- .default_num = 500
+ .default_num = 500,
+ .unit = "milliseconds",
+ .text = "Time to wait for a key binding to repeat, if it is bound "
+ "with the '-r' flag."
},
{ .name = "set-titles",
.type = OPTIONS_TABLE_FLAG,
.scope = OPTIONS_TABLE_SESSION,
- .default_num = 0
+ .default_num = 0,
+ .text = "Whether to set the terminal title, if supported."
},
{ .name = "set-titles-string",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SESSION,
- .default_str = "#S:#I:#W - \"#T\" #{session_alerts}"
+ .default_str = "#S:#I:#W - \"#T\" #{session_alerts}",
+ .text = "Format of the terminal title to set."
},
{ .name = "silence-action",
.type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_SESSION,
.choices = options_table_bell_action_list,
- .default_num = ALERT_OTHER
+ .default_num = ALERT_OTHER,
+ .text = "Action to take on a silence alert."
},
{ .name = "status",
.type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_SESSION,
.choices = options_table_status_list,
- .default_num = 1
+ .default_num = 1,
+ .text = "Number of lines in the status line."
},
{ .name = "status-bg",
.type = OPTIONS_TABLE_COLOUR,
.scope = OPTIONS_TABLE_SESSION,
.default_num = 8,
+ .text = "Background colour of the status line. This option is "
+ "deprecated, use 'status-style' instead."
},
{ .name = "status-fg",
.type = OPTIONS_TABLE_COLOUR,
.scope = OPTIONS_TABLE_SESSION,
.default_num = 8,
+ .text = "Foreground colour of the status line. This option is "
+ "deprecated, use 'status-style' instead."
},
{ .name = "status-format",
@@ -497,6 +573,11 @@ const struct options_table_entry options_table[] = {
.scope = OPTIONS_TABLE_SESSION,
.flags = OPTIONS_TABLE_IS_ARRAY,
.default_arr = options_table_status_format_default,
+ .text = "Formats for the status lines. "
+ "Each array member is the format for one status line. "
+ "The default status line is made up of several components "
+ "which may be configured individually with other option such "
+ "as 'status-left'."
},
{ .name = "status-interval",
@@ -504,27 +585,32 @@ const struct options_table_entry options_table[] = {
.scope = OPTIONS_TABLE_SESSION,
.minimum = 0,
.maximum = INT_MAX,
- .default_num = 15
+ .default_num = 15,
+ .unit = "seconds",
+ .text = "Number of seconds between status line updates."
},
{ .name = "status-justify",
.type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_SESSION,
.choices = options_table_status_justify_list,
- .default_num = 0
+ .default_num = 0,
+ .text = "Position of the window list in the status line."
},
{ .name = "status-keys",
.type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_SESSION,
.choices = options_table_status_keys_list,
- .default_num = MODEKEY_EMACS
+ .default_num = MODEKEY_EMACS,
+ .text = "Key set to use at the command prompt."
},
{ .name = "status-left",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SESSION,
- .default_str = "[#S] "
+ .default_str = "[#{session_name}] ",
+ .text = "Contents of the left side of the status line."
},
{ .name = "status-left-length",
@@ -532,7 +618,8 @@ const struct options_table_entry options_table[] = {
.scope = OPTIONS_TABLE_SESSION,
.minimum = 0,
.maximum = SHRT_MAX,
- .default_num = 10
+ .default_num = 10,
+ .text = "Maximum width of the left side of the status line."
},
{ .name = "status-left-style",
@@ -540,22 +627,26 @@ const struct options_table_entry options_table[] = {
.scope = OPTIONS_TABLE_SESSION,
.default_str = "default",
.flags = OPTIONS_TABLE_IS_STYLE,
- .separator = ","
+ .separator = ",",
+ .text = "Style of the left side of the status line."
},
{ .name = "status-position",
.type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_SESSION,
.choices = options_table_status_position_list,
- .default_num = 1
+ .default_num = 1,
+ .text = "Position of the status line."
},
{ .name = "status-right",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SESSION,
.default_str = "#{?window_bigger,"
- "[#{window_offset_x}#,#{window_offset_y}] ,}"
- "\"#{=21:pane_title}\" %H:%M %d-%b-%y"
+ "[#{window_offset_x}#,#{window_offset_y}] ,}"
+ "\"#{=21:pane_title}\" %H:%M %d-%b-%y",
+ .text = "Contents of the right side of the status line."
+
},
{ .name = "status-right-length",
@@ -563,7 +654,8 @@ const struct options_table_entry options_table[] = {
.scope = OPTIONS_TABLE_SESSION,
.minimum = 0,
.maximum = SHRT_MAX,
- .default_num = 40
+ .default_num = 40,
+ .text = "Maximum width of the right side of the status line."
},
{ .name = "status-right-style",
@@ -571,7 +663,8 @@ const struct options_table_entry options_table[] = {
.scope = OPTIONS_TABLE_SESSION,
.default_str = "default",
.flags = OPTIONS_TABLE_IS_STYLE,
- .separator = ","
+ .separator = ",",
+ .text = "Style of the right side of the status line."
},
{ .name = "status-style",
@@ -579,7 +672,8 @@ const struct options_table_entry options_table[] = {
.scope = OPTIONS_TABLE_SESSION,
.default_str = "bg=green,fg=black",
.flags = OPTIONS_TABLE_IS_STYLE,
- .separator = ","
+ .separator = ",",
+ .text = "Style of the status line."
},
{ .name = "update-environment",
@@ -587,79 +681,100 @@ const struct options_table_entry options_table[] = {
.scope = OPTIONS_TABLE_SESSION,
.flags = OPTIONS_TABLE_IS_ARRAY,
.default_str = "DISPLAY KRB5CCNAME SSH_ASKPASS SSH_AUTH_SOCK "
- "SSH_AGENT_PID SSH_CONNECTION WINDOWID XAUTHORITY"
+ "SSH_AGENT_PID SSH_CONNECTION WINDOWID XAUTHORITY",
+ .text = "List of environment variables to update in the session "
+ "environment when a client is attached."
},
{ .name = "visual-activity",
.type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_SESSION,
.choices = options_table_visual_bell_list,
- .default_num = VISUAL_OFF
+ .default_num = VISUAL_OFF,
+ .text = "How activity alerts should be shown: a message ('on'), "
+ "a message and a bell ('both') or nothing ('off')."
},
{ .name = "visual-bell",
.type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_SESSION,
.choices = options_table_visual_bell_list,
- .default_num = VISUAL_OFF
+ .default_num = VISUAL_OFF,
+ .text = "How bell alerts should be shown: a message ('on'), "
+ "a message and a bell ('both') or nothing ('off')."
},
{ .name = "visual-silence",
.type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_SESSION,
.choices = options_table_visual_bell_list,
- .default_num = VISUAL_OFF
+ .default_num = VISUAL_OFF,
+ .text = "How silence alerts should be shown: a message ('on'), "
+ "a message and a bell ('both') or nothing ('off')."
},
{ .name = "word-separators",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SESSION,
- .default_str = " "
+ .default_str = " ",
+ .text = "Characters considered to separate words."
},
/* Window options. */
{ .name = "aggressive-resize",
.type = OPTIONS_TABLE_FLAG,
.scope = OPTIONS_TABLE_WINDOW,
- .default_num = 0
+ .default_num = 0,
+ .text = "When 'window-size' is 'smallest', whether the maximum size "
+ "of a window is the smallest attached session where it is "
+ "the current window ('on') or the smallest session it is "
+ "linked to ('off')."
},
{ .name = "allow-rename",
.type = OPTIONS_TABLE_FLAG,
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
- .default_num = 0
+ .default_num = 0,
+ .text = "Whether applications are allowed to use the escape sequence "
+ "to rename windows."
},
{ .name = "alternate-screen",
.type = OPTIONS_TABLE_FLAG,
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
- .default_num = 1
+ .default_num = 1,
+ .text = "Whether applications are allowed to use the alternate "
+ "screen."
},
{ .name = "automatic-rename",
.type = OPTIONS_TABLE_FLAG,
.scope = OPTIONS_TABLE_WINDOW,
- .default_num = 1
+ .default_num = 1,
+ .text = "Whether windows are automatically renamed."
},
{ .name = "automatic-rename-format",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW,
.default_str = "#{?pane_in_mode,[tmux],#{pane_current_command}}"
- "#{?pane_dead,[dead],}"
+ "#{?pane_dead,[dead],}",
+ .text = "Format used to automatically rename windows."
},
{ .name = "clock-mode-colour",
.type = OPTIONS_TABLE_COLOUR,
.scope = OPTIONS_TABLE_WINDOW,
- .default_num = 4
+ .default_num = 4,
+ .text = "Colour of the clock in clock mode."
},
{ .name = "clock-mode-style",
.type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_WINDOW,
.choices = options_table_clock_mode_style_list,
- .default_num = 1
+ .default_num = 1,
+ .text = "Time format of the clock in clock mode."
},
{ .name = "copy-mode-match-style",
@@ -667,7 +782,8 @@ const struct options_table_entry options_table[] = {
.scope = OPTIONS_TABLE_WINDOW,
.default_str = "bg=cyan,fg=black",
.flags = OPTIONS_TABLE_IS_STYLE,
- .separator = ","
+ .separator = ",",
+ .text = "Style of search matches in copy mode."
},
{ .name = "copy-mode-current-match-style",
@@ -675,26 +791,32 @@ const struct options_table_entry options_table[] = {
.scope = OPTIONS_TABLE_WINDOW,
.default_str = "bg=magenta,fg=black",
.flags = OPTIONS_TABLE_IS_STYLE,
- .separator = ","
+ .separator = ",",
+ .text = "Style of the current search match in copy mode."
},
{ .name = "main-pane-height",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW,
- .default_str = "24"
+ .default_str = "24",
+ .text = "Height of the main pane in the 'main-horizontal' layout. "
+ "This may be a percentage, for example '10%'."
},
{ .name = "main-pane-width",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW,
- .default_str = "80"
+ .default_str = "80",
+ .text = "Width of the main pane in the 'main-vertical' layout. "
+ "This may be a percentage, for example '10%'."
},
{ .name = "mode-keys",
.type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_WINDOW,
.choices = options_table_mode_keys_list,
- .default_num = MODEKEY_EMACS
+ .default_num = MODEKEY_EMACS,
+ .text = "Key set used in copy mode."
},
{ .name = "mode-style",
@@ -702,19 +824,22 @@ const struct options_table_entry options_table[] = {
.scope = OPTIONS_TABLE_WINDOW,
.default_str = "bg=yellow,fg=black",
.flags = OPTIONS_TABLE_IS_STYLE,
- .separator = ","
+ .separator = ",",
+ .text = "Style of indicators and highlighting in modes."
},
{ .name = "monitor-activity",
.type = OPTIONS_TABLE_FLAG,
.scope = OPTIONS_TABLE_WINDOW,
- .default_num = 0
+ .default_num = 0,
+ .text = "Whether an alert is triggered by activity."
},
{ .name = "monitor-bell",
.type = OPTIONS_TABLE_FLAG,
.scope = OPTIONS_TABLE_WINDOW,
- .default_num = 1
+ .default_num = 1,
+ .text = "Whether an alert is triggered by a bell."
},
{ .name = "monitor-silence",
@@ -722,19 +847,26 @@ const struct options_table_entry options_table[] = {
.scope = OPTIONS_TABLE_WINDOW,
.minimum = 0,
.maximum = INT_MAX,
- .default_num = 0
+ .default_num = 0,
+ .text = "Time after which an alert is triggered by silence. "
+ "Zero means no alert."
+
},
{ .name = "other-pane-height",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW,
- .default_str = "0"
+ .default_str = "0",
+ .text = "Height of the other panes in the 'main-horizontal' layout. "
+ "This may be a percentage, for example '10%'."
},
{ .name = "other-pane-width",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW,
- .default_str = "0"
+ .default_str = "0",
+ .text = "Height of the other panes in the 'main-vertical' layout. "
+ "This may be a percentage, for example '10%'."
},
{ .name = "pane-active-border-style",
@@ -742,7 +874,8 @@ const struct options_table_entry options_table[] = {
.scope = OPTIONS_TABLE_WINDOW,
.default_str = "#{?pane_in_mode,fg=yellow,#{?synchronize-panes,fg=red,fg=green}}",
.flags = OPTIONS_TABLE_IS_STYLE,
- .separator = ","
+ .separator = ",",
+ .text = "Style of the active pane border."
},
{ .name = "pane-base-index",
@@ -750,21 +883,24 @@ const struct options_table_entry options_table[] = {
.scope = OPTIONS_TABLE_WINDOW,
.minimum = 0,
.maximum = USHRT_MAX,
- .default_num = 0
+ .default_num = 0,
+ .text = "Index of the first pane in each window."
},
{ .name = "pane-border-format",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW,
.default_str = "#{?pane_active,#[reverse],}#{pane_index}#[default] "
- "\"#{pane_title}\""
+ "\"#{pane_title}\"",
+ .text = "Format of text in the pane status lines."
},
{ .name = "pane-border-status",
.type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_WINDOW,
.choices = options_table_pane_status_list,
- .default_num = PANE_STATUS_OFF
+ .default_num = PANE_STATUS_OFF,
+ .text = "Position of the pane status lines."
},
{ .name = "pane-border-style",
@@ -772,19 +908,23 @@ const struct options_table_entry options_table[] = {
.scope = OPTIONS_TABLE_WINDOW,
.default_str = "default",
.flags = OPTIONS_TABLE_IS_STYLE,
- .separator = ","
+ .separator = ",",
+ .text = "Style of the pane status lines."
},
{ .name = "remain-on-exit",
.type = OPTIONS_TABLE_FLAG,
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
- .default_num = 0
+ .default_num = 0,
+ .text = "Whether panes should remain ('on') or be automatically "
+ "killed ('off') when the program inside exits."
},
{ .name = "synchronize-panes",
.type = OPTIONS_TABLE_FLAG,
.scope = OPTIONS_TABLE_WINDOW,
- .default_num = 0
+ .default_num = 0,
+ .text = "Whether typing should be sent to all panes simultaneously."
},
{ .name = "window-active-style",
@@ -792,14 +932,20 @@ const struct options_table_entry options_table[] = {
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
.default_str = "default",
.flags = OPTIONS_TABLE_IS_STYLE,
- .separator = ","
+ .separator = ",",
+ .text = "Default style of the active pane."
},
{ .name = "window-size",
.type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_WINDOW,
.choices = options_table_window_size_list,
- .default_num = WINDOW_SIZE_LATEST
+ .default_num = WINDOW_SIZE_LATEST,
+ .text = "How window size is calculated. "
+ "'latest' uses the size of the most recently used client, "
+ "'largest' the largest client, 'smallest' the smallest "
+ "client and 'manual' a size set by the 'resize-window' "
+ "command."
},
{ .name = "window-style",
@@ -807,7 +953,8 @@ const struct options_table_entry options_table[] = {
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
.default_str = "default",
.flags = OPTIONS_TABLE_IS_STYLE,
- .separator = ","
+ .separator = ",",
+ .text = "Default style of panes that are not the active pane."
},
{ .name = "window-status-activity-style",
@@ -815,7 +962,8 @@ const struct options_table_entry options_table[] = {
.scope = OPTIONS_TABLE_WINDOW,
.default_str = "reverse",
.flags = OPTIONS_TABLE_IS_STYLE,
- .separator = ","
+ .separator = ",",
+ .text = "Style of windows in the status line with an activity alert."
},
{ .name = "window-status-bell-style",
@@ -823,13 +971,15 @@ const struct options_table_entry options_table[] = {
.scope = OPTIONS_TABLE_WINDOW,
.default_str = "reverse",
.flags = OPTIONS_TABLE_IS_STYLE,
- .separator = ","
+ .separator = ",",
+ .text = "Style of windows in the status line with a bell alert."
},
{ .name = "window-status-current-format",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW,
- .default_str = "#I:#W#{?window_flags,#{window_flags}, }"
+ .default_str = "#I:#W#{?window_flags,#{window_flags}, }",
+ .text = "Format of the current window in the status line."
},
{ .name = "window-status-current-style",
@@ -837,13 +987,16 @@ const struct options_table_entry options_table[] = {
.scope = OPTIONS_TABLE_WINDOW,
.default_str = "default",
.flags = OPTIONS_TABLE_IS_STYLE,
- .separator = ","
+ .separator = ",",
+ .text = "Style of the current window in the status line."
},
{ .name = "window-status-format",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW,
- .default_str = "#I:#W#{?window_flags,#{window_flags}, }"
+ .default_str = "#I:#W#{?window_flags,#{window_flags}, }",
+ .text = "Format of windows in the status line, except the current "
+ "window."
},
{ .name = "window-status-last-style",
@@ -851,13 +1004,15 @@ const struct options_table_entry options_table[] = {
.scope = OPTIONS_TABLE_WINDOW,
.default_str = "default",
.flags = OPTIONS_TABLE_IS_STYLE,
- .separator = ","
+ .separator = ",",
+ .text = "Style of the last window in the status line."
},
{ .name = "window-status-separator",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW,
- .default_str = " "
+ .default_str = " ",
+ .text = "Separator between windows in the status line."
},
{ .name = "window-status-style",
@@ -865,19 +1020,24 @@ const struct options_table_entry options_table[] = {
.scope = OPTIONS_TABLE_WINDOW,
.default_str = "default",
.flags = OPTIONS_TABLE_IS_STYLE,
- .separator = ","
+ .separator = ",",
+ .text = "Style of windows in the status line, except the current and "
+ "last windows."
},
{ .name = "wrap-search",
.type = OPTIONS_TABLE_FLAG,
.scope = OPTIONS_TABLE_WINDOW,
- .default_num = 1
+ .default_num = 1,
+ .text = "Whether searching in copy mode should wrap at the top or "
+ "bottom."
},
{ .name = "xterm-keys",
.type = OPTIONS_TABLE_FLAG,
.scope = OPTIONS_TABLE_WINDOW,
- .default_num = 1
+ .default_num = 1,
+ .text = "Whether xterm-style function key sequences should be sent."
},
/* Hook options. */
diff --git a/usr.bin/tmux/options.c b/usr.bin/tmux/options.c
index 58c5337898c..9bdd432dd94 100644
--- a/usr.bin/tmux/options.c
+++ b/usr.bin/tmux/options.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: options.c,v 1.56 2020/05/16 15:01:31 nicm Exp $ */
+/* $OpenBSD: options.c,v 1.57 2020/05/16 16:02:24 nicm Exp $ */
/*
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -19,6 +19,7 @@
#include <sys/types.h>
#include <ctype.h>
+#include <fnmatch.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
@@ -116,7 +117,7 @@ options_value_free(struct options_entry *o, union options_value *ov)
}
static char *
-options_value_tostring(struct options_entry *o, union options_value *ov,
+options_value_to_string(struct options_entry *o, union options_value *ov,
int numeric)
{
char *s;
@@ -175,6 +176,12 @@ options_free(struct options *oo)
free(oo);
}
+struct options *
+options_get_parent(struct options *oo)
+{
+ return (oo->parent);
+}
+
void
options_set_parent(struct options *oo, struct options *parent)
{
@@ -262,6 +269,35 @@ options_default(struct options *oo, const struct options_table_entry *oe)
return (o);
}
+char *
+options_default_to_string(const struct options_table_entry *oe)
+{
+ char *s;
+
+ switch (oe->type) {
+ case OPTIONS_TABLE_STRING:
+ case OPTIONS_TABLE_COMMAND:
+ s = xstrdup(oe->default_str);
+ break;
+ case OPTIONS_TABLE_NUMBER:
+ xasprintf(&s, "%lld", oe->default_num);
+ break;
+ case OPTIONS_TABLE_KEY:
+ s = xstrdup(key_string_lookup_key(oe->default_num));
+ break;
+ case OPTIONS_TABLE_COLOUR:
+ s = xstrdup(colour_tostring(oe->default_num));
+ break;
+ case OPTIONS_TABLE_FLAG:
+ s = xstrdup(oe->default_num ? "on" : "off");
+ break;
+ case OPTIONS_TABLE_CHOICE:
+ s = xstrdup(oe->choices[oe->default_num]);
+ break;
+ }
+ return (s);
+}
+
static struct options_entry *
options_add(struct options *oo, const char *name)
{
@@ -299,6 +335,12 @@ options_name(struct options_entry *o)
return (o->name);
}
+struct options *
+options_owner(struct options_entry *o)
+{
+ return (o->owner);
+}
+
const struct options_table_entry *
options_table_entry(struct options_entry *o)
{
@@ -492,19 +534,19 @@ options_array_item_value(struct options_array_item *a)
}
int
-options_isarray(struct options_entry *o)
+options_is_array(struct options_entry *o)
{
return (OPTIONS_IS_ARRAY(o));
}
int
-options_isstring(struct options_entry *o)
+options_is_string(struct options_entry *o)
{
return (OPTIONS_IS_STRING(o));
}
char *
-options_tostring(struct options_entry *o, int idx, int numeric)
+options_to_string(struct options_entry *o, int idx, int numeric)
{
struct options_array_item *a;
@@ -514,9 +556,9 @@ options_tostring(struct options_entry *o, int idx, int numeric)
a = options_array_item(o, idx);
if (a == NULL)
return (xstrdup(""));
- return (options_value_tostring(o, &a->value, numeric));
+ return (options_value_to_string(o, &a->value, numeric));
}
- return (options_value_tostring(o, &o->value, numeric));
+ return (options_value_to_string(o, &o->value, numeric));
}
char *
@@ -866,3 +908,201 @@ options_string_to_style(struct options *oo, const char *name,
}
return (&o->style);
}
+
+static int
+options_from_string_check(const struct options_table_entry *oe,
+ const char *value, char **cause)
+{
+ struct style sy;
+
+ if (oe == NULL)
+ return (0);
+ if (strcmp(oe->name, "default-shell") == 0 && !checkshell(value)) {
+ xasprintf(cause, "not a suitable shell: %s", value);
+ return (-1);
+ }
+ if (oe->pattern != NULL && fnmatch(oe->pattern, value, 0) != 0) {
+ xasprintf(cause, "value is invalid: %s", value);
+ return (-1);
+ }
+ if ((oe->flags & OPTIONS_TABLE_IS_STYLE) &&
+ strstr(value, "#{") == NULL &&
+ style_parse(&sy, &grid_default_cell, value) != 0) {
+ xasprintf(cause, "invalid style: %s", value);
+ return (-1);
+ }
+ return (0);
+}
+
+static int
+options_from_string_flag(struct options *oo, const char *name,
+ const char *value, char **cause)
+{
+ int flag;
+
+ if (value == NULL || *value == '\0')
+ flag = !options_get_number(oo, name);
+ else if (strcmp(value, "1") == 0 ||
+ strcasecmp(value, "on") == 0 ||
+ strcasecmp(value, "yes") == 0)
+ flag = 1;
+ else if (strcmp(value, "0") == 0 ||
+ strcasecmp(value, "off") == 0 ||
+ strcasecmp(value, "no") == 0)
+ flag = 0;
+ else {
+ xasprintf(cause, "bad value: %s", value);
+ return (-1);
+ }
+ options_set_number(oo, name, flag);
+ return (0);
+}
+
+static int
+options_from_string_choice(const struct options_table_entry *oe,
+ struct options *oo, const char *name, const char *value, char **cause)
+{
+ const char **cp;
+ int n, choice = -1;
+
+ if (value == NULL) {
+ choice = options_get_number(oo, name);
+ if (choice < 2)
+ choice = !choice;
+ } else {
+ n = 0;
+ for (cp = oe->choices; *cp != NULL; cp++) {
+ if (strcmp(*cp, value) == 0)
+ choice = n;
+ n++;
+ }
+ if (choice == -1) {
+ xasprintf(cause, "unknown value: %s", value);
+ return (-1);
+ }
+ }
+ options_set_number(oo, name, choice);
+ return (0);
+}
+
+int
+options_from_string(struct options *oo, const struct options_table_entry *oe,
+ const char *name, const char *value, int append, char **cause)
+{
+ enum options_table_type type;
+ long long number;
+ const char *errstr, *new;
+ char *old;
+ key_code key;
+
+ if (oe != NULL) {
+ if (value == NULL &&
+ oe->type != OPTIONS_TABLE_FLAG &&
+ oe->type != OPTIONS_TABLE_CHOICE) {
+ xasprintf(cause, "empty value");
+ return (-1);
+ }
+ type = oe->type;
+ } else {
+ if (*name != '@') {
+ xasprintf(cause, "bad option name");
+ return (-1);
+ }
+ type = OPTIONS_TABLE_STRING;
+ }
+
+ switch (type) {
+ case OPTIONS_TABLE_STRING:
+ old = xstrdup(options_get_string(oo, name));
+ options_set_string(oo, name, append, "%s", value);
+
+ new = options_get_string(oo, name);
+ if (options_from_string_check(oe, new, cause) != 0) {
+ options_set_string(oo, name, 0, "%s", old);
+ free(old);
+ return (-1);
+ }
+ free(old);
+ return (0);
+ case OPTIONS_TABLE_NUMBER:
+ number = strtonum(value, oe->minimum, oe->maximum, &errstr);
+ if (errstr != NULL) {
+ xasprintf(cause, "value is %s: %s", errstr, value);
+ return (-1);
+ }
+ options_set_number(oo, name, number);
+ return (0);
+ case OPTIONS_TABLE_KEY:
+ key = key_string_lookup_string(value);
+ if (key == KEYC_UNKNOWN) {
+ xasprintf(cause, "bad key: %s", value);
+ return (-1);
+ }
+ options_set_number(oo, name, key);
+ return (0);
+ case OPTIONS_TABLE_COLOUR:
+ if ((number = colour_fromstring(value)) == -1) {
+ xasprintf(cause, "bad colour: %s", value);
+ return (-1);
+ }
+ options_set_number(oo, name, number);
+ return (0);
+ case OPTIONS_TABLE_FLAG:
+ return (options_from_string_flag(oo, name, value, cause));
+ case OPTIONS_TABLE_CHOICE:
+ return (options_from_string_choice(oe, oo, name, value, cause));
+ case OPTIONS_TABLE_COMMAND:
+ break;
+ }
+ return (-1);
+}
+
+void
+options_push_changes(const char *name)
+{
+ struct client *loop;
+ struct session *s;
+ struct window *w;
+ struct window_pane *wp;
+
+ if (strcmp(name, "automatic-rename") == 0) {
+ RB_FOREACH(w, windows, &windows) {
+ if (w->active == NULL)
+ continue;
+ if (options_get_number(w->options, "automatic-rename"))
+ w->active->flags |= PANE_CHANGED;
+ }
+ }
+ if (strcmp(name, "key-table") == 0) {
+ TAILQ_FOREACH(loop, &clients, entry)
+ server_client_set_key_table(loop, NULL);
+ }
+ if (strcmp(name, "user-keys") == 0) {
+ TAILQ_FOREACH(loop, &clients, entry) {
+ if (loop->tty.flags & TTY_OPENED)
+ tty_keys_build(&loop->tty);
+ }
+ }
+ if (strcmp(name, "status") == 0 ||
+ strcmp(name, "status-interval") == 0)
+ status_timer_start_all();
+ if (strcmp(name, "monitor-silence") == 0)
+ alerts_reset_all();
+ if (strcmp(name, "window-style") == 0 ||
+ strcmp(name, "window-active-style") == 0) {
+ RB_FOREACH(wp, window_pane_tree, &all_window_panes)
+ wp->flags |= PANE_STYLECHANGED;
+ }
+ if (strcmp(name, "pane-border-status") == 0) {
+ RB_FOREACH(w, windows, &windows)
+ layout_fix_panes(w);
+ }
+ RB_FOREACH(s, sessions, &sessions)
+ status_update_cache(s);
+
+ recalculate_sizes();
+ TAILQ_FOREACH(loop, &clients, entry) {
+ if (loop->session != NULL)
+ server_redraw_client(loop);
+ }
+}
diff --git a/usr.bin/tmux/screen-write.c b/usr.bin/tmux/screen-write.c
index ebe09254ba4..a9cc6aa9999 100644
--- a/usr.bin/tmux/screen-write.c
+++ b/usr.bin/tmux/screen-write.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: screen-write.c,v 1.178 2020/05/16 15:34:08 nicm Exp $ */
+/* $OpenBSD: screen-write.c,v 1.179 2020/05/16 16:02:24 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -360,7 +360,97 @@ screen_write_strlen(const char *fmt, ...)
return (size);
}
-/* Write simple string (no UTF-8 or maximum length). */
+/* Write string wrapped over lines. */
+int
+screen_write_text(struct screen_write_ctx *ctx, u_int cx, u_int width,
+ u_int lines, int more, const struct grid_cell *gcp, const char *fmt, ...)
+{
+ struct screen *s = ctx->s;
+ va_list ap;
+ char *tmp;
+ u_int cy = s->cy, i, end, next, idx = 0, at, left;
+ struct utf8_data *text;
+ struct grid_cell gc;
+
+ memcpy(&gc, gcp, sizeof gc);
+
+ va_start(ap, fmt);
+ xvasprintf(&tmp, fmt, ap);
+ va_end(ap);
+
+ text = utf8_fromcstr(tmp);
+ free(tmp);
+
+ left = (cx + width) - s->cx;
+ for (;;) {
+ /* Find the end of what can fit on the line. */
+ at = 0;
+ for (end = idx; text[end].size != 0; end++) {
+ if (text[end].size == 1 && text[end].data[0] == '\n')
+ break;
+ if (at + text[end].width > left)
+ break;
+ at += text[end].width;
+ }
+
+ /*
+ * If we're on a space, that's the end. If not, walk back to
+ * try and find one.
+ */
+ if (text[end].size == 0)
+ next = end;
+ else if (text[end].size == 1 && text[end].data[0] == '\n')
+ next = end + 1;
+ else if (text[end].size == 1 && text[end].data[0] == ' ')
+ next = end + 1;
+ else {
+ for (i = end; i > idx; i--) {
+ if (text[i].size == 1 && text[i].data[0] == ' ')
+ break;
+ }
+ if (i != idx) {
+ next = i + 1;
+ end = i;
+ } else
+ next = end;
+ }
+
+ /* Print the line. */
+ for (i = idx; i < end; i++) {
+ utf8_copy(&gc.data, &text[i]);
+ screen_write_cell(ctx, &gc);
+ }
+
+ /* If at the bottom, stop. */
+ idx = next;
+ if (s->cy == cy + lines - 1 || text[idx].size == 0)
+ break;
+
+ screen_write_cursormove(ctx, cx, s->cy + 1, 0);
+ left = width;
+ }
+
+ /*
+ * Fail if on the last line and there is more to come or at the end, or
+ * if the text was not entirely consumed.
+ */
+ if ((s->cy == cy + lines - 1 && (!more || s->cx == cx + width)) ||
+ text[idx].size != 0) {
+ free(text);
+ return (0);
+ }
+ free(text);
+
+ /*
+ * If no more to come, move to the next line. Otherwise, leave on
+ * the same line (except if at the end).
+ */
+ if (!more || s->cx == cx + width)
+ screen_write_cursormove(ctx, cx, s->cy + 1, 0);
+ return (1);
+}
+
+/* Write simple string (no maximum length). */
void
screen_write_puts(struct screen_write_ctx *ctx, const struct grid_cell *gcp,
const char *fmt, ...)
diff --git a/usr.bin/tmux/style.c b/usr.bin/tmux/style.c
index e2f28ffab0d..9ff3ed41bb4 100644
--- a/usr.bin/tmux/style.c
+++ b/usr.bin/tmux/style.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: style.c,v 1.27 2020/05/16 15:01:31 nicm Exp $ */
+/* $OpenBSD: style.c,v 1.28 2020/05/16 16:02:24 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -31,6 +31,7 @@
/* Default style. */
static struct style style_default = {
{ { { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 0 },
+ 0,
8,
STYLE_ALIGN_DEFAULT,
@@ -78,7 +79,11 @@ style_parse(struct style *sy, const struct grid_cell *base, const char *in)
sy->gc.bg = base->bg;
sy->gc.attr = base->attr;
sy->gc.flags = base->flags;
- } else if (strcasecmp(tmp, "push-default") == 0)
+ } else if (strcasecmp(tmp, "ignore") == 0)
+ sy->ignore = 1;
+ else if (strcasecmp(tmp, "noignore") == 0)
+ sy->ignore = 0;
+ else if (strcasecmp(tmp, "push-default") == 0)
sy->default_type = STYLE_DEFAULT_PUSH;
else if (strcasecmp(tmp, "pop-default") == 0)
sy->default_type = STYLE_DEFAULT_POP;
diff --git a/usr.bin/tmux/tmux.1 b/usr.bin/tmux/tmux.1
index 32125f4ebfe..ea3c06f4565 100644
--- a/usr.bin/tmux/tmux.1
+++ b/usr.bin/tmux/tmux.1
@@ -1,4 +1,4 @@
-.\" $OpenBSD: tmux.1,v 1.765 2020/05/16 15:48:35 nicm Exp $
+.\" $OpenBSD: tmux.1,v 1.766 2020/05/16 16:02:24 nicm Exp $
.\"
.\" Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
.\"
@@ -1952,6 +1952,53 @@ includes all sessions in any session groups in the tree rather than only the
first.
This command works only if at least one client is attached.
.It Xo
+.Ic customize-mode
+.Op Fl NZ
+.Op Fl F Ar format
+.Op Fl f Ar filter
+.Op Fl t Ar target-pane
+.Op Ar template
+.Xc
+Put a pane into customize mode, where options and key bindings may be browsed
+and modified from a list.
+Option values in the list are shown for the active pane in the current window.
+.Fl Z
+zooms the pane.
+The following keys may be used in customize mode:
+.Bl -column "Key" "Function" -offset indent
+.It Sy "Key" Ta Sy "Function"
+.It Li "Enter" Ta "Set pane, window, session or global option value"
+.It Li "Up" Ta "Select previous item"
+.It Li "Down" Ta "Select next item"
+.It Li "+" Ta "Expand selected item"
+.It Li "-" Ta "Collapse selected item"
+.It Li "M-+" Ta "Expand all items"
+.It Li "M--" Ta "Collapse all items"
+.It Li "s" Ta "Set option value or key attribute"
+.It Li "S" Ta "Set global option value"
+.It Li "w" Ta "Set window option value, if option is for pane and window"
+.It Li "u" Ta "Unset an option (set to default value if global) or unbind a key"
+.It Li "U" Ta "Unset tagged options and unbind tagged keys"
+.It Li "C-s" Ta "Search by name"
+.It Li "n" Ta "Repeat last search"
+.It Li "t" Ta "Toggle if item is tagged"
+.It Li "T" Ta "Tag no items"
+.It Li "C-t" Ta "Tag all items"
+.It Li "f" Ta "Enter a format to filter items"
+.It Li "v" Ta "Toggle option information"
+.It Li "q" Ta "Exit mode"
+.El
+.Pp
+.Fl f
+specifies an initial filter: the filter is a format - if it evaluates to zero,
+the item in the list is not shown, otherwise it is shown.
+If a filter would lead to an empty list, it is ignored.
+.Fl F
+specifies the format for each item in the tree.
+.Fl N
+starts without the option information.
+This command works only if at least one client is attached.
+.It Xo
.Ic display-panes
.Op Fl b
.Op Fl d Ar duration
diff --git a/usr.bin/tmux/tmux.h b/usr.bin/tmux/tmux.h
index a115c6503dd..0a7177f1ab5 100644
--- a/usr.bin/tmux/tmux.h
+++ b/usr.bin/tmux/tmux.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: tmux.h,v 1.1036 2020/05/16 15:54:20 nicm Exp $ */
+/* $OpenBSD: tmux.h,v 1.1037 2020/05/16 16:02:24 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -745,6 +745,7 @@ enum style_default_type {
/* Style option. */
struct style {
struct grid_cell gc;
+ int ignore;
int fill;
enum style_align align;
@@ -1665,6 +1666,7 @@ RB_HEAD(key_bindings, key_binding);
struct key_table {
const char *name;
struct key_bindings key_bindings;
+ struct key_bindings default_key_bindings;
u_int references;
@@ -1719,6 +1721,9 @@ struct options_table_entry {
const char *separator;
const char *pattern;
+
+ const char *text;
+ const char *unit;
};
/* Common command usages. */
@@ -1896,6 +1901,7 @@ void notify_pane(const char *, struct window_pane *);
/* options.c */
struct options *options_create(struct options *);
void options_free(struct options *);
+struct options *options_get_parent(struct options *);
void options_set_parent(struct options *, struct options *);
struct options_entry *options_first(struct options *);
struct options_entry *options_next(struct options_entry *);
@@ -1903,7 +1909,9 @@ struct options_entry *options_empty(struct options *,
const struct options_table_entry *);
struct options_entry *options_default(struct options *,
const struct options_table_entry *);
+char *options_default_to_string(const struct options_table_entry *);
const char *options_name(struct options_entry *);
+struct options *options_owner(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 *);
@@ -1918,9 +1926,9 @@ struct options_array_item *options_array_first(struct options_entry *);
struct options_array_item *options_array_next(struct options_array_item *);
u_int options_array_item_index(struct options_array_item *);
union options_value *options_array_item_value(struct options_array_item *);
-int options_isarray(struct options_entry *);
-int options_isstring(struct options_entry *);
-char *options_tostring(struct options_entry *, int, int);
+int options_is_array(struct options_entry *);
+int options_is_string(struct options_entry *);
+char *options_to_string(struct options_entry *, int, int);
char *options_parse(const char *, int *);
struct options_entry *options_parse_get(struct options *, const char *, int *,
int);
@@ -1940,6 +1948,10 @@ int options_scope_from_flags(struct args *, int,
struct cmd_find_state *, struct options **, char **);
struct style *options_string_to_style(struct options *, const char *,
struct format_tree *);
+int options_from_string(struct options *,
+ const struct options_table_entry *, const char *,
+ const char *, int, char **);
+void options_push_changes(const char *);
/* options-table.c */
extern const struct options_table_entry options_table[];
@@ -2231,6 +2243,7 @@ struct key_table *key_bindings_first_table(void);
struct key_table *key_bindings_next_table(struct key_table *);
void key_bindings_unref_table(struct key_table *);
struct key_binding *key_bindings_get(struct key_table *, key_code);
+struct key_binding *key_bindings_get_default(struct key_table *, key_code);
struct key_binding *key_bindings_first(struct key_table *);
struct key_binding *key_bindings_next(struct key_table *, struct key_binding *);
void key_bindings_add(const char *, key_code, const char *, int,
@@ -2460,6 +2473,8 @@ void screen_write_start_callback(struct screen_write_ctx *, struct screen *,
void screen_write_stop(struct screen_write_ctx *);
void screen_write_reset(struct screen_write_ctx *);
size_t printflike(1, 2) screen_write_strlen(const char *, ...);
+int printflike(7, 8) screen_write_text(struct screen_write_ctx *, u_int, u_int,
+ u_int, int, const struct grid_cell *, const char *, ...);
void printflike(3, 4) screen_write_puts(struct screen_write_ctx *,
const struct grid_cell *, const char *, ...);
void printflike(4, 5) screen_write_nputs(struct screen_write_ctx *,
@@ -2678,19 +2693,23 @@ typedef void (*mode_tree_draw_cb)(void *, void *, struct screen_write_ctx *,
u_int, u_int);
typedef int (*mode_tree_search_cb)(void *, void *, const char *);
typedef void (*mode_tree_menu_cb)(void *, struct client *, key_code);
+typedef u_int (*mode_tree_height_cb)(void *, u_int);
typedef void (*mode_tree_each_cb)(void *, void *, struct client *, key_code);
u_int mode_tree_count_tagged(struct mode_tree_data *);
void *mode_tree_get_current(struct mode_tree_data *);
+const char *mode_tree_get_current_name(struct mode_tree_data *);
void mode_tree_expand_current(struct mode_tree_data *);
+void mode_tree_collapse_current(struct mode_tree_data *);
void mode_tree_expand(struct mode_tree_data *, uint64_t);
int mode_tree_set_current(struct mode_tree_data *, uint64_t);
void mode_tree_each_tagged(struct mode_tree_data *, mode_tree_each_cb,
struct client *, key_code, int);
+void mode_tree_up(struct mode_tree_data *, int);
void mode_tree_down(struct mode_tree_data *, int);
struct mode_tree_data *mode_tree_start(struct window_pane *, struct args *,
mode_tree_build_cb, mode_tree_draw_cb, mode_tree_search_cb,
- mode_tree_menu_cb, void *, const struct menu_item *, const char **,
- u_int, struct screen **);
+ mode_tree_menu_cb, mode_tree_height_cb, void *,
+ const struct menu_item *, const char **, u_int, struct screen **);
void mode_tree_zoom(struct mode_tree_data *, struct args *);
void mode_tree_build(struct mode_tree_data *);
void mode_tree_free(struct mode_tree_data *);
@@ -2698,6 +2717,7 @@ void mode_tree_resize(struct mode_tree_data *, u_int, u_int);
struct mode_tree_item *mode_tree_add(struct mode_tree_data *,
struct mode_tree_item *, void *, uint64_t, const char *,
const char *, int);
+void mode_tree_draw_as_parent(struct mode_tree_item *);
void mode_tree_remove(struct mode_tree_data *, struct mode_tree_item *);
void mode_tree_draw(struct mode_tree_data *);
int mode_tree_key(struct mode_tree_data *, struct client *, key_code *,
@@ -2728,6 +2748,9 @@ void window_copy_start_drag(struct client *, struct mouse_event *);
char *window_copy_get_word(struct window_pane *, u_int, u_int);
char *window_copy_get_line(struct window_pane *, u_int);
+/* window-option.c */
+extern const struct window_mode window_customize_mode;
+
/* names.c */
void check_window_name(struct window *);
char *default_window_name(struct window *);
diff --git a/usr.bin/tmux/window-buffer.c b/usr.bin/tmux/window-buffer.c
index 2bb78a34ce6..6105662cbbf 100644
--- a/usr.bin/tmux/window-buffer.c
+++ b/usr.bin/tmux/window-buffer.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: window-buffer.c,v 1.29 2020/05/16 15:35:19 nicm Exp $ */
+/* $OpenBSD: window-buffer.c,v 1.30 2020/05/16 16:02:24 nicm Exp $ */
/*
* Copyright (c) 2017 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -298,8 +298,8 @@ window_buffer_init(struct window_mode_entry *wme, struct cmd_find_state *fs,
data->command = xstrdup(args->argv[0]);
data->data = mode_tree_start(wp, args, window_buffer_build,
- window_buffer_draw, window_buffer_search, window_buffer_menu, data,
- window_buffer_menu_items, window_buffer_sort_list,
+ window_buffer_draw, window_buffer_search, window_buffer_menu, NULL,
+ data, window_buffer_menu_items, window_buffer_sort_list,
nitems(window_buffer_sort_list), &s);
mode_tree_zoom(data->data, args);
diff --git a/usr.bin/tmux/window-client.c b/usr.bin/tmux/window-client.c
index ee026f8a2da..d987fbe40a8 100644
--- a/usr.bin/tmux/window-client.c
+++ b/usr.bin/tmux/window-client.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: window-client.c,v 1.27 2020/05/16 14:10:29 nicm Exp $ */
+/* $OpenBSD: window-client.c,v 1.28 2020/05/16 16:02:24 nicm Exp $ */
/*
* Copyright (c) 2017 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -271,7 +271,7 @@ window_client_init(struct window_mode_entry *wme,
data->command = xstrdup(args->argv[0]);
data->data = mode_tree_start(wp, args, window_client_build,
- window_client_draw, NULL, window_client_menu, data,
+ window_client_draw, NULL, window_client_menu, NULL, data,
window_client_menu_items, window_client_sort_list,
nitems(window_client_sort_list), &s);
mode_tree_zoom(data->data, args);
diff --git a/usr.bin/tmux/window-customize.c b/usr.bin/tmux/window-customize.c
new file mode 100644
index 00000000000..d5ccb41d462
--- /dev/null
+++ b/usr.bin/tmux/window-customize.c
@@ -0,0 +1,1430 @@
+/* $OpenBSD: window-customize.c,v 1.1 2020/05/16 16:02:24 nicm Exp $ */
+
+/*
+ * Copyright (c) 2020 Nicholas Marriott <nicholas.marriott@gmail.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
+ * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "tmux.h"
+
+static struct screen *window_customize_init(struct window_mode_entry *,
+ struct cmd_find_state *, struct args *);
+static void window_customize_free(struct window_mode_entry *);
+static void window_customize_resize(struct window_mode_entry *,
+ u_int, u_int);
+static void window_customize_key(struct window_mode_entry *,
+ struct client *, struct session *,
+ struct winlink *, key_code, struct mouse_event *);
+
+#define WINDOW_CUSTOMIZE_DEFAULT_FORMAT \
+ "#{?is_option," \
+ "#{?option_is_global,,#[reverse](#{option_scope})#[default] }" \
+ "#[ignore]" \
+ "#{option_value}#{?option_unit, #{option_unit},}" \
+ "," \
+ "#{key}" \
+ "}"
+
+static const struct menu_item window_customize_menu_items[] = {
+ { "Select", '\r', NULL },
+ { "Expand", KEYC_RIGHT, NULL },
+ { "", KEYC_NONE, NULL },
+ { "Tag", 't', NULL },
+ { "Tag All", '\024', NULL },
+ { "Tag None", 'T', NULL },
+ { "", KEYC_NONE, NULL },
+ { "Cancel", 'q', NULL },
+
+ { NULL, KEYC_NONE, NULL }
+};
+
+const struct window_mode window_customize_mode = {
+ .name = "options-mode",
+ .default_format = WINDOW_CUSTOMIZE_DEFAULT_FORMAT,
+
+ .init = window_customize_init,
+ .free = window_customize_free,
+ .resize = window_customize_resize,
+ .key = window_customize_key,
+};
+
+enum window_customize_scope {
+ WINDOW_CUSTOMIZE_NONE,
+ WINDOW_CUSTOMIZE_KEY,
+ WINDOW_CUSTOMIZE_SERVER,
+ WINDOW_CUSTOMIZE_GLOBAL_SESSION,
+ WINDOW_CUSTOMIZE_SESSION,
+ WINDOW_CUSTOMIZE_GLOBAL_WINDOW,
+ WINDOW_CUSTOMIZE_WINDOW,
+ WINDOW_CUSTOMIZE_PANE
+};
+
+struct window_customize_itemdata {
+ struct window_customize_modedata *data;
+ enum window_customize_scope scope;
+
+ char *table;
+ key_code key;
+
+ struct options *oo;
+ char *name;
+ int idx;
+};
+
+struct window_customize_modedata {
+ struct window_pane *wp;
+ int dead;
+ int references;
+
+ struct mode_tree_data *data;
+ char *format;
+ int hide_global;
+
+ struct window_customize_itemdata **item_list;
+ u_int item_size;
+
+ struct cmd_find_state fs;
+};
+
+static uint64_t
+window_customize_get_tag(struct options_entry *o, int idx,
+ const struct options_table_entry *oe)
+{
+ uint64_t offset;
+
+ if (oe == NULL)
+ return ((uint64_t)o);
+ offset = ((char *)oe - (char *)options_table) / sizeof *options_table;
+ return ((2ULL << 62)|(offset << 32)|((idx + 1) << 1)|1);
+}
+
+static struct options *
+window_customize_get_tree(enum window_customize_scope scope,
+ struct cmd_find_state *fs)
+{
+ switch (scope) {
+ case WINDOW_CUSTOMIZE_NONE:
+ case WINDOW_CUSTOMIZE_KEY:
+ return (NULL);
+ case WINDOW_CUSTOMIZE_SERVER:
+ return (global_options);
+ case WINDOW_CUSTOMIZE_GLOBAL_SESSION:
+ return (global_s_options);
+ case WINDOW_CUSTOMIZE_SESSION:
+ return (fs->s->options);
+ case WINDOW_CUSTOMIZE_GLOBAL_WINDOW:
+ return (global_w_options);
+ case WINDOW_CUSTOMIZE_WINDOW:
+ return (fs->w->options);
+ case WINDOW_CUSTOMIZE_PANE:
+ return (fs->wp->options);
+ }
+ return (NULL);
+}
+
+static int
+window_customize_check_item(struct window_customize_modedata *data,
+ struct window_customize_itemdata *item, struct cmd_find_state *fsp)
+{
+ struct cmd_find_state fs;
+
+ if (fsp == NULL)
+ fsp = &fs;
+
+ if (cmd_find_valid_state(&data->fs))
+ cmd_find_copy_state(fsp, &data->fs);
+ else
+ cmd_find_from_pane(fsp, data->wp, 0);
+ return (item->oo == window_customize_get_tree(item->scope, fsp));
+}
+
+static int
+window_customize_get_key(struct window_customize_itemdata *item,
+ struct key_table **ktp, struct key_binding **bdp)
+{
+ struct key_table *kt;
+ struct key_binding *bd;
+
+ kt = key_bindings_get_table(item->table, 0);
+ if (kt == NULL)
+ return (0);
+ bd = key_bindings_get(kt, item->key);
+ if (bd == NULL)
+ return (0);
+
+ if (ktp != NULL)
+ *ktp = kt;
+ if (bdp != NULL)
+ *bdp = bd;
+ return (1);
+}
+
+static char *
+window_customize_scope_text(enum window_customize_scope scope,
+ struct cmd_find_state *fs)
+{
+ char *s;
+ u_int idx;
+
+ switch (scope) {
+ case WINDOW_CUSTOMIZE_NONE:
+ case WINDOW_CUSTOMIZE_KEY:
+ case WINDOW_CUSTOMIZE_SERVER:
+ case WINDOW_CUSTOMIZE_GLOBAL_SESSION:
+ case WINDOW_CUSTOMIZE_GLOBAL_WINDOW:
+ s = xstrdup("");
+ break;
+ case WINDOW_CUSTOMIZE_PANE:
+ window_pane_index(fs->wp, &idx);
+ xasprintf(&s, "pane %u", idx);
+ break;
+ case WINDOW_CUSTOMIZE_SESSION:
+ xasprintf(&s, "session %s", fs->s->name);
+ break;
+ case WINDOW_CUSTOMIZE_WINDOW:
+ xasprintf(&s, "window %u", fs->wl->idx);
+ break;
+ }
+ return (s);
+}
+
+static struct window_customize_itemdata *
+window_customize_add_item(struct window_customize_modedata *data)
+{
+ struct window_customize_itemdata *item;
+
+ data->item_list = xreallocarray(data->item_list, data->item_size + 1,
+ sizeof *data->item_list);
+ item = data->item_list[data->item_size++] = xcalloc(1, sizeof *item);
+ return (item);
+}
+
+static void
+window_customize_free_item(struct window_customize_itemdata *item)
+{
+ free(item->table);
+ free(item->name);
+ free(item);
+}
+
+static void
+window_customize_build_array(struct window_customize_modedata *data,
+ struct mode_tree_item *top, enum window_customize_scope scope,
+ struct options_entry *o, struct format_tree *ft)
+{
+ const struct options_table_entry *oe = options_table_entry(o);
+ struct options *oo = options_owner(o);
+ struct window_customize_itemdata *item;
+ struct options_array_item *ai;
+ char *name, *value, *text;
+ u_int idx;
+ uint64_t tag;
+
+ ai = options_array_first(o);
+ while (ai != NULL) {
+ idx = options_array_item_index(ai);
+
+ xasprintf(&name, "%s[%u]", options_name(o), idx);
+ format_add(ft, "option_name", "%s", name);
+ value = options_to_string(o, idx, 0);
+ format_add(ft, "option_value", "%s", value);
+
+ item = window_customize_add_item(data);
+ item->scope = scope;
+ item->oo = oo;
+ item->name = xstrdup(options_name(o));
+ item->idx = idx;
+
+ text = format_expand(ft, data->format);
+ tag = window_customize_get_tag(o, idx, oe);
+ mode_tree_add(data->data, top, item, tag, name, text, -1);
+ free(text);
+
+ free(name);
+ free(value);
+
+ ai = options_array_next(ai);
+ }
+}
+
+static void
+window_customize_build_option(struct window_customize_modedata *data,
+ struct mode_tree_item *top, enum window_customize_scope scope,
+ struct options_entry *o, struct format_tree *ft,
+ const char *filter, struct cmd_find_state *fs)
+{
+ const struct options_table_entry *oe = options_table_entry(o);
+ struct options *oo = options_owner(o);
+ const char *name = options_name(o);
+ struct window_customize_itemdata *item;
+ char *text, *expanded, *value;
+ int global = 0, array = 0;
+ uint64_t tag;
+
+ if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_HOOK))
+ return;
+ if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY))
+ array = 1;
+
+ if (scope == WINDOW_CUSTOMIZE_SERVER ||
+ scope == WINDOW_CUSTOMIZE_GLOBAL_SESSION ||
+ scope == WINDOW_CUSTOMIZE_GLOBAL_WINDOW)
+ global = 1;
+ if (data->hide_global && global)
+ return;
+
+ format_add(ft, "option_name", "%s", name);
+ format_add(ft, "option_is_global", "%d", global);
+ format_add(ft, "option_is_array", "%d", array);
+
+ text = window_customize_scope_text(scope, fs);
+ format_add(ft, "option_scope", "%s", text);
+ free(text);
+
+ if (oe != NULL && oe->unit != NULL)
+ format_add(ft, "option_unit", "%s", oe->unit);
+ else
+ format_add(ft, "option_unit", "%s", "");
+
+ if (!array) {
+ value = options_to_string(o, -1, 0);
+ format_add(ft, "option_value", "%s", value);
+ free(value);
+ }
+
+ if (filter != NULL) {
+ expanded = format_expand(ft, filter);
+ if (!format_true(expanded)) {
+ free(expanded);
+ return;
+ }
+ free(expanded);
+ }
+ item = window_customize_add_item(data);
+ item->oo = oo;
+ item->scope = scope;
+ item->name = xstrdup(name);
+ item->idx = -1;
+
+ if (array)
+ text = NULL;
+ else
+ text = format_expand(ft, data->format);
+ tag = window_customize_get_tag(o, -1, oe);
+ top = mode_tree_add(data->data, top, item, tag, name, text, 0);
+ free(text);
+
+ if (array)
+ window_customize_build_array(data, top, scope, o, ft);
+}
+
+static void
+window_customize_find_user_options(struct options *oo, const char ***list,
+ u_int *size)
+{
+ struct options_entry *o;
+ const char *name;
+ u_int i;
+
+ o = options_first(oo);
+ while (o != NULL) {
+ name = options_name(o);
+ if (*name != '@') {
+ o = options_next(o);
+ continue;
+ }
+ for (i = 0; i < *size; i++) {
+ if (strcmp((*list)[i], name) == 0)
+ break;
+ }
+ if (i != *size) {
+ o = options_next(o);
+ continue;
+ }
+ *list = xreallocarray(*list, (*size) + 1, sizeof **list);
+ (*list)[(*size)++] = name;
+
+ o = options_next(o);
+ }
+}
+
+static void
+window_customize_build_options(struct window_customize_modedata *data,
+ const char *title, uint64_t tag,
+ enum window_customize_scope scope0, struct options *oo0,
+ enum window_customize_scope scope1, struct options *oo1,
+ enum window_customize_scope scope2, struct options *oo2,
+ struct format_tree *ft, const char *filter, struct cmd_find_state *fs)
+{
+ struct mode_tree_item *top;
+ struct options_entry *o, *loop;
+ const char **list = NULL, *name;
+ u_int size = 0, i;
+ enum window_customize_scope scope;
+
+ top = mode_tree_add(data->data, NULL, NULL, tag, title, NULL, 0);
+
+ /*
+ * We get the options from the first tree, but build it using the
+ * values from the other two. Any tree can have user options so we need
+ * to build a separate list of them.
+ */
+
+ window_customize_find_user_options(oo0, &list, &size);
+ if (oo1 != NULL)
+ window_customize_find_user_options(oo1, &list, &size);
+ if (oo2 != NULL)
+ window_customize_find_user_options(oo2, &list, &size);
+
+ for (i = 0; i < size; i++) {
+ if (oo2 != NULL)
+ o = options_get(oo0, list[i]);
+ else if (oo1 != NULL)
+ o = options_get(oo1, list[i]);
+ else
+ o = options_get(oo2, list[i]);
+ if (options_owner(o) == oo2)
+ scope = scope2;
+ else if (options_owner(o) == oo1)
+ scope = scope1;
+ else
+ scope = scope0;
+ window_customize_build_option(data, top, scope, o, ft, filter,
+ fs);
+ }
+ free(list);
+
+ loop = options_first(oo0);
+ while (loop != NULL) {
+ name = options_name(loop);
+ if (*name == '@') {
+ loop = options_next(loop);
+ continue;
+ }
+ if (oo2 != NULL)
+ o = options_get(oo2, name);
+ else if (oo1 != NULL)
+ o = options_get(oo1, name);
+ else
+ o = loop;
+ if (options_owner(o) == oo2)
+ scope = scope2;
+ else if (options_owner(o) == oo1)
+ scope = scope1;
+ else
+ scope = scope0;
+ window_customize_build_option(data, top, scope, o, ft, filter,
+ fs);
+ loop = options_next(loop);
+ }
+}
+
+static void
+window_customize_build_keys(struct window_customize_modedata *data,
+ struct key_table *kt, struct format_tree *ft, const char *filter,
+ struct cmd_find_state *fs, u_int number)
+{
+ struct mode_tree_item *top, *child, *mti;
+ struct window_customize_itemdata *item;
+ struct key_binding *bd;
+ char *title, *text, *tmp, *expanded;
+ const char *flag;
+ uint64_t tag;
+
+ tag = (1ULL << 62)|((uint64_t)number << 54)|1;
+
+ xasprintf(&title, "Key Table - %s", kt->name);
+ top = mode_tree_add(data->data, NULL, NULL, tag, title, NULL, 0);
+ free(title);
+
+ ft = format_create_from_state(NULL, NULL, fs);
+ format_add(ft, "is_option", "0");
+ format_add(ft, "is_key", "1");
+
+ bd = key_bindings_first(kt);
+ while (bd != NULL) {
+ format_add(ft, "key", "%s", key_string_lookup_key(bd->key));
+ if (bd->note != NULL)
+ format_add(ft, "key_note", "%s", bd->note);
+ if (filter != NULL) {
+ expanded = format_expand(ft, filter);
+ if (!format_true(expanded)) {
+ free(expanded);
+ continue;
+ }
+ free(expanded);
+ }
+
+ item = window_customize_add_item(data);
+ item->scope = WINDOW_CUSTOMIZE_KEY;
+ item->table = xstrdup(kt->name);
+ item->key = bd->key;
+
+ expanded = format_expand(ft, data->format);
+ child = mode_tree_add(data->data, top, item, (uint64_t)bd,
+ expanded, NULL, 0);
+ free(expanded);
+
+ tmp = cmd_list_print(bd->cmdlist, 0);
+ xasprintf(&text, "#[ignore]%s", tmp);
+ free(tmp);
+ mti = mode_tree_add(data->data, child, item,
+ tag|(bd->key << 3)|(0 << 1)|1, "Command", text, -1);
+ mode_tree_draw_as_parent(mti);
+ free(text);
+
+ if (bd->note != NULL)
+ xasprintf(&text, "#[ignore]%s", bd->note);
+ else
+ text = xstrdup("");
+ mti = mode_tree_add(data->data, child, item,
+ tag|(bd->key << 3)|(1 << 1)|1, "Note", text, -1);
+ mode_tree_draw_as_parent(mti);
+ free(text);
+
+ if (bd->flags & KEY_BINDING_REPEAT)
+ flag = "on";
+ else
+ flag = "off";
+ mti = mode_tree_add(data->data, child, item,
+ tag|(bd->key << 3)|(2 << 1)|1, "Repeat", flag, -1);
+ mode_tree_draw_as_parent(mti);
+
+ bd = key_bindings_next(kt, bd);
+ }
+
+ format_free(ft);
+}
+
+static void
+window_customize_build(void *modedata,
+ __unused struct mode_tree_sort_criteria *sort_crit, __unused uint64_t *tag,
+ const char *filter)
+{
+ struct window_customize_modedata *data = modedata;
+ struct cmd_find_state fs;
+ struct format_tree *ft;
+ u_int i;
+ struct key_table *kt;
+
+ for (i = 0; i < data->item_size; i++)
+ window_customize_free_item(data->item_list[i]);
+ free(data->item_list);
+ data->item_list = NULL;
+ data->item_size = 0;
+
+ if (cmd_find_valid_state(&data->fs))
+ cmd_find_copy_state(&fs, &data->fs);
+ else
+ cmd_find_from_pane(&fs, data->wp, 0);
+
+ ft = format_create_from_state(NULL, NULL, &fs);
+ format_add(ft, "is_option", "1");
+ format_add(ft, "is_key", "0");
+
+ window_customize_build_options(data, "Server Options",
+ (3ULL << 62)|(OPTIONS_TABLE_SERVER << 1)|1,
+ WINDOW_CUSTOMIZE_SERVER, global_options,
+ WINDOW_CUSTOMIZE_NONE, NULL,
+ WINDOW_CUSTOMIZE_NONE, NULL,
+ ft, filter, &fs);
+ window_customize_build_options(data, "Session Options",
+ (3ULL << 62)|(OPTIONS_TABLE_SESSION << 1)|1,
+ WINDOW_CUSTOMIZE_GLOBAL_SESSION, global_s_options,
+ WINDOW_CUSTOMIZE_SESSION, fs.s->options,
+ WINDOW_CUSTOMIZE_NONE, NULL,
+ ft, filter, &fs);
+ window_customize_build_options(data, "Window & Pane Options",
+ (3ULL << 62)|(OPTIONS_TABLE_WINDOW << 1)|1,
+ WINDOW_CUSTOMIZE_GLOBAL_WINDOW, global_w_options,
+ WINDOW_CUSTOMIZE_WINDOW, fs.w->options,
+ WINDOW_CUSTOMIZE_PANE, fs.wp->options,
+ ft, filter, &fs);
+
+ format_free(ft);
+ ft = format_create_from_state(NULL, NULL, &fs);
+
+ i = 0;
+ kt = key_bindings_first_table();
+ while (kt != NULL) {
+ if (!RB_EMPTY(&kt->key_bindings)) {
+ window_customize_build_keys(data, kt, ft, filter, &fs,
+ i);
+ if (++i == 256)
+ break;
+ }
+ kt = key_bindings_next_table(kt);
+ }
+
+ format_free(ft);
+}
+
+static void
+window_customize_draw_key(__unused struct window_customize_modedata *data,
+ struct window_customize_itemdata *item, struct screen_write_ctx *ctx,
+ u_int sx, u_int sy)
+{
+ struct screen *s = ctx->s;
+ u_int cx = s->cx, cy = s->cy;
+ struct key_table *kt;
+ struct key_binding *bd, *default_bd;
+ const char *note, *period = "";
+ char *cmd, *default_cmd;
+
+ if (item == NULL || !window_customize_get_key(item, &kt, &bd))
+ return;
+
+ note = bd->note;
+ if (note == NULL)
+ note = "There is no note for this key.";
+ if (*note != '\0' && note[strlen (note) - 1] != '.')
+ period = ".";
+ if (!screen_write_text(ctx, cx, sx, sy, 0, &grid_default_cell, "%s%s",
+ note, period))
+ return;
+ screen_write_cursormove(ctx, cx, s->cy + 1, 0); /* skip line */
+ if (s->cy >= cy + sy - 1)
+ return;
+
+ if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0,
+ &grid_default_cell, "This key is in the %s table.", kt->name))
+ return;
+ if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0,
+ &grid_default_cell, "This key %s repeat.",
+ (bd->flags & KEY_BINDING_REPEAT) ? "does" : "does not"))
+ return;
+ screen_write_cursormove(ctx, cx, s->cy + 1, 0); /* skip line */
+ if (s->cy >= cy + sy - 1)
+ return;
+
+ cmd = cmd_list_print(bd->cmdlist, 0);
+ if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0,
+ &grid_default_cell, "Command: %s", cmd)) {
+ free(cmd);
+ return;
+ }
+ default_bd = key_bindings_get_default(kt, bd->key);
+ if (default_bd != NULL) {
+ default_cmd = cmd_list_print(default_bd->cmdlist, 0);
+ if (strcmp(cmd, default_cmd) != 0 &&
+ !screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0,
+ &grid_default_cell, "The default is: %s", default_cmd)) {
+ free(default_cmd);
+ free(cmd);
+ return;
+ }
+ free(default_cmd);
+ }
+ free(cmd);
+}
+
+static void
+window_customize_draw_option(struct window_customize_modedata *data,
+ struct window_customize_itemdata *item, struct screen_write_ctx *ctx,
+ u_int sx, u_int sy)
+{
+ struct screen *s = ctx->s;
+ u_int cx = s->cx, cy = s->cy;
+ int idx;
+ struct options_entry *o, *parent;
+ struct options *go, *wo;
+ const struct options_table_entry *oe;
+ struct grid_cell gc;
+ const char **choice, *text, *name;
+ const char *space = "", *unit = "";
+ char *value = NULL, *expanded;
+ char *default_value = NULL;
+ char choices[256] = "";
+ struct cmd_find_state fs;
+ struct format_tree *ft;
+
+ if (!window_customize_check_item(data, item, &fs))
+ return;
+ name = item->name;
+ idx = item->idx;
+
+ o = options_get(item->oo, name);
+ if (o == NULL)
+ return;
+ oe = options_table_entry(o);
+
+ if (oe != NULL && oe->unit != NULL) {
+ space = " ";
+ unit = oe->unit;
+ }
+ ft = format_create_from_state(NULL, NULL, &fs);
+
+ if (oe == NULL)
+ text = "This is a user option.";
+ else if (oe->text == NULL)
+ text = "This option doesn't have a description.";
+ else
+ text = oe->text;
+ if (!screen_write_text(ctx, cx, sx, sy, 0, &grid_default_cell, "%s",
+ text))
+ goto out;
+ screen_write_cursormove(ctx, cx, s->cy + 1, 0); /* skip line */
+ if (s->cy >= cy + sy - 1)
+ goto out;
+
+ if (oe == NULL)
+ text = "user";
+ else if ((oe->scope & (OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE)) ==
+ (OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE))
+ text = "window and pane";
+ else if (oe->scope & OPTIONS_TABLE_WINDOW)
+ text = "window";
+ else if (oe->scope & OPTIONS_TABLE_SESSION)
+ text = "session";
+ else
+ text = "server";
+ if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0,
+ &grid_default_cell, "This is a %s option.", text))
+ goto out;
+ if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY)) {
+ if (idx != -1) {
+ if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy),
+ 0, &grid_default_cell,
+ "This is an array option, index %u.", idx))
+ goto out;
+ } else {
+ if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy),
+ 0, &grid_default_cell, "This is an array option."))
+ goto out;
+ }
+ if (idx == -1)
+ goto out;
+ }
+ screen_write_cursormove(ctx, cx, s->cy + 1, 0); /* skip line */
+ if (s->cy >= cy + sy - 1)
+ goto out;
+
+ value = options_to_string(o, idx, 0);
+ if (oe != NULL && idx == -1) {
+ default_value = options_default_to_string(oe);
+ if (strcmp(default_value, value) == 0) {
+ free(default_value);
+ default_value = NULL;
+ }
+ }
+ if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0,
+ &grid_default_cell, "Option value: %s%s%s", value, space, unit))
+ goto out;
+ if (oe == NULL || oe->type == OPTIONS_TABLE_STRING) {
+ expanded = format_expand(ft, value);
+ if (strcmp(expanded, value) != 0) {
+ if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy),
+ 0, &grid_default_cell, "This expands to: %s",
+ expanded))
+ goto out;
+ }
+ free(expanded);
+ }
+ if (oe != NULL && oe->type == OPTIONS_TABLE_CHOICE) {
+ for (choice = oe->choices; *choice != NULL; choice++) {
+ strlcat(choices, *choice, sizeof choices);
+ strlcat(choices, ", ", sizeof choices);
+ }
+ choices[strlen(choices) - 2] = '\0';
+ if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0,
+ &grid_default_cell, "Available values are: %s",
+ choices))
+ goto out;
+ }
+ if (oe != NULL && oe->type == OPTIONS_TABLE_COLOUR) {
+ if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 1,
+ &grid_default_cell, "This is a colour option: "))
+ goto out;
+ memcpy(&gc, &grid_default_cell, sizeof gc);
+ gc.fg = options_get_number(item->oo, name);
+ if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0, &gc,
+ "EXAMPLE"))
+ goto out;
+ }
+ if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_STYLE)) {
+ if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 1,
+ &grid_default_cell, "This is a style option: "))
+ goto out;
+ style_apply(&gc, item->oo, name, ft);
+ if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0, &gc,
+ "EXAMPLE"))
+ goto out;
+ }
+ if (default_value != NULL) {
+ if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0,
+ &grid_default_cell, "The default is: %s%s%s", default_value,
+ space, unit))
+ goto out;
+ }
+
+ screen_write_cursormove(ctx, cx, s->cy + 1, 0); /* skip line */
+ if (s->cy > cy + sy - 1)
+ goto out;
+ if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY)) {
+ wo = NULL;
+ go = NULL;
+ } else {
+ switch (item->scope) {
+ case WINDOW_CUSTOMIZE_PANE:
+ wo = options_get_parent(item->oo);
+ go = options_get_parent(wo);
+ break;
+ case WINDOW_CUSTOMIZE_WINDOW:
+ case WINDOW_CUSTOMIZE_SESSION:
+ wo = NULL;
+ go = options_get_parent(item->oo);
+ break;
+ default:
+ wo = NULL;
+ go = NULL;
+ break;
+ }
+ }
+ if (wo != NULL && options_owner(o) != wo) {
+ parent = options_get_only(wo, name);
+ if (parent != NULL) {
+ value = options_to_string(parent, -1 , 0);
+ if (!screen_write_text(ctx, s->cx, sx,
+ sy - (s->cy - cy), 0, &grid_default_cell,
+ "Window value (from window %u): %s%s%s", fs.wl->idx,
+ value, space, unit))
+ goto out;
+ }
+ }
+ if (go != NULL && options_owner(o) != go) {
+ parent = options_get_only(go, name);
+ if (parent != NULL) {
+ value = options_to_string(parent, -1 , 0);
+ if (!screen_write_text(ctx, s->cx, sx,
+ sy - (s->cy - cy), 0, &grid_default_cell,
+ "Global value: %s%s%s", value, space, unit))
+ goto out;
+ }
+ }
+
+out:
+ free(value);
+ free(default_value);
+ format_free(ft);
+}
+
+static void
+window_customize_draw(void *modedata, void *itemdata,
+ struct screen_write_ctx *ctx, u_int sx, u_int sy)
+{
+ struct window_customize_modedata *data = modedata;
+ struct window_customize_itemdata *item = itemdata;
+
+ if (item == NULL)
+ return;
+
+ if (item->scope == WINDOW_CUSTOMIZE_KEY)
+ window_customize_draw_key(data, item, ctx, sx, sy);
+ else
+ window_customize_draw_option(data, item, ctx, sx, sy);
+}
+
+static void
+window_customize_menu(void *modedata, struct client *c, key_code key)
+{
+ struct window_customize_modedata *data = modedata;
+ struct window_pane *wp = data->wp;
+ struct window_mode_entry *wme;
+
+ wme = TAILQ_FIRST(&wp->modes);
+ if (wme == NULL || wme->data != modedata)
+ return;
+ window_customize_key(wme, c, NULL, NULL, key, NULL);
+}
+
+static u_int
+window_customize_height(__unused void *modedata, __unused u_int height)
+{
+ return (12);
+}
+
+static struct screen *
+window_customize_init(struct window_mode_entry *wme, struct cmd_find_state *fs,
+ struct args *args)
+{
+ struct window_pane *wp = wme->wp;
+ struct window_customize_modedata *data;
+ struct screen *s;
+
+ wme->data = data = xcalloc(1, sizeof *data);
+ data->wp = wp;
+ data->references = 1;
+
+ memcpy(&data->fs, fs, sizeof data->fs);
+
+ if (args == NULL || !args_has(args, 'F'))
+ data->format = xstrdup(WINDOW_CUSTOMIZE_DEFAULT_FORMAT);
+ else
+ data->format = xstrdup(args_get(args, 'F'));
+
+ data->data = mode_tree_start(wp, args, window_customize_build,
+ window_customize_draw, NULL, window_customize_menu,
+ window_customize_height, data, window_customize_menu_items, NULL, 0,
+ &s);
+ mode_tree_zoom(data->data, args);
+
+ mode_tree_build(data->data);
+ mode_tree_draw(data->data);
+
+ return (s);
+}
+
+static void
+window_customize_destroy(struct window_customize_modedata *data)
+{
+ u_int i;
+
+ if (--data->references != 0)
+ return;
+
+ for (i = 0; i < data->item_size; i++)
+ window_customize_free_item(data->item_list[i]);
+ free(data->item_list);
+
+ free(data->format);
+
+ free(data);
+}
+
+static void
+window_customize_free(struct window_mode_entry *wme)
+{
+ struct window_customize_modedata *data = wme->data;
+
+ if (data == NULL)
+ return;
+
+ data->dead = 1;
+ mode_tree_free(data->data);
+ window_customize_destroy(data);
+}
+
+static void
+window_customize_resize(struct window_mode_entry *wme, u_int sx, u_int sy)
+{
+ struct window_customize_modedata *data = wme->data;
+
+ mode_tree_resize(data->data, sx, sy);
+}
+
+static void
+window_customize_free_callback(void *modedata)
+{
+ window_customize_destroy(modedata);
+}
+
+static void
+window_customize_free_item_callback(void *itemdata)
+{
+ struct window_customize_itemdata *item = itemdata;
+ struct window_customize_modedata *data = item->data;
+
+ window_customize_free_item(item);
+ window_customize_destroy(data);
+}
+
+static int
+window_customize_set_option_callback(struct client *c, void *itemdata,
+ const char *s, __unused int done)
+{
+ struct window_customize_itemdata *item = itemdata;
+ struct window_customize_modedata *data = item->data;
+ struct options_entry *o;
+ const struct options_table_entry *oe;
+ struct options *oo = item->oo;
+ const char *name = item->name;
+ char *cause;
+ int idx = item->idx;
+
+ if (s == NULL || *s == '\0' || data->dead)
+ return (0);
+ if (item == NULL || !window_customize_check_item(data, item, NULL))
+ return (0);
+ o = options_get(oo, name);
+ if (o == NULL)
+ return (0);
+ oe = options_table_entry(o);
+
+ if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY)) {
+ if (idx == -1) {
+ for (idx = 0; idx < INT_MAX; idx++) {
+ if (options_array_get(o, idx) == NULL)
+ break;
+ }
+ }
+ if (options_array_set(o, idx, s, 0, &cause) != 0)
+ goto fail;
+ } else {
+ if (options_from_string(oo, oe, name, s, 0, &cause) != 0)
+ goto fail;
+ }
+
+ options_push_changes(item->name);
+ mode_tree_build(data->data);
+ mode_tree_draw(data->data);
+ data->wp->flags |= PANE_REDRAW;
+
+ return (0);
+
+fail:
+ *cause = toupper((u_char)*cause);
+ status_message_set(c, 1, "%s", cause);
+ free(cause);
+ return (0);
+}
+
+static void
+window_customize_set_option(struct client *c,
+ struct window_customize_modedata *data,
+ struct window_customize_itemdata *item, int global, int pane)
+{
+ struct options_entry *o;
+ const struct options_table_entry *oe;
+ struct options *oo;
+ struct window_customize_itemdata *new_item;
+ int flag, idx = item->idx;
+ enum window_customize_scope scope;
+ u_int choice;
+ const char *name = item->name, *space = "";
+ char *prompt, *value, *text;
+ struct cmd_find_state fs;
+
+ if (item == NULL || !window_customize_check_item(data, item, &fs))
+ return;
+ o = options_get(item->oo, name);
+ if (o == NULL)
+ return;
+
+ oe = options_table_entry(o);
+ if (~oe->scope & OPTIONS_TABLE_PANE)
+ pane = 0;
+ if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY)) {
+ scope = item->scope;
+ oo = item->oo;
+ } else {
+ if (global) {
+ switch (item->scope) {
+ case WINDOW_CUSTOMIZE_NONE:
+ case WINDOW_CUSTOMIZE_KEY:
+ case WINDOW_CUSTOMIZE_SERVER:
+ case WINDOW_CUSTOMIZE_GLOBAL_SESSION:
+ case WINDOW_CUSTOMIZE_GLOBAL_WINDOW:
+ scope = item->scope;
+ break;
+ case WINDOW_CUSTOMIZE_SESSION:
+ scope = WINDOW_CUSTOMIZE_GLOBAL_SESSION;
+ break;
+ case WINDOW_CUSTOMIZE_WINDOW:
+ case WINDOW_CUSTOMIZE_PANE:
+ scope = WINDOW_CUSTOMIZE_GLOBAL_WINDOW;
+ break;
+ }
+ } else {
+ switch (item->scope) {
+ case WINDOW_CUSTOMIZE_NONE:
+ case WINDOW_CUSTOMIZE_KEY:
+ case WINDOW_CUSTOMIZE_SERVER:
+ case WINDOW_CUSTOMIZE_SESSION:
+ scope = item->scope;
+ break;
+ case WINDOW_CUSTOMIZE_WINDOW:
+ case WINDOW_CUSTOMIZE_PANE:
+ if (pane)
+ scope = WINDOW_CUSTOMIZE_PANE;
+ else
+ scope = WINDOW_CUSTOMIZE_WINDOW;
+ break;
+ case WINDOW_CUSTOMIZE_GLOBAL_SESSION:
+ scope = WINDOW_CUSTOMIZE_SESSION;
+ break;
+ case WINDOW_CUSTOMIZE_GLOBAL_WINDOW:
+ if (pane)
+ scope = WINDOW_CUSTOMIZE_PANE;
+ else
+ scope = WINDOW_CUSTOMIZE_WINDOW;
+ break;
+ }
+ }
+ if (scope == item->scope)
+ oo = item->oo;
+ else
+ oo = window_customize_get_tree(scope, &fs);
+ }
+
+ if (oe != NULL && oe->type == OPTIONS_TABLE_FLAG) {
+ flag = options_get_number(oo, name);
+ options_set_number(oo, name, !flag);
+ } else if (oe != NULL && oe->type == OPTIONS_TABLE_CHOICE) {
+ choice = options_get_number(oo, name);
+ if (oe->choices[choice + 1] == NULL)
+ choice = 0;
+ else
+ choice++;
+ options_set_number(oo, name, choice);
+ } else {
+ text = window_customize_scope_text(scope, &fs);
+ if (*text != '\0')
+ space = ", for ";
+ else if (scope != WINDOW_CUSTOMIZE_SERVER)
+ space = ", global";
+ if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY)) {
+ if (idx == -1) {
+ xasprintf(&prompt, "(%s[+]%s%s) ", name, space,
+ text);
+ } else {
+ xasprintf(&prompt, "(%s[%d]%s%s) ", name, idx,
+ space, text);
+ }
+ } else
+ xasprintf(&prompt, "(%s%s%s) ", name, space, text);
+ free(text);
+
+ value = options_to_string(o, idx, 0);
+
+ new_item = xcalloc(1, sizeof *new_item);
+ new_item->data = data;
+ new_item->scope = scope;
+ new_item->oo = oo;
+ new_item->name = xstrdup(name);
+ new_item->idx = idx;
+
+ data->references++;
+ status_prompt_set(c, prompt, value,
+ window_customize_set_option_callback,
+ window_customize_free_item_callback, new_item,
+ PROMPT_NOFORMAT);
+
+ free(prompt);
+ free(value);
+ }
+}
+
+static void
+window_customize_unset_option(struct window_customize_modedata *data,
+ struct window_customize_itemdata *item)
+{
+ struct options_entry *o;
+ const struct options_table_entry *oe;
+
+ if (item == NULL || !window_customize_check_item(data, item, NULL))
+ return;
+
+ o = options_get(item->oo, item->name);
+ if (o == NULL)
+ return;
+ if (item->idx != -1) {
+ if (item == mode_tree_get_current(data->data))
+ mode_tree_up(data->data, 0);
+ options_array_set(o, item->idx, NULL, 0, NULL);
+ return;
+ }
+ oe = options_table_entry(o);
+ if (oe != NULL &&
+ options_owner(o) != global_options &&
+ options_owner(o) != global_s_options &&
+ options_owner(o) != global_w_options)
+ options_remove(o);
+ else
+ options_default(options_owner(o), oe);
+}
+
+static int
+window_customize_set_command_callback(struct client *c, void *itemdata,
+ const char *s, __unused int done)
+{
+ struct window_customize_itemdata *item = itemdata;
+ struct window_customize_modedata *data = item->data;
+ struct key_binding *bd;
+ struct cmd_parse_result *pr;
+ char *error;
+
+ if (s == NULL || *s == '\0' || data->dead)
+ return (0);
+ if (item == NULL || !window_customize_get_key(item, NULL, &bd))
+ return (0);
+
+ pr = cmd_parse_from_string(s, NULL);
+ switch (pr->status) {
+ case CMD_PARSE_EMPTY:
+ error = xstrdup("empty command");
+ goto fail;
+ case CMD_PARSE_ERROR:
+ error = pr->error;
+ goto fail;
+ case CMD_PARSE_SUCCESS:
+ break;
+ }
+ cmd_list_free(bd->cmdlist);
+ bd->cmdlist = pr->cmdlist;
+
+ mode_tree_build(data->data);
+ mode_tree_draw(data->data);
+ data->wp->flags |= PANE_REDRAW;
+
+ return (0);
+
+fail:
+ *error = toupper((u_char)*error);
+ status_message_set(c, 1, "%s", error);
+ free(error);
+ return (0);
+}
+
+static int
+window_customize_set_note_callback(__unused struct client *c, void *itemdata,
+ const char *s, __unused int done)
+{
+ struct window_customize_itemdata *item = itemdata;
+ struct window_customize_modedata *data = item->data;
+ struct key_binding *bd;
+
+ if (s == NULL || *s == '\0' || data->dead)
+ return (0);
+ if (item == NULL || !window_customize_get_key(item, NULL, &bd))
+ return (0);
+
+ free((void *)bd->note);
+ bd->note = xstrdup(s);
+
+ mode_tree_build(data->data);
+ mode_tree_draw(data->data);
+ data->wp->flags |= PANE_REDRAW;
+
+ return (0);
+}
+
+static void
+window_customize_set_key(struct client *c,
+ struct window_customize_modedata *data,
+ struct window_customize_itemdata *item)
+{
+ key_code key = item->key;
+ struct key_binding *bd;
+ const char *s;
+ char *prompt, *value;
+ struct window_customize_itemdata *new_item;
+
+ if (item == NULL || !window_customize_get_key(item, NULL, &bd))
+ return;
+
+ s = mode_tree_get_current_name(data->data);
+ if (strcmp(s, "Repeat") == 0)
+ bd->flags ^= KEY_BINDING_REPEAT;
+ else if (strcmp(s, "Command") == 0) {
+ xasprintf(&prompt, "(%s) ", key_string_lookup_key(key));
+ value = cmd_list_print(bd->cmdlist, 0);
+
+ new_item = xcalloc(1, sizeof *new_item);
+ new_item->data = data;
+ new_item->scope = item->scope;
+ new_item->table = xstrdup(item->table);
+ new_item->key = key;
+
+ data->references++;
+ status_prompt_set(c, prompt, value,
+ window_customize_set_command_callback,
+ window_customize_free_item_callback, new_item,
+ PROMPT_NOFORMAT);
+ free(prompt);
+ free(value);
+ } else if (strcmp(s, "Note") == 0) {
+ xasprintf(&prompt, "(%s) ", key_string_lookup_key(key));
+
+ new_item = xcalloc(1, sizeof *new_item);
+ new_item->data = data;
+ new_item->scope = item->scope;
+ new_item->table = xstrdup(item->table);
+ new_item->key = key;
+
+ data->references++;
+ status_prompt_set(c, prompt, (bd->note == NULL ? "" : bd->note),
+ window_customize_set_note_callback,
+ window_customize_free_item_callback, new_item,
+ PROMPT_NOFORMAT);
+ free(prompt);
+ }
+}
+
+static void
+window_customize_unset_key(struct window_customize_modedata *data,
+ struct window_customize_itemdata *item)
+{
+ struct key_table *kt;
+ struct key_binding *bd;
+
+ if (item == NULL || !window_customize_get_key(item, &kt, &bd))
+ return;
+
+ if (item == mode_tree_get_current(data->data)) {
+ mode_tree_collapse_current(data->data);
+ mode_tree_up(data->data, 0);
+ }
+ key_bindings_remove(kt->name, bd->key);
+}
+
+static void
+window_customize_unset_each(void *modedata, void *itemdata,
+ __unused struct client *c, __unused key_code key)
+{
+ struct window_customize_itemdata *item = itemdata;
+
+ if (item->scope == WINDOW_CUSTOMIZE_KEY)
+ window_customize_unset_key(modedata, item);
+ else {
+ window_customize_unset_option(modedata, item);
+ options_push_changes(item->name);
+ }
+}
+
+static int
+window_customize_unset_current_callback(__unused struct client *c,
+ void *modedata, const char *s, __unused int done)
+{
+ struct window_customize_modedata *data = modedata;
+ struct window_customize_itemdata *item;
+
+ if (s == NULL || *s == '\0' || data->dead)
+ return (0);
+ if (tolower((u_char) s[0]) != 'y' || s[1] != '\0')
+ return (0);
+
+ item = mode_tree_get_current(data->data);
+ if (item->scope == WINDOW_CUSTOMIZE_KEY)
+ window_customize_unset_key(data, item);
+ else {
+ window_customize_unset_option(data, item);
+ options_push_changes(item->name);
+ }
+ mode_tree_build(data->data);
+ mode_tree_draw(data->data);
+ data->wp->flags |= PANE_REDRAW;
+
+ return (0);
+}
+
+static int
+window_customize_unset_tagged_callback(struct client *c, void *modedata,
+ const char *s, __unused int done)
+{
+ struct window_customize_modedata *data = modedata;
+
+ if (s == NULL || *s == '\0' || data->dead)
+ return (0);
+ if (tolower((u_char) s[0]) != 'y' || s[1] != '\0')
+ return (0);
+
+ mode_tree_each_tagged(data->data, window_customize_unset_each, c,
+ KEYC_NONE, 0);
+ mode_tree_build(data->data);
+ mode_tree_draw(data->data);
+ data->wp->flags |= PANE_REDRAW;
+
+ return (0);
+}
+
+static void
+window_customize_key(struct window_mode_entry *wme, struct client *c,
+ __unused struct session *s, __unused struct winlink *wl, key_code key,
+ struct mouse_event *m)
+{
+ struct window_pane *wp = wme->wp;
+ struct window_customize_modedata *data = wme->data;
+ struct window_customize_itemdata *item, *new_item;
+ int finished;
+ char *prompt;
+ u_int tagged;
+
+ item = mode_tree_get_current(data->data);
+ finished = mode_tree_key(data->data, c, &key, m, NULL, NULL);
+ if (item != (new_item = mode_tree_get_current(data->data)))
+ item = new_item;
+
+ switch (key) {
+ case '\r':
+ case 's':
+ if (item == NULL)
+ break;
+ if (item->scope == WINDOW_CUSTOMIZE_KEY)
+ window_customize_set_key(c, data, item);
+ else {
+ window_customize_set_option(c, data, item, 0, 1);
+ options_push_changes(item->name);
+ }
+ mode_tree_build(data->data);
+ break;
+ case 'w':
+ if (item == NULL || item->scope == WINDOW_CUSTOMIZE_KEY)
+ break;
+ window_customize_set_option(c, data, item, 0, 0);
+ options_push_changes(item->name);
+ mode_tree_build(data->data);
+ break;
+ case 'S':
+ case 'W':
+ if (item == NULL || item->scope == WINDOW_CUSTOMIZE_KEY)
+ break;
+ window_customize_set_option(c, data, item, 1, 0);
+ options_push_changes(item->name);
+ mode_tree_build(data->data);
+ break;
+ case 'u':
+ if (item == NULL)
+ break;
+ if (item->scope == WINDOW_CUSTOMIZE_KEY) {
+ xasprintf(&prompt, "Unbind key %s? ",
+ key_string_lookup_key(item->key));
+ } else
+ xasprintf(&prompt, "Unset option %s? ", item->name);
+ data->references++;
+ status_prompt_set(c, prompt, "",
+ window_customize_unset_current_callback,
+ window_customize_free_callback, data,
+ PROMPT_SINGLE|PROMPT_NOFORMAT);
+ free(prompt);
+ break;
+ case 'U':
+ tagged = mode_tree_count_tagged(data->data);
+ if (tagged == 0)
+ break;
+ xasprintf(&prompt, "Unset or unbind %u tagged? ", tagged);
+ data->references++;
+ status_prompt_set(c, prompt, "",
+ window_customize_unset_tagged_callback,
+ window_customize_free_callback, data,
+ PROMPT_SINGLE|PROMPT_NOFORMAT);
+ free(prompt);
+ break;
+ case 'H':
+ data->hide_global = !data->hide_global;
+ mode_tree_build(data->data);
+ break;
+ }
+ if (finished)
+ window_pane_reset_mode(wp);
+ else {
+ mode_tree_draw(data->data);
+ wp->flags |= PANE_REDRAW;
+ }
+}
diff --git a/usr.bin/tmux/window-tree.c b/usr.bin/tmux/window-tree.c
index 1a590e1b709..fdd72fe907a 100644
--- a/usr.bin/tmux/window-tree.c
+++ b/usr.bin/tmux/window-tree.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: window-tree.c,v 1.48 2020/05/16 14:10:29 nicm Exp $ */
+/* $OpenBSD: window-tree.c,v 1.49 2020/05/16 16:02:24 nicm Exp $ */
/*
* Copyright (c) 2017 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -885,7 +885,7 @@ window_tree_init(struct window_mode_entry *wme, struct cmd_find_state *fs,
data->squash_groups = !args_has(args, 'G');
data->data = mode_tree_start(wp, args, window_tree_build,
- window_tree_draw, window_tree_search, window_tree_menu, data,
+ window_tree_draw, window_tree_search, window_tree_menu, NULL, data,
window_tree_menu_items, window_tree_sort_list,
nitems(window_tree_sort_list), &s);
mode_tree_zoom(data->data, args);