diff options
author | Josh Rahm <rahm@google.com> | 2023-01-27 23:04:47 +0000 |
---|---|---|
committer | Josh Rahm <rahm@google.com> | 2023-01-27 23:04:47 +0000 |
commit | e64dc03ff7b867826c7fc76d6fff210ad3382e33 (patch) | |
tree | 9cf71b02a7ee8f6d39ae1207201dc0745bdb8be5 | |
parent | fb15fd116097b98c1b839cfdc76a7d84e206e6d5 (diff) | |
parent | f5af3cfb211c12897b15e3b5a3b29c4bbb0493a8 (diff) | |
download | rtmux-e64dc03ff7b867826c7fc76d6fff210ad3382e33.tar.gz rtmux-e64dc03ff7b867826c7fc76d6fff210ad3382e33.tar.bz2 rtmux-e64dc03ff7b867826c7fc76d6fff210ad3382e33.zip |
Merge remote-tracking branch 'upstream/master' into rahm
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)); @@ -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); @@ -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); @@ -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); } - } @@ -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); + } +} @@ -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); @@ -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); } } @@ -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); @@ -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)); } @@ -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)); @@ -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); @@ -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); } @@ -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", @@ -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. */ @@ -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; /* @@ -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). */ @@ -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, "/"); @@ -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; @@ -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 @@ -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); @@ -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; @@ -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); +} @@ -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)) @@ -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; @@ -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; @@ -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); } |