aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJosh Rahm <rahm@google.com>2023-01-27 23:04:47 +0000
committerJosh Rahm <rahm@google.com>2023-01-27 23:04:47 +0000
commite64dc03ff7b867826c7fc76d6fff210ad3382e33 (patch)
tree9cf71b02a7ee8f6d39ae1207201dc0745bdb8be5
parentfb15fd116097b98c1b839cfdc76a7d84e206e6d5 (diff)
parentf5af3cfb211c12897b15e3b5a3b29c4bbb0493a8 (diff)
downloadrtmux-e64dc03ff7b867826c7fc76d6fff210ad3382e33.tar.gz
rtmux-e64dc03ff7b867826c7fc76d6fff210ad3382e33.tar.bz2
rtmux-e64dc03ff7b867826c7fc76d6fff210ad3382e33.zip
Merge remote-tracking branch 'upstream/master' into rahm
-rw-r--r--arguments.c213
-rw-r--r--client.c9
-rw-r--r--cmd-break-pane.c1
-rw-r--r--cmd-capture-pane.c18
-rw-r--r--cmd-display-menu.c29
-rw-r--r--cmd-display-message.c10
-rw-r--r--cmd-find-window.c4
-rw-r--r--cmd-find.c10
-rw-r--r--cmd-join-pane.c1
-rw-r--r--cmd-list-keys.c11
-rw-r--r--cmd-parse.y3
-rw-r--r--cmd-queue.c96
-rw-r--r--cmd-save-buffer.c13
-rw-r--r--cmd-send-keys.c37
-rw-r--r--cmd-swap-pane.c2
-rw-r--r--colour.c42
-rw-r--r--compat.h1
-rw-r--r--compat/getpeereid.c5
-rw-r--r--compat/systemd.c10
-rw-r--r--configure.ac6
-rw-r--r--control-notify.c26
-rw-r--r--control.c4
-rw-r--r--environ.c15
-rw-r--r--file.c48
-rw-r--r--format.c30
-rw-r--r--grid-view.c2
-rw-r--r--grid.c88
-rw-r--r--input-keys.c26
-rw-r--r--input.c143
-rw-r--r--key-bindings.c5
-rw-r--r--key-string.c4
-rw-r--r--menu.c120
-rw-r--r--mode-tree.c4
-rw-r--r--notify.c37
-rw-r--r--options-table.c23
-rw-r--r--paste.c11
-rw-r--r--popup.c2
-rw-r--r--regress/conf/2eae5d47049c1f6d0bef3db4e171aed7.conf2
-rw-r--r--regress/conf/a46e6e84cd1071105aa807256dbc158d.conf2
-rw-r--r--regress/conf/ad21dbb0893240563ddfdd954b9903a1.conf2
-rw-r--r--regress/conf/b9f0ce1976ec62ec60dc5da7dd92c160.conf6
-rw-r--r--regress/conf/e2661d67d0d45a8647fb95de76ec8174.conf2
-rw-r--r--regress/input-keys.sh299
-rw-r--r--regress/tty-keys.sh361
-rw-r--r--screen-write.c27
-rw-r--r--server-client.c28
-rw-r--r--session.c9
-rw-r--r--spawn.c2
-rw-r--r--status.c41
-rw-r--r--tmux-protocol.h7
-rw-r--r--tmux.1113
-rw-r--r--tmux.c2
-rw-r--r--tmux.h100
-rw-r--r--tty-features.c64
-rw-r--r--tty-keys.c218
-rw-r--r--tty-term.c5
-rw-r--r--tty.c57
-rw-r--r--utf8.c11
-rw-r--r--window-buffer.c7
-rw-r--r--window-copy.c78
-rw-r--r--window.c22
61 files changed, 2083 insertions, 491 deletions
diff --git a/arguments.c b/arguments.c
index 46777f7f..9a3115dc 100644
--- a/arguments.c
+++ b/arguments.c
@@ -37,6 +37,10 @@ struct args_entry {
u_char flag;
struct args_values values;
u_int count;
+
+ int flags;
+#define ARGS_ENTRY_OPTIONAL_VALUE 0x1
+
RB_ENTRY(args_entry) entry;
};
@@ -122,6 +126,99 @@ args_create(void)
return (args);
}
+/* Parse a single flag. */
+static int
+args_parse_flag_argument(struct args_value *values, u_int count, char **cause,
+ struct args *args, u_int *i, const char *string, int flag,
+ int optional_argument)
+{
+ struct args_value *argument, *new;
+ const char *s;
+
+ new = xcalloc(1, sizeof *new);
+ if (*string != '\0') {
+ new->type = ARGS_STRING;
+ new->string = xstrdup(string);
+ goto out;
+ }
+
+ if (*i == count)
+ argument = NULL;
+ else {
+ argument = &values[*i];
+ if (argument->type != ARGS_STRING) {
+ xasprintf(cause, "-%c argument must be a string", flag);
+ return (-1);
+ }
+ }
+ if (argument == NULL) {
+ if (optional_argument) {
+ log_debug("%s: -%c (optional)", __func__, flag);
+ args_set(args, flag, NULL, ARGS_ENTRY_OPTIONAL_VALUE);
+ return (0); /* either - or end */
+ }
+ xasprintf(cause, "-%c expects an argument", flag);
+ return (-1);
+ }
+ args_copy_value(new, argument);
+ (*i)++;
+
+out:
+ s = args_value_as_string(new);
+ log_debug("%s: -%c = %s", __func__, flag, s);
+ args_set(args, flag, new, 0);
+ return (0);
+}
+
+/* Parse flags argument. */
+static int
+args_parse_flags(const struct args_parse *parse, struct args_value *values,
+ u_int count, char **cause, struct args *args, int *i)
+{
+ struct args_value *value;
+ u_char flag;
+ const char *found, *string;
+ int optional_argument;
+
+ value = &values[*i];
+ if (value->type != ARGS_STRING)
+ return (1);
+
+ string = value->string;
+ log_debug("%s: next %s", __func__, string);
+ if (*string++ != '-' || *string == '\0')
+ return (1);
+ (*i)++;
+ if (string[0] == '-' && string[1] == '\0')
+ return (1);
+
+ for (;;) {
+ flag = *string++;
+ if (flag == '\0')
+ return (0);
+ if (flag == '?')
+ return (-1);
+ if (!isalnum(flag)) {
+ xasprintf(cause, "invalid flag -%c", flag);
+ return (-1);
+ }
+
+ found = strchr(parse->template, flag);
+ if (found == NULL) {
+ xasprintf(cause, "unknown flag -%c", flag);
+ return (-1);
+ }
+ if (found[1] != ':') {
+ log_debug("%s: -%c", __func__, flag);
+ args_set(args, flag, NULL, 0);
+ continue;
+ }
+ optional_argument = (found[2] == ':');
+ return (args_parse_flag_argument(values, count, cause, args, i,
+ string, flag, optional_argument));
+ }
+}
+
/* Parse arguments into a new argument set. */
struct args *
args_parse(const struct args_parse *parse, struct args_value *values,
@@ -131,86 +228,21 @@ args_parse(const struct args_parse *parse, struct args_value *values,
u_int i;
enum args_parse_type type;
struct args_value *value, *new;
- u_char flag;
- const char *found, *string, *s;
- int optional_argument;
+ const char *s;
+ int stop;
if (count == 0)
return (args_create());
args = args_create();
for (i = 1; i < count; /* nothing */) {
- value = &values[i];
- if (value->type != ARGS_STRING)
- break;
-
- string = value->string;
- if (*string++ != '-' || *string == '\0')
- break;
- i++;
- if (string[0] == '-' && string[1] == '\0')
- break;
-
- for (;;) {
- flag = *string++;
- if (flag == '\0')
- break;
- if (flag == '?') {
- args_free(args);
- return (NULL);
- }
- if (!isalnum(flag)) {
- xasprintf(cause, "invalid flag -%c", flag);
- args_free(args);
- return (NULL);
- }
- found = strchr(parse->template, flag);
- if (found == NULL) {
- xasprintf(cause, "unknown flag -%c", flag);
- args_free(args);
- return (NULL);
- }
- if (*++found != ':') {
- log_debug("%s: -%c", __func__, flag);
- args_set(args, flag, NULL);
- continue;
- }
- if (*found == ':') {
- optional_argument = 1;
- found++;
- }
- new = xcalloc(1, sizeof *new);
- if (*string != '\0') {
- new->type = ARGS_STRING;
- new->string = xstrdup(string);
- } else {
- if (i == count) {
- if (optional_argument) {
- log_debug("%s: -%c", __func__,
- flag);
- args_set(args, flag, NULL);
- continue;
- }
- xasprintf(cause,
- "-%c expects an argument",
- flag);
- args_free(args);
- return (NULL);
- }
- if (values[i].type != ARGS_STRING) {
- xasprintf(cause,
- "-%c argument must be a string",
- flag);
- args_free(args);
- return (NULL);
- }
- args_copy_value(new, &values[i++]);
- }
- s = args_value_as_string(new);
- log_debug("%s: -%c = %s", __func__, flag, s);
- args_set(args, flag, new);
- break;
+ stop = args_parse_flags(parse, values, count, cause, args, &i);
+ if (stop == -1) {
+ args_free(args);
+ return (NULL);
}
+ if (stop == 1)
+ break;
}
log_debug("%s: flags end at %u of %u", __func__, i, count);
if (i != count) {
@@ -323,13 +355,13 @@ args_copy(struct args *args, int argc, char **argv)
RB_FOREACH(entry, args_tree, &args->tree) {
if (TAILQ_EMPTY(&entry->values)) {
for (i = 0; i < entry->count; i++)
- args_set(new_args, entry->flag, NULL);
+ args_set(new_args, entry->flag, NULL, 0);
continue;
}
TAILQ_FOREACH(value, &entry->values, entry) {
new_value = xcalloc(1, sizeof *new_value);
args_copy_copy_value(new_value, value, argc, argv);
- args_set(new_args, entry->flag, new_value);
+ args_set(new_args, entry->flag, new_value, 0);
}
}
if (args->count == 0)
@@ -487,6 +519,7 @@ args_print(struct args *args)
char *buf;
u_int i, j;
struct args_entry *entry;
+ struct args_entry *last = NULL;
struct args_value *value;
len = 1;
@@ -494,6 +527,8 @@ args_print(struct args *args)
/* Process the flags first. */
RB_FOREACH(entry, args_tree, &args->tree) {
+ if (entry->flags & ARGS_ENTRY_OPTIONAL_VALUE)
+ continue;
if (!TAILQ_EMPTY(&entry->values))
continue;
@@ -505,6 +540,16 @@ args_print(struct args *args)
/* Then the flags with arguments. */
RB_FOREACH(entry, args_tree, &args->tree) {
+ if (entry->flags & ARGS_ENTRY_OPTIONAL_VALUE) {
+ if (*buf != '\0')
+ args_print_add(&buf, &len, " -%c", entry->flag);
+ else
+ args_print_add(&buf, &len, "-%c", entry->flag);
+ last = entry;
+ continue;
+ }
+ if (TAILQ_EMPTY(&entry->values))
+ continue;
TAILQ_FOREACH(value, &entry->values, entry) {
if (*buf != '\0')
args_print_add(&buf, &len, " -%c", entry->flag);
@@ -512,7 +557,10 @@ args_print(struct args *args)
args_print_add(&buf, &len, "-%c", entry->flag);
args_print_add_value(&buf, &len, value);
}
+ last = entry;
}
+ if (last && (last->flags & ARGS_ENTRY_OPTIONAL_VALUE))
+ args_print_add(&buf, &len, " --");
/* And finally the argument vector. */
for (i = 0; i < args->count; i++)
@@ -582,7 +630,7 @@ args_has(struct args *args, u_char flag)
/* Set argument value in the arguments tree. */
void
-args_set(struct args *args, u_char flag, struct args_value *value)
+args_set(struct args *args, u_char flag, struct args_value *value, int flags)
{
struct args_entry *entry;
@@ -591,6 +639,7 @@ args_set(struct args *args, u_char flag, struct args_value *value)
entry = xcalloc(1, sizeof *entry);
entry->flag = flag;
entry->count = 1;
+ entry->flags = flags;
TAILQ_INIT(&entry->values);
RB_INSERT(args_tree, &args->tree, entry);
} else
@@ -895,6 +944,10 @@ args_percentage(struct args *args, u_char flag, long long minval,
*cause = xstrdup("missing");
return (0);
}
+ if (TAILQ_EMPTY(&entry->values)) {
+ *cause = xstrdup("empty");
+ return (0);
+ }
value = TAILQ_LAST(&entry->values, args_values)->string;
return (args_string_percentage(value, minval, maxval, curval, cause));
}
@@ -909,6 +962,10 @@ args_string_percentage(const char *value, long long minval, long long maxval,
size_t valuelen = strlen(value);
char *copy;
+ if (valuelen == 0) {
+ *cause = xstrdup("empty");
+ return (0);
+ }
if (value[valuelen - 1] == '%') {
copy = xstrdup(value);
copy[valuelen - 1] = '\0';
@@ -955,6 +1012,10 @@ args_percentage_and_expand(struct args *args, u_char flag, long long minval,
*cause = xstrdup("missing");
return (0);
}
+ if (TAILQ_EMPTY(&entry->values)) {
+ *cause = xstrdup("empty");
+ return (0);
+ }
value = TAILQ_LAST(&entry->values, args_values)->string;
return (args_string_percentage_and_expand(value, minval, maxval, curval,
item, cause));
diff --git a/client.c b/client.c
index df6cee94..374c1146 100644
--- a/client.c
+++ b/client.c
@@ -284,6 +284,12 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags,
log_debug("flags are %#llx", (unsigned long long)client_flags);
/* Initialize the client socket and start the server. */
+#ifdef HAVE_SYSTEMD
+ if (systemd_activated()) {
+ /* socket-based activation, do not even try to be a client. */
+ fd = server_start(client_proc, flags, base, 0, NULL);
+ } else
+#endif
fd = client_connect(base, socket_path, client_flags);
if (fd == -1) {
if (errno == ECONNREFUSED) {
@@ -694,6 +700,9 @@ client_dispatch_wait(struct imsg *imsg)
!(client_flags & CLIENT_CONTROL), client_file_check_cb,
NULL);
break;
+ case MSG_READ_CANCEL:
+ file_read_cancel(&client_files, imsg);
+ break;
case MSG_WRITE_OPEN:
file_write_open(&client_files, client_peer, imsg, 1,
!(client_flags & CLIENT_CONTROL), client_file_check_cb,
diff --git a/cmd-break-pane.c b/cmd-break-pane.c
index 4f38d4bd..9c4b1508 100644
--- a/cmd-break-pane.c
+++ b/cmd-break-pane.c
@@ -115,6 +115,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
layout_init(w, wp);
wp->flags |= PANE_CHANGED;
+ colour_palette_from_option(&wp->palette, wp->options);
if (idx == -1)
idx = -1 - options_get_number(dst_s->options, "base-index");
diff --git a/cmd-capture-pane.c b/cmd-capture-pane.c
index 422f87d6..57e9716d 100644
--- a/cmd-capture-pane.c
+++ b/cmd-capture-pane.c
@@ -40,7 +40,7 @@ const struct cmd_entry cmd_capture_pane_entry = {
.alias = "capturep",
.args = { "ab:CeE:JNpPqS:t:", 0, 0, NULL },
- .usage = "[-aCeJNpPq] " CMD_BUFFER_USAGE " [-E end-line] "
+ .usage = "[-aCeJNpPqT] " CMD_BUFFER_USAGE " [-E end-line] "
"[-S start-line] " CMD_TARGET_PANE_USAGE,
.target = { 't', CMD_FIND_PANE, 0 },
@@ -110,7 +110,7 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item,
struct grid *gd;
const struct grid_line *gl;
struct grid_cell *gc = NULL;
- int n, with_codes, escape_c0, join_lines, no_trim;
+ int n, join_lines, flags = 0;
u_int i, sx, top, bottom, tmp;
char *cause, *buf, *line;
const char *Sflag, *Eflag;
@@ -169,15 +169,19 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item,
top = tmp;
}
- with_codes = args_has(args, 'e');
- escape_c0 = args_has(args, 'C');
join_lines = args_has(args, 'J');
- no_trim = args_has(args, 'N');
+ if (args_has(args, 'e'))
+ flags |= GRID_STRING_WITH_SEQUENCES;
+ if (args_has(args, 'C'))
+ flags |= GRID_STRING_ESCAPE_SEQUENCES;
+ if (!join_lines && !args_has(args, 'T'))
+ flags |= GRID_STRING_EMPTY_CELLS;
+ if (!join_lines && !args_has(args, 'N'))
+ flags |= GRID_STRING_TRIM_SPACES;
buf = NULL;
for (i = top; i <= bottom; i++) {
- line = grid_string_cells(gd, 0, i, sx, &gc, with_codes,
- escape_c0, !join_lines && !no_trim, wp->screen);
+ line = grid_string_cells(gd, 0, i, sx, &gc, flags, wp->screen);
linelen = strlen(line);
buf = cmd_capture_pane_append(buf, len, line, linelen);
diff --git a/cmd-display-menu.c b/cmd-display-menu.c
index e6a503b1..22cc5d9f 100644
--- a/cmd-display-menu.c
+++ b/cmd-display-menu.c
@@ -38,9 +38,10 @@ const struct cmd_entry cmd_display_menu_entry = {
.name = "display-menu",
.alias = "menu",
- .args = { "c:t:OT:x:y:", 1, -1, cmd_display_menu_args_parse },
- .usage = "[-O] [-c target-client] " CMD_TARGET_PANE_USAGE " [-T title] "
- "[-x position] [-y position] name key command ...",
+ .args = { "c:t:S:OT:x:y:", 1, -1, cmd_display_menu_args_parse },
+ .usage = "[-O] [-c target-client] [-S starting-choice] "
+ CMD_TARGET_PANE_USAGE " [-T title] [-x position] "
+ "[-y position] name key command ...",
.target = { 't', CMD_FIND_PANE, 0 },
@@ -287,13 +288,27 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
struct menu *menu = NULL;
struct menu_item menu_item;
const char *key, *name;
- char *title;
- int flags = 0;
+ char *title, *cause;
+ int flags = 0, starting_choice = 0;
u_int px, py, i, count = args_count(args);
if (tc->overlay_draw != NULL)
return (CMD_RETURN_NORMAL);
+ if (args_has(args, 'S')) {
+ if (strcmp(args_get(args, 'S'), "-") == 0)
+ starting_choice = -1;
+ else {
+ starting_choice = args_strtonum(args, 'S', 0, UINT_MAX,
+ &cause);
+ if (cause != NULL) {
+ cmdq_error(item, "starting choice %s", cause);
+ free(cause);
+ return (CMD_RETURN_ERROR);
+ }
+ }
+ }
+
if (args_has(args, 'T'))
title = format_single_from_target(item, args_get(args, 'T'));
else
@@ -340,8 +355,8 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
flags |= MENU_STAYOPEN;
if (!event->m.valid)
flags |= MENU_NOMOUSE;
- if (menu_display(menu, flags, item, px, py, tc, target, NULL,
- NULL) != 0)
+ if (menu_display(menu, flags, starting_choice, item, px, py, tc, target,
+ NULL, NULL) != 0)
return (CMD_RETURN_NORMAL);
return (CMD_RETURN_WAIT);
}
diff --git a/cmd-display-message.c b/cmd-display-message.c
index 7828f694..f5e91020 100644
--- a/cmd-display-message.c
+++ b/cmd-display-message.c
@@ -39,8 +39,8 @@ const struct cmd_entry cmd_display_message_entry = {
.name = "display-message",
.alias = "display",
- .args = { "ac:d:INpt:F:v", 0, 1, NULL },
- .usage = "[-aINpv] [-c target-client] [-d delay] [-F format] "
+ .args = { "ac:d:lINpt:F:v", 0, 1, NULL },
+ .usage = "[-aIlNpv] [-c target-client] [-d delay] [-F format] "
CMD_TARGET_PANE_USAGE " [message]",
.target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL },
@@ -132,7 +132,11 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_NORMAL);
}
- msg = format_expand_time(ft, template);
+ if (args_has(args, 'l'))
+ msg = xstrdup(template);
+ else
+ msg = format_expand_time(ft, template);
+
if (cmdq_get_client(item) == NULL)
cmdq_error(item, "%s", msg);
else if (args_has(args, 'p'))
diff --git a/cmd-find-window.c b/cmd-find-window.c
index 6e07537c..cb9afacb 100644
--- a/cmd-find-window.c
+++ b/cmd-find-window.c
@@ -103,8 +103,8 @@ cmd_find_window_exec(struct cmd *self, struct cmdq_item *item)
new_args = args_create();
if (args_has(args, 'Z'))
- args_set(new_args, 'Z', NULL);
- args_set(new_args, 'f', filter);
+ args_set(new_args, 'Z', NULL, 0);
+ args_set(new_args, 'f', filter, 0);
window_pane_set_mode(wp, NULL, &window_tree_mode, target, new_args);
args_free(new_args);
diff --git a/cmd-find.c b/cmd-find.c
index e98090d2..154cadac 100644
--- a/cmd-find.c
+++ b/cmd-find.c
@@ -587,22 +587,22 @@ cmd_find_get_pane_with_window(struct cmd_find_state *fs, const char *pane)
return (-1);
return (0);
} else if (strcmp(pane, "{up-of}") == 0) {
- fs->wp = window_pane_find_up(fs->current->wp);
+ fs->wp = window_pane_find_up(fs->w->active);
if (fs->wp == NULL)
return (-1);
return (0);
} else if (strcmp(pane, "{down-of}") == 0) {
- fs->wp = window_pane_find_down(fs->current->wp);
+ fs->wp = window_pane_find_down(fs->w->active);
if (fs->wp == NULL)
return (-1);
return (0);
} else if (strcmp(pane, "{left-of}") == 0) {
- fs->wp = window_pane_find_left(fs->current->wp);
+ fs->wp = window_pane_find_left(fs->w->active);
if (fs->wp == NULL)
return (-1);
return (0);
} else if (strcmp(pane, "{right-of}") == 0) {
- fs->wp = window_pane_find_right(fs->current->wp);
+ fs->wp = window_pane_find_right(fs->w->active);
if (fs->wp == NULL)
return (-1);
return (0);
@@ -614,7 +614,7 @@ cmd_find_get_pane_with_window(struct cmd_find_state *fs, const char *pane)
n = strtonum(pane + 1, 1, INT_MAX, NULL);
else
n = 1;
- wp = fs->current->wp;
+ wp = fs->w->active;
if (pane[0] == '+')
fs->wp = window_pane_next_by_number(fs->w, wp, n);
else
diff --git a/cmd-join-pane.c b/cmd-join-pane.c
index e82b4cde..da1ba9ae 100644
--- a/cmd-join-pane.c
+++ b/cmd-join-pane.c
@@ -155,6 +155,7 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
else
TAILQ_INSERT_AFTER(&dst_w->panes, dst_wp, src_wp, entry);
layout_assign_pane(lc, src_wp, 0);
+ colour_palette_from_option(&src_wp->palette, src_wp->options);
recalculate_sizes();
diff --git a/cmd-list-keys.c b/cmd-list-keys.c
index ae9f995c..395b147c 100644
--- a/cmd-list-keys.c
+++ b/cmd-list-keys.c
@@ -148,6 +148,7 @@ static enum cmd_retval
cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = cmd_get_args(self);
+ struct client *tc = cmdq_get_target_client(item);
struct key_table *table;
struct key_binding *bd;
const char *tablename, *r, *keystr;
@@ -296,9 +297,15 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
strlcat(tmp, cp, tmpsize);
free(cp);
- cmdq_print(item, "bind-key %s", tmp);
-
+ if (args_has(args, '1') && tc != NULL) {
+ status_message_set(tc, -1, 1, 0, "bind-key %s",
+ tmp);
+ } else
+ cmdq_print(item, "bind-key %s", tmp);
free(key);
+
+ if (args_has(args, '1'))
+ break;
bd = key_bindings_next(table, bd);
}
table = key_bindings_next_table(table);
diff --git a/cmd-parse.y b/cmd-parse.y
index 1d692770..cdf026f3 100644
--- a/cmd-parse.y
+++ b/cmd-parse.y
@@ -1086,7 +1086,8 @@ cmd_parse_from_arguments(struct args_value *values, u_int count,
arg->type = CMD_PARSE_STRING;
arg->string = copy;
TAILQ_INSERT_TAIL(&cmd->arguments, arg, entry);
- }
+ } else
+ free(copy);
} else if (values[i].type == ARGS_COMMANDS) {
arg = xcalloc(1, sizeof *arg);
arg->type = CMD_PARSE_PARSED_COMMANDS;
diff --git a/cmd-queue.c b/cmd-queue.c
index 8325e2e8..78b379de 100644
--- a/cmd-queue.c
+++ b/cmd-queue.c
@@ -823,43 +823,89 @@ cmdq_guard(struct cmdq_item *item, const char *guard, int flags)
/* Show message from command. */
void
-cmdq_print(struct cmdq_item *item, const char *fmt, ...)
+cmdq_print_data(struct cmdq_item *item, int parse, struct evbuffer *evb)
{
struct client *c = item->client;
+ void *data = EVBUFFER_DATA(evb);
+ size_t size = EVBUFFER_LENGTH(evb);
struct window_pane *wp;
struct window_mode_entry *wme;
- va_list ap;
- char *tmp, *msg;
-
- va_start(ap, fmt);
- xvasprintf(&msg, fmt, ap);
- va_end(ap);
+ char *sanitized, *msg, *line;
- log_debug("%s: %s", __func__, msg);
+ if (!parse) {
+ utf8_stravisx(&msg, data, size,
+ VIS_OCTAL|VIS_CSTYLE|VIS_NOSLASH);
+ log_debug("%s: %s", __func__, msg);
+ } else {
+ msg = EVBUFFER_DATA(evb);
+ if (msg[size - 1] != '\0')
+ evbuffer_add(evb, "", 1);
+ }
if (c == NULL)
- /* nothing */;
- else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) {
+ goto out;
+
+ if (c->session == NULL || (c->flags & CLIENT_CONTROL)) {
if (~c->flags & CLIENT_UTF8) {
- tmp = msg;
- msg = utf8_sanitize(tmp);
- free(tmp);
+ sanitized = utf8_sanitize(msg);
+ if (c->flags & CLIENT_CONTROL)
+ control_write(c, "%s", sanitized);
+ else
+ file_print(c, "%s\n", sanitized);
+ free(sanitized);
+ } else {
+ if (c->flags & CLIENT_CONTROL)
+ control_write(c, "%s", msg);
+ else
+ file_print(c, "%s\n", msg);
}
- if (c->flags & CLIENT_CONTROL)
- control_write(c, "%s", msg);
- else
- file_print(c, "%s\n", msg);
- } else {
- wp = server_client_get_pane(c);
- wme = TAILQ_FIRST(&wp->modes);
- if (wme == NULL || wme->mode != &window_view_mode) {
- window_pane_set_mode(wp, NULL, &window_view_mode, NULL,
- NULL);
+ goto out;
+ }
+
+ wp = server_client_get_pane(c);
+ wme = TAILQ_FIRST(&wp->modes);
+ if (wme == NULL || wme->mode != &window_view_mode)
+ window_pane_set_mode(wp, NULL, &window_view_mode, NULL, NULL);
+ if (parse) {
+ do {
+ line = evbuffer_readln(evb, NULL, EVBUFFER_EOL_LF);
+ if (line != NULL) {
+ window_copy_add(wp, 1, "%s", line);
+ free(line);
+ }
+ } while (line != NULL);
+
+ size = EVBUFFER_LENGTH(evb);
+ if (size != 0) {
+ line = EVBUFFER_DATA(evb);
+ window_copy_add(wp, 1, "%.*s", (int)size, line);
}
+ } else
window_copy_add(wp, 0, "%s", msg);
- }
- free(msg);
+out:
+ if (!parse)
+ free(msg);
+
+}
+
+/* Show message from command. */
+void
+cmdq_print(struct cmdq_item *item, const char *fmt, ...)
+{
+ va_list ap;
+ struct evbuffer *evb;
+
+ evb = evbuffer_new();
+ if (evb == NULL)
+ fatalx("out of memory");
+
+ va_start(ap, fmt);
+ evbuffer_add_vprintf(evb, fmt, ap);
+ va_end(ap);
+
+ cmdq_print_data(item, 0, evb);
+ evbuffer_free(evb);
}
/* Show error from command. */
diff --git a/cmd-save-buffer.c b/cmd-save-buffer.c
index 7d678372..3e81500d 100644
--- a/cmd-save-buffer.c
+++ b/cmd-save-buffer.c
@@ -78,7 +78,8 @@ cmd_save_buffer_exec(struct cmd *self, struct cmdq_item *item)
int flags;
const char *bufname = args_get(args, 'b'), *bufdata;
size_t bufsize;
- char *path, *tmp;
+ char *path;
+ struct evbuffer *evb;
if (bufname == NULL) {
if ((pb = paste_get_top(NULL)) == NULL) {
@@ -96,10 +97,12 @@ cmd_save_buffer_exec(struct cmd *self, struct cmdq_item *item)
if (cmd_get_entry(self) == &cmd_show_buffer_entry) {
if (c->session != NULL || (c->flags & CLIENT_CONTROL)) {
- utf8_stravisx(&tmp, bufdata, bufsize,
- VIS_OCTAL|VIS_CSTYLE|VIS_TAB);
- cmdq_print(item, "%s", tmp);
- free(tmp);
+ evb = evbuffer_new();
+ if (evb == NULL)
+ fatalx("out of memory");
+ evbuffer_add(evb, bufdata, bufsize);
+ cmdq_print_data(item, 1, evb);
+ evbuffer_free(evb);
return (CMD_RETURN_NORMAL);
}
path = xstrdup("-");
diff --git a/cmd-send-keys.c b/cmd-send-keys.c
index e22d94a6..ac99a6fd 100644
--- a/cmd-send-keys.c
+++ b/cmd-send-keys.c
@@ -33,13 +33,13 @@ const struct cmd_entry cmd_send_keys_entry = {
.name = "send-keys",
.alias = "send",
- .args = { "FHlMN:Rt:X", 0, -1, NULL },
- .usage = "[-FHlMRX] [-N repeat-count] " CMD_TARGET_PANE_USAGE
- " key ...",
+ .args = { "c:FHKlMN:Rt:X", 0, -1, NULL },
+ .usage = "[-FHKlMRX] [-c target-client] [-N repeat-count] "
+ CMD_TARGET_PANE_USAGE " key ...",
.target = { 't', CMD_FIND_PANE, 0 },
- .flags = CMD_AFTERHOOK,
+ .flags = CMD_AFTERHOOK|CMD_CLIENT_CFLAG|CMD_CLIENT_CANFAIL,
.exec = cmd_send_keys_exec
};
@@ -58,7 +58,7 @@ const struct cmd_entry cmd_send_prefix_entry = {
static struct cmdq_item *
cmd_send_keys_inject_key(struct cmdq_item *item, struct cmdq_item *after,
- key_code key)
+ struct args *args, key_code key)
{
struct cmd_find_state *target = cmdq_get_target(item);
struct client *tc = cmdq_get_target_client(item);
@@ -66,8 +66,20 @@ cmd_send_keys_inject_key(struct cmdq_item *item, struct cmdq_item *after,
struct winlink *wl = target->wl;
struct window_pane *wp = target->wp;
struct window_mode_entry *wme;
- struct key_table *table;
+ struct key_table *table = NULL;
struct key_binding *bd;
+ struct key_event *event;
+
+ if (args_has(args, 'K')) {
+ if (tc == NULL)
+ return (item);
+ event = xmalloc(sizeof *event);
+ event->key = key|KEYC_SENT;
+ memset(&event->m, 0, sizeof event->m);
+ if (server_client_handle_key(tc, event) == 0)
+ free(event);
+ return (item);
+ }
wme = TAILQ_FIRST(&wp->modes);
if (wme == NULL || wme->mode->key_table == NULL) {
@@ -102,14 +114,16 @@ cmd_send_keys_inject_string(struct cmdq_item *item, struct cmdq_item *after,
n = strtol(s, &endptr, 16);
if (*s =='\0' || n < 0 || n > 0xff || *endptr != '\0')
return (item);
- return (cmd_send_keys_inject_key(item, after, KEYC_LITERAL|n));
+ return (cmd_send_keys_inject_key(item, after, args,
+ KEYC_LITERAL|n));
}
literal = args_has(args, 'l');
if (!literal) {
key = key_string_lookup_string(s);
if (key != KEYC_NONE && key != KEYC_UNKNOWN) {
- after = cmd_send_keys_inject_key(item, after, key);
+ after = cmd_send_keys_inject_key(item, after, args,
+ key);
if (after != NULL)
return (after);
}
@@ -125,7 +139,8 @@ cmd_send_keys_inject_string(struct cmdq_item *item, struct cmdq_item *after,
continue;
key = uc;
}
- after = cmd_send_keys_inject_key(item, after, key);
+ after = cmd_send_keys_inject_key(item, after, args,
+ key);
}
free(ud);
}
@@ -193,7 +208,7 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item)
key = options_get_number(s->options, "prefix2");
else
key = options_get_number(s->options, "prefix");
- cmd_send_keys_inject_key(item, item, key);
+ cmd_send_keys_inject_key(item, item, args, key);
return (CMD_RETURN_NORMAL);
}
@@ -207,7 +222,7 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item)
if (args_has(args, 'N') || args_has(args, 'R'))
return (CMD_RETURN_NORMAL);
for (; np != 0; np--)
- cmd_send_keys_inject_key(item, NULL, event->key);
+ cmd_send_keys_inject_key(item, NULL, args, event->key);
return (CMD_RETURN_NORMAL);
}
diff --git a/cmd-swap-pane.c b/cmd-swap-pane.c
index 4191b894..80c20c80 100644
--- a/cmd-swap-pane.c
+++ b/cmd-swap-pane.c
@@ -132,6 +132,8 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item)
src_w->last = NULL;
if (dst_w->last == dst_wp)
dst_w->last = NULL;
+ colour_palette_from_option(&src_wp->palette, src_wp->options);
+ colour_palette_from_option(&dst_wp->palette, dst_wp->options);
}
server_redraw_window(src_w);
server_redraw_window(dst_w);
diff --git a/colour.c b/colour.c
index a282d182..9bde646f 100644
--- a/colour.c
+++ b/colour.c
@@ -960,6 +960,47 @@ colour_byname(const char *name)
return (-1);
}
+/* Parse colour from an X11 string. */
+int
+colour_parseX11(const char *p)
+{
+ double c, m, y, k = 0;
+ u_int r, g, b;
+ size_t len = strlen(p);
+ int colour = -1;
+ char *copy;
+
+ if ((len == 12 && sscanf(p, "rgb:%02x/%02x/%02x", &r, &g, &b) == 3) ||
+ (len == 7 && sscanf(p, "#%02x%02x%02x", &r, &g, &b) == 3) ||
+ sscanf(p, "%d,%d,%d", &r, &g, &b) == 3)
+ colour = colour_join_rgb(r, g, b);
+ else if ((len == 18 &&
+ sscanf(p, "rgb:%04x/%04x/%04x", &r, &g, &b) == 3) ||
+ (len == 13 && sscanf(p, "#%04x%04x%04x", &r, &g, &b) == 3))
+ colour = colour_join_rgb(r >> 8, g >> 8, b >> 8);
+ else if ((sscanf(p, "cmyk:%lf/%lf/%lf/%lf", &c, &m, &y, &k) == 4 ||
+ sscanf(p, "cmy:%lf/%lf/%lf", &c, &m, &y) == 3) &&
+ c >= 0 && c <= 1 && m >= 0 && m <= 1 &&
+ y >= 0 && y <= 1 && k >= 0 && k <= 1) {
+ colour = colour_join_rgb(
+ (1 - c) * (1 - k) * 255,
+ (1 - m) * (1 - k) * 255,
+ (1 - y) * (1 - k) * 255);
+ } else {
+ while (len != 0 && *p == ' ') {
+ p++;
+ len--;
+ }
+ while (len != 0 && p[len - 1] == ' ')
+ len--;
+ copy = xstrndup(p, len);
+ colour = colour_byname(copy);
+ free(copy);
+ }
+ log_debug("%s: %s = %s", __func__, p, colour_tostring(colour));
+ return (colour);
+}
+
/* Initialize palette. */
void
colour_palette_init(struct colour_palette *p)
@@ -1069,5 +1110,4 @@ colour_palette_from_option(struct colour_palette *p, struct options *oo)
}
a = options_array_next(a);
}
-
}
diff --git a/compat.h b/compat.h
index 6eb97619..aefc0d38 100644
--- a/compat.h
+++ b/compat.h
@@ -423,6 +423,7 @@ void *recallocarray(void *, size_t, size_t, size_t);
#ifdef HAVE_SYSTEMD
/* systemd.c */
+int systemd_activated(void);
int systemd_create_socket(int, char **);
#endif
diff --git a/compat/getpeereid.c b/compat/getpeereid.c
index c194e886..b79f420a 100644
--- a/compat/getpeereid.c
+++ b/compat/getpeereid.c
@@ -18,6 +18,7 @@
#include <sys/socket.h>
#include <stdio.h>
+#include <unistd.h>
#ifdef HAVE_UCRED_H
#include <ucred.h>
@@ -49,6 +50,8 @@ getpeereid(int s, uid_t *uid, gid_t *gid)
ucred_free(ucred);
return (0);
#else
- return (getuid());
+ *uid = geteuid();
+ *gid = getegid();
+ return (0);
#endif
}
diff --git a/compat/systemd.c b/compat/systemd.c
index 7317e43a..cce42ed4 100644
--- a/compat/systemd.c
+++ b/compat/systemd.c
@@ -21,15 +21,23 @@
#include <systemd/sd-daemon.h>
+#include <string.h>
+
#include "tmux.h"
int
+systemd_activated(void)
+{
+ return (sd_listen_fds(0) >= 1);
+}
+
+int
systemd_create_socket(int flags, char **cause)
{
int fds;
int fd;
struct sockaddr_un sa;
- int addrlen = sizeof sa;
+ socklen_t addrlen = sizeof sa;
fds = sd_listen_fds(0);
if (fds > 1) { /* too many file descriptors */
diff --git a/configure.ac b/configure.ac
index 276f71c8..8e846042 100644
--- a/configure.ac
+++ b/configure.ac
@@ -267,6 +267,12 @@ if test "x$found_libevent" = xno; then
AC_MSG_ERROR("libevent not found")
fi
+# Look for yacc.
+AC_CHECK_PROG(found_yacc, $YACC, yes, no)
+if test "x$found_yacc" = xno; then
+ AC_MSG_ERROR("yacc not found")
+fi
+
# Look for ncurses or curses. Try pkg-config first then directly for the
# library.
PKG_CHECK_MODULES(
diff --git a/control-notify.c b/control-notify.c
index 6ff0e436..30f94194 100644
--- a/control-notify.c
+++ b/control-notify.c
@@ -234,3 +234,29 @@ control_notify_session_window_changed(struct session *s)
s->curw->window->id);
}
}
+
+void
+control_notify_paste_buffer_changed(const char *name)
+{
+ struct client *c;
+
+ TAILQ_FOREACH(c, &clients, entry) {
+ if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
+ continue;
+
+ control_write(c, "%%paste-buffer-changed %s", name);
+ }
+}
+
+void
+control_notify_paste_buffer_deleted(const char *name)
+{
+ struct client *c;
+
+ TAILQ_FOREACH(c, &clients, entry) {
+ if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
+ continue;
+
+ control_write(c, "%%paste-buffer-deleted %s", name);
+ }
+}
diff --git a/control.c b/control.c
index 302c2956..578d04cb 100644
--- a/control.c
+++ b/control.c
@@ -775,12 +775,16 @@ control_start(struct client *c)
cs->read_event = bufferevent_new(c->fd, control_read_callback,
control_write_callback, control_error_callback, c);
+ if (cs->read_event == NULL)
+ fatalx("out of memory");
if (c->flags & CLIENT_CONTROLCONTROL)
cs->write_event = cs->read_event;
else {
cs->write_event = bufferevent_new(c->out_fd, NULL,
control_write_callback, control_error_callback, c);
+ if (cs->write_event == NULL)
+ fatalx("out of memory");
}
bufferevent_setwatermark(cs->write_event, EV_WRITE, CONTROL_BUFFER_LOW,
0);
diff --git a/environ.c b/environ.c
index 74d672e0..5abf383c 100644
--- a/environ.c
+++ b/environ.c
@@ -182,9 +182,11 @@ void
environ_update(struct options *oo, struct environ *src, struct environ *dst)
{
struct environ_entry *envent;
+ struct environ_entry *envent1;
struct options_entry *o;
struct options_array_item *a;
union options_value *ov;
+ int found;
o = options_get(oo, "update-environment");
if (o == NULL)
@@ -192,14 +194,15 @@ environ_update(struct options *oo, struct environ *src, struct environ *dst)
a = options_array_first(o);
while (a != NULL) {
ov = options_array_item_value(a);
- RB_FOREACH(envent, environ, src) {
- if (fnmatch(ov->string, envent->name, 0) == 0)
- break;
+ found = 0;
+ RB_FOREACH_SAFE(envent, environ, src, envent1) {
+ if (fnmatch(ov->string, envent->name, 0) == 0) {
+ environ_set(dst, envent->name, 0, "%s", envent->value);
+ found = 1;
+ }
}
- if (envent == NULL)
+ if (!found)
environ_clear(dst, ov->string);
- else
- environ_set(dst, envent->name, 0, "%s", envent->value);
a = options_array_next(a);
}
}
diff --git a/file.c b/file.c
index b2f155fe..3c1096be 100644
--- a/file.c
+++ b/file.c
@@ -149,7 +149,8 @@ file_fire_done_cb(__unused int fd, __unused short events, void *arg)
struct client_file *cf = arg;
struct client *c = cf->c;
- if (cf->cb != NULL && (c == NULL || (~c->flags & CLIENT_DEAD)))
+ if (cf->cb != NULL &&
+ (cf->closed || c == NULL || (~c->flags & CLIENT_DEAD)))
cf->cb(c, cf->path, cf->error, 1, cf->buffer, cf->data);
file_free(cf);
}
@@ -352,7 +353,7 @@ done:
}
/* Read a file. */
-void
+struct client_file *
file_read(struct client *c, const char *path, client_file_cb cb, void *cbdata)
{
struct client_file *cf;
@@ -420,10 +421,27 @@ skip:
goto done;
}
free(msg);
- return;
+ return cf;
done:
file_fire_done(cf);
+ return NULL;
+}
+
+/* Cancel a file read. */
+void
+file_cancel(struct client_file *cf)
+{
+ struct msg_read_cancel msg;
+
+ log_debug("read cancel file %d", cf->stream);
+
+ if (cf->closed)
+ return;
+ cf->closed = 1;
+
+ msg.stream = cf->stream;
+ proc_send(cf->peer, MSG_READ_CANCEL, -1, &msg, sizeof msg);
}
/* Push event, fired if there is more writing to be done. */
@@ -585,6 +603,8 @@ file_write_open(struct client_files *files, struct tmuxpeer *peer,
cf->event = bufferevent_new(cf->fd, NULL, file_write_callback,
file_write_error_callback, cf);
+ if (cf->event == NULL)
+ fatalx("out of memory");
bufferevent_enable(cf->event, EV_WRITE);
goto reply;
@@ -744,6 +764,8 @@ file_read_open(struct client_files *files, struct tmuxpeer *peer,
cf->event = bufferevent_new(cf->fd, file_read_callback, NULL,
file_read_error_callback, cf);
+ if (cf->event == NULL)
+ fatalx("out of memory");
bufferevent_enable(cf->event, EV_READ);
return;
@@ -753,6 +775,24 @@ reply:
proc_send(peer, MSG_READ_DONE, -1, &reply, sizeof reply);
}
+/* Handle a read cancel message (client). */
+void
+file_read_cancel(struct client_files *files, struct imsg *imsg)
+{
+ struct msg_read_cancel *msg = imsg->data;
+ size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
+ struct client_file find, *cf;
+
+ if (msglen != sizeof *msg)
+ fatalx("bad MSG_READ_CANCEL size");
+ find.stream = msg->stream;
+ if ((cf = RB_FIND(client_files, files, &find)) == NULL)
+ fatalx("unknown stream number");
+ log_debug("cancel file %d", cf->stream);
+
+ file_read_error_callback(NULL, 0, cf);
+}
+
/* Handle a write ready message (server). */
void
file_write_ready(struct client_files *files, struct imsg *imsg)
@@ -790,7 +830,7 @@ file_read_data(struct client_files *files, struct imsg *imsg)
return;
log_debug("file %d read %zu bytes", cf->stream, bsize);
- if (cf->error == 0) {
+ if (cf->error == 0 && !cf->closed) {
if (evbuffer_add(cf->buffer, bdata, bsize) != 0) {
cf->error = ENOMEM;
file_fire_done(cf);
diff --git a/format.c b/format.c
index 8041f728..ee6930b0 100644
--- a/format.c
+++ b/format.c
@@ -3575,7 +3575,32 @@ found:
return (found);
}
-/* Remove escaped characters from string. */
+/* Unescape escaped characters. */
+static char *
+format_unescape(const char *s)
+{
+ char *out, *cp;
+ int brackets = 0;
+
+ cp = out = xmalloc(strlen(s) + 1);
+ for (; *s != '\0'; s++) {
+ if (*s == '#' && s[1] == '{')
+ brackets++;
+ if (brackets == 0 &&
+ *s == '#' &&
+ strchr(",#{}:", s[1]) != NULL) {
+ *cp++ = *++s;
+ continue;
+ }
+ if (*s == '}')
+ brackets--;
+ *cp++ = *s;
+ }
+ *cp = '\0';
+ return (out);
+}
+
+/* Remove escaped characters. */
static char *
format_strip(const char *s)
{
@@ -4338,7 +4363,8 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
/* Is this a literal string? */
if (modifiers & FORMAT_LITERAL) {
- value = xstrdup(copy);
+ format_log(es, "literal string is '%s'", copy);
+ value = format_unescape(copy);
goto done;
}
diff --git a/grid-view.c b/grid-view.c
index 689ac4e4..4d687339 100644
--- a/grid-view.c
+++ b/grid-view.c
@@ -231,5 +231,5 @@ grid_view_string_cells(struct grid *gd, u_int px, u_int py, u_int nx)
px = grid_view_x(gd, px);
py = grid_view_y(gd, py);
- return (grid_string_cells(gd, px, py, nx, NULL, 0, 0, 0, NULL));
+ return (grid_string_cells(gd, px, py, nx, NULL, 0, NULL));
}
diff --git a/grid.c b/grid.c
index b1afd398..58de03a3 100644
--- a/grid.c
+++ b/grid.c
@@ -861,40 +861,45 @@ grid_string_cells_us(const struct grid_cell *gc, int *values)
/* Add on SGR code. */
static void
grid_string_cells_add_code(char *buf, size_t len, u_int n, int *s, int *newc,
- int *oldc, size_t nnewc, size_t noldc, int escape_c0)
+ int *oldc, size_t nnewc, size_t noldc, int flags)
{
u_int i;
char tmp[64];
-
- if (nnewc != 0 &&
- (nnewc != noldc ||
- memcmp(newc, oldc, nnewc * sizeof newc[0]) != 0 ||
- (n != 0 && s[0] == 0))) {
- if (escape_c0)
- strlcat(buf, "\\033[", len);
+ int reset = (n != 0 && s[0] == 0);
+
+ if (nnewc == 0)
+ return; /* no code to add */
+ if (!reset &&
+ nnewc == noldc &&
+ memcmp(newc, oldc, nnewc * sizeof newc[0]) == 0)
+ return; /* no reset and colour unchanged */
+ if (reset && (newc[0] == 49 || newc[0] == 39))
+ return; /* reset and colour default */
+
+ if (flags & GRID_STRING_ESCAPE_SEQUENCES)
+ strlcat(buf, "\\033[", len);
+ else
+ strlcat(buf, "\033[", len);
+ for (i = 0; i < nnewc; i++) {
+ if (i + 1 < nnewc)
+ xsnprintf(tmp, sizeof tmp, "%d;", newc[i]);
else
- strlcat(buf, "\033[", len);
- for (i = 0; i < nnewc; i++) {
- if (i + 1 < nnewc)
- xsnprintf(tmp, sizeof tmp, "%d;", newc[i]);
- else
- xsnprintf(tmp, sizeof tmp, "%d", newc[i]);
- strlcat(buf, tmp, len);
- }
- strlcat(buf, "m", len);
+ xsnprintf(tmp, sizeof tmp, "%d", newc[i]);
+ strlcat(buf, tmp, len);
}
+ strlcat(buf, "m", len);
}
static int
grid_string_cells_add_hyperlink(char *buf, size_t len, const char *id,
- const char *uri, int escape_c0)
+ const char *uri, int flags)
{
char *tmp;
if (strlen(uri) + strlen(id) + 17 >= len)
return (0);
- if (escape_c0)
+ if (flags & GRID_STRING_ESCAPE_SEQUENCES)
strlcat(buf, "\\033]8;", len);
else
strlcat(buf, "\033]8;", len);
@@ -905,7 +910,7 @@ grid_string_cells_add_hyperlink(char *buf, size_t len, const char *id,
} else
strlcat(buf, ";", len);
strlcat(buf, uri, len);
- if (escape_c0)
+ if (flags & GRID_STRING_ESCAPE_SEQUENCES)
strlcat(buf, "\\033\\\\", len);
else
strlcat(buf, "\033\\", len);
@@ -918,7 +923,7 @@ grid_string_cells_add_hyperlink(char *buf, size_t len, const char *id,
*/
static void
grid_string_cells_code(const struct grid_cell *lastgc,
- const struct grid_cell *gc, char *buf, size_t len, int escape_c0,
+ const struct grid_cell *gc, char *buf, size_t len, int flags,
struct screen *sc, int *has_link)
{
int oldc[64], newc[64], s[128];
@@ -927,7 +932,7 @@ grid_string_cells_code(const struct grid_cell *lastgc,
char tmp[64];
const char *uri, *id;
- struct {
+ static const struct {
u_int mask;
u_int code;
} attrs[] = {
@@ -966,7 +971,7 @@ grid_string_cells_code(const struct grid_cell *lastgc,
/* Write the attributes. */
*buf = '\0';
if (n > 0) {
- if (escape_c0)
+ if (flags & GRID_STRING_ESCAPE_SEQUENCES)
strlcat(buf, "\\033[", len);
else
strlcat(buf, "\033[", len);
@@ -988,29 +993,29 @@ grid_string_cells_code(const struct grid_cell *lastgc,
nnewc = grid_string_cells_fg(gc, newc);
noldc = grid_string_cells_fg(lastgc, oldc);
grid_string_cells_add_code(buf, len, n, s, newc, oldc, nnewc, noldc,
- escape_c0);
+ flags);
/* If the background colour changed, append its parameters. */
nnewc = grid_string_cells_bg(gc, newc);
noldc = grid_string_cells_bg(lastgc, oldc);
grid_string_cells_add_code(buf, len, n, s, newc, oldc, nnewc, noldc,
- escape_c0);
+ flags);
/* If the underscore colour changed, append its parameters. */
nnewc = grid_string_cells_us(gc, newc);
noldc = grid_string_cells_us(lastgc, oldc);
grid_string_cells_add_code(buf, len, n, s, newc, oldc, nnewc, noldc,
- escape_c0);
+ flags);
/* Append shift in/shift out if needed. */
if ((attr & GRID_ATTR_CHARSET) && !(lastattr & GRID_ATTR_CHARSET)) {
- if (escape_c0)
+ if (flags & GRID_STRING_ESCAPE_SEQUENCES)
strlcat(buf, "\\016", len); /* SO */
else
strlcat(buf, "\016", len); /* SO */
}
if (!(attr & GRID_ATTR_CHARSET) && (lastattr & GRID_ATTR_CHARSET)) {
- if (escape_c0)
+ if (flags & GRID_STRING_ESCAPE_SEQUENCES)
strlcat(buf, "\\017", len); /* SI */
else
strlcat(buf, "\017", len); /* SI */
@@ -1020,10 +1025,10 @@ grid_string_cells_code(const struct grid_cell *lastgc,
if (sc != NULL && sc->hyperlinks != NULL && lastgc->link != gc->link) {
if (hyperlinks_get(sc->hyperlinks, gc->link, &uri, &id, NULL)) {
*has_link = grid_string_cells_add_hyperlink(buf, len,
- id, uri, escape_c0);
+ id, uri, flags);
} else if (*has_link) {
grid_string_cells_add_hyperlink(buf, len, "", "",
- escape_c0);
+ flags);
*has_link = 0;
}
}
@@ -1032,15 +1037,14 @@ grid_string_cells_code(const struct grid_cell *lastgc,
/* Convert cells into a string. */
char *
grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx,
- struct grid_cell **lastgc, int with_codes, int escape_c0, int trim,
- struct screen *s)
+ struct grid_cell **lastgc, int flags, struct screen *s)
{
struct grid_cell gc;
static struct grid_cell lastgc1;
const char *data;
char *buf, code[8192];
size_t len, off, size, codelen;
- u_int xx, has_link = 0;
+ u_int xx, has_link = 0, end;
const struct grid_line *gl;
if (lastgc != NULL && *lastgc == NULL) {
@@ -1053,16 +1057,20 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx,
off = 0;
gl = grid_peek_line(gd, py);
+ if (flags & GRID_STRING_EMPTY_CELLS)
+ end = gl->cellsize;
+ else
+ end = gl->cellused;
for (xx = px; xx < px + nx; xx++) {
- if (gl == NULL || xx >= gl->cellused)
+ if (gl == NULL || xx >= end)
break;
grid_get_cell(gd, xx, py, &gc);
if (gc.flags & GRID_FLAG_PADDING)
continue;
- if (with_codes) {
+ if (flags & GRID_STRING_WITH_SEQUENCES) {
grid_string_cells_code(*lastgc, &gc, code, sizeof code,
- escape_c0, s, &has_link);
+ flags, s, &has_link);
codelen = strlen(code);
memcpy(*lastgc, &gc, sizeof **lastgc);
} else
@@ -1070,7 +1078,9 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx,
data = gc.data.data;
size = gc.data.size;
- if (escape_c0 && size == 1 && *data == '\\') {
+ if ((flags & GRID_STRING_ESCAPE_SEQUENCES) &&
+ size == 1 &&
+ *data == '\\') {
data = "\\\\";
size = 2;
}
@@ -1090,7 +1100,7 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx,
if (has_link) {
grid_string_cells_add_hyperlink(code, sizeof code, "", "",
- escape_c0);
+ flags);
codelen = strlen(code);
while (len < off + size + codelen + 1) {
buf = xreallocarray(buf, 2, len);
@@ -1100,7 +1110,7 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx,
off += codelen;
}
- if (trim) {
+ if (flags & GRID_STRING_TRIM_SPACES) {
while (off > 0 && buf[off - 1] == ' ')
off--;
}
diff --git a/input-keys.c b/input-keys.c
index bb4edf79..318885b2 100644
--- a/input-keys.c
+++ b/input-keys.c
@@ -306,6 +306,20 @@ static struct input_key_entry input_key_defaults[] = {
},
{ .key = KEYC_DC|KEYC_BUILD_MODIFIERS,
.data = "\033[3;_~"
+ },
+
+ /* Tab and modifiers. */
+ { .key = '\011'|KEYC_CTRL,
+ .data = "\011"
+ },
+ { .key = '\011'|KEYC_CTRL|KEYC_EXTENDED,
+ .data = "\033[9;5u"
+ },
+ { .key = '\011'|KEYC_CTRL|KEYC_SHIFT,
+ .data = "\033[Z"
+ },
+ { .key = '\011'|KEYC_CTRL|KEYC_SHIFT|KEYC_EXTENDED,
+ .data = "\033[1;5Z"
}
};
static const key_code input_key_modifiers[] = {
@@ -416,7 +430,7 @@ input_key_write(const char *from, struct bufferevent *bev, const char *data,
int
input_key(struct screen *s, struct bufferevent *bev, key_code key)
{
- struct input_key_entry *ike;
+ struct input_key_entry *ike = NULL;
key_code justkey, newkey, outkey, modifiers;
struct utf8_data ud;
char tmp[64], modifier;
@@ -468,15 +482,23 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key)
key &= ~KEYC_KEYPAD;
if (~s->mode & MODE_KCURSOR)
key &= ~KEYC_CURSOR;
- ike = input_key_get(key);
+ if (s->mode & MODE_KEXTENDED)
+ ike = input_key_get(key|KEYC_EXTENDED);
+ if (ike == NULL)
+ ike = input_key_get(key);
if (ike == NULL && (key & KEYC_META) && (~key & KEYC_IMPLIED_META))
ike = input_key_get(key & ~KEYC_META);
if (ike == NULL && (key & KEYC_CURSOR))
ike = input_key_get(key & ~KEYC_CURSOR);
if (ike == NULL && (key & KEYC_KEYPAD))
ike = input_key_get(key & ~KEYC_KEYPAD);
+ if (ike == NULL && (key & KEYC_EXTENDED))
+ ike = input_key_get(key & ~KEYC_EXTENDED);
if (ike != NULL) {
log_debug("found key 0x%llx: \"%s\"", key, ike->data);
+ if ((key == KEYC_PASTE_START || key == KEYC_PASTE_END) &&
+ (~s->mode & MODE_BRACKETPASTE))
+ return (0);
if ((key & KEYC_META) && (~key & KEYC_IMPLIED_META))
input_key_write(__func__, bev, "\033", 1);
input_key_write(__func__, bev, ike->data, strlen(ike->data));
diff --git a/input.c b/input.c
index 4252428b..adbea179 100644
--- a/input.c
+++ b/input.c
@@ -1086,6 +1086,7 @@ input_reply(struct input_ctx *ictx, const char *fmt, ...)
xvasprintf(&reply, fmt, ap);
va_end(ap);
+ log_debug("%s: %s", __func__, reply);
bufferevent_write(bev, reply, strlen(reply));
free(reply);
}
@@ -1345,8 +1346,8 @@ input_csi_dispatch(struct input_ctx *ictx)
if (ictx->flags & INPUT_DISCARD)
return (0);
- log_debug("%s: '%c' \"%s\" \"%s\"",
- __func__, ictx->ch, ictx->interm_buf, ictx->param_buf);
+ log_debug("%s: '%c' \"%s\" \"%s\"", __func__, ictx->ch,
+ ictx->interm_buf, ictx->param_buf);
if (input_split(ictx) != 0)
return (0);
@@ -1755,7 +1756,6 @@ static void
input_csi_dispatch_sm_private(struct input_ctx *ictx)
{
struct screen_write_ctx *sctx = &ictx->ctx;
- struct window_pane *wp = ictx->wp;
struct grid_cell *gc = &ictx->cell.cell;
u_int i;
@@ -1797,17 +1797,7 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx)
screen_write_mode_set(sctx, MODE_MOUSE_ALL);
break;
case 1004:
- if (sctx->s->mode & MODE_FOCUSON)
- break;
screen_write_mode_set(sctx, MODE_FOCUSON);
- if (wp == NULL)
- break;
- if (!options_get_number(global_options, "focus-events"))
- break;
- if (wp->flags & PANE_FOCUSED)
- bufferevent_write(wp->event, "\033[I", 3);
- else
- bufferevent_write(wp->event, "\033[O", 3);
break;
case 1005:
screen_write_mode_set(sctx, MODE_MOUSE_UTF8);
@@ -1899,7 +1889,7 @@ input_csi_dispatch_winops(struct input_ctx *ictx)
}
break;
case 18:
- input_reply(ictx, "\033[8;%u;%ut", x, y);
+ input_reply(ictx, "\033[8;%u;%ut", y, x);
break;
default:
log_debug("%s: unknown '%c'", __func__, ictx->ch);
@@ -2247,17 +2237,22 @@ input_dcs_dispatch(struct input_ctx *ictx)
size_t len = ictx->input_len;
const char prefix[] = "tmux;";
const u_int prefixlen = (sizeof prefix) - 1;
+ long long allow_passthrough = 0;
if (wp == NULL)
return (0);
if (ictx->flags & INPUT_DISCARD)
return (0);
- if (!options_get_number(ictx->wp->options, "allow-passthrough"))
+ allow_passthrough = options_get_number(wp->options,
+ "allow-passthrough");
+ if (!allow_passthrough)
return (0);
log_debug("%s: \"%s\"", __func__, buf);
- if (len >= prefixlen && strncmp(buf, prefix, prefixlen) == 0)
- screen_write_rawstring(sctx, buf + prefixlen, len - prefixlen);
+ if (len >= prefixlen && strncmp(buf, prefix, prefixlen) == 0) {
+ screen_write_rawstring(sctx, buf + prefixlen, len - prefixlen,
+ allow_passthrough == 2);
+ }
return (0);
}
@@ -2462,47 +2457,6 @@ input_top_bit_set(struct input_ctx *ictx)
return (0);
}
-/* Parse colour from OSC. */
-static int
-input_osc_parse_colour(const char *p)
-{
- double c, m, y, k = 0;
- u_int r, g, b;
- size_t len = strlen(p);
- int colour = -1;
- char *copy;
-
- if ((len == 12 && sscanf(p, "rgb:%02x/%02x/%02x", &r, &g, &b) == 3) ||
- (len == 7 && sscanf(p, "#%02x%02x%02x", &r, &g, &b) == 3) ||
- sscanf(p, "%d,%d,%d", &r, &g, &b) == 3)
- colour = colour_join_rgb(r, g, b);
- else if ((len == 18 &&
- sscanf(p, "rgb:%04x/%04x/%04x", &r, &g, &b) == 3) ||
- (len == 13 && sscanf(p, "#%04x%04x%04x", &r, &g, &b) == 3))
- colour = colour_join_rgb(r >> 8, g >> 8, b >> 8);
- else if ((sscanf(p, "cmyk:%lf/%lf/%lf/%lf", &c, &m, &y, &k) == 4 ||
- sscanf(p, "cmy:%lf/%lf/%lf", &c, &m, &y) == 3) &&
- c >= 0 && c <= 1 && m >= 0 && m <= 1 &&
- y >= 0 && y <= 1 && k >= 0 && k <= 1) {
- colour = colour_join_rgb(
- (1 - c) * (1 - k) * 255,
- (1 - m) * (1 - k) * 255,
- (1 - y) * (1 - k) * 255);
- } else {
- while (len != 0 && *p == ' ') {
- p++;
- len--;
- }
- while (len != 0 && p[len - 1] == ' ')
- len--;
- copy = xstrndup(p, len);
- colour = colour_byname(copy);
- free(copy);
- }
- log_debug("%s: %s = %s", __func__, p, colour_tostring(colour));
- return (colour);
-}
-
/* Reply to a colour request. */
static void
input_osc_colour_reply(struct input_ctx *ictx, u_int n, int c)
@@ -2551,7 +2505,7 @@ input_osc_4(struct input_ctx *ictx, const char *p)
input_osc_colour_reply(ictx, 4, c);
continue;
}
- if ((c = input_osc_parse_colour(s)) == -1) {
+ if ((c = colour_parseX11(s)) == -1) {
s = next;
continue;
}
@@ -2607,6 +2561,47 @@ bad:
free(id);
}
+/*
+ * Get a client with a foreground for the pane. There isn't much to choose
+ * between them so just use the first.
+ */
+static int
+input_get_fg_client(struct window_pane *wp)
+{
+ struct window *w = wp->window;
+ struct client *loop;
+
+ TAILQ_FOREACH(loop, &clients, entry) {
+ if (loop->flags & CLIENT_UNATTACHEDFLAGS)
+ continue;
+ if (loop->session == NULL || !session_has(loop->session, w))
+ continue;
+ if (loop->tty.fg == -1)
+ continue;
+ return (loop->tty.fg);
+ }
+ return (-1);
+}
+
+/* Get a client with a background for the pane. */
+static int
+input_get_bg_client(struct window_pane *wp)
+{
+ struct window *w = wp->window;
+ struct client *loop;
+
+ TAILQ_FOREACH(loop, &clients, entry) {
+ if (loop->flags & CLIENT_UNATTACHEDFLAGS)
+ continue;
+ if (loop->session == NULL || !session_has(loop->session, w))
+ continue;
+ if (loop->tty.bg == -1)
+ continue;
+ return (loop->tty.bg);
+ }
+ return (-1);
+}
+
/* Handle the OSC 10 sequence for setting and querying foreground colour. */
static void
input_osc_10(struct input_ctx *ictx, const char *p)
@@ -2616,14 +2611,18 @@ input_osc_10(struct input_ctx *ictx, const char *p)
int c;
if (strcmp(p, "?") == 0) {
- if (wp != NULL) {
- tty_default_colours(&defaults, wp);
- input_osc_colour_reply(ictx, 10, defaults.fg);
- }
+ if (wp == NULL)
+ return;
+ tty_default_colours(&defaults, wp);
+ if (COLOUR_DEFAULT(defaults.fg))
+ c = input_get_fg_client(wp);
+ else
+ c = defaults.fg;
+ input_osc_colour_reply(ictx, 10, c);
return;
}
- if ((c = input_osc_parse_colour(p)) == -1) {
+ if ((c = colour_parseX11(p)) == -1) {
log_debug("bad OSC 10: %s", p);
return;
}
@@ -2660,14 +2659,18 @@ input_osc_11(struct input_ctx *ictx, const char *p)
int c;
if (strcmp(p, "?") == 0) {
- if (wp != NULL) {
- tty_default_colours(&defaults, wp);
- input_osc_colour_reply(ictx, 11, defaults.bg);
- }
+ if (wp == NULL)
+ return;
+ tty_default_colours(&defaults, wp);
+ if (COLOUR_DEFAULT(defaults.bg))
+ c = input_get_bg_client(wp);
+ else
+ c = defaults.bg;
+ input_osc_colour_reply(ictx, 11, c);
return;
}
- if ((c = input_osc_parse_colour(p)) == -1) {
+ if ((c = colour_parseX11(p)) == -1) {
log_debug("bad OSC 11: %s", p);
return;
}
@@ -2712,7 +2715,7 @@ input_osc_12(struct input_ctx *ictx, const char *p)
return;
}
- if ((c = input_osc_parse_colour(p)) == -1) {
+ if ((c = colour_parseX11(p)) == -1) {
log_debug("bad OSC 12: %s", p);
return;
}
diff --git a/key-bindings.c b/key-bindings.c
index bcc6004d..528e0b73 100644
--- a/key-bindings.c
+++ b/key-bindings.c
@@ -344,7 +344,7 @@ key_bindings_init_done(__unused struct cmdq_item *item, __unused void *data)
void
key_bindings_init(void)
{
- static const char *defaults[] = {
+ static const char *const defaults[] = {
/* Prefix keys. */
"bind -N 'Send the prefix key' C-b { send-prefix }",
"bind -N 'Rotate through the panes' C-o { rotate-window }",
@@ -605,6 +605,7 @@ key_bindings_init(void)
"bind -Tcopy-mode-vi h { send -X cursor-left }",
"bind -Tcopy-mode-vi j { send -X cursor-down }",
"bind -Tcopy-mode-vi k { send -X cursor-up }",
+ "bind -Tcopy-mode-vi z { send -X scroll-middle }",
"bind -Tcopy-mode-vi l { send -X cursor-right }",
"bind -Tcopy-mode-vi n { send -X search-again }",
"bind -Tcopy-mode-vi o { send -X other-end }",
@@ -616,6 +617,8 @@ key_bindings_init(void)
"bind -Tcopy-mode-vi '{' { send -X previous-paragraph }",
"bind -Tcopy-mode-vi '}' { send -X next-paragraph }",
"bind -Tcopy-mode-vi % { send -X next-matching-bracket }",
+ "bind -Tcopy-mode-vi Home { send -X start-of-line }",
+ "bind -Tcopy-mode-vi End { send -X end-of-line }",
"bind -Tcopy-mode-vi MouseDown1Pane { select-pane }",
"bind -Tcopy-mode-vi MouseDrag1Pane { select-pane; send -X begin-selection }",
"bind -Tcopy-mode-vi MouseDragEnd1Pane { send -X copy-pipe-and-cancel }",
diff --git a/key-string.c b/key-string.c
index 0ca91306..699d460f 100644
--- a/key-string.c
+++ b/key-string.c
@@ -460,6 +460,10 @@ out:
strlcat(out, "I", sizeof out);
if (saved & KEYC_BUILD_MODIFIERS)
strlcat(out, "B", sizeof out);
+ if (saved & KEYC_EXTENDED)
+ strlcat(out, "E", sizeof out);
+ if (saved & KEYC_SENT)
+ strlcat(out, "S", sizeof out);
strlcat(out, "]", sizeof out);
}
return (out);
diff --git a/menu.c b/menu.c
index dc3b289f..0ff180aa 100644
--- a/menu.c
+++ b/menu.c
@@ -323,27 +323,64 @@ menu_key_cb(struct client *c, void *data, struct key_event *event)
} while ((name == NULL || *name == '-') && md->choice != old);
c->flags |= CLIENT_REDRAWOVERLAY;
return (0);
- case 'g':
case KEYC_PPAGE:
case '\002': /* C-b */
- if (md->choice > 5)
- md->choice -= 5;
- else
+ if (md->choice < 6)
md->choice = 0;
- while (md->choice != count && (name == NULL || *name == '-'))
- md->choice++;
- if (md->choice == count)
- md->choice = -1;
+ else {
+ i = 5;
+ while (i > 0) {
+ md->choice--;
+ name = menu->items[md->choice].name;
+ if (md->choice != 0 &&
+ (name != NULL && *name != '-'))
+ i--;
+ else if (md->choice == 0)
+ break;
+ }
+ }
c->flags |= CLIENT_REDRAWOVERLAY;
break;
- case 'G':
case KEYC_NPAGE:
- if (md->choice > count - 6)
+ if (md->choice > count - 6) {
md->choice = count - 1;
- else
- md->choice += 5;
- while (md->choice != -1 && (name == NULL || *name == '-'))
+ name = menu->items[md->choice].name;
+ } else {
+ i = 5;
+ while (i > 0) {
+ md->choice++;
+ name = menu->items[md->choice].name;
+ if (md->choice != count - 1 &&
+ (name != NULL && *name != '-'))
+ i++;
+ else if (md->choice == count - 1)
+ break;
+ }
+ }
+ while (name == NULL || *name == '-') {
md->choice--;
+ name = menu->items[md->choice].name;
+ }
+ c->flags |= CLIENT_REDRAWOVERLAY;
+ break;
+ case 'g':
+ case KEYC_HOME:
+ md->choice = 0;
+ name = menu->items[md->choice].name;
+ while (name == NULL || *name == '-') {
+ md->choice++;
+ name = menu->items[md->choice].name;
+ }
+ c->flags |= CLIENT_REDRAWOVERLAY;
+ break;
+ case 'G':
+ case KEYC_END:
+ md->choice = count - 1;
+ name = menu->items[md->choice].name;
+ while (name == NULL || *name == '-') {
+ md->choice--;
+ name = menu->items[md->choice].name;
+ }
c->flags |= CLIENT_REDRAWOVERLAY;
break;
case '\006': /* C-f */
@@ -390,12 +427,12 @@ chosen:
}
struct menu_data *
-menu_prepare(struct menu *menu, int flags, struct cmdq_item *item, u_int px,
- u_int py, struct client *c, struct cmd_find_state *fs, menu_choice_cb cb,
- void *data)
+menu_prepare(struct menu *menu, int flags, int starting_choice,
+ struct cmdq_item *item, u_int px, u_int py, struct client *c,
+ struct cmd_find_state *fs, menu_choice_cb cb, void *data)
{
struct menu_data *md;
- u_int i;
+ int choice;
const char *name;
if (c->tty.sx < menu->width + 4 || c->tty.sy < menu->count + 2)
@@ -420,18 +457,38 @@ menu_prepare(struct menu *menu, int flags, struct cmdq_item *item, u_int px,
md->py = py;
md->menu = menu;
+ md->choice = -1;
+
if (md->flags & MENU_NOMOUSE) {
- for (i = 0; i < menu->count; i++) {
- name = menu->items[i].name;
- if (name != NULL && *name != '-')
- break;
+ if (starting_choice >= (int)menu->count) {
+ starting_choice = menu->count - 1;
+ choice = starting_choice + 1;
+ for (;;) {
+ name = menu->items[choice - 1].name;
+ if (name != NULL && *name != '-') {
+ md->choice = choice - 1;
+ break;
+ }
+ if (--choice == 0)
+ choice = menu->count;
+ if (choice == starting_choice + 1)
+ break;
+ }
+ } else if (starting_choice >= 0) {
+ choice = starting_choice;
+ for (;;) {
+ name = menu->items[choice].name;
+ if (name != NULL && *name != '-') {
+ md->choice = choice;
+ break;
+ }
+ if (++choice == (int)menu->count)
+ choice = 0;
+ if (choice == starting_choice)
+ break;
+ }
}
- if (i != menu->count)
- md->choice = i;
- else
- md->choice = -1;
- } else
- md->choice = -1;
+ }
md->cb = cb;
md->data = data;
@@ -439,13 +496,14 @@ menu_prepare(struct menu *menu, int flags, struct cmdq_item *item, u_int px,
}
int
-menu_display(struct menu *menu, int flags, struct cmdq_item *item, u_int px,
- u_int py, struct client *c, struct cmd_find_state *fs, menu_choice_cb cb,
- void *data)
+menu_display(struct menu *menu, int flags, int starting_choice,
+ struct cmdq_item *item, u_int px, u_int py, struct client *c,
+ struct cmd_find_state *fs, menu_choice_cb cb, void *data)
{
struct menu_data *md;
- md = menu_prepare(menu, flags, item, px, py, c, fs, cb, data);
+ md = menu_prepare(menu, flags, starting_choice, item, px, py, c, fs, cb,
+ data);
if (md == NULL)
return (-1);
server_client_set_overlay(c, 0, NULL, menu_mode_cb, menu_draw_cb,
diff --git a/mode-tree.c b/mode-tree.c
index c007e27f..9d465e7b 100644
--- a/mode-tree.c
+++ b/mode-tree.c
@@ -962,8 +962,8 @@ mode_tree_display_menu(struct mode_tree_data *mtd, struct client *c, u_int x,
x -= (menu->width + 4) / 2;
else
x = 0;
- if (menu_display(menu, 0, NULL, x, y, c, NULL, mode_tree_menu_callback,
- mtm) != 0)
+ if (menu_display(menu, 0, 0, NULL, x, y, c, NULL,
+ mode_tree_menu_callback, mtm) != 0)
menu_free(menu);
}
diff --git a/notify.c b/notify.c
index 85255857..97919487 100644
--- a/notify.c
+++ b/notify.c
@@ -32,6 +32,7 @@ struct notify_entry {
struct session *session;
struct window *window;
int pane;
+ const char *pbname;
};
static struct cmdq_item *
@@ -149,6 +150,10 @@ notify_callback(struct cmdq_item *item, void *data)
control_notify_session_closed(ne->session);
if (strcmp(ne->name, "session-window-changed") == 0)
control_notify_session_window_changed(ne->session);
+ if (strcmp(ne->name, "paste-buffer-changed") == 0)
+ control_notify_paste_buffer_changed(ne->pbname);
+ if (strcmp(ne->name, "paste-buffer-deleted") == 0)
+ control_notify_paste_buffer_deleted(ne->pbname);
notify_insert_hook(item, ne);
@@ -164,6 +169,7 @@ notify_callback(struct cmdq_item *item, void *data)
format_free(ne->formats);
free((void *)ne->name);
+ free((void *)ne->pbname);
free(ne);
return (CMD_RETURN_NORMAL);
@@ -171,7 +177,8 @@ notify_callback(struct cmdq_item *item, void *data)
static void
notify_add(const char *name, struct cmd_find_state *fs, struct client *c,
- struct session *s, struct window *w, struct window_pane *wp)
+ struct session *s, struct window *w, struct window_pane *wp,
+ const char *pbname)
{
struct notify_entry *ne;
struct cmdq_item *item;
@@ -187,6 +194,7 @@ notify_add(const char *name, struct cmd_find_state *fs, struct client *c,
ne->session = s;
ne->window = w;
ne->pane = (wp != NULL ? wp->id : -1);
+ ne->pbname = (pbname != NULL ? xstrdup(pbname) : NULL);
ne->formats = format_create(NULL, NULL, 0, FORMAT_NOJOBS);
format_add(ne->formats, "hook", "%s", name);
@@ -248,7 +256,7 @@ notify_client(const char *name, struct client *c)
struct cmd_find_state fs;
cmd_find_from_client(&fs, c, 0);
- notify_add(name, &fs, c, NULL, NULL, NULL);
+ notify_add(name, &fs, c, NULL, NULL, NULL, NULL);
}
void
@@ -260,7 +268,7 @@ notify_session(const char *name, struct session *s)
cmd_find_from_session(&fs, s, 0);
else
cmd_find_from_nothing(&fs, 0);
- notify_add(name, &fs, NULL, s, NULL, NULL);
+ notify_add(name, &fs, NULL, s, NULL, NULL, NULL);
}
void
@@ -269,7 +277,7 @@ notify_winlink(const char *name, struct winlink *wl)
struct cmd_find_state fs;
cmd_find_from_winlink(&fs, wl, 0);
- notify_add(name, &fs, NULL, wl->session, wl->window, NULL);
+ notify_add(name, &fs, NULL, wl->session, wl->window, NULL, NULL);
}
void
@@ -278,7 +286,7 @@ notify_session_window(const char *name, struct session *s, struct window *w)
struct cmd_find_state fs;
cmd_find_from_session_window(&fs, s, w, 0);
- notify_add(name, &fs, NULL, s, w, NULL);
+ notify_add(name, &fs, NULL, s, w, NULL, NULL);
}
void
@@ -287,7 +295,7 @@ notify_window(const char *name, struct window *w)
struct cmd_find_state fs;
cmd_find_from_window(&fs, w, 0);
- notify_add(name, &fs, NULL, NULL, w, NULL);
+ notify_add(name, &fs, NULL, NULL, w, NULL, NULL);
}
void
@@ -296,5 +304,20 @@ notify_pane(const char *name, struct window_pane *wp)
struct cmd_find_state fs;
cmd_find_from_pane(&fs, wp, 0);
- notify_add(name, &fs, NULL, NULL, NULL, wp);
+ notify_add(name, &fs, NULL, NULL, NULL, wp, NULL);
+}
+
+void
+notify_paste_buffer(const char *pbname, int deleted)
+{
+ struct cmd_find_state fs;
+
+ cmd_find_clear_state(&fs, 0);
+ if (deleted) {
+ notify_add("paste-buffer-deleted", &fs, NULL, NULL, NULL, NULL,
+ pbname);
+ } else {
+ notify_add("paste-buffer-changed", &fs, NULL, NULL, NULL, NULL,
+ pbname);
+ }
}
diff --git a/options-table.c b/options-table.c
index b442d65e..d4e7b204 100644
--- a/options-table.c
+++ b/options-table.c
@@ -41,6 +41,9 @@ static const char *options_table_clock_mode_style_list[] = {
static const char *options_table_status_list[] = {
"off", "on", "2", "3", "4", "5", NULL
};
+static const char *options_table_message_line_list[] = {
+ "0", "1", "2", "3", "4", NULL
+};
static const char *options_table_status_keys_list[] = {
"emacs", "vi", NULL
};
@@ -87,6 +90,9 @@ static const char *options_table_detach_on_destroy_list[] = {
static const char *options_table_extended_keys_list[] = {
"off", "on", "always", NULL
};
+static const char *options_table_allow_passthrough_list[] = {
+ "off", "on", "all", NULL
+};
/* Status line format. */
#define OPTIONS_TABLE_STATUS_FORMAT1 \
@@ -538,13 +544,21 @@ const struct options_table_entry options_table[] = {
"'mode-keys' is set to 'vi'."
},
+ { .name = "message-line",
+ .type = OPTIONS_TABLE_CHOICE,
+ .scope = OPTIONS_TABLE_SESSION,
+ .choices = options_table_message_line_list,
+ .default_num = 0,
+ .text = "Position (line) of messages and the command prompt."
+ },
+
{ .name = "message-style",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SESSION,
.default_str = "bg=yellow,fg=black",
.flags = OPTIONS_TABLE_IS_STYLE,
.separator = ",",
- .text = "Style of the command prompt."
+ .text = "Style of messages and the command prompt."
},
{ .name = "mouse",
@@ -803,11 +817,14 @@ const struct options_table_entry options_table[] = {
},
{ .name = "allow-passthrough",
- .type = OPTIONS_TABLE_FLAG,
+ .type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
+ .choices = options_table_allow_passthrough_list,
.default_num = 0,
.text = "Whether applications are allowed to use the escape sequence "
- "to bypass tmux."
+ "to bypass tmux. Can be 'off' (disallowed), 'on' (allowed "
+ "if the pane is visible), or 'all' (allowed even if the pane "
+ "is invisible)."
},
{ .name = "allow-rename",
diff --git a/paste.c b/paste.c
index ad9d71e5..84cdecb0 100644
--- a/paste.c
+++ b/paste.c
@@ -150,6 +150,8 @@ paste_get_name(const char *name)
void
paste_free(struct paste_buffer *pb)
{
+ notify_paste_buffer(pb->name, 1);
+
RB_REMOVE(paste_name_tree, &paste_by_name, pb);
RB_REMOVE(paste_time_tree, &paste_by_time, pb);
if (pb->automatic)
@@ -206,6 +208,8 @@ paste_add(const char *prefix, char *data, size_t size)
pb->order = paste_next_order++;
RB_INSERT(paste_name_tree, &paste_by_name, pb);
RB_INSERT(paste_time_tree, &paste_by_time, pb);
+
+ notify_paste_buffer(pb->name, 0);
}
/* Rename a paste buffer. */
@@ -253,6 +257,9 @@ paste_rename(const char *oldname, const char *newname, char **cause)
RB_INSERT(paste_name_tree, &paste_by_name, pb);
+ notify_paste_buffer(oldname, 1);
+ notify_paste_buffer(newname, 0);
+
return (0);
}
@@ -301,6 +308,8 @@ paste_set(char *data, size_t size, const char *name, char **cause)
RB_INSERT(paste_name_tree, &paste_by_name, pb);
RB_INSERT(paste_time_tree, &paste_by_time, pb);
+ notify_paste_buffer(name, 0);
+
return (0);
}
@@ -311,6 +320,8 @@ paste_replace(struct paste_buffer *pb, char *data, size_t size)
free(pb->data);
pb->data = data;
pb->size = size;
+
+ notify_paste_buffer(pb->name, 0);
}
/* Convert start of buffer into a nice string. */
diff --git a/popup.c b/popup.c
index 2e57153d..f5c6a37c 100644
--- a/popup.c
+++ b/popup.c
@@ -573,7 +573,7 @@ menu:
x = m->x - (pd->menu->width + 4) / 2;
else
x = 0;
- pd->md = menu_prepare(pd->menu, 0, NULL, x, m->y, c, NULL,
+ pd->md = menu_prepare(pd->menu, 0, 0, NULL, x, m->y, c, NULL,
popup_menu_done, pd);
c->flags |= CLIENT_REDRAWOVERLAY;
diff --git a/regress/conf/2eae5d47049c1f6d0bef3db4e171aed7.conf b/regress/conf/2eae5d47049c1f6d0bef3db4e171aed7.conf
index c09adc24..c3a4da4b 100644
--- a/regress/conf/2eae5d47049c1f6d0bef3db4e171aed7.conf
+++ b/regress/conf/2eae5d47049c1f6d0bef3db4e171aed7.conf
@@ -12,7 +12,7 @@ set-window-option -g pane-base-index 1
unbind ^B
bind ^B select-pane -t :.+
-# Reload config wtih a key
+# Reload config with a key
bind-key r source-file ~/.tmux.conf \; display "Config reloaded!"
# Mouse works as expected
diff --git a/regress/conf/a46e6e84cd1071105aa807256dbc158d.conf b/regress/conf/a46e6e84cd1071105aa807256dbc158d.conf
index bfbb2d3e..d8fd9d20 100644
--- a/regress/conf/a46e6e84cd1071105aa807256dbc158d.conf
+++ b/regress/conf/a46e6e84cd1071105aa807256dbc158d.conf
@@ -24,7 +24,7 @@ set-option -g default-terminal 'screen-256color'
# allow Vim to receive focus events from terminal window
set-option -g focus-events on
-# allow Vim to recieve modifier keys: Shift, Control, Alt
+# allow Vim to receive modifier keys: Shift, Control, Alt
set-window-option -g xterm-keys on
# prevent tmux from catching modifier keys meant for Vim
diff --git a/regress/conf/ad21dbb0893240563ddfdd954b9903a1.conf b/regress/conf/ad21dbb0893240563ddfdd954b9903a1.conf
index 27d8f310..0c41caa4 100644
--- a/regress/conf/ad21dbb0893240563ddfdd954b9903a1.conf
+++ b/regress/conf/ad21dbb0893240563ddfdd954b9903a1.conf
@@ -552,7 +552,7 @@ setw -g status-keys emacs
# Changelog: https://github.com/tmux/tmux/blob/master/CHANGES
# style colors: default, black, red, green, yellow, blue, magenta, cyan, white,
-# colour0-colour255, hexdecimal RGB string '#ffffff'
+# colour0-colour255, hexadecimal RGB string '#ffffff'
# Use $SCRIPTS/bash/256-colors.sh to figure out the color number you want
# style attributes: none, bold/bright, dim, underscore, blink, reverse, hidden,
# or italics
diff --git a/regress/conf/b9f0ce1976ec62ec60dc5da7dd92c160.conf b/regress/conf/b9f0ce1976ec62ec60dc5da7dd92c160.conf
index 0a878369..845002c2 100644
--- a/regress/conf/b9f0ce1976ec62ec60dc5da7dd92c160.conf
+++ b/regress/conf/b9f0ce1976ec62ec60dc5da7dd92c160.conf
@@ -1,6 +1,6 @@
-# none of these attempts worked, to bind keys, except sometimes during the sesssion. Oh well.
+# none of these attempts worked, to bind keys, except sometimes during the session. Oh well.
# I thought maybe that was because F1 is handled differently in a console than in X, but
-# even just C-1 didnt work. Using just "a" or "x" as the key did, but not yet sure why not "C-".
+# even just C-1 didn't work. Using just "a" or "x" as the key did, but not yet sure why not "C-".
#bind-key -T root C-1 attach-session -t$0
#But this one works now, only picks the wrong one? Mbe need2understand what "$1" or $0 mean, better,
#but with the stub maybe this doesn't matter:
@@ -47,7 +47,7 @@ select-window -t :=3
#$3 for email (mutt)
new-session sula
new-window sula ; send-keys mutt Enter
-#nah, probly betr not?:
+#nah, probably better not?:
#send-keys -l z
#send-keys -l "thepassifdecide"
#send-keys Enter
diff --git a/regress/conf/e2661d67d0d45a8647fb95de76ec8174.conf b/regress/conf/e2661d67d0d45a8647fb95de76ec8174.conf
index 79f46df1..3cf1ae9d 100644
--- a/regress/conf/e2661d67d0d45a8647fb95de76ec8174.conf
+++ b/regress/conf/e2661d67d0d45a8647fb95de76ec8174.conf
@@ -63,7 +63,7 @@ bind N command-prompt -p hosts: 'run-shell -b "bash -c \"~/lbin/nw %% >/dev/null
#05:59 < Celti> annihilannic: I believe the #{pane_in_mode} format does what you want
#05:59 < Celti> put it in your statusline
#05:59 < Celti> annihilannic: No, my mistake, I should have read farther down, you want #{pane_synchronized}
-# only works in tmux 2.0?, higher than 1.6.3 anyawy
+# only works in tmux 2.0?, higher than 1.6.3 anyway
set-option -g window-status-format ' #I:#W#F#{?pane_synchronized,S,}'
#set-option -g window-status-current-format ' #I:#W#{?pane_synchronized,[sync],}#F'
# to highlight in red when sync is on... not sure why I did this with set-window-option instead of set-option, perhaps
diff --git a/regress/input-keys.sh b/regress/input-keys.sh
new file mode 100644
index 00000000..262d12a6
--- /dev/null
+++ b/regress/input-keys.sh
@@ -0,0 +1,299 @@
+#!/bin/sh
+
+PATH=/bin:/usr/bin
+TERM=screen
+
+[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
+TMUX="$TEST_TMUX -Ltest"
+$TMUX kill-server 2>/dev/null
+
+$TMUX -f/dev/null new -x20 -y2 -d || exit 1
+
+sleep 0.1
+
+exit_status=0
+
+assert_key () {
+ key=$1
+ expected_code=$2
+
+ $TMUX new-window -- sh -c 'stty raw -echo && cat -tv'
+ $TMUX send-keys "$key" $
+
+ actual_code=$($TMUX capturep -p | head -1 | sed -e 's/\$$//')
+ $TMUX kill-window
+
+ if [ "$actual_code" = "$expected_code" ]; then
+ if [ -n "$VERBOSE" ]; then
+ echo "[PASS] $key -> $actual_code"
+ fi
+ else
+ echo "[FAIL] $key -> $expected_code (Got: $actual_code)"
+ exit_status=1
+ fi
+
+ shift
+ shift
+
+ if [ "$1" = "--" ]; then
+ shift
+ assert_key "$@"
+ fi
+}
+
+assert_key 'C-Space' '^@'
+assert_key 'C-a' '^A' -- 'M-C-a' '^[^A'
+assert_key 'C-b' '^B' -- 'M-C-b' '^[^B'
+assert_key 'C-c' '^C' -- 'M-C-c' '^[^C'
+assert_key 'C-d' '^D' -- 'M-C-d' '^[^D'
+assert_key 'C-e' '^E' -- 'M-C-e' '^[^E'
+assert_key 'C-f' '^F' -- 'M-C-f' '^[^F'
+assert_key 'C-g' '^G' -- 'M-C-g' '^[^G'
+assert_key 'C-h' '^H' -- 'M-C-h' '^[^H'
+assert_key 'C-i' '^I' -- 'M-C-i' '^[^I'
+assert_key 'C-j' '' -- 'M-C-j' '^[' # NL
+assert_key 'C-k' '^K' -- 'M-C-k' '^[^K'
+assert_key 'C-l' '^L' -- 'M-C-l' '^[^L'
+assert_key 'C-m' '^M' -- 'M-C-m' '^[^M'
+assert_key 'C-n' '^N' -- 'M-C-n' '^[^N'
+assert_key 'C-o' '^O' -- 'M-C-o' '^[^O'
+assert_key 'C-p' '^P' -- 'M-C-p' '^[^P'
+assert_key 'C-q' '^Q' -- 'M-C-q' '^[^Q'
+assert_key 'C-r' '^R' -- 'M-C-r' '^[^R'
+assert_key 'C-s' '^S' -- 'M-C-s' '^[^S'
+assert_key 'C-t' '^T' -- 'M-C-t' '^[^T'
+assert_key 'C-u' '^U' -- 'M-C-u' '^[^U'
+assert_key 'C-v' '^V' -- 'M-C-v' '^[^V'
+assert_key 'C-w' '^W' -- 'M-C-w' '^[^W'
+assert_key 'C-x' '^X' -- 'M-C-x' '^[^X'
+assert_key 'C-y' '^Y' -- 'M-C-y' '^[^Y'
+assert_key 'C-z' '^Z' -- 'M-C-z' '^[^Z'
+assert_key 'Escape' '^[' -- 'M-Escape' '^[^['
+assert_key "C-\\" "^\\" -- "M-C-\\" "^[^\\"
+assert_key 'C-]' '^]' -- 'M-C-]' '^[^]'
+assert_key 'C-^' '^^' -- 'M-C-^' '^[^^'
+assert_key 'C-_' '^_' -- 'M-C-_' '^[^_'
+assert_key 'Space' ' ' -- 'M-Space' '^[ '
+assert_key '!' '!' -- 'M-!' '^[!'
+assert_key '"' '"' -- 'M-"' '^["'
+assert_key '#' '#' -- 'M-#' '^[#'
+assert_key '$' '$' -- 'M-$' '^[$'
+assert_key '%' '%' -- 'M-%' '^[%'
+assert_key '&' '&' -- 'M-&' '^[&'
+assert_key "'" "'" -- "M-'" "^['"
+assert_key '(' '(' -- 'M-(' '^[('
+assert_key ')' ')' -- 'M-)' '^[)'
+assert_key '*' '*' -- 'M-*' '^[*'
+assert_key '+' '+' -- 'M-+' '^[+'
+assert_key ',' ',' -- 'M-,' '^[,'
+assert_key '-' '-' -- 'M--' '^[-'
+assert_key '.' '.' -- 'M-.' '^[.'
+assert_key '/' '/' -- 'M-/' '^[/'
+assert_key '0' '0' -- 'M-0' '^[0'
+assert_key '1' '1' -- 'M-1' '^[1'
+assert_key '2' '2' -- 'M-2' '^[2'
+assert_key '3' '3' -- 'M-3' '^[3'
+assert_key '4' '4' -- 'M-4' '^[4'
+assert_key '5' '5' -- 'M-5' '^[5'
+assert_key '6' '6' -- 'M-6' '^[6'
+assert_key '7' '7' -- 'M-7' '^[7'
+assert_key '8' '8' -- 'M-8' '^[8'
+assert_key '9' '9' -- 'M-9' '^[9'
+assert_key ':' ':' -- 'M-:' '^[:'
+assert_key '\;' ';' -- 'M-\;' '^[;'
+assert_key '<' '<' -- 'M-<' '^[<'
+assert_key '=' '=' -- 'M-=' '^[='
+assert_key '>' '>' -- 'M->' '^[>'
+assert_key '?' '?' -- 'M-?' '^[?'
+assert_key '@' '@' -- 'M-@' '^[@'
+assert_key 'A' 'A' -- 'M-A' '^[A'
+assert_key 'B' 'B' -- 'M-B' '^[B'
+assert_key 'C' 'C' -- 'M-C' '^[C'
+assert_key 'D' 'D' -- 'M-D' '^[D'
+assert_key 'E' 'E' -- 'M-E' '^[E'
+assert_key 'F' 'F' -- 'M-F' '^[F'
+assert_key 'G' 'G' -- 'M-G' '^[G'
+assert_key 'H' 'H' -- 'M-H' '^[H'
+assert_key 'I' 'I' -- 'M-I' '^[I'
+assert_key 'J' 'J' -- 'M-J' '^[J'
+assert_key 'K' 'K' -- 'M-K' '^[K'
+assert_key 'L' 'L' -- 'M-L' '^[L'
+assert_key 'M' 'M' -- 'M-M' '^[M'
+assert_key 'N' 'N' -- 'M-N' '^[N'
+assert_key 'O' 'O' -- 'M-O' '^[O'
+assert_key 'P' 'P' -- 'M-P' '^[P'
+assert_key 'Q' 'Q' -- 'M-Q' '^[Q'
+assert_key 'R' 'R' -- 'M-R' '^[R'
+assert_key 'S' 'S' -- 'M-S' '^[S'
+assert_key 'T' 'T' -- 'M-T' '^[T'
+assert_key 'U' 'U' -- 'M-U' '^[U'
+assert_key 'V' 'V' -- 'M-V' '^[V'
+assert_key 'W' 'W' -- 'M-W' '^[W'
+assert_key 'X' 'X' -- 'M-X' '^[X'
+assert_key 'Y' 'Y' -- 'M-Y' '^[Y'
+assert_key 'Z' 'Z' -- 'M-Z' '^[Z'
+assert_key '[' '[' -- 'M-[' '^[['
+assert_key "\\" "\\" -- "M-\\" "^[\\"
+assert_key ']' ']' -- 'M-]' '^[]'
+assert_key '^' '^' -- 'M-^' '^[^'
+assert_key '_' '_' -- 'M-_' '^[_'
+assert_key '`' '`' -- 'M-`' '^[`'
+assert_key 'a' 'a' -- 'M-a' '^[a'
+assert_key 'b' 'b' -- 'M-b' '^[b'
+assert_key 'c' 'c' -- 'M-c' '^[c'
+assert_key 'd' 'd' -- 'M-d' '^[d'
+assert_key 'e' 'e' -- 'M-e' '^[e'
+assert_key 'f' 'f' -- 'M-f' '^[f'
+assert_key 'g' 'g' -- 'M-g' '^[g'
+assert_key 'h' 'h' -- 'M-h' '^[h'
+assert_key 'i' 'i' -- 'M-i' '^[i'
+assert_key 'j' 'j' -- 'M-j' '^[j'
+assert_key 'k' 'k' -- 'M-k' '^[k'
+assert_key 'l' 'l' -- 'M-l' '^[l'
+assert_key 'm' 'm' -- 'M-m' '^[m'
+assert_key 'n' 'n' -- 'M-n' '^[n'
+assert_key 'o' 'o' -- 'M-o' '^[o'
+assert_key 'p' 'p' -- 'M-p' '^[p'
+assert_key 'q' 'q' -- 'M-q' '^[q'
+assert_key 'r' 'r' -- 'M-r' '^[r'
+assert_key 's' 's' -- 'M-s' '^[s'
+assert_key 't' 't' -- 'M-t' '^[t'
+assert_key 'u' 'u' -- 'M-u' '^[u'
+assert_key 'v' 'v' -- 'M-v' '^[v'
+assert_key 'w' 'w' -- 'M-w' '^[w'
+assert_key 'x' 'x' -- 'M-x' '^[x'
+assert_key 'y' 'y' -- 'M-y' '^[y'
+assert_key 'z' 'z' -- 'M-z' '^[z'
+assert_key '{' '{' -- 'M-{' '^[{'
+assert_key '|' '|' -- 'M-|' '^[|'
+assert_key '}' '}' -- 'M-}' '^[}'
+assert_key '~' '~' -- 'M-~' '^[~'
+
+assert_key 'Tab' '^I' -- 'M-Tab' '^[^I'
+assert_key 'BSpace' '^?' -- 'M-BSpace' '^[^?'
+
+## These cannot be sent, is that intentional?
+## assert_key 'PasteStart' "^[[200~"
+## assert_key 'PasteEnd' "^[[201~"
+
+assert_key 'F1' "^[OP"
+assert_key 'F2' "^[OQ"
+assert_key 'F3' "^[OR"
+assert_key 'F4' "^[OS"
+assert_key 'F5' "^[[15~"
+assert_key 'F6' "^[[17~"
+assert_key 'F8' "^[[19~"
+assert_key 'F9' "^[[20~"
+assert_key 'F10' "^[[21~"
+assert_key 'F11' "^[[23~"
+assert_key 'F12' "^[[24~"
+
+assert_key 'IC' '^[[2~'
+assert_key 'Insert' '^[[2~'
+assert_key 'DC' '^[[3~'
+assert_key 'Delete' '^[[3~'
+
+## Why do these differ from tty-keys?
+assert_key 'Home' '^[[1~'
+assert_key 'End' '^[[4~'
+
+assert_key 'NPage' '^[[6~'
+assert_key 'PageDown' '^[[6~'
+assert_key 'PgDn' '^[[6~'
+assert_key 'PPage' '^[[5~'
+assert_key 'PageUp' '^[[5~'
+assert_key 'PgUp' '^[[5~'
+
+assert_key 'BTab' '^[[Z'
+assert_key 'C-S-Tab' '^[[Z'
+
+assert_key 'Up' '^[[A'
+assert_key 'Down' '^[[B'
+assert_key 'Right' '^[[C'
+assert_key 'Left' '^[[D'
+
+# assert_key 'KPEnter'
+assert_key 'KP*' '*' -- 'M-KP*' '^[*'
+assert_key 'KP+' '+' -- 'M-KP+' '^[+'
+assert_key 'KP-' '-' -- 'M-KP-' '^[-'
+assert_key 'KP.' '.' -- 'M-KP.' '^[.'
+assert_key 'KP/' '/' -- 'M-KP/' '^[/'
+assert_key 'KP0' '0' -- 'M-KP0' '^[0'
+assert_key 'KP1' '1' -- 'M-KP1' '^[1'
+assert_key 'KP2' '2' -- 'M-KP2' '^[2'
+assert_key 'KP3' '3' -- 'M-KP3' '^[3'
+assert_key 'KP4' '4' -- 'M-KP4' '^[4'
+assert_key 'KP5' '5' -- 'M-KP5' '^[5'
+assert_key 'KP6' '6' -- 'M-KP6' '^[6'
+assert_key 'KP7' '7' -- 'M-KP7' '^[7'
+assert_key 'KP8' '8' -- 'M-KP8' '^[8'
+assert_key 'KP9' '9' -- 'M-KP9' '^[9'
+
+# Extended keys
+$TMUX set -g extended-keys always
+
+assert_extended_key () {
+ extended_key=$1
+ expected_code_pattern=$2
+
+ expected_code=$(printf '%s' "$expected_code_pattern" | sed -e 's/;_/;2/')
+ assert_key "S-$extended_key" "$expected_code"
+
+ expected_code=$(printf '%s' "$expected_code_pattern" | sed -e 's/;_/;3/')
+ assert_key "M-$extended_key" "$expected_code"
+
+ expected_code=$(printf '%s' "$expected_code_pattern" | sed -e 's/;_/;4/')
+ assert_key "S-M-$extended_key" "$expected_code"
+
+ expected_code=$(printf '%s' "$expected_code_pattern" | sed -e 's/;_/;5/')
+ assert_key "C-$extended_key" "$expected_code"
+
+ expected_code=$(printf '%s' "$expected_code_pattern" | sed -e 's/;_/;6/')
+ assert_key "S-C-$extended_key" "$expected_code"
+
+ expected_code=$(printf '%s' "$expected_code_pattern" | sed -e 's/;_/;7/')
+ assert_key "C-M-$extended_key" "$expected_code"
+
+ expected_code=$(printf '%s' "$expected_code_pattern" | sed -e 's/;_/;8/')
+ assert_key "S-C-M-$extended_key" "$expected_code"
+}
+
+## Many of these pass without extended keys enabled -- are they extended keys?
+assert_extended_key 'F1' '^[[1;_P'
+assert_extended_key 'F2' "^[[1;_Q"
+assert_extended_key 'F3' "^[[1;_R"
+assert_extended_key 'F4' "^[[1;_S"
+assert_extended_key 'F5' "^[[15;_~"
+assert_extended_key 'F6' "^[[17;_~"
+assert_extended_key 'F8' "^[[19;_~"
+assert_extended_key 'F9' "^[[20;_~"
+assert_extended_key 'F10' "^[[21;_~"
+assert_extended_key 'F11' "^[[23;_~"
+assert_extended_key 'F12' "^[[24;_~"
+
+assert_extended_key 'Up' '^[[1;_A'
+assert_extended_key 'Down' '^[[1;_B'
+assert_extended_key 'Right' '^[[1;_C'
+assert_extended_key 'Left' '^[[1;_D'
+
+assert_extended_key 'Home' '^[[1;_H'
+assert_extended_key 'End' '^[[1;_F'
+
+assert_extended_key 'PPage' '^[[5;_~'
+assert_extended_key 'PageUp' '^[[5;_~'
+assert_extended_key 'PgUp' '^[[5;_~'
+assert_extended_key 'NPage' '^[[6;_~'
+assert_extended_key 'PageDown' '^[[6;_~'
+assert_extended_key 'PgDn' '^[[6;_~'
+
+assert_extended_key 'IC' '^[[2;_~'
+assert_extended_key 'Insert' '^[[2;_~'
+assert_extended_key 'DC' '^[[3;_~'
+assert_extended_key 'Delete' '^[[3;_~'
+
+assert_key 'C-Tab' "^[[9;5u"
+assert_key 'C-S-Tab' "^[[1;5Z"
+
+$TMUX kill-server 2>/dev/null
+
+exit $exit_status
diff --git a/regress/tty-keys.sh b/regress/tty-keys.sh
new file mode 100644
index 00000000..1fcc3657
--- /dev/null
+++ b/regress/tty-keys.sh
@@ -0,0 +1,361 @@
+#!/bin/sh
+
+PATH=/bin:/usr/bin
+TERM=screen
+
+[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
+TMUX="$TEST_TMUX -Ltest"
+$TMUX kill-server 2>/dev/null
+TMUX2="$TEST_TMUX -Ltest2"
+$TMUX2 kill-server 2>/dev/null
+
+TMP=$(mktemp)
+trap "rm -f $TMP" 0 1 15
+
+$TMUX2 -f/dev/null new -d || exit 1
+$TMUX -f/dev/null new -d "$TMUX2 attach" || exit 1
+sleep 0.1
+
+exit_status=0
+
+format_string () {
+ case $1 in
+ *\')
+ printf '"%%%%"'
+ ;;
+ *)
+ printf "'%%%%'"
+ ;;
+ esac
+}
+
+assert_key () {
+ keys=$1
+ expected_name=$2
+ format_string=$(format_string "$expected_name")
+
+ $TMUX2 command-prompt -k 'display-message -pl '"$format_string" > "$TMP" &
+ sleep 0.05
+
+ $TMUX send-keys $keys
+
+ wait
+
+ keys=$(printf '%s' "$keys" | sed -e 's/Escape/\\\\033/g' | tr -d '[:space:]')
+ actual_name=$(tr -d '[:space:]' < "$TMP")
+
+ if [ "$actual_name" = "$expected_name" ]; then
+ if [ -n "$VERBOSE" ]; then
+ echo "[PASS] $keys -> $actual_name"
+ fi
+ else
+ echo "[FAIL] $keys -> $expected_name (Got: '$actual_name')"
+ exit_status=1
+ fi
+
+ if [ "$3" = "--" ]; then
+ shift; shift; shift
+ assert_key "$@"
+ fi
+
+}
+
+assert_key 0x00 'C-Space' # -- 'Escape 0x00' 'M-C-Space'
+assert_key 0x01 'C-a' -- 'Escape 0x01' 'M-C-a'
+assert_key 0x02 'C-b' -- 'Escape 0x02' 'M-C-b'
+assert_key 0x03 'C-c' -- 'Escape 0x03' 'M-C-c'
+assert_key 0x04 'C-d' -- 'Escape 0x04' 'M-C-d'
+assert_key 0x05 'C-e' -- 'Escape 0x05' 'M-C-e'
+assert_key 0x06 'C-f' -- 'Escape 0x06' 'M-C-f'
+assert_key 0x07 'C-g' -- 'Escape 0x07' 'M-C-g'
+assert_key 0x08 'C-h' -- 'Escape 0x08' 'M-C-h'
+assert_key 0x09 'Tab' -- 'Escape 0x09' 'M-Tab'
+assert_key 0x0A 'C-j' -- 'Escape 0x0A' 'M-C-j'
+assert_key 0x0B 'C-k' -- 'Escape 0x0B' 'M-C-k'
+assert_key 0x0C 'C-l' -- 'Escape 0x0C' 'M-C-l'
+assert_key 0x0D 'Enter' -- 'Escape 0x0D' 'M-Enter'
+assert_key 0x0E 'C-n' -- 'Escape 0x0E' 'M-C-n'
+assert_key 0x0F 'C-o' -- 'Escape 0x0F' 'M-C-o'
+assert_key 0x10 'C-p' -- 'Escape 0x10' 'M-C-p'
+assert_key 0x11 'C-q' -- 'Escape 0x11' 'M-C-q'
+assert_key 0x12 'C-r' -- 'Escape 0x12' 'M-C-r'
+assert_key 0x13 'C-s' -- 'Escape 0x13' 'M-C-s'
+assert_key 0x14 'C-t' -- 'Escape 0x14' 'M-C-t'
+assert_key 0x15 'C-u' -- 'Escape 0x15' 'M-C-u'
+assert_key 0x16 'C-v' -- 'Escape 0x16' 'M-C-v'
+assert_key 0x17 'C-w' -- 'Escape 0x17' 'M-C-w'
+assert_key 0x18 'C-x' -- 'Escape 0x18' 'M-C-x'
+assert_key 0x19 'C-y' -- 'Escape 0x19' 'M-C-y'
+assert_key 0x1A 'C-z' -- 'Escape 0x1A' 'M-C-z'
+assert_key 0x1B 'Escape' -- 'Escape 0x1B' 'M-Escape'
+assert_key 0x1C "C-\\" -- 'Escape 0x1C' "M-C-\\"
+assert_key 0x1D 'C-]' -- 'Escape 0x1D' 'M-C-]'
+assert_key 0x1E 'C-^' -- 'Escape 0x1E' 'M-C-^'
+assert_key 0x1F 'C-_' -- 'Escape 0x1F' 'M-C-_'
+assert_key 0x20 'Space' -- 'Escape 0x20' 'M-Space'
+assert_key 0x21 '!' -- 'Escape 0x21' 'M-!'
+assert_key 0x22 '"' -- 'Escape 0x22' 'M-"'
+assert_key 0x23 '#' -- 'Escape 0x23'= 'M-#'
+assert_key 0x24 '$' -- 'Escape 0x24'= 'M-$'
+assert_key 0x25 '%' -- 'Escape 0x25'= 'M-%'
+assert_key 0x26 '&' -- 'Escape 0x26'= 'M-&'
+assert_key 0x27 "'" -- 'Escape 0x27' "M-'"
+assert_key 0x28 '(' -- 'Escape 0x28' 'M-('
+assert_key 0x29 ')' -- 'Escape 0x29' 'M-)'
+assert_key 0x2A '*' -- 'Escape 0x2A' 'M-*'
+assert_key 0x2B '+' -- 'Escape 0x2B' 'M-+'
+assert_key 0x2C ',' -- 'Escape 0x2C' 'M-,'
+assert_key 0x2D '-' -- 'Escape 0x2D' 'M--'
+assert_key 0x2E '.' -- 'Escape 0x2E' 'M-.'
+assert_key 0x2F '/' -- 'Escape 0x2F' 'M-/'
+assert_key 0x30 '0' -- 'Escape 0x30' 'M-0'
+assert_key 0x31 '1' -- 'Escape 0x31' 'M-1'
+assert_key 0x32 '2' -- 'Escape 0x32' 'M-2'
+assert_key 0x33 '3' -- 'Escape 0x33' 'M-3'
+assert_key 0x34 '4' -- 'Escape 0x34' 'M-4'
+assert_key 0x35 '5' -- 'Escape 0x35' 'M-5'
+assert_key 0x36 '6' -- 'Escape 0x36' 'M-6'
+assert_key 0x37 '7' -- 'Escape 0x37' 'M-7'
+assert_key 0x38 '8' -- 'Escape 0x38' 'M-8'
+assert_key 0x39 '9' -- 'Escape 0x39' 'M-9'
+assert_key 0x3A ':' -- 'Escape 0x3A' 'M-:'
+assert_key 0x3B ';' -- 'Escape 0x3B' 'M-;'
+assert_key 0x3C '<' -- 'Escape 0x3C' 'M-<'
+assert_key 0x3D '=' -- 'Escape 0x3D' 'M-='
+assert_key 0x3E '>' -- 'Escape 0x3E' 'M->'
+assert_key 0x3F '?' -- 'Escape 0x3F' 'M-?'
+assert_key 0x40 '@' -- 'Escape 0x40' 'M-@'
+assert_key 0x41 'A' -- 'Escape 0x41' 'M-A'
+assert_key 0x42 'B' -- 'Escape 0x42' 'M-B'
+assert_key 0x43 'C' -- 'Escape 0x43' 'M-C'
+assert_key 0x44 'D' -- 'Escape 0x44' 'M-D'
+assert_key 0x45 'E' -- 'Escape 0x45' 'M-E'
+assert_key 0x46 'F' -- 'Escape 0x46' 'M-F'
+assert_key 0x47 'G' -- 'Escape 0x47' 'M-G'
+assert_key 0x48 'H' -- 'Escape 0x48' 'M-H'
+assert_key 0x49 'I' -- 'Escape 0x49' 'M-I'
+assert_key 0x4A 'J' -- 'Escape 0x4A' 'M-J'
+assert_key 0x4B 'K' -- 'Escape 0x4B' 'M-K'
+assert_key 0x4C 'L' -- 'Escape 0x4C' 'M-L'
+assert_key 0x4D 'M' -- 'Escape 0x4D' 'M-M'
+assert_key 0x4E 'N' -- 'Escape 0x4E' 'M-N'
+assert_key 0x4F 'O' -- 'Escape 0x4F' 'M-O'
+assert_key 0x50 'P' -- 'Escape 0x50' 'M-P'
+assert_key 0x51 'Q' -- 'Escape 0x51' 'M-Q'
+assert_key 0x52 'R' -- 'Escape 0x52' 'M-R'
+assert_key 0x53 'S' -- 'Escape 0x53' 'M-S'
+assert_key 0x54 'T' -- 'Escape 0x54' 'M-T'
+assert_key 0x55 'U' -- 'Escape 0x55' 'M-U'
+assert_key 0x56 'V' -- 'Escape 0x56' 'M-V'
+assert_key 0x57 'W' -- 'Escape 0x57' 'M-W'
+assert_key 0x58 'X' -- 'Escape 0x58' 'M-X'
+assert_key 0x59 'Y' -- 'Escape 0x59' 'M-Y'
+assert_key 0x5A 'Z' -- 'Escape 0x5A' 'M-Z'
+assert_key 0x5B '[' -- 'Escape 0x5B' 'M-['
+assert_key 0x5C "\\" -- 'Escape 0x5C' "M-\\"
+assert_key 0x5D ']' -- 'Escape 0x5D' 'M-]'
+assert_key 0x5E '^' -- 'Escape 0x5E' 'M-^'
+assert_key 0x5F '_' -- 'Escape 0x5F' 'M-_'
+assert_key 0x60 '`' -- 'Escape 0x60' 'M-`'
+assert_key 0x61 'a' -- 'Escape 0x61' 'M-a'
+assert_key 0x62 'b' -- 'Escape 0x62' 'M-b'
+assert_key 0x63 'c' -- 'Escape 0x63' 'M-c'
+assert_key 0x64 'd' -- 'Escape 0x64' 'M-d'
+assert_key 0x65 'e' -- 'Escape 0x65' 'M-e'
+assert_key 0x66 'f' -- 'Escape 0x66' 'M-f'
+assert_key 0x67 'g' -- 'Escape 0x67' 'M-g'
+assert_key 0x68 'h' -- 'Escape 0x68' 'M-h'
+assert_key 0x69 'i' -- 'Escape 0x69' 'M-i'
+assert_key 0x6A 'j' -- 'Escape 0x6A' 'M-j'
+assert_key 0x6B 'k' -- 'Escape 0x6B' 'M-k'
+assert_key 0x6C 'l' -- 'Escape 0x6C' 'M-l'
+assert_key 0x6D 'm' -- 'Escape 0x6D' 'M-m'
+assert_key 0x6E 'n' -- 'Escape 0x6E' 'M-n'
+assert_key 0x6F 'o' -- 'Escape 0x6F' 'M-o'
+assert_key 0x70 'p' -- 'Escape 0x70' 'M-p'
+assert_key 0x71 'q' -- 'Escape 0x71' 'M-q'
+assert_key 0x72 'r' -- 'Escape 0x72' 'M-r'
+assert_key 0x73 's' -- 'Escape 0x73' 'M-s'
+assert_key 0x74 't' -- 'Escape 0x74' 'M-t'
+assert_key 0x75 'u' -- 'Escape 0x75' 'M-u'
+assert_key 0x76 'v' -- 'Escape 0x76' 'M-v'
+assert_key 0x77 'w' -- 'Escape 0x77' 'M-w'
+assert_key 0x78 'x' -- 'Escape 0x78' 'M-x'
+assert_key 0x79 'y' -- 'Escape 0x79' 'M-y'
+assert_key 0x7A 'z' -- 'Escape 0x7A' 'M-z'
+assert_key 0x7B '{' -- 'Escape 0x7B' 'M-{'
+assert_key 0x7C '|' -- 'Escape 0x7C' 'M-|'
+assert_key 0x7D '}' -- 'Escape 0x7D' 'M-}'
+assert_key 0x7E '~' -- 'Escape 0x7E' 'M-~'
+assert_key 0x7F 'BSpace' -- 'Escape 0x7F' 'M-BSpace'
+
+# Numeric keypad
+assert_key 'Escape OM' 'KPEnter' -- 'Escape Escape OM' 'M-KPEnter'
+assert_key 'Escape Oj' 'KP*' -- 'Escape Escape Oj' 'M-KP*'
+assert_key 'Escape Ok' 'KP+' -- 'Escape Escape Ok' 'M-KP+'
+assert_key 'Escape Om' 'KP-' -- 'Escape Escape Om' 'M-KP-'
+assert_key 'Escape On' 'KP.' -- 'Escape Escape On' 'M-KP.'
+assert_key 'Escape Oo' 'KP/' -- 'Escape Escape Oo' 'M-KP/'
+assert_key 'Escape Op' 'KP0' -- 'Escape Escape Op' 'M-KP0'
+assert_key 'Escape Oq' 'KP1' -- 'Escape Escape Oq' 'M-KP1'
+assert_key 'Escape Or' 'KP2' -- 'Escape Escape Or' 'M-KP2'
+assert_key 'Escape Os' 'KP3' -- 'Escape Escape Os' 'M-KP3'
+assert_key 'Escape Ot' 'KP4' -- 'Escape Escape Ot' 'M-KP4'
+assert_key 'Escape Ou' 'KP5' -- 'Escape Escape Ou' 'M-KP5'
+assert_key 'Escape Ov' 'KP6' -- 'Escape Escape Ov' 'M-KP6'
+assert_key 'Escape Ow' 'KP7' -- 'Escape Escape Ow' 'M-KP7'
+assert_key 'Escape Ox' 'KP8' -- 'Escape Escape Ox' 'M-KP8'
+assert_key 'Escape Oy' 'KP9' -- 'Escape Escape Oy' 'M-KP9'
+
+# Arrow keys
+assert_key 'Escape OA' 'Up' -- 'Escape Escape OA' 'M-Up'
+assert_key 'Escape OB' 'Down' -- 'Escape Escape OB' 'M-Down'
+assert_key 'Escape OC' 'Right' -- 'Escape Escape OC' 'M-Right'
+assert_key 'Escape OD' 'Left' -- 'Escape Escape OD' 'M-Left'
+
+assert_key 'Escape [A' 'Up' -- 'Escape Escape [A' 'M-Up'
+assert_key 'Escape [B' 'Down' -- 'Escape Escape [B' 'M-Down'
+assert_key 'Escape [C' 'Right' -- 'Escape Escape [C' 'M-Right'
+assert_key 'Escape [D' 'Left' -- 'Escape Escape [D' 'M-Left'
+
+# Other xterm keys
+assert_key 'Escape OH' 'Home' -- 'Escape Escape OH' 'M-Home'
+assert_key 'Escape OF' 'End' -- 'Escape Escape OF' 'M-End'
+
+assert_key 'Escape [H' 'Home' -- 'Escape Escape [H' 'M-Home'
+assert_key 'Escape [F' 'End' -- 'Escape Escape [F' 'M-End'
+
+# rxvt arrow keys
+assert_key 'Escape Oa' 'C-Up'
+assert_key 'Escape Ob' 'C-Down'
+assert_key 'Escape Oc' 'C-Right'
+assert_key 'Escape Od' 'C-Left'
+assert_key 'Escape [a' 'S-Up'
+assert_key 'Escape [b' 'S-Down'
+assert_key 'Escape [c' 'S-Right'
+assert_key 'Escape [d' 'S-Left'
+
+# rxvt function keys
+assert_key 'Escape [11~' 'F1'
+assert_key 'Escape [12~' 'F2'
+assert_key 'Escape [13~' 'F3'
+assert_key 'Escape [14~' 'F4'
+assert_key 'Escape [15~' 'F5'
+assert_key 'Escape [17~' 'F6'
+assert_key 'Escape [18~' 'F7'
+assert_key 'Escape [19~' 'F8'
+assert_key 'Escape [20~' 'F9'
+assert_key 'Escape [21~' 'F10'
+assert_key 'Escape [23~' 'F11'
+assert_key 'Escape [24~' 'F12'
+
+# With TERM=screen, these will be seen as F11 and F12
+# assert_key 'Escape [23~' 'S-F1'
+# assert_key 'Escape [24~' 'S-F2'
+assert_key 'Escape [25~' 'S-F3'
+assert_key 'Escape [26~' 'S-F4'
+assert_key 'Escape [28~' 'S-F5'
+assert_key 'Escape [29~' 'S-F6'
+assert_key 'Escape [31~' 'S-F7'
+assert_key 'Escape [32~' 'S-F8'
+assert_key 'Escape [33~' 'S-F9'
+assert_key 'Escape [34~' 'S-F10'
+assert_key 'Escape [23$' 'S-F11'
+assert_key 'Escape [24$' 'S-F12'
+
+assert_key 'Escape [11^' 'C-F1'
+assert_key 'Escape [12^' 'C-F2'
+assert_key 'Escape [13^' 'C-F3'
+assert_key 'Escape [14^' 'C-F4'
+assert_key 'Escape [15^' 'C-F5'
+assert_key 'Escape [17^' 'C-F6'
+assert_key 'Escape [18^' 'C-F7'
+assert_key 'Escape [19^' 'C-F8'
+assert_key 'Escape [20^' 'C-F9'
+assert_key 'Escape [21^' 'C-F10'
+assert_key 'Escape [23^' 'C-F11'
+assert_key 'Escape [24^' 'C-F12'
+
+assert_key 'Escape [11@' 'C-S-F1'
+assert_key 'Escape [12@' 'C-S-F2'
+assert_key 'Escape [13@' 'C-S-F3'
+assert_key 'Escape [14@' 'C-S-F4'
+assert_key 'Escape [15@' 'C-S-F5'
+assert_key 'Escape [17@' 'C-S-F6'
+assert_key 'Escape [18@' 'C-S-F7'
+assert_key 'Escape [19@' 'C-S-F8'
+assert_key 'Escape [20@' 'C-S-F9'
+assert_key 'Escape [21@' 'C-S-F10'
+assert_key 'Escape [23@' 'C-S-F11'
+assert_key 'Escape [24@' 'C-S-F12'
+
+# Focus tracking
+assert_key 'Escape [I' 'FocusIn'
+assert_key 'Escape [O' 'FocusOut'
+
+# Paste keys
+assert_key 'Escape [200~' 'PasteStart'
+assert_key 'Escape [201~' 'PasteEnd'
+
+assert_key 'Escape [Z' 'BTab'
+
+assert_extended_key () {
+ code=$1
+ key_name=$2
+
+ assert_key "Escape [${code};5u" "C-$key_name"
+ assert_key "Escape [${code};7u" "M-C-$key_name"
+}
+
+# Extended keys
+# assert_extended_key 65 'A'
+# assert_extended_key 66 'B'
+# assert_extended_key 67 'C'
+# assert_extended_key 68 'D'
+# assert_extended_key 69 'E'
+# assert_extended_key 70 'F'
+# assert_extended_key 71 'G'
+# assert_extended_key 72 'H'
+# assert_extended_key 73 'I'
+# assert_extended_key 74 'J'
+# assert_extended_key 75 'K'
+# assert_extended_key 76 'L'
+# assert_extended_key 77 'M'
+# assert_extended_key 78 'N'
+# assert_extended_key 79 'O'
+# assert_extended_key 80 'P'
+# assert_extended_key 81 'Q'
+# assert_extended_key 82 'R'
+# assert_extended_key 83 'S'
+# assert_extended_key 84 'T'
+# assert_extended_key 85 'U'
+# assert_extended_key 86 'V'
+# assert_extended_key 87 'W'
+# assert_extended_key 88 'X'
+# assert_extended_key 89 'Y'
+# assert_extended_key 90 'Z'
+# assert_extended_key 123 '{'
+# assert_extended_key 124 '|'
+# assert_extended_key 125 '}'
+
+# assert_key 'Escape [105;5u' 'C-i'
+# assert_key 'Escape [73;5u' 'C-I'
+
+# assert_key 'Escape [109;5u' 'C-m'
+# assert_key 'Escape [77;5u' 'C-M'
+
+# assert_key 'Escape [91;5u' 'C-['
+assert_key 'Escape [123;5u' 'C-{'
+
+# assert_key 'Escape [64;5u' 'C-@'
+
+assert_key 'Escape [32;2u' 'S-Space'
+# assert_key 'Escape [32;6u' 'C-S-Space'
+
+assert_key 'Escape [9;5u' 'C-Tab'
+assert_key 'Escape [1;5Z' 'C-S-Tab'
+
+$TMUX kill-server 2>/dev/null
+$TMUX2 kill-server 2>/dev/null
+
+exit $exit_status
diff --git a/screen-write.c b/screen-write.c
index 213b533c..59d289ec 100644
--- a/screen-write.c
+++ b/screen-write.c
@@ -1506,7 +1506,8 @@ screen_write_fullredraw(struct screen_write_ctx *ctx)
screen_write_collect_flush(ctx, 0, __func__);
screen_write_initctx(ctx, &ttyctx, 1);
- ttyctx.redraw_cb(&ttyctx);
+ if (ttyctx.redraw_cb != NULL)
+ ttyctx.redraw_cb(&ttyctx);
}
/* Trim collected items. */
@@ -1819,7 +1820,7 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
struct grid_cell tmp_gc, now_gc;
struct tty_ctx ttyctx;
u_int sx = screen_size_x(s), sy = screen_size_y(s);
- u_int width = gc->data.width, xx, last, cx, cy;
+ u_int width = gc->data.width, xx, last, cy;
int selected, skip = 1;
/* Ignore padding cells. */
@@ -1852,12 +1853,12 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
ctx->flags &= ~SCREEN_WRITE_ZWJ;
screen_write_collect_flush(ctx, 0, __func__);
if ((gc = screen_write_combine(ctx, ud, &xx)) != NULL) {
- cx = s->cx; cy = s->cy;
+ cy = s->cy;
screen_write_set_cursor(ctx, xx, s->cy);
screen_write_initctx(ctx, &ttyctx, 0);
ttyctx.cell = gc;
tty_write(tty_cmd_cell, &ttyctx);
- s->cx = cx; s->cy = cy;
+ s->cx = xx + 1 + gc->data.width; s->cy = cy;
}
return;
}
@@ -2015,6 +2016,14 @@ screen_write_combine(struct screen_write_ctx *ctx, const struct utf8_data *ud,
memcpy(gc.data.data + gc.data.size, ud->data, ud->size);
gc.data.size += ud->size;
+ /* If this is U+FE0F VARIATION SELECTOR-16, force the width to 2. */
+ if (gc.data.width == 1 &&
+ ud->size == 3 &&
+ memcmp(ud->data, "\357\270\217", 3) == 0) {
+ grid_view_set_padding(gd, (*xx) + 1, s->cy);
+ gc.data.width = 2;
+ }
+
/* Set the new cell. */
grid_view_set_cell(gd, *xx, s->cy, &gc);
@@ -2100,13 +2109,15 @@ screen_write_setselection(struct screen_write_ctx *ctx, const char *flags,
/* Write unmodified string. */
void
-screen_write_rawstring(struct screen_write_ctx *ctx, u_char *str, u_int len)
+screen_write_rawstring(struct screen_write_ctx *ctx, u_char *str, u_int len,
+ int allow_invisible_panes)
{
struct tty_ctx ttyctx;
screen_write_initctx(ctx, &ttyctx, 0);
ttyctx.ptr = str;
ttyctx.num = len;
+ ttyctx.allow_invisible_panes = allow_invisible_panes;
tty_write(tty_cmd_rawstring, &ttyctx);
}
@@ -2126,7 +2137,8 @@ screen_write_alternateon(struct screen_write_ctx *ctx, struct grid_cell *gc,
screen_alternate_on(ctx->s, gc, cursor);
screen_write_initctx(ctx, &ttyctx, 1);
- ttyctx.redraw_cb(&ttyctx);
+ if (ttyctx.redraw_cb != NULL)
+ ttyctx.redraw_cb(&ttyctx);
}
/* Turn alternate screen off. */
@@ -2144,5 +2156,6 @@ screen_write_alternateoff(struct screen_write_ctx *ctx, struct grid_cell *gc,
screen_alternate_off(ctx->s, gc, cursor);
screen_write_initctx(ctx, &ttyctx, 1);
- ttyctx.redraw_cb(&ttyctx);
+ if (ttyctx.redraw_cb != NULL)
+ ttyctx.redraw_cb(&ttyctx);
}
diff --git a/server-client.c b/server-client.c
index c5591055..f591bce8 100644
--- a/server-client.c
+++ b/server-client.c
@@ -42,6 +42,7 @@ static void server_client_check_modes(struct client *);
static void server_client_set_title(struct client *);
static void server_client_set_path(struct client *);
static void server_client_reset_state(struct client *);
+static int server_client_is_bracket_pasting(struct client *, key_code);
static int server_client_assume_paste(struct session *);
static void server_client_update_latest(struct client *);
@@ -1754,6 +1755,25 @@ out:
return (key);
}
+/* Is this a bracket paste key? */
+static int
+server_client_is_bracket_pasting(struct client *c, key_code key)
+{
+ if (key == KEYC_PASTE_START) {
+ c->flags |= CLIENT_BRACKETPASTING;
+ log_debug("%s: bracket paste on", c->name);
+ return (1);
+ }
+
+ if (key == KEYC_PASTE_END) {
+ c->flags &= ~CLIENT_BRACKETPASTING;
+ log_debug("%s: bracket paste off", c->name);
+ return (1);
+ }
+
+ return !!(c->flags & CLIENT_BRACKETPASTING);
+}
+
/* Is this fast enough to probably be a paste? */
static int
server_client_assume_paste(struct session *s)
@@ -1862,8 +1882,14 @@ server_client_key_callback(struct cmdq_item *item, void *data)
if (KEYC_IS_MOUSE(key) && !options_get_number(s->options, "mouse"))
goto forward_key;
+ /* Forward if bracket pasting. */
+ if (server_client_is_bracket_pasting(c, key))
+ goto forward_key;
+
/* Treat everything as a regular key when pasting is detected. */
- if (!KEYC_IS_MOUSE(key) && server_client_assume_paste(s))
+ if (!KEYC_IS_MOUSE(key) &&
+ (~key & KEYC_SENT) &&
+ server_client_assume_paste(s))
goto forward_key;
/*
diff --git a/session.c b/session.c
index 75028882..71435cdf 100644
--- a/session.c
+++ b/session.c
@@ -708,7 +708,7 @@ session_renumber_windows(struct session *s)
struct winlink *wl, *wl1, *wl_new;
struct winlinks old_wins;
struct winlink_stack old_lastw;
- int new_idx, new_curw_idx;
+ int new_idx, new_curw_idx, marked_idx = -1;
/* Save and replace old window list. */
memcpy(&old_wins, &s->windows, sizeof old_wins);
@@ -725,6 +725,8 @@ session_renumber_windows(struct session *s)
winlink_set_window(wl_new, wl->window);
wl_new->flags |= wl->flags & WINLINK_ALERTFLAGS;
+ if (wl == marked_pane.wl)
+ marked_idx = wl_new->idx;
if (wl == s->curw)
new_curw_idx = wl_new->idx;
@@ -741,6 +743,11 @@ session_renumber_windows(struct session *s)
}
/* Set the current window. */
+ if (marked_idx != -1) {
+ marked_pane.wl = winlink_find_by_index(&s->windows, marked_idx);
+ if (marked_pane.wl == NULL)
+ server_clear_marked();
+ }
s->curw = winlink_find_by_index(&s->windows, new_curw_idx);
/* Free the old winlinks (reducing window references too). */
diff --git a/spawn.c b/spawn.c
index 2cb2b65e..e63133fe 100644
--- a/spawn.c
+++ b/spawn.c
@@ -389,7 +389,7 @@ spawn_pane(struct spawn_context *sc, char **cause)
*/
if (chdir(new_wp->cwd) == 0)
environ_set(child, "PWD", 0, "%s", new_wp->cwd);
- else if ((tmp = find_home()) != NULL || chdir(tmp) == 0)
+ else if ((tmp = find_home()) != NULL && chdir(tmp) == 0)
environ_set(child, "PWD", 0, "%s", tmp);
else if (chdir("/") == 0)
environ_set(child, "PWD", 0, "/");
diff --git a/status.c b/status.c
index 929276d2..08952f58 100644
--- a/status.c
+++ b/status.c
@@ -263,6 +263,17 @@ status_line_size(struct client *c)
return (s->statuslines);
}
+/* Get the prompt line number for client's session. 1 means at the bottom. */
+static u_int
+status_prompt_line_at(struct client *c)
+{
+ struct session *s = c->session;
+
+ if (c->flags & (CLIENT_STATUSOFF|CLIENT_CONTROL))
+ return (1);
+ return (options_get_number(s->options, "message-line"));
+}
+
/* Get window at window list position. */
struct style_range *
status_get_range(struct client *c, u_int x, u_int y)
@@ -533,7 +544,7 @@ status_message_redraw(struct client *c)
struct session *s = c->session;
struct screen old_screen;
size_t len;
- u_int lines, offset;
+ u_int lines, offset, messageline;
struct grid_cell gc;
struct format_tree *ft;
@@ -546,6 +557,10 @@ status_message_redraw(struct client *c)
lines = 1;
screen_init(sl->active, c->tty.sx, lines, 0);
+ messageline = status_prompt_line_at(c);
+ if (messageline > lines - 1)
+ messageline = lines - 1;
+
len = screen_write_strlen("%s", c->message_string);
if (len > c->tty.sx)
len = c->tty.sx;
@@ -555,11 +570,11 @@ status_message_redraw(struct client *c)
format_free(ft);
screen_write_start(&ctx, sl->active);
- screen_write_fast_copy(&ctx, &sl->screen, 0, 0, c->tty.sx, lines - 1);
- screen_write_cursormove(&ctx, 0, lines - 1, 0);
+ screen_write_fast_copy(&ctx, &sl->screen, 0, 0, c->tty.sx, lines);
+ screen_write_cursormove(&ctx, 0, messageline, 0);
for (offset = 0; offset < c->tty.sx; offset++)
screen_write_putc(&ctx, &gc, ' ');
- screen_write_cursormove(&ctx, 0, lines - 1, 0);
+ screen_write_cursormove(&ctx, 0, messageline, 0);
if (c->message_ignore_styles)
screen_write_nputs(&ctx, len, &gc, "%s", c->message_string);
else
@@ -695,7 +710,7 @@ status_prompt_redraw(struct client *c)
struct session *s = c->session;
struct screen old_screen;
u_int i, lines, offset, left, start, width;
- u_int pcursor, pwidth;
+ u_int pcursor, pwidth, promptline;
struct grid_cell gc, cursorgc;
struct format_tree *ft;
@@ -708,6 +723,10 @@ status_prompt_redraw(struct client *c)
lines = 1;
screen_init(sl->active, c->tty.sx, lines, 0);
+ promptline = status_prompt_line_at(c);
+ if (promptline > lines - 1)
+ promptline = lines - 1;
+
ft = format_create_defaults(NULL, c, NULL, NULL, NULL);
if (c->prompt_mode == PROMPT_COMMAND)
style_apply(&gc, s->options, "message-command-style", ft);
@@ -723,13 +742,13 @@ status_prompt_redraw(struct client *c)
start = c->tty.sx;
screen_write_start(&ctx, sl->active);
- screen_write_fast_copy(&ctx, &sl->screen, 0, 0, c->tty.sx, lines - 1);
- screen_write_cursormove(&ctx, 0, lines - 1, 0);
+ screen_write_fast_copy(&ctx, &sl->screen, 0, 0, c->tty.sx, lines);
+ screen_write_cursormove(&ctx, 0, promptline, 0);
for (offset = 0; offset < c->tty.sx; offset++)
screen_write_putc(&ctx, &gc, ' ');
- screen_write_cursormove(&ctx, 0, lines - 1, 0);
+ screen_write_cursormove(&ctx, 0, promptline, 0);
format_draw(&ctx, &gc, start, c->prompt_string, NULL, 0);
- screen_write_cursormove(&ctx, start, lines - 1, 0);
+ screen_write_cursormove(&ctx, start, promptline, 0);
left = c->tty.sx - start;
if (left == 0)
@@ -1747,7 +1766,7 @@ status_prompt_complete_list_menu(struct client *c, char **list, u_int size,
else
offset = 0;
- if (menu_display(menu, MENU_NOMOUSE|MENU_TAB, NULL, offset,
+ if (menu_display(menu, MENU_NOMOUSE|MENU_TAB, 0, NULL, offset,
py, c, NULL, status_prompt_menu_callback, spm) != 0) {
menu_free(menu);
free(spm);
@@ -1840,7 +1859,7 @@ status_prompt_complete_window_menu(struct client *c, struct session *s,
else
offset = 0;
- if (menu_display(menu, MENU_NOMOUSE|MENU_TAB, NULL, offset,
+ if (menu_display(menu, MENU_NOMOUSE|MENU_TAB, 0, NULL, offset,
py, c, NULL, status_prompt_menu_callback, spm) != 0) {
menu_free(menu);
free(spm);
diff --git a/tmux-protocol.h b/tmux-protocol.h
index 08422291..3cf00c09 100644
--- a/tmux-protocol.h
+++ b/tmux-protocol.h
@@ -66,7 +66,8 @@ enum msgtype {
MSG_WRITE_OPEN,
MSG_WRITE,
MSG_WRITE_READY,
- MSG_WRITE_CLOSE
+ MSG_WRITE_CLOSE,
+ MSG_READ_CANCEL
};
/*
@@ -92,6 +93,10 @@ struct msg_read_done {
int error;
};
+struct msg_read_cancel {
+ int stream;
+};
+
struct msg_write_open {
int stream;
int fd;
diff --git a/tmux.1 b/tmux.1
index 0c078fba..4c4b94aa 100644
--- a/tmux.1
+++ b/tmux.1
@@ -23,7 +23,7 @@
.Sh SYNOPSIS
.Nm tmux
.Bk -words
-.Op Fl 2CDluvV
+.Op Fl 2CDlNuVv
.Op Fl c Ar shell-command
.Op Fl f Ar file
.Op Fl L Ar socket-name
@@ -206,6 +206,12 @@ If
is specified, the default socket directory is not used and any
.Fl L
flag is ignored.
+.It Fl T Ar features
+Set terminal features for the client.
+This is a comma-separated list of features.
+See the
+.Ic terminal-features
+option.
.It Fl u
Write UTF-8 output to the terminal even if the first environment
variable of
@@ -217,14 +223,10 @@ that is set does not contain
.Qq UTF-8
or
.Qq UTF8 .
-This is equivalent to
-.Fl T Ar UTF-8 .
-.It Fl T Ar features
-Set terminal features for the client.
-This is a comma-separated list of features.
-See the
-.Ic terminal-features
-option.
+.It Fl V
+Report the
+.Nm
+version.
.It Fl v
Request verbose logging.
Log messages will be saved into
@@ -249,10 +251,6 @@ signal may be sent to the
server process to toggle logging between on (as if
.Fl v
was given) and off.
-.It Fl V
-Report the
-.Nm
-version.
.It Ar command Op Ar flags
This specifies one of a set of commands used to control
.Nm ,
@@ -547,7 +545,7 @@ for example in these
.Xr sh 1
commands:
.Bd -literal -offset indent
-$ tmux neww\e\e; splitw
+$ tmux neww\e; splitw
.Ed
.Pp
Or:
@@ -966,7 +964,7 @@ Will run
directly without invoking the shell.
.Pp
.Ar command
-.Op Ar arguments
+.Op Ar argument ...
refers to a
.Nm
command, either passed with the command and arguments separately, for example:
@@ -1543,8 +1541,7 @@ show debugging information about jobs and terminals.
.Tg source
.It Xo Ic source-file
.Op Fl Fnqv
-.Ar path
-.Ar ...
+.Ar path ...
.Xc
.D1 Pq alias: Ic source
Execute commands from one or more files specified by
@@ -1813,6 +1810,9 @@ The following commands are supported in copy mode:
.It Li "search-forward <for>" Ta "/" Ta ""
.It Li "search-forward-incremental <for>" Ta "" Ta "C-s"
.It Li "search-forward-text <for>" Ta "" Ta ""
+.It Li "scroll-bottom" Ta "" Ta ""
+.It Li "scroll-middle" Ta "z" Ta ""
+.It Li "scroll-top" Ta "" Ta ""
.It Li "search-reverse" Ta "N" Ta "N"
.It Li "select-line" Ta "V" Ta ""
.It Li "select-word" Ta "" Ta ""
@@ -1974,7 +1974,7 @@ For example:
$ tmux list-windows
0: ksh [159x48]
layout: bb62,159x48,0,0{79x48,0,0,79x48,80,0}
-$ tmux select-layout bb62,159x48,0,0{79x48,0,0,79x48,80,0}
+$ tmux select-layout 'bb62,159x48,0,0{79x48,0,0,79x48,80,0}'
.Ed
.Pp
.Nm
@@ -2015,7 +2015,7 @@ but a different format may be specified with
.Fl F .
.Tg capturep
.It Xo Ic capture-pane
-.Op Fl aepPqCJN
+.Op Fl aAepPqCJN
.Op Fl b Ar buffer-name
.Op Fl E Ar end-line
.Op Fl S Ar start-line
@@ -2040,10 +2040,15 @@ is given, the output includes escape sequences for text and background
attributes.
.Fl C
also escapes non-printable characters as octal \exxx.
+.Fl T
+ignores trailing positions that do not contain a character.
.Fl N
preserves trailing spaces at each line's end and
.Fl J
-preserves trailing spaces and joins any wrapped lines.
+preserves trailing spaces and joins any wrapped lines;
+.Fl J
+implies
+.Fl T .
.Fl P
captures only any output that the pane has received that is the beginning of an
as-yet incomplete escape sequence.
@@ -3117,7 +3122,7 @@ Commands related to key bindings are as follows:
.Op Fl nr
.Op Fl N Ar note
.Op Fl T Ar key-table
-.Ar key command Op Ar arguments
+.Ar key command Op Ar argument ...
.Xc
.D1 Pq alias: Ic bind
Bind key
@@ -3209,13 +3214,14 @@ lists only the first matching key.
lists the command for keys that do not have a note rather than skipping them.
.Tg send
.It Xo Ic send-keys
-.Op Fl FHlMRX
+.Op Fl FHKlMRX
+.Op Fl c Ar target-client
.Op Fl N Ar repeat-count
.Op Fl t Ar target-pane
-.Ar key Ar ...
+.Ar key ...
.Xc
.D1 Pq alias: Ic send
-Send a key or keys to a window.
+Send a key or keys to a window or client.
Each argument
.Ar key
is the name of the key (such as
@@ -3224,6 +3230,12 @@ or
.Ql NPage )
to send; if the string is not recognised as a key, it is sent as a series of
characters.
+If
+.Fl K
+is given, keys are sent to
+.Ar target-client ,
+so they are looked up in the client's key table, rather than to
+.Ar target-pane .
All arguments are sent sequentially from first to last.
If no keys are given and the command is bound to a key, then that key is used.
.Pp
@@ -3684,6 +3696,8 @@ Supports the overline SGR attribute.
Supports the DECFRA rectangle fill escape sequence.
.It RGB
Supports RGB colour with the SGR escape sequences.
+.It sixel
+Supports SIXEL graphics.
.It strikethrough
Supports the strikethrough SGR escape sequence.
.It sync
@@ -3877,6 +3891,10 @@ For how to specify
see the
.Sx STYLES
section.
+.It Xo Ic message-line
+.Op Ic 0 | 1 | 2 | 3 | 4
+.Xc
+Set line on which status line messages and the command prompt are shown.
.It Ic message-style Ar style
Set status line message style.
This is used for messages and for the command prompt.
@@ -4466,11 +4484,17 @@ Available pane options are:
.Pp
.Bl -tag -width Ds -compact
.It Xo Ic allow-passthrough
-.Op Ic on | off
+.Op Ic on | off | all
.Xc
Allow programs in the pane to bypass
.Nm
using a terminal escape sequence (\eePtmux;...\ee\e\e).
+If set to
+.Ic on ,
+passthrough sequences will be allowed only if the pane is visible.
+If set to
+.Ic all ,
+they will be allowed even if the pane is invisible.
.Pp
.It Xo Ic allow-rename
.Op Ic on | off
@@ -5067,6 +5091,15 @@ would change
.Ql abABab
into
.Ql bxBxbx .
+A different delimiter character may also be used, to avoid collisions with
+literal slashes in the pattern.
+For example,
+.Ql s|foo/|bar/|:
+will substitute
+.Ql foo/
+with
+.Ql bar/
+throughout.
.Pp
In addition, the last line of a shell command's output may be inserted using
.Ql #() .
@@ -5785,13 +5818,13 @@ until it is dismissed.
.Op Fl O
.Op Fl c Ar target-client
.Op Fl t Ar target-pane
+.Op Fl S Ar starting-choice
.Op Fl T Ar title
.Op Fl x Ar position
.Op Fl y Ar position
.Ar name
.Ar key
-.Ar command
-.Ar ...
+.Ar command Op Ar argument ...
.Xc
.D1 Pq alias: Ic menu
Display a menu on
@@ -5815,6 +5848,9 @@ command should be omitted.
.Fl T
is a format for the menu title (see
.Sx FORMATS ) .
+.Fl S
+sets the menu item selected by default, if the menu is not bound to a mouse key
+binding.
.Pp
.Fl x
and
@@ -5874,7 +5910,7 @@ The following keys are also available:
.El
.Tg display
.It Xo Ic display-message
-.Op Fl aINpv
+.Op Fl aIlNpv
.Op Fl c Ar target-client
.Op Fl d Ar delay
.Op Fl t Ar target-pane
@@ -5896,7 +5932,12 @@ is not given, the
option is used; a delay of zero waits for a key press.
.Ql N
ignores key presses and closes only after the delay expires.
-The format of
+If
+.Fl l
+is given,
+.Ar message
+is printed unchanged.
+Otherwise, the format of
.Ar message
is described in the
.Sx FORMATS
@@ -6429,6 +6470,10 @@ These are set automatically if the
capability is present.
.It Em \&Hls
Set or clear a hyperlink annotation.
+.It Em \&Nobr
+Tell
+.Nm
+that the terminal does not use bright colors for bold display.
.It Em \&Rect
Tell
.Nm
@@ -6460,6 +6505,8 @@ Set the opening sequence for the working directory notification.
The sequence is terminated using the standard
.Em fsl
capability.
+.It Em \&Sxl
+Indicates that the terminal supports SIXEL.
.It Em \&Sync
Start (parameter is 1) or end (parameter is 2) a synchronized update.
.It Em \&Tc
@@ -6591,6 +6638,14 @@ escapes non-printable characters and backslash as octal \\xxx.
The pane with ID
.Ar pane-id
has changed mode.
+.It Ic %paste-buffer-changed Ar name
+Paste buffer
+.Ar name
+has been changed.
+.It Ic %paste-buffer-deleted Ar name
+Paste buffer
+.Ar name
+has been deleted.
.It Ic %pause Ar pane-id
The pane has been paused (if the
.Ar pause-after
diff --git a/tmux.c b/tmux.c
index b9f2be30..ef78e7b4 100644
--- a/tmux.c
+++ b/tmux.c
@@ -53,7 +53,7 @@ static __dead void
usage(void)
{
fprintf(stderr,
- "usage: %s [-2CDlNuvV] [-c shell-command] [-f file] [-L socket-name]\n"
+ "usage: %s [-2CDlNuVv] [-c shell-command] [-f file] [-L socket-name]\n"
" [-S socket-path] [-T features] [command [flags]]\n",
getprogname());
exit(1);
diff --git a/tmux.h b/tmux.h
index d097720e..11bae046 100644
--- a/tmux.h
+++ b/tmux.h
@@ -132,12 +132,14 @@ struct winlink;
#define KEYC_SHIFT 0x00400000000000ULL
/* Key flag bits. */
-#define KEYC_LITERAL 0x01000000000000ULL
-#define KEYC_KEYPAD 0x02000000000000ULL
-#define KEYC_CURSOR 0x04000000000000ULL
+#define KEYC_LITERAL 0x01000000000000ULL
+#define KEYC_KEYPAD 0x02000000000000ULL
+#define KEYC_CURSOR 0x04000000000000ULL
#define KEYC_IMPLIED_META 0x08000000000000ULL
#define KEYC_BUILD_MODIFIERS 0x10000000000000ULL
-#define KEYC_VI 0x20000000000000ULL
+#define KEYC_VI 0x20000000000000ULL
+#define KEYC_EXTENDED 0x40000000000000ULL
+#define KEYC_SENT 0x80000000000000ULL
/* Masks for key bits. */
#define KEYC_MASK_MODIFIERS 0x00f00000000000ULL
@@ -515,6 +517,7 @@ enum tty_code_code {
TTYC_KUP6,
TTYC_KUP7,
TTYC_MS,
+ TTYC_NOBR,
TTYC_OL,
TTYC_OP,
TTYC_RECT,
@@ -542,6 +545,7 @@ enum tty_code_code {
TTYC_SMUL,
TTYC_SMULX,
TTYC_SMXX,
+ TTYC_SXL,
TTYC_SS,
TTYC_SWD,
TTYC_SYNC,
@@ -667,6 +671,14 @@ struct colour_palette {
#define GRID_LINE_EXTENDED 0x2
#define GRID_LINE_DEAD 0x4
+/* Grid string flags. */
+#define GRID_STRING_WITH_SEQUENCES 0x1
+#define GRID_STRING_ESCAPE_SEQUENCES 0x2
+#define GRID_STRING_TRIM_SPACES 0x4
+#define GRID_STRING_USED_ONLY 0x8
+#define GRID_STRING_EMPTY_CELLS 0x10
+
+/* Cell positions. */
#define CELL_INSIDE 0
#define CELL_TOPBOTTOM 1
#define CELL_LEFTRIGHT 2
@@ -681,6 +693,7 @@ struct colour_palette {
#define CELL_JOIN 11
#define CELL_OUTSIDE 12
+/* Cell borders. */
#define CELL_BORDERS " xqlkmjwvtun~"
#define SIMPLE_BORDERS " |-+++++++++."
#define PADDED_BORDERS " "
@@ -1339,6 +1352,7 @@ struct tty_term {
#define TERM_DECFRA 0x8
#define TERM_RGBCOLOURS 0x10
#define TERM_VT100LIKE 0x20
+#define TERM_SIXEL 0x40
int flags;
LIST_ENTRY(tty_term) entry;
@@ -1368,6 +1382,8 @@ struct tty {
u_int osy;
int mode;
+ int fg;
+ int bg;
u_int rlower;
u_int rupper;
@@ -1395,9 +1411,14 @@ struct tty {
#define TTY_OPENED 0x20
#define TTY_OSC52QUERY 0x40
#define TTY_BLOCK 0x80
-#define TTY_HAVEDA 0x100
+#define TTY_HAVEDA 0x100 /* Primary DA. */
#define TTY_HAVEXDA 0x200
#define TTY_SYNCING 0x400
+#define TTY_HAVEDA2 0x800 /* Secondary DA. */
+#define TTY_HAVEFG 0x1000
+#define TTY_HAVEBG 0x2000
+#define TTY_ALL_REQUEST_FLAGS \
+ (TTY_HAVEDA|TTY_HAVEDA2|TTY_HAVEXDA|TTY_HAVEFG|TTY_HAVEBG)
int flags;
struct tty_term *term;
@@ -1433,37 +1454,44 @@ struct tty_ctx {
void *ptr2;
/*
+ * Whether this command should be sent even when the pane is not
+ * visible (used for a passthrough sequence when allow-passthrough is
+ * "all").
+ */
+ int allow_invisible_panes;
+
+ /*
* Cursor and region position before the screen was updated - this is
* where the command should be applied; the values in the screen have
* already been updated.
*/
- u_int ocx;
- u_int ocy;
+ u_int ocx;
+ u_int ocy;
- u_int orupper;
- u_int orlower;
+ u_int orupper;
+ u_int orlower;
/* Target region (usually pane) offset and size. */
- u_int xoff;
- u_int yoff;
- u_int rxoff;
- u_int ryoff;
- u_int sx;
- u_int sy;
+ u_int xoff;
+ u_int yoff;
+ u_int rxoff;
+ u_int ryoff;
+ u_int sx;
+ u_int sy;
/* The background colour used for clearing (erasing). */
- u_int bg;
+ u_int bg;
/* The default colours and palette. */
- struct grid_cell defaults;
- struct colour_palette *palette;
+ struct grid_cell defaults;
+ struct colour_palette *palette;
/* Containing region (usually window) offset and size. */
- int bigger;
- u_int wox;
- u_int woy;
- u_int wsx;
- u_int wsy;
+ int bigger;
+ u_int wox;
+ u_int woy;
+ u_int wsx;
+ u_int wsy;
};
/* Saved message entry. */
@@ -1785,6 +1813,7 @@ struct client {
#define CLIENT_CONTROL_WAITEXIT 0x200000000ULL
#define CLIENT_WINDOWSIZECHANGED 0x400000000ULL
#define CLIENT_CLIPBOARDBUFFER 0x800000000ULL
+#define CLIENT_BRACKETPASTING 0x1000000000ULL
#define CLIENT_ALLREDRAWFLAGS \
(CLIENT_REDRAWWINDOW| \
CLIENT_REDRAWSTATUS| \
@@ -2147,6 +2176,7 @@ void notify_winlink(const char *, struct winlink *);
void notify_session_window(const char *, struct session *, struct window *);
void notify_window(const char *, struct window *);
void notify_pane(const char *, struct window_pane *);
+void notify_paste_buffer(const char *, int);
/* options.c */
struct options *options_create(struct options *);
@@ -2366,7 +2396,7 @@ void tty_keys_free(struct tty *);
int tty_keys_next(struct tty *);
/* arguments.c */
-void args_set(struct args *, u_char, struct args_value *);
+void args_set(struct args *, u_char, struct args_value *, int);
struct args *args_create(void);
struct args *args_parse(const struct args_parse *, struct args_value *,
u_int, char **);
@@ -2529,6 +2559,7 @@ u_int cmdq_next(struct client *);
struct cmdq_item *cmdq_running(struct client *);
void cmdq_guard(struct cmdq_item *, const char *, int);
void printflike(2, 3) cmdq_print(struct cmdq_item *, const char *, ...);
+void cmdq_print_data(struct cmdq_item *, int, struct evbuffer *);
void printflike(2, 3) cmdq_error(struct cmdq_item *, const char *, ...);
/* cmd-wait-for.c */
@@ -2583,7 +2614,9 @@ void file_print_buffer(struct client *, void *, size_t);
void printflike(2, 3) file_error(struct client *, const char *, ...);
void file_write(struct client *, const char *, int, const void *, size_t,
client_file_cb, void *);
-void file_read(struct client *, const char *, client_file_cb, void *);
+struct client_file *file_read(struct client *, const char *, client_file_cb,
+ void *);
+void file_cancel(struct client_file *);
void file_push(struct client_file *);
int file_write_left(struct client_files *);
void file_write_open(struct client_files *, struct tmuxpeer *,
@@ -2595,6 +2628,7 @@ void file_read_open(struct client_files *, struct tmuxpeer *, struct imsg *,
void file_write_ready(struct client_files *, struct imsg *);
void file_read_data(struct client_files *, struct imsg *);
void file_read_done(struct client_files *, struct imsg *);
+void file_read_cancel(struct client_files *, struct imsg *);
/* server.c */
extern struct tmuxproc *server_proc;
@@ -2738,6 +2772,7 @@ int colour_fromstring(const char *s);
int colour_256toRGB(int);
int colour_256to16(int);
int colour_byname(const char *);
+int colour_parseX11(const char *);
void colour_palette_init(struct colour_palette *);
void colour_palette_clear(struct colour_palette *);
void colour_palette_free(struct colour_palette *);
@@ -2776,7 +2811,7 @@ void grid_clear_lines(struct grid *, u_int, u_int, u_int);
void grid_move_lines(struct grid *, u_int, u_int, u_int, u_int);
void grid_move_cells(struct grid *, u_int, u_int, u_int, u_int, u_int);
char *grid_string_cells(struct grid *, u_int, u_int, u_int,
- struct grid_cell **, int, int, int, struct screen *);
+ struct grid_cell **, int, struct screen *);
void grid_duplicate_lines(struct grid *, u_int, struct grid *, u_int,
u_int);
void grid_reflow(struct grid *, u_int);
@@ -2891,7 +2926,8 @@ void screen_write_collect_add(struct screen_write_ctx *,
void screen_write_cell(struct screen_write_ctx *, const struct grid_cell *);
void screen_write_setselection(struct screen_write_ctx *, const char *,
u_char *, u_int);
-void screen_write_rawstring(struct screen_write_ctx *, u_char *, u_int);
+void screen_write_rawstring(struct screen_write_ctx *, u_char *, u_int,
+ int);
void screen_write_alternateon(struct screen_write_ctx *,
struct grid_cell *, int);
void screen_write_alternateoff(struct screen_write_ctx *,
@@ -3167,6 +3203,8 @@ void control_notify_session_renamed(struct session *);
void control_notify_session_created(struct session *);
void control_notify_session_closed(struct session *);
void control_notify_session_window_changed(struct session *);
+void control_notify_paste_buffer_changed(const char *);
+void control_notify_paste_buffer_deleted(const char *);
/* session.c */
extern struct sessions sessions;
@@ -3257,11 +3295,11 @@ void menu_add_item(struct menu *, const struct menu_item *,
struct cmdq_item *, struct client *,
struct cmd_find_state *);
void menu_free(struct menu *);
-struct menu_data *menu_prepare(struct menu *, int, struct cmdq_item *, u_int,
- u_int, struct client *, struct cmd_find_state *,
+struct menu_data *menu_prepare(struct menu *, int, int, struct cmdq_item *,
+ u_int, u_int, struct client *, struct cmd_find_state *,
menu_choice_cb, void *);
-int menu_display(struct menu *, int, struct cmdq_item *, u_int,
- u_int, struct client *, struct cmd_find_state *,
+int menu_display(struct menu *, int, int, struct cmdq_item *,
+ u_int, u_int, struct client *, struct cmd_find_state *,
menu_choice_cb, void *);
struct screen *menu_mode_cb(struct client *, void *, u_int *, u_int *);
void menu_check_cb(struct client *, void *, u_int, u_int, u_int,
diff --git a/tty-features.c b/tty-features.c
index 64f0039a..dca40fad 100644
--- a/tty-features.c
+++ b/tty-features.c
@@ -42,13 +42,13 @@
/* A named terminal feature. */
struct tty_feature {
- const char *name;
- const char **capabilities;
- int flags;
+ const char *name;
+ const char *const *capabilities;
+ int flags;
};
/* Terminal has xterm(1) title setting. */
-static const char *tty_feature_title_capabilities[] = {
+static const char *const tty_feature_title_capabilities[] = {
"tsl=\\E]0;", /* should be using TS really */
"fsl=\\a",
NULL
@@ -60,7 +60,7 @@ static const struct tty_feature tty_feature_title = {
};
/* Terminal has OSC 7 working directory. */
-static const char *tty_feature_osc7_capabilities[] = {
+static const char *const tty_feature_osc7_capabilities[] = {
"Swd=\\E]7;",
"fsl=\\a",
NULL
@@ -72,7 +72,7 @@ static const struct tty_feature tty_feature_osc7 = {
};
/* Terminal has mouse support. */
-static const char *tty_feature_mouse_capabilities[] = {
+static const char *const tty_feature_mouse_capabilities[] = {
"kmous=\\E[M",
NULL
};
@@ -83,7 +83,7 @@ static const struct tty_feature tty_feature_mouse = {
};
/* Terminal can set the clipboard with OSC 52. */
-static const char *tty_feature_clipboard_capabilities[] = {
+static const char *const tty_feature_clipboard_capabilities[] = {
"Ms=\\E]52;%p1%s;%p2%s\\a",
NULL
};
@@ -113,7 +113,7 @@ static const struct tty_feature tty_feature_hyperlinks = {
* terminals with RGB have versions that do not allow setting colours from the
* 256 palette.
*/
-static const char *tty_feature_rgb_capabilities[] = {
+static const char *const tty_feature_rgb_capabilities[] = {
"AX",
"setrgbf=\\E[38;2;%p1%d;%p2%d;%p3%dm",
"setrgbb=\\E[48;2;%p1%d;%p2%d;%p3%dm",
@@ -128,7 +128,7 @@ static const struct tty_feature tty_feature_rgb = {
};
/* Terminal supports 256 colours. */
-static const char *tty_feature_256_capabilities[] = {
+static const char *const tty_feature_256_capabilities[] = {
"AX",
"setab=\\E[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m",
"setaf=\\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m",
@@ -141,7 +141,7 @@ static const struct tty_feature tty_feature_256 = {
};
/* Terminal supports overline. */
-static const char *tty_feature_overline_capabilities[] = {
+static const char *const tty_feature_overline_capabilities[] = {
"Smol=\\E[53m",
NULL
};
@@ -152,7 +152,7 @@ static const struct tty_feature tty_feature_overline = {
};
/* Terminal supports underscore styles. */
-static const char *tty_feature_usstyle_capabilities[] = {
+static const char *const tty_feature_usstyle_capabilities[] = {
"Smulx=\\E[4::%p1%dm",
"Setulc=\\E[58::2::%p1%{65536}%/%d::%p1%{256}%/%{255}%&%d::%p1%{255}%&%d%;m",
"ol=\\E[59m",
@@ -165,7 +165,7 @@ static const struct tty_feature tty_feature_usstyle = {
};
/* Terminal supports bracketed paste. */
-static const char *tty_feature_bpaste_capabilities[] = {
+static const char *const tty_feature_bpaste_capabilities[] = {
"Enbp=\\E[?2004h",
"Dsbp=\\E[?2004l",
NULL
@@ -177,7 +177,7 @@ static const struct tty_feature tty_feature_bpaste = {
};
/* Terminal supports focus reporting. */
-static const char *tty_feature_focus_capabilities[] = {
+static const char *const tty_feature_focus_capabilities[] = {
"Enfcs=\\E[?1004h",
"Dsfcs=\\E[?1004l",
NULL
@@ -189,7 +189,7 @@ static const struct tty_feature tty_feature_focus = {
};
/* Terminal supports cursor styles. */
-static const char *tty_feature_cstyle_capabilities[] = {
+static const char *const tty_feature_cstyle_capabilities[] = {
"Ss=\\E[%p1%d q",
"Se=\\E[2 q",
NULL
@@ -201,7 +201,7 @@ static const struct tty_feature tty_feature_cstyle = {
};
/* Terminal supports cursor colours. */
-static const char *tty_feature_ccolour_capabilities[] = {
+static const char *const tty_feature_ccolour_capabilities[] = {
"Cs=\\E]12;%p1%s\\a",
"Cr=\\E]112\\a",
NULL
@@ -213,7 +213,7 @@ static const struct tty_feature tty_feature_ccolour = {
};
/* Terminal supports strikethrough. */
-static const char *tty_feature_strikethrough_capabilities[] = {
+static const char *const tty_feature_strikethrough_capabilities[] = {
"smxx=\\E[9m",
NULL
};
@@ -224,7 +224,7 @@ static const struct tty_feature tty_feature_strikethrough = {
};
/* Terminal supports synchronized updates. */
-static const char *tty_feature_sync_capabilities[] = {
+static const char *const tty_feature_sync_capabilities[] = {
"Sync=\\EP=%p1%ds\\E\\\\",
NULL
};
@@ -235,7 +235,7 @@ static const struct tty_feature tty_feature_sync = {
};
/* Terminal supports extended keys. */
-static const char *tty_feature_extkeys_capabilities[] = {
+static const char *const tty_feature_extkeys_capabilities[] = {
"Eneks=\\E[>4;1m",
"Dseks=\\E[>4m",
NULL
@@ -247,7 +247,7 @@ static const struct tty_feature tty_feature_extkeys = {
};
/* Terminal supports DECSLRM margins. */
-static const char *tty_feature_margins_capabilities[] = {
+static const char *const tty_feature_margins_capabilities[] = {
"Enmg=\\E[?69h",
"Dsmg=\\E[?69l",
"Clmg=\\E[s",
@@ -261,7 +261,7 @@ static const struct tty_feature tty_feature_margins = {
};
/* Terminal supports DECFRA rectangle fill. */
-static const char *tty_feature_rectfill_capabilities[] = {
+static const char *const tty_feature_rectfill_capabilities[] = {
"Rect",
NULL
};
@@ -272,7 +272,7 @@ static const struct tty_feature tty_feature_rectfill = {
};
/* Use builtin function keys only. */
-static const char *tty_feature_ignorefkeys_capabilities[] = {
+static const char *const tty_feature_ignorefkeys_capabilities[] = {
"kf0@",
"kf1@",
"kf2@",
@@ -345,8 +345,19 @@ static const struct tty_feature tty_feature_ignorefkeys = {
0
};
+/* Terminal has sixel capability. */
+static const char *const tty_feature_sixel_capabilities[] = {
+ "Sxl",
+ NULL
+};
+static const struct tty_feature tty_feature_sixel = {
+ "sixel",
+ tty_feature_sixel_capabilities,
+ TERM_SIXEL
+};
+
/* Available terminal features. */
-static const struct tty_feature *tty_features[] = {
+static const struct tty_feature *const tty_features[] = {
&tty_feature_256,
&tty_feature_bpaste,
&tty_feature_ccolour,
@@ -362,6 +373,7 @@ static const struct tty_feature *tty_features[] = {
&tty_feature_overline,
&tty_feature_rectfill,
&tty_feature_rgb,
+ &tty_feature_sixel,
&tty_feature_strikethrough,
&tty_feature_sync,
&tty_feature_title,
@@ -420,9 +432,9 @@ tty_get_features(int feat)
int
tty_apply_features(struct tty_term *term, int feat)
{
- const struct tty_feature *tf;
- const char **capability;
- u_int i;
+ const struct tty_feature *tf;
+ const char *const *capability;
+ u_int i;
if (feat == 0)
return (0);
@@ -453,7 +465,7 @@ tty_apply_features(struct tty_term *term, int feat)
void
tty_default_features(int *feat, const char *name, u_int version)
{
- static struct {
+ static const struct {
const char *name;
u_int version;
const char *features;
diff --git a/tty-keys.c b/tty-keys.c
index bb9ec231..5677f133 100644
--- a/tty-keys.c
+++ b/tty-keys.c
@@ -55,8 +55,11 @@ static int tty_keys_clipboard(struct tty *, const char *, size_t,
size_t *);
static int tty_keys_device_attributes(struct tty *, const char *, size_t,
size_t *);
+static int tty_keys_device_attributes2(struct tty *, const char *, size_t,
+ size_t *);
static int tty_keys_extended_device_attributes(struct tty *, const char *,
size_t, size_t *);
+static int tty_keys_colours(struct tty *, const char *, size_t, size_t *);
/* A key tree entry. */
struct tty_key {
@@ -208,6 +211,9 @@ static const struct tty_default_key_raw tty_default_raw_keys[] = {
/* Paste keys. */
{ "\033[200~", KEYC_PASTE_START },
{ "\033[201~", KEYC_PASTE_END },
+
+ /* Extended keys. */
+ { "\033[1;5Z", '\011'|KEYC_CTRL|KEYC_SHIFT },
};
/* Default xterm keys. */
@@ -681,7 +687,7 @@ tty_keys_next(struct tty *tty)
goto partial_key;
}
- /* Is this a device attributes response? */
+ /* Is this a primary device attributes response? */
switch (tty_keys_device_attributes(tty, buf, len, &size)) {
case 0: /* yes */
key = KEYC_UNKNOWN;
@@ -692,6 +698,17 @@ tty_keys_next(struct tty *tty)
goto partial_key;
}
+ /* Is this a secondary device attributes response? */
+ switch (tty_keys_device_attributes2(tty, buf, len, &size)) {
+ case 0: /* yes */
+ key = KEYC_UNKNOWN;
+ goto complete_key;
+ case -1: /* no, or not valid */
+ break;
+ case 1: /* partial */
+ goto partial_key;
+ }
+
/* Is this an extended device attributes response? */
switch (tty_keys_extended_device_attributes(tty, buf, len, &size)) {
case 0: /* yes */
@@ -703,6 +720,17 @@ tty_keys_next(struct tty *tty)
goto partial_key;
}
+ /* Is this a colours response? */
+ switch (tty_keys_colours(tty, buf, len, &size)) {
+ case 0: /* yes */
+ key = KEYC_UNKNOWN;
+ goto complete_key;
+ case -1: /* no, or not valid */
+ break;
+ case 1: /* partial */
+ goto partial_key;
+ }
+
/* Is this a mouse key press? */
switch (tty_keys_mouse(tty, buf, len, &size, &m)) {
case 0: /* yes */
@@ -1232,7 +1260,7 @@ tty_keys_clipboard(struct tty *tty, const char *buf, size_t len, size_t *size)
}
/*
- * Handle secondary device attributes input. Returns 0 for success, -1 for
+ * Handle primary device attributes input. Returns 0 for success, -1 for
* failure, 1 for partial.
*/
static int
@@ -1240,17 +1268,15 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len,
size_t *size)
{
struct client *c = tty->client;
+ int *features = &c->term_features;
u_int i, n = 0;
- char tmp[64], *endptr, p[32] = { 0 }, *cp, *next;
+ char tmp[128], *endptr, p[32] = { 0 }, *cp, *next;
*size = 0;
if (tty->flags & TTY_HAVEDA)
return (-1);
- /*
- * First three bytes are always \033[>. Some older Terminal.app
- * versions respond as for DA (\033[?) so accept and ignore that.
- */
+ /* First three bytes are always \033[?. */
if (buf[0] != '\033')
return (-1);
if (len == 1)
@@ -1259,27 +1285,97 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len,
return (-1);
if (len == 2)
return (1);
- if (buf[2] != '>' && buf[2] != '?')
+ if (buf[2] != '?')
return (-1);
if (len == 3)
return (1);
- /* Copy the rest up to a 'c'. */
- for (i = 0; i < (sizeof tmp) - 1; i++) {
+ /* Copy the rest up to a c. */
+ for (i = 0; i < (sizeof tmp); i++) {
if (3 + i == len)
return (1);
if (buf[3 + i] == 'c')
break;
tmp[i] = buf[3 + i];
}
- if (i == (sizeof tmp) - 1)
+ if (i == (sizeof tmp))
return (-1);
tmp[i] = '\0';
*size = 4 + i;
- /* Ignore DA response. */
- if (buf[2] == '?')
- return (0);
+ /* Convert all arguments to numbers. */
+ cp = tmp;
+ while ((next = strsep(&cp, ";")) != NULL) {
+ p[n] = strtoul(next, &endptr, 10);
+ if (*endptr != '\0')
+ p[n] = 0;
+ if (++n == nitems(p))
+ break;
+ }
+
+ /* Add terminal features. */
+ switch (p[0]) {
+ case 62: /* VT220 */
+ case 63: /* VT320 */
+ case 64: /* VT420 */
+ for (i = 1; i < n; i++) {
+ log_debug("%s: DA feature: %d", c->name, p[i]);
+ if (p[i] == 4)
+ tty_add_features(features, "sixel", ",");
+ }
+ break;
+ }
+ log_debug("%s: received primary DA %.*s", c->name, (int)*size, buf);
+
+ tty_update_features(tty);
+ tty->flags |= TTY_HAVEDA;
+
+ return (0);
+}
+
+/*
+ * Handle secondary device attributes input. Returns 0 for success, -1 for
+ * failure, 1 for partial.
+ */
+static int
+tty_keys_device_attributes2(struct tty *tty, const char *buf, size_t len,
+ size_t *size)
+{
+ struct client *c = tty->client;
+ int *features = &c->term_features;
+ u_int i, n = 0;
+ char tmp[128], *endptr, p[32] = { 0 }, *cp, *next;
+
+ *size = 0;
+ if (tty->flags & TTY_HAVEDA2)
+ return (-1);
+
+ /* First three bytes are always \033[>. */
+ if (buf[0] != '\033')
+ return (-1);
+ if (len == 1)
+ return (1);
+ if (buf[1] != '[')
+ return (-1);
+ if (len == 2)
+ return (1);
+ if (buf[2] != '>')
+ return (-1);
+ if (len == 3)
+ return (1);
+
+ /* Copy the rest up to a c. */
+ for (i = 0; i < (sizeof tmp); i++) {
+ if (3 + i == len)
+ return (1);
+ if (buf[3 + i] == 'c')
+ break;
+ tmp[i] = buf[3 + i];
+ }
+ if (i == (sizeof tmp))
+ return (-1);
+ tmp[i] = '\0';
+ *size = 4 + i;
/* Convert all arguments to numbers. */
cp = tmp;
@@ -1287,28 +1383,29 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len,
p[n] = strtoul(next, &endptr, 10);
if (*endptr != '\0')
p[n] = 0;
- n++;
+ if (++n == nitems(p))
+ break;
}
/* Add terminal features. */
switch (p[0]) {
case 41: /* VT420 */
- tty_add_features(&c->term_features, "margins,rectfill", ",");
+ tty_add_features(features, "margins,rectfill", ",");
break;
case 'M': /* mintty */
- tty_default_features(&c->term_features, "mintty", 0);
+ tty_default_features(features, "mintty", 0);
break;
case 'T': /* tmux */
- tty_default_features(&c->term_features, "tmux", 0);
+ tty_default_features(features, "tmux", 0);
break;
case 'U': /* rxvt-unicode */
- tty_default_features(&c->term_features, "rxvt-unicode", 0);
+ tty_default_features(features, "rxvt-unicode", 0);
break;
}
log_debug("%s: received secondary DA %.*s", c->name, (int)*size, buf);
tty_update_features(tty);
- tty->flags |= TTY_HAVEDA;
+ tty->flags |= TTY_HAVEDA2;
return (0);
}
@@ -1322,6 +1419,7 @@ tty_keys_extended_device_attributes(struct tty *tty, const char *buf,
size_t len, size_t *size)
{
struct client *c = tty->client;
+ int *features = &c->term_features;
u_int i;
char tmp[128];
@@ -1347,7 +1445,7 @@ tty_keys_extended_device_attributes(struct tty *tty, const char *buf,
if (len == 4)
return (1);
- /* Copy the rest up to a '\033\\'. */
+ /* Copy the rest up to \033\. */
for (i = 0; i < (sizeof tmp) - 1; i++) {
if (4 + i == len)
return (1);
@@ -1362,13 +1460,13 @@ tty_keys_extended_device_attributes(struct tty *tty, const char *buf,
/* Add terminal features. */
if (strncmp(tmp, "iTerm2 ", 7) == 0)
- tty_default_features(&c->term_features, "iTerm2", 0);
+ tty_default_features(features, "iTerm2", 0);
else if (strncmp(tmp, "tmux ", 5) == 0)
- tty_default_features(&c->term_features, "tmux", 0);
+ tty_default_features(features, "tmux", 0);
else if (strncmp(tmp, "XTerm(", 6) == 0)
- tty_default_features(&c->term_features, "XTerm", 0);
+ tty_default_features(features, "XTerm", 0);
else if (strncmp(tmp, "mintty ", 7) == 0)
- tty_default_features(&c->term_features, "mintty", 0);
+ tty_default_features(features, "mintty", 0);
log_debug("%s: received extended DA %.*s", c->name, (int)*size, buf);
free(c->term_type);
@@ -1379,3 +1477,73 @@ tty_keys_extended_device_attributes(struct tty *tty, const char *buf,
return (0);
}
+
+/*
+ * Handle foreground or background input. Returns 0 for success, -1 for
+ * failure, 1 for partial.
+ */
+static int
+tty_keys_colours(struct tty *tty, const char *buf, size_t len, size_t *size)
+{
+ struct client *c = tty->client;
+ u_int i;
+ char tmp[128];
+ int n;
+
+ *size = 0;
+ if ((tty->flags & TTY_HAVEFG) && (tty->flags & TTY_HAVEBG))
+ return (-1);
+
+ /* First four bytes are always \033]1 and 0 or 1 and ;. */
+ if (buf[0] != '\033')
+ return (-1);
+ if (len == 1)
+ return (1);
+ if (buf[1] != ']')
+ return (-1);
+ if (len == 2)
+ return (1);
+ if (buf[2] != '1')
+ return (-1);
+ if (len == 3)
+ return (1);
+ if (buf[3] != '0' && buf[3] != '1')
+ return (-1);
+ if (len == 4)
+ return (1);
+ if (buf[4] != ';')
+ return (-1);
+ if (len == 5)
+ return (1);
+
+ /* Copy the rest up to \033\ or \007. */
+ for (i = 0; i < (sizeof tmp) - 1; i++) {
+ if (5 + i == len)
+ return (1);
+ if (buf[5 + i - 1] == '\033' && buf[5 + i] == '\\')
+ break;
+ if (buf[5 + i] == '\007')
+ break;
+ tmp[i] = buf[5 + i];
+ }
+ if (i == (sizeof tmp) - 1)
+ return (-1);
+ if (tmp[i - 1] == '\033')
+ tmp[i - 1] = '\0';
+ else
+ tmp[i] = '\0';
+ *size = 6 + i;
+
+ n = colour_parseX11(tmp);
+ if (n != -1 && buf[3] == '0') {
+ log_debug("%s: foreground is %s", c->name, colour_tostring(n));
+ tty->fg = n;
+ tty->flags |= TTY_HAVEFG;
+ } else if (n != -1) {
+ log_debug("%s: background is %s", c->name, colour_tostring(n));
+ tty->bg = n;
+ tty->flags |= TTY_HAVEBG;
+ }
+
+ return (0);
+}
diff --git a/tty-term.c b/tty-term.c
index 4b02e2c5..4e9b7799 100644
--- a/tty-term.c
+++ b/tty-term.c
@@ -250,6 +250,7 @@ static const struct tty_term_code_entry tty_term_codes[] = {
[TTYC_KUP6] = { TTYCODE_STRING, "kUP6" },
[TTYC_KUP7] = { TTYCODE_STRING, "kUP7" },
[TTYC_MS] = { TTYCODE_STRING, "Ms" },
+ [TTYC_NOBR] = { TTYCODE_STRING, "Nobr" },
[TTYC_OL] = { TTYCODE_STRING, "ol" },
[TTYC_OP] = { TTYCODE_STRING, "op" },
[TTYC_RECT] = { TTYCODE_STRING, "Rect" },
@@ -267,6 +268,7 @@ static const struct tty_term_code_entry tty_term_codes[] = {
[TTYC_SETRGBF] = { TTYCODE_STRING, "setrgbf" },
[TTYC_SETULC] = { TTYCODE_STRING, "Setulc" },
[TTYC_SE] = { TTYCODE_STRING, "Se" },
+ [TTYC_SXL] = { TTYCODE_FLAG, "Sxl" },
[TTYC_SGR0] = { TTYCODE_STRING, "sgr0" },
[TTYC_SITM] = { TTYCODE_STRING, "sitm" },
[TTYC_SMACS] = { TTYCODE_STRING, "smacs" },
@@ -455,6 +457,9 @@ tty_term_apply_overrides(struct tty_term *term)
a = options_array_next(a);
}
+ /* Log the SIXEL flag. */
+ log_debug("SIXEL flag is %d", !!(term->flags & TERM_SIXEL));
+
/* Update the RGB flag if the terminal has RGB colours. */
if (tty_term_has(term, TTYC_SETRGBF) &&
tty_term_has(term, TTYC_SETRGBB))
diff --git a/tty.c b/tty.c
index 1f373821..c8a2a6ee 100644
--- a/tty.c
+++ b/tty.c
@@ -108,6 +108,7 @@ tty_init(struct tty *tty, struct client *c)
tty->cstyle = SCREEN_CURSOR_DEFAULT;
tty->ccolour = -1;
+ tty->fg = tty->bg = -1;
if (tcgetattr(c->fd, &tty->tio) != 0)
return (-1);
@@ -286,7 +287,6 @@ tty_open(struct tty *tty, char **cause)
evtimer_set(&tty->timer, tty_timer_callback, tty);
tty_start_tty(tty);
-
tty_keys_build(tty);
return (0);
@@ -299,9 +299,9 @@ tty_start_timer_callback(__unused int fd, __unused short events, void *data)
struct client *c = tty->client;
log_debug("%s: start timer fired", c->name);
- if ((tty->flags & (TTY_HAVEDA|TTY_HAVEXDA)) == 0)
+ if ((tty->flags & (TTY_HAVEDA|TTY_HAVEDA2|TTY_HAVEXDA)) == 0)
tty_update_features(tty);
- tty->flags |= (TTY_HAVEDA|TTY_HAVEXDA);
+ tty->flags |= TTY_ALL_REQUEST_FLAGS;
}
void
@@ -341,6 +341,8 @@ tty_start_tty(struct tty *tty)
tty_puts(tty, "\033[?1000l\033[?1002l\033[?1003l");
tty_puts(tty, "\033[?1006l\033[?1005l");
}
+ if (tty_term_has(tty->term, TTYC_ENBP))
+ tty_putcode(tty, TTYC_ENBP);
evtimer_set(&tty->start_timer, tty_start_timer_callback, tty);
evtimer_add(&tty->start_timer, &tv);
@@ -363,12 +365,18 @@ tty_send_requests(struct tty *tty)
return;
if (tty->term->flags & TERM_VT100LIKE) {
- if (~tty->flags & TTY_HAVEDA)
+ if (~tty->term->flags & TTY_HAVEDA)
+ tty_puts(tty, "\033[c");
+ if (~tty->flags & TTY_HAVEDA2)
tty_puts(tty, "\033[>c");
if (~tty->flags & TTY_HAVEXDA)
tty_puts(tty, "\033[>q");
+ if (~tty->flags & TTY_HAVEFG)
+ tty_puts(tty, "\033]10;?\033\\");
+ if (~tty->flags & TTY_HAVEBG)
+ tty_puts(tty, "\033]11;?\033\\");
} else
- tty->flags |= (TTY_HAVEDA|TTY_HAVEXDA);
+ tty->flags |= TTY_ALL_REQUEST_FLAGS;
}
void
@@ -411,8 +419,6 @@ tty_stop_tty(struct tty *tty)
else if (tty_term_has(tty->term, TTYC_SS))
tty_raw(tty, tty_term_string1(tty->term, TTYC_SS, 0));
}
- if (tty->mode & MODE_BRACKETPASTE)
- tty_raw(tty, tty_term_string(tty->term, TTYC_DSBP));
if (tty->ccolour != -1)
tty_raw(tty, tty_term_string(tty->term, TTYC_CR));
@@ -421,6 +427,8 @@ tty_stop_tty(struct tty *tty)
tty_raw(tty, "\033[?1000l\033[?1002l\033[?1003l");
tty_raw(tty, "\033[?1006l\033[?1005l");
}
+ if (tty_term_has(tty->term, TTYC_DSBP))
+ tty_raw(tty, tty_term_string(tty->term, TTYC_DSBP));
if (tty->term->flags & TERM_VT100LIKE)
tty_raw(tty, "\033[?7727l");
@@ -819,12 +827,6 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s)
else if (mode & MODE_MOUSE_STANDARD)
tty_puts(tty, "\033[?1000h");
}
- if (changed & MODE_BRACKETPASTE) {
- if (mode & MODE_BRACKETPASTE)
- tty_putcode(tty, TTYC_ENBP);
- else
- tty_putcode(tty, TTYC_DSBP);
- }
tty->mode = mode;
}
@@ -1626,13 +1628,20 @@ tty_write(void (*cmdfn)(struct tty *, const struct tty_ctx *),
if (ctx->set_client_cb == NULL)
return;
TAILQ_FOREACH(c, &clients, entry) {
- if (!tty_client_ready(c))
- continue;
- state = ctx->set_client_cb(ctx, c);
- if (state == -1)
- break;
- if (state == 0)
- continue;
+ if (ctx->allow_invisible_panes) {
+ if (c->session == NULL ||
+ c->tty.term == NULL ||
+ c->flags & CLIENT_SUSPENDED)
+ continue;
+ } else {
+ if (!tty_client_ready(c))
+ continue;
+ state = ctx->set_client_cb(ctx, c);
+ if (state == -1)
+ break;
+ if (state == 0)
+ continue;
+ }
cmdfn(&c->tty, ctx);
}
}
@@ -2683,12 +2692,14 @@ tty_check_fg(struct tty *tty, struct colour_palette *palette,
/*
* Perform substitution if this pane has a palette. If the bright
- * attribute is set, use the bright entry in the palette by changing to
- * the aixterm colour.
+ * attribute is set and Nobr is not present, use the bright entry in
+ * the palette by changing to the aixterm colour
*/
if (~gc->flags & GRID_FLAG_NOPALETTE) {
c = gc->fg;
- if (c < 8 && gc->attr & GRID_ATTR_BRIGHT)
+ if (c < 8 &&
+ gc->attr & GRID_ATTR_BRIGHT &&
+ !tty_term_has(tty->term, TTYC_NOBR))
c += 90;
if ((c = colour_palette_get(palette, c)) != -1)
gc->fg = c;
diff --git a/utf8.c b/utf8.c
index df75a769..042ddf89 100644
--- a/utf8.c
+++ b/utf8.c
@@ -229,14 +229,23 @@ utf8_width(struct utf8_data *ud, int *width)
case 0:
return (UTF8_ERROR);
}
+ log_debug("UTF-8 %.*s is %08X", (int)ud->size, ud->data, (u_int)wc);
#ifdef HAVE_UTF8PROC
*width = utf8proc_wcwidth(wc);
+ log_debug("utf8proc_wcwidth(%08X) returned %d", (u_int)wc, *width);
#else
*width = wcwidth(wc);
+ log_debug("wcwidth(%08X) returned %d", (u_int)wc, *width);
+ if (*width < 0) {
+ /*
+ * C1 control characters are nonprintable, so they are always
+ * zero width.
+ */
+ *width = (wc >= 0x80 && wc <= 0x9f) ? 0 : 1;
+ }
#endif
if (*width >= 0 && *width <= 0xff)
return (UTF8_DONE);
- log_debug("UTF-8 %.*s, wcwidth() %d", (int)ud->size, ud->data, *width);
return (UTF8_ERROR);
}
diff --git a/window-buffer.c b/window-buffer.c
index 2f52ee3f..2e0a9e84 100644
--- a/window-buffer.c
+++ b/window-buffer.c
@@ -508,6 +508,11 @@ window_buffer_key(struct window_mode_entry *wme, struct client *c,
struct window_buffer_itemdata *item;
int finished;
+ if (paste_get_top(NULL) == NULL) {
+ finished = 1;
+ goto out;
+ }
+
finished = mode_tree_key(mtd, c, &key, m, NULL, NULL);
switch (key) {
case 'e':
@@ -534,6 +539,8 @@ window_buffer_key(struct window_mode_entry *wme, struct client *c,
finished = 1;
break;
}
+
+out:
if (finished || paste_get_top(NULL) == NULL)
window_pane_reset_mode(wp);
else {
diff --git a/window-copy.c b/window-copy.c
index ab7ed5b1..ed481d70 100644
--- a/window-copy.c
+++ b/window-copy.c
@@ -1250,6 +1250,64 @@ window_copy_cmd_cursor_right(struct window_copy_cmd_state *cs)
return (WINDOW_COPY_CMD_NOTHING);
}
+/* Scroll line containing the cursor to the given position. */
+static enum window_copy_cmd_action
+window_copy_cmd_scroll_to(struct window_copy_cmd_state *cs, u_int to)
+{
+ struct window_mode_entry *wme = cs->wme;
+ struct window_copy_mode_data *data = wme->data;
+ u_int oy, delta;
+ int scroll_up; /* >0 up, <0 down */
+
+ scroll_up = data->cy - to;
+ delta = abs(scroll_up);
+ oy = screen_hsize(data->backing) - data->oy;
+
+ /*
+ * oy is the maximum scroll down amount, while data->oy is the maximum
+ * scroll up amount.
+ */
+ if (scroll_up > 0 && data->oy >= delta) {
+ window_copy_scroll_up(wme, delta);
+ data->cy -= delta;
+ } else if (scroll_up < 0 && oy >= delta) {
+ window_copy_scroll_down(wme, delta);
+ data->cy += delta;
+ }
+
+ window_copy_update_selection(wme, 0, 0);
+ return (WINDOW_COPY_CMD_REDRAW);
+}
+
+/* Scroll line containing the cursor to the bottom. */
+static enum window_copy_cmd_action
+window_copy_cmd_scroll_bottom(struct window_copy_cmd_state *cs)
+{
+ struct window_copy_mode_data *data = cs->wme->data;
+ u_int bottom;
+
+ bottom = screen_size_y(&data->screen) - 1;
+ return (window_copy_cmd_scroll_to(cs, bottom));
+}
+
+/* Scroll line containing the cursor to the middle. */
+static enum window_copy_cmd_action
+window_copy_cmd_scroll_middle(struct window_copy_cmd_state *cs)
+{
+ struct window_copy_mode_data *data = cs->wme->data;
+ u_int mid_value;
+
+ mid_value = (screen_size_y(&data->screen) - 1) / 2;
+ return (window_copy_cmd_scroll_to(cs, mid_value));
+}
+
+/* Scroll line containing the cursor to the top. */
+static enum window_copy_cmd_action
+window_copy_cmd_scroll_top(struct window_copy_cmd_state *cs)
+{
+ return (window_copy_cmd_scroll_to(cs, 0));
+}
+
static enum window_copy_cmd_action
window_copy_cmd_cursor_up(struct window_copy_cmd_state *cs)
{
@@ -2768,6 +2826,12 @@ static const struct {
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_refresh_from_pane
},
+ { .command = "scroll-bottom",
+ .minargs = 0,
+ .maxargs = 0,
+ .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
+ .f = window_copy_cmd_scroll_bottom
+ },
{ .command = "scroll-down",
.minargs = 0,
.maxargs = 0,
@@ -2780,6 +2844,18 @@ static const struct {
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_scroll_down_and_cancel
},
+ { .command = "scroll-middle",
+ .minargs = 0,
+ .maxargs = 0,
+ .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
+ .f = window_copy_cmd_scroll_middle
+ },
+ { .command = "scroll-top",
+ .minargs = 0,
+ .maxargs = 0,
+ .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
+ .f = window_copy_cmd_scroll_top
+ },
{ .command = "scroll-up",
.minargs = 0,
.maxargs = 0,
@@ -3673,6 +3749,7 @@ window_copy_search(struct window_mode_entry *wme, int direction, int regex)
if (direction &&
window_copy_search_mark_at(data, fx, fy, &at) == 0 &&
at > 0 &&
+ data->searchmark != NULL &&
data->searchmark[at] == data->searchmark[at - 1]) {
window_copy_move_after_search_mark(data, &fx, &fy,
wrapflag);
@@ -3705,6 +3782,7 @@ window_copy_search(struct window_mode_entry *wme, int direction, int regex)
&start) == 0) {
while (window_copy_search_mark_at(data, fx, fy,
&at) == 0 &&
+ data->searchmark != NULL &&
data->searchmark[at] ==
data->searchmark[start]) {
data->cx = fx;
diff --git a/window.c b/window.c
index f689f01d..60a21a58 100644
--- a/window.c
+++ b/window.c
@@ -64,6 +64,7 @@ static u_int next_active_point;
struct window_pane_input_data {
struct cmdq_item *item;
u_int wp;
+ struct client_file *file;
};
static struct window_pane *window_pane_create(struct window *, u_int, u_int,
@@ -1043,6 +1044,8 @@ window_pane_set_event(struct window_pane *wp)
wp->event = bufferevent_new(wp->fd, window_pane_read_callback,
NULL, window_pane_error_callback, wp);
+ if (wp->event == NULL)
+ fatalx("out of memory");
wp->ictx = input_init(wp, wp->event, &wp->palette);
bufferevent_enable(wp->event, EV_READ|EV_WRITE);
@@ -1541,18 +1544,18 @@ window_pane_input_callback(struct client *c, __unused const char *path,
size_t len = EVBUFFER_LENGTH(buffer);
wp = window_pane_find_by_id(cdata->wp);
- if (wp == NULL || closed || error != 0 || (c->flags & CLIENT_DEAD)) {
- if (wp == NULL)
+ if (cdata->file != NULL && (wp == NULL || c->flags & CLIENT_DEAD)) {
+ if (wp == NULL) {
+ c->retval = 1;
c->flags |= CLIENT_EXIT;
-
- evbuffer_drain(buffer, len);
+ }
+ file_cancel(cdata->file);
+ } else if (cdata->file == NULL || closed || error != 0) {
cmdq_continue(cdata->item);
-
server_client_unref(c);
free(cdata);
- return;
- }
- input_parse_buffer(wp, buf, len);
+ } else
+ input_parse_buffer(wp, buf, len);
evbuffer_drain(buffer, len);
}
@@ -1575,9 +1578,8 @@ window_pane_start_input(struct window_pane *wp, struct cmdq_item *item,
cdata = xmalloc(sizeof *cdata);
cdata->item = item;
cdata->wp = wp->id;
-
+ cdata->file = file_read(c, "-", window_pane_input_callback, cdata);
c->references++;
- file_read(c, "-", window_pane_input_callback, cdata);
return (0);
}