diff --git a/arguments.c b/arguments.c index 3315cef2..0883dd7a 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,101 @@ 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->string[0] == '-') + argument = NULL; + } + 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 != ':') { + log_debug("%s: -%c", __func__, flag); + args_set(args, flag, NULL, 0); + continue; + } + optional_argument = (*found == ':'); + 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 +230,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 +357,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 +521,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 +529,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 +542,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 +559,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 +632,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 +641,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 diff --git a/client.c b/client.c index 7f712ffb..374c1146 100644 --- a/client.c +++ b/client.c @@ -700,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-find-window.c b/cmd-find-window.c index 6e07537c..cb9afacb 100644 --- a/cmd-find-window.c +++ b/cmd-find-window.c @@ -103,8 +103,8 @@ cmd_find_window_exec(struct cmd *self, struct cmdq_item *item) new_args = args_create(); if (args_has(args, 'Z')) - args_set(new_args, 'Z', NULL); - args_set(new_args, 'f', filter); + args_set(new_args, 'Z', NULL, 0); + args_set(new_args, 'f', filter, 0); window_pane_set_mode(wp, NULL, &window_tree_mode, target, new_args); args_free(new_args); diff --git a/cmd-queue.c b/cmd-queue.c index 9f6b4650..78b379de 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -833,7 +833,8 @@ cmdq_print_data(struct cmdq_item *item, int parse, struct evbuffer *evb) char *sanitized, *msg, *line; if (!parse) { - utf8_stravisx(&msg, data, size, VIS_OCTAL|VIS_CSTYLE|VIS_TAB); + utf8_stravisx(&msg, data, size, + VIS_OCTAL|VIS_CSTYLE|VIS_NOSLASH); log_debug("%s: %s", __func__, msg); } else { msg = EVBUFFER_DATA(evb); diff --git a/cmd-send-keys.c b/cmd-send-keys.c index e22d94a6..a7c5ee10 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; + 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/colour.c b/colour.c index a282d182..9bde646f 100644 --- a/colour.c +++ b/colour.c @@ -960,6 +960,47 @@ colour_byname(const char *name) return (-1); } +/* Parse colour from an X11 string. */ +int +colour_parseX11(const char *p) +{ + double c, m, y, k = 0; + u_int r, g, b; + size_t len = strlen(p); + int colour = -1; + char *copy; + + if ((len == 12 && sscanf(p, "rgb:%02x/%02x/%02x", &r, &g, &b) == 3) || + (len == 7 && sscanf(p, "#%02x%02x%02x", &r, &g, &b) == 3) || + sscanf(p, "%d,%d,%d", &r, &g, &b) == 3) + colour = colour_join_rgb(r, g, b); + else if ((len == 18 && + sscanf(p, "rgb:%04x/%04x/%04x", &r, &g, &b) == 3) || + (len == 13 && sscanf(p, "#%04x%04x%04x", &r, &g, &b) == 3)) + colour = colour_join_rgb(r >> 8, g >> 8, b >> 8); + else if ((sscanf(p, "cmyk:%lf/%lf/%lf/%lf", &c, &m, &y, &k) == 4 || + sscanf(p, "cmy:%lf/%lf/%lf", &c, &m, &y) == 3) && + c >= 0 && c <= 1 && m >= 0 && m <= 1 && + y >= 0 && y <= 1 && k >= 0 && k <= 1) { + colour = colour_join_rgb( + (1 - c) * (1 - k) * 255, + (1 - m) * (1 - k) * 255, + (1 - y) * (1 - k) * 255); + } else { + while (len != 0 && *p == ' ') { + p++; + len--; + } + while (len != 0 && p[len - 1] == ' ') + len--; + copy = xstrndup(p, len); + colour = colour_byname(copy); + free(copy); + } + log_debug("%s: %s = %s", __func__, p, colour_tostring(colour)); + return (colour); +} + /* Initialize palette. */ void colour_palette_init(struct colour_palette *p) @@ -1069,5 +1110,4 @@ colour_palette_from_option(struct colour_palette *p, struct options *oo) } a = options_array_next(a); } - } diff --git a/file.c b/file.c index 04a907bf..3c1096be 100644 --- a/file.c +++ b/file.c @@ -149,7 +149,8 @@ file_fire_done_cb(__unused int fd, __unused short events, void *arg) struct client_file *cf = arg; struct client *c = cf->c; - if (cf->cb != NULL && (c == NULL || (~c->flags & CLIENT_DEAD))) + if (cf->cb != NULL && + (cf->closed || c == NULL || (~c->flags & CLIENT_DEAD))) cf->cb(c, cf->path, cf->error, 1, cf->buffer, cf->data); file_free(cf); } @@ -352,7 +353,7 @@ done: } /* Read a file. */ -void +struct client_file * file_read(struct client *c, const char *path, client_file_cb cb, void *cbdata) { struct client_file *cf; @@ -420,10 +421,27 @@ skip: goto done; } free(msg); - return; + return cf; done: file_fire_done(cf); + return NULL; +} + +/* Cancel a file read. */ +void +file_cancel(struct client_file *cf) +{ + struct msg_read_cancel msg; + + log_debug("read cancel file %d", cf->stream); + + if (cf->closed) + return; + cf->closed = 1; + + msg.stream = cf->stream; + proc_send(cf->peer, MSG_READ_CANCEL, -1, &msg, sizeof msg); } /* Push event, fired if there is more writing to be done. */ @@ -757,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) @@ -794,7 +830,7 @@ file_read_data(struct client_files *files, struct imsg *imsg) return; log_debug("file %d read %zu bytes", cf->stream, bsize); - if (cf->error == 0) { + if (cf->error == 0 && !cf->closed) { if (evbuffer_add(cf->buffer, bdata, bsize) != 0) { cf->error = ENOMEM; file_fire_done(cf); diff --git a/input.c b/input.c index f162b92f..adbea179 100644 --- a/input.c +++ b/input.c @@ -1086,6 +1086,7 @@ input_reply(struct input_ctx *ictx, const char *fmt, ...) xvasprintf(&reply, fmt, ap); va_end(ap); + log_debug("%s: %s", __func__, reply); bufferevent_write(bev, reply, strlen(reply)); free(reply); } @@ -2456,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) @@ -2545,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; } @@ -2601,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) @@ -2610,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; } @@ -2654,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; } @@ -2706,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/screen-write.c b/screen-write.c index 24195708..59d289ec 100644 --- a/screen-write.c +++ b/screen-write.c @@ -1820,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. */ @@ -1853,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; } @@ -2016,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); diff --git a/tmux-protocol.h b/tmux-protocol.h index 08422291..3cf00c09 100644 --- a/tmux-protocol.h +++ b/tmux-protocol.h @@ -66,7 +66,8 @@ enum msgtype { MSG_WRITE_OPEN, MSG_WRITE, MSG_WRITE_READY, - MSG_WRITE_CLOSE + MSG_WRITE_CLOSE, + MSG_READ_CANCEL }; /* @@ -92,6 +93,10 @@ struct msg_read_done { int error; }; +struct msg_read_cancel { + int stream; +}; + struct msg_write_open { int stream; int fd; diff --git a/tmux.1 b/tmux.1 index 1e325154..b0848f67 100644 --- a/tmux.1 +++ b/tmux.1 @@ -964,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: @@ -1541,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 @@ -3123,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 @@ -3215,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 @@ -3230,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 @@ -5817,8 +5823,7 @@ until it is dismissed. .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 diff --git a/tmux.h b/tmux.h index 5a065126..93b0c2cf 100644 --- a/tmux.h +++ b/tmux.h @@ -1381,6 +1381,8 @@ struct tty { u_int osy; int mode; + int fg; + int bg; u_int rlower; u_int rupper; @@ -1411,7 +1413,11 @@ struct tty { #define TTY_HAVEDA 0x100 /* Primary DA. */ #define TTY_HAVEXDA 0x200 #define TTY_SYNCING 0x400 -#define TTY_HAVEDA2 0x800 /* Seconday DA. */ +#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; @@ -2388,7 +2394,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 **); @@ -2606,7 +2612,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 *, @@ -2618,6 +2626,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; @@ -2761,6 +2770,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 *); diff --git a/tty-keys.c b/tty-keys.c index 6fe121f0..87c7afd8 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -59,6 +59,7 @@ 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 { @@ -719,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 */ @@ -1278,7 +1290,7 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len, if (len == 3) return (1); - /* Copy the rest up to a 'c'. */ + /* Copy the rest up to a c. */ for (i = 0; i < (sizeof tmp); i++) { if (3 + i == len) return (1); @@ -1352,7 +1364,7 @@ tty_keys_device_attributes2(struct tty *tty, const char *buf, size_t len, if (len == 3) return (1); - /* Copy the rest up to a 'c'. */ + /* Copy the rest up to a c. */ for (i = 0; i < (sizeof tmp); i++) { if (3 + i == len) return (1); @@ -1433,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); @@ -1465,3 +1477,68 @@ 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\. */ + for (i = 0; i < (sizeof tmp) - 1; i++) { + if (5 + i == len) + return (1); + if (buf[5 + i - 1] == '\033' && buf[5 + i] == '\\') + break; + tmp[i] = buf[5 + i]; + } + if (i == (sizeof tmp) - 1) + return (-1); + tmp[i - 1] = '\0'; + *size = 6 + i; + + n = colour_parseX11(tmp); + if (n != -1 && buf[3] == '0') { + log_debug("%s: foreground is %s", c->name, colour_tostring(n)); + tty->fg = n; + tty->flags |= TTY_HAVEFG; + } else if (n != -1) { + log_debug("%s: background is %s", c->name, colour_tostring(n)); + tty->bg = n; + tty->flags |= TTY_HAVEBG; + } + + return (0); +} diff --git a/tty.c b/tty.c index 43a2961f..d31a2cab 100644 --- a/tty.c +++ b/tty.c @@ -108,6 +108,7 @@ tty_init(struct tty *tty, struct client *c) tty->cstyle = SCREEN_CURSOR_DEFAULT; tty->ccolour = -1; + tty->fg = tty->bg = -1; if (tcgetattr(c->fd, &tty->tio) != 0) return (-1); @@ -286,7 +287,6 @@ tty_open(struct tty *tty, char **cause) evtimer_set(&tty->timer, tty_timer_callback, tty); tty_start_tty(tty); - tty_keys_build(tty); return (0); @@ -301,7 +301,7 @@ tty_start_timer_callback(__unused int fd, __unused short events, void *data) log_debug("%s: start timer fired", c->name); if ((tty->flags & (TTY_HAVEDA|TTY_HAVEDA2|TTY_HAVEXDA)) == 0) tty_update_features(tty); - tty->flags |= (TTY_HAVEDA|TTY_HAVEDA2|TTY_HAVEXDA); + tty->flags |= TTY_ALL_REQUEST_FLAGS; } void @@ -369,8 +369,12 @@ tty_send_requests(struct tty *tty) 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_HAVEDA2|TTY_HAVEXDA); + tty->flags |= TTY_ALL_REQUEST_FLAGS; } void diff --git a/utf8.c b/utf8.c index df75a769..604be36f 100644 --- a/utf8.c +++ b/utf8.c @@ -233,10 +233,10 @@ utf8_width(struct utf8_data *ud, int *width) *width = utf8proc_wcwidth(wc); #else *width = wcwidth(wc); -#endif + log_debug("UTF-8 %.*s %#x, wcwidth() %d", (int)ud->size, ud->data, + (u_int)wc, *width); 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.c b/window.c index fe184bb4..06510436 100644 --- a/window.c +++ b/window.c @@ -64,6 +64,7 @@ static u_int next_active_point; struct window_pane_input_data { struct cmdq_item *item; u_int wp; + struct client_file *file; }; static struct window_pane *window_pane_create(struct window *, u_int, u_int, @@ -1543,18 +1544,16 @@ 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 (cdata->file != NULL && (wp == NULL || c->flags & CLIENT_DEAD)) { if (wp == NULL) 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); } @@ -1577,9 +1576,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); }