diff --git a/.github/README.md b/.github/README.md index c1c35534..5590d0b2 100644 --- a/.github/README.md +++ b/.github/README.md @@ -19,6 +19,12 @@ suitable yacc (yacc or bison) are needed. ## Installation +### Binary packages + +Some platforms provide binary packages for tmux, although these are sometimes +out of date. Examples are listed on +[this page](https://github.com/tmux/tmux/wiki/Installing). + ### From release tarball To build and install tmux from a release tarball, use: @@ -31,6 +37,9 @@ sudo make install tmux can use the utempter library to update utmp(5), if it is installed - run configure with `--enable-utempter` to enable this. +For more detailed instructions on building and installing tmux, see +[this page](https://github.com/tmux/tmux/wiki/Installing). + ### From version control To get and build the latest from version control - note that this requires @@ -72,7 +81,7 @@ And a bash(1) completion file at: https://github.com/imomaliev/tmux-bash-completion -For debugging, run tmux with `-v` or `-vv` to generate server and client log +For debugging, run tmux with `-v` or `-vv` to generate server and client log files in the current directory. ## Support diff --git a/CHANGES b/CHANGES index cf00a5b0..d385ed16 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,33 @@ -CHANGES FROM 3.1b TO 3.2 +CHANGES FROM 3.2 TO 3.3 + +* Fire focus events even when the pane is in a mode. + +* Add -O flag to display-menu to not automatically close when all mouse buttons + are released. + +* Allow fnmatch(3) wildcards in update-environment. + +* Disable nested job expansion so that the result of #() is not expanded again. + +* Use the setal capability as well as (tmux's) Setulc. + +* Add -q flag to unbind-key to hide errors. + +* Allow -N without a command to change or add a note to an existing key. + +* Add a -w flag to set- and load-buffer to send to clipboard using OSC 52. + +* Add -F to set-environment and source-file. + +* Allow colour to be spelt as color in various places. + +* Add n: modifier to get length of a format. + +* Respond to OSC colour requests if a colour is available. + +* Add a -d option to display-message to set delay. + +CHANGES FROM 3.1c TO 3.2 * Add a way for control mode clients to subscribe to a format and be notified of changes rather than having to poll. @@ -265,6 +294,11 @@ CHANGES FROM 3.1b TO 3.2 * Add number operators for formats (+, -, *, / and m), +CHANGED FROM 3.1b TO 3.1c + +* Do not write after the end of the array and overwrite the stack when + colon-separated SGR sequences contain empty arguments. + CHANGES FROM 3.1a TO 3.1b * Fix build on systems without sys/queue.h. diff --git a/alerts.c b/alerts.c index 9675934a..0f2eb179 100644 --- a/alerts.c +++ b/alerts.c @@ -200,7 +200,7 @@ alerts_check_bell(struct window *w) * not check WINLINK_BELL). */ s = wl->session; - if (s->curw != wl) { + if (s->curw != wl || s->attached == 0) { wl->flags |= WINLINK_BELL; server_status_session(s); } @@ -236,7 +236,7 @@ alerts_check_activity(struct window *w) if (wl->flags & WINLINK_ACTIVITY) continue; s = wl->session; - if (s->curw != wl) { + if (s->curw != wl || s->attached == 0) { wl->flags |= WINLINK_ACTIVITY; server_status_session(s); } @@ -272,7 +272,7 @@ alerts_check_silence(struct window *w) if (wl->flags & WINLINK_SILENCE) continue; s = wl->session; - if (s->curw != wl) { + if (s->curw != wl || s->attached == 0) { wl->flags |= WINLINK_SILENCE; server_status_session(s); } @@ -316,9 +316,9 @@ alerts_set_message(struct winlink *wl, const char *type, const char *option) if (visual == VISUAL_OFF) continue; if (c->session->curw == wl) - status_message_set(c, 1, "%s in current window", type); + status_message_set(c, -1, 1, "%s in current window", type); else { - status_message_set(c, 1, "%s in window %d", type, + status_message_set(c, -1, 1, "%s in window %d", type, wl->idx); } } diff --git a/client.c b/client.c index b652754b..757e4aa8 100644 --- a/client.c +++ b/client.c @@ -36,6 +36,7 @@ static struct tmuxproc *client_proc; static struct tmuxpeer *client_peer; static uint64_t client_flags; +static int client_suspended; static enum { CLIENT_EXIT_NONE, CLIENT_EXIT_DETACHED, @@ -59,7 +60,8 @@ static struct client_files client_files = RB_INITIALIZER(&client_files); static __dead void client_exec(const char *,const char *); static int client_get_lock(char *); -static int client_connect(struct event_base *, const char *, int); +static int client_connect(struct event_base *, const char *, + uint64_t); static void client_send_identify(const char *, const char *, int); static void client_signal(int); static void client_dispatch(struct imsg *, void *); @@ -100,7 +102,7 @@ client_get_lock(char *lockfile) /* Connect client to server. */ static int -client_connect(struct event_base *base, const char *path, int flags) +client_connect(struct event_base *base, const char *path, uint64_t flags) { struct sockaddr_un sa; size_t size; @@ -220,7 +222,7 @@ static void client_exit(void) { struct client_file *cf; - size_t left; + size_t left; int waiting = 0; RB_FOREACH (cf, client_files, &client_files) { @@ -238,7 +240,8 @@ client_exit(void) /* Client main loop. */ int -client_main(struct event_base *base, int argc, char **argv, int flags, int feat) +client_main(struct event_base *base, int argc, char **argv, uint64_t flags, + int feat) { struct cmd_parse_result *pr; struct msg_command *data; @@ -284,7 +287,7 @@ client_main(struct event_base *base, int argc, char **argv, int flags, int feat) /* Save the flags. */ client_flags = flags; - log_debug("flags are %#llx", client_flags); + log_debug("flags are %#llx", (unsigned long long)client_flags); /* Initialize the client socket and start the server. */ fd = client_connect(base, socket_path, client_flags); @@ -442,6 +445,8 @@ client_send_identify(const char *ttynam, const char *cwd, int feat) pid_t pid; proc_send(client_peer, MSG_IDENTIFY_FLAGS, -1, &flags, sizeof flags); + proc_send(client_peer, MSG_IDENTIFY_LONGFLAGS, -1, &client_flags, + sizeof client_flags); if ((s = getenv("TERM")) == NULL) s = ""; @@ -761,6 +766,7 @@ client_signal(int sig) struct sigaction sigact; int status; + log_debug("%s: %s", __func__, strsignal(sig)); if (sig == SIGCHLD) waitpid(WAIT_ANY, &status, WNOHANG); else if (!client_attached) { @@ -774,7 +780,8 @@ client_signal(int sig) proc_send(client_peer, MSG_EXITING, -1, NULL, 0); break; case SIGTERM: - client_exitreason = CLIENT_EXIT_TERMINATED; + if (!client_suspended) + client_exitreason = CLIENT_EXIT_TERMINATED; client_exitval = 1; proc_send(client_peer, MSG_EXITING, -1, NULL, 0); break; @@ -789,6 +796,7 @@ client_signal(int sig) if (sigaction(SIGTSTP, &sigact, NULL) != 0) fatal("sigaction failed"); proc_send(client_peer, MSG_WAKEUP, -1, NULL, 0); + client_suspended = 0; break; } } @@ -891,7 +899,8 @@ client_dispatch_wait(struct imsg *imsg) fatalx("bad MSG_FLAGS string"); memcpy(&client_flags, data, sizeof client_flags); - log_debug("new flags are %#llx", client_flags); + log_debug("new flags are %#llx", + (unsigned long long)client_flags); break; case MSG_SHELL: if (datalen == 0 || data[datalen - 1] != '\0') @@ -944,7 +953,8 @@ client_dispatch_attached(struct imsg *imsg) fatalx("bad MSG_FLAGS string"); memcpy(&client_flags, data, sizeof client_flags); - log_debug("new flags are %#llx", client_flags); + log_debug("new flags are %#llx", + (unsigned long long)client_flags); break; case MSG_DETACH: case MSG_DETACHKILL: @@ -999,6 +1009,7 @@ client_dispatch_attached(struct imsg *imsg) sigact.sa_handler = SIG_DFL; if (sigaction(SIGTSTP, &sigact, NULL) != 0) fatal("sigaction failed"); + client_suspended = 1; kill(getpid(), SIGTSTP); break; case MSG_LOCK: diff --git a/cmd-attach-session.c b/cmd-attach-session.c index 38d9c024..6a7ebba7 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -59,7 +59,7 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag, struct session *s; struct winlink *wl; struct window_pane *wp; - char *cause; + char *cwd, *cause; enum msgtype msgtype; if (RB_EMPTY(&sessions)) { @@ -99,8 +99,9 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag, } if (cflag != NULL) { + cwd = format_single(item, cflag, c, s, wl, wp); free((void *)s->cwd); - s->cwd = format_single(item, cflag, c, s, wl, wp); + s->cwd = cwd; } if (fflag) server_client_set_flags(c, fflag); diff --git a/cmd-bind-key.c b/cmd-bind-key.c index dcb56c06..b4e4167c 100644 --- a/cmd-bind-key.c +++ b/cmd-bind-key.c @@ -33,9 +33,9 @@ const struct cmd_entry cmd_bind_key_entry = { .name = "bind-key", .alias = "bind", - .args = { "nrN:T:", 2, -1 }, + .args = { "nrN:T:", 1, -1 }, .usage = "[-nr] [-T key-table] [-N note] key " - "command [arguments]", + "[command [arguments]]", .flags = CMD_AFTERHOOK, .exec = cmd_bind_key_exec @@ -46,7 +46,7 @@ cmd_bind_key_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); key_code key; - const char *tablename, *note; + const char *tablename, *note = args_get(args, 'N'); struct cmd_parse_result *pr; char **argv = args->argv; int argc = args->argc, repeat; @@ -65,22 +65,24 @@ cmd_bind_key_exec(struct cmd *self, struct cmdq_item *item) tablename = "prefix"; repeat = args_has(args, 'r'); - if (argc == 2) - pr = cmd_parse_from_string(argv[1], NULL); - else - pr = cmd_parse_from_arguments(argc - 1, argv + 1, NULL); - switch (pr->status) { - case CMD_PARSE_EMPTY: - cmdq_error(item, "empty command"); - return (CMD_RETURN_ERROR); - case CMD_PARSE_ERROR: - cmdq_error(item, "%s", pr->error); - free(pr->error); - return (CMD_RETURN_ERROR); - case CMD_PARSE_SUCCESS: - break; - } - note = args_get(args, 'N'); - key_bindings_add(tablename, key, note, repeat, pr->cmdlist); + if (argc != 1) { + if (argc == 2) + pr = cmd_parse_from_string(argv[1], NULL); + else + pr = cmd_parse_from_arguments(argc - 1, argv + 1, NULL); + switch (pr->status) { + case CMD_PARSE_EMPTY: + cmdq_error(item, "empty command"); + return (CMD_RETURN_ERROR); + case CMD_PARSE_ERROR: + cmdq_error(item, "%s", pr->error); + free(pr->error); + return (CMD_RETURN_ERROR); + case CMD_PARSE_SUCCESS: + break; + } + key_bindings_add(tablename, key, note, repeat, pr->cmdlist); + } else + key_bindings_add(tablename, key, note, repeat, NULL); return (CMD_RETURN_NORMAL); } diff --git a/cmd-display-menu.c b/cmd-display-menu.c index ae322444..205d1243 100644 --- a/cmd-display-menu.c +++ b/cmd-display-menu.c @@ -36,8 +36,8 @@ const struct cmd_entry cmd_display_menu_entry = { .name = "display-menu", .alias = "menu", - .args = { "c:t:T:x:y:", 1, -1 }, - .usage = "[-c target-client] " CMD_TARGET_PANE_USAGE " [-T title] " + .args = { "c:t:OT:x:y:", 1, -1 }, + .usage = "[-O] [-c target-client] " CMD_TARGET_PANE_USAGE " [-T title] " "[-x position] [-y position] name key command ...", .target = { 't', CMD_FIND_PANE, 0 }, @@ -229,6 +229,8 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item) cmd_display_menu_get_position(tc, item, args, &px, &py, menu->width + 4, menu->count + 2); + if (args_has(args, 'O')) + flags |= MENU_STAYOPEN; if (!event->m.valid) flags |= MENU_NOMOUSE; if (menu_display(menu, flags, item, px, py, tc, target, NULL, diff --git a/cmd-display-message.c b/cmd-display-message.c index 634f0a93..fc9c4851 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:Ipt:F:v", 0, 1 }, - .usage = "[-aIpv] [-c target-client] [-F format] " + .args = { "acd:Ipt:F:v", 0, 1 }, + .usage = "[-aIpv] [-c target-client] [-d delay] [-F format] " CMD_TARGET_PANE_USAGE " [message]", .target = { 't', CMD_FIND_PANE, 0 }, @@ -68,6 +68,7 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item) struct window_pane *wp = target->wp; const char *template; char *msg, *cause; + int delay = -1; struct format_tree *ft; int flags; @@ -85,6 +86,15 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_ERROR); } + if (args_has(args, 'd')) { + delay = args_strtonum(args, 'd', 0, UINT_MAX, &cause); + if (cause != NULL) { + cmdq_error(item, "delay %s", cause); + free(cause); + return (CMD_RETURN_ERROR); + } + } + template = args_get(args, 'F'); if (args->argc != 0) template = args->argv[0]; @@ -117,7 +127,7 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item) if (args_has(args, 'p')) cmdq_print(item, "%s", msg); else if (tc != NULL) - status_message_set(tc, 0, "%s", msg); + status_message_set(tc, delay, 0, "%s", msg); free(msg); format_free(ft); diff --git a/cmd-display-panes.c b/cmd-display-panes.c index a13a06ae..352b2e4d 100644 --- a/cmd-display-panes.c +++ b/cmd-display-panes.c @@ -55,11 +55,11 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx, struct session *s = c->session; struct options *oo = s->options; struct window *w = wp->window; - struct grid_cell gc; - u_int idx, px, py, i, j, xoff, yoff, sx, sy; + struct grid_cell fgc, bgc; + u_int pane, idx, px, py, i, j, xoff, yoff, sx, sy; int colour, active_colour; - char buf[16], *ptr; - size_t len; + char buf[16], lbuf[16], rbuf[16], *ptr; + size_t len, llen, rlen; if (wp->xoff + wp->sx <= ctx->ox || wp->xoff >= ctx->ox + ctx->sx || @@ -109,29 +109,50 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx, px = sx / 2; py = sy / 2; - if (window_pane_index(wp, &idx) != 0) + if (window_pane_index(wp, &pane) != 0) fatalx("index not found"); - len = xsnprintf(buf, sizeof buf, "%u", idx); + len = xsnprintf(buf, sizeof buf, "%u", pane); if (sx < len) return; colour = options_get_number(oo, "display-panes-colour"); active_colour = options_get_number(oo, "display-panes-active-colour"); + memcpy(&fgc, &grid_default_cell, sizeof fgc); + memcpy(&bgc, &grid_default_cell, sizeof bgc); + if (w->active == wp) { + fgc.fg = active_colour; + bgc.bg = active_colour; + } else { + fgc.fg = colour; + bgc.bg = colour; + } + + rlen = xsnprintf(rbuf, sizeof rbuf, "%ux%u", wp->sx, wp->sy); + if (pane > 9 && pane < 35) + llen = xsnprintf(lbuf, sizeof lbuf, "%c", 'a' + (pane - 10)); + else + llen = 0; + if (sx < len * 6 || sy < 5) { - tty_cursor(tty, xoff + px - len / 2, yoff + py); - goto draw_text; + tty_attributes(tty, &fgc, &grid_default_cell, NULL); + if (sx >= len + llen + 1) { + len += llen + 1; + tty_cursor(tty, xoff + px - len / 2, yoff + py); + tty_putn(tty, buf, len, len); + tty_putn(tty, " ", 1, 1); + tty_putn(tty, lbuf, llen, llen); + } else { + tty_cursor(tty, xoff + px - len / 2, yoff + py); + tty_putn(tty, buf, len, len); + } + goto out; } px -= len * 3; py -= 2; - memcpy(&gc, &grid_default_cell, sizeof gc); - if (w->active == wp) - gc.bg = active_colour; - else - gc.bg = colour; - tty_attributes(tty, &gc, &grid_default_cell, NULL); + tty_attributes(tty, &bgc, &grid_default_cell, NULL); for (ptr = buf; *ptr != '\0'; ptr++) { if (*ptr < '0' || *ptr > '9') continue; @@ -147,20 +168,20 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx, px += 6; } - len = xsnprintf(buf, sizeof buf, "%ux%u", wp->sx, wp->sy); - if (sx < len || sy < 6) - return; - tty_cursor(tty, xoff + sx - len, yoff); - -draw_text: - memcpy(&gc, &grid_default_cell, sizeof gc); - if (w->active == wp) - gc.fg = active_colour; - else - gc.fg = colour; - tty_attributes(tty, &gc, &grid_default_cell, NULL); - tty_puts(tty, buf); + if (sy <= 6) + goto out; + tty_attributes(tty, &fgc, &grid_default_cell, NULL); + if (rlen != 0 && sx >= rlen) { + tty_cursor(tty, xoff + sx - rlen, yoff); + tty_putn(tty, rbuf, rlen, rlen); + } + if (llen != 0) { + tty_cursor(tty, xoff + sx / 2 + len * 3 - llen - 1, + yoff + py + 5); + tty_putn(tty, lbuf, llen, llen); + } +out: tty_cursor(tty, 0, 0); } @@ -197,11 +218,21 @@ cmd_display_panes_key(struct client *c, struct key_event *event) struct window *w = c->session->curw->window; struct window_pane *wp; enum cmd_parse_status status; + u_int index; + key_code key; - if (event->key < '0' || event->key > '9') + if (event->key >= '0' && event->key <= '9') + index = event->key - '0'; + else if ((event->key & KEYC_MASK_MODIFIERS) == 0) { + key = (event->key & KEYC_MASK_KEY); + if (key >= 'a' && key <= 'z') + index = 10 + (key - 'a'); + else + return (-1); + } else return (-1); - wp = window_pane_at_index(w, event->key - '0'); + wp = window_pane_at_index(w, index); if (wp == NULL) return (1); window_unzoom(w); diff --git a/cmd-list-keys.c b/cmd-list-keys.c index b3bdbd12..dd82e57e 100644 --- a/cmd-list-keys.c +++ b/cmd-list-keys.c @@ -114,7 +114,7 @@ cmd_list_keys_print_notes(struct cmdq_item *item, struct args *args, note = xstrdup(bd->note); tmp = utf8_padcstr(key, keywidth + 1); if (args_has(args, '1') && tc != NULL) - status_message_set(tc, 1, "%s%s%s", prefix, tmp, note); + status_message_set(tc, -1, 1, "%s%s%s", prefix, tmp, note); else cmdq_print(item, "%s%s%s", prefix, tmp, note); free(tmp); diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c index 49e834d6..bca9a860 100644 --- a/cmd-load-buffer.c +++ b/cmd-load-buffer.c @@ -37,14 +37,15 @@ const struct cmd_entry cmd_load_buffer_entry = { .name = "load-buffer", .alias = "loadb", - .args = { "b:", 1, 1 }, - .usage = CMD_BUFFER_USAGE " path", + .args = { "b:t:w", 1, 1 }, + .usage = CMD_BUFFER_USAGE " " CMD_TARGET_CLIENT_USAGE " path", - .flags = CMD_AFTERHOOK, + .flags = CMD_AFTERHOOK|CMD_CLIENT_TFLAG|CMD_CLIENT_CANFAIL, .exec = cmd_load_buffer_exec }; struct cmd_load_buffer_data { + struct client *client; struct cmdq_item *item; char *name; }; @@ -54,6 +55,7 @@ cmd_load_buffer_done(__unused struct client *c, const char *path, int error, int closed, struct evbuffer *buffer, void *data) { struct cmd_load_buffer_data *cdata = data; + struct client *tc = cdata->client; struct cmdq_item *item = cdata->item; void *bdata = EVBUFFER_DATA(buffer); size_t bsize = EVBUFFER_LENGTH(buffer); @@ -72,7 +74,12 @@ cmd_load_buffer_done(__unused struct client *c, const char *path, int error, cmdq_error(item, "%s", cause); free(cause); free(copy); - } + } else if (tc != NULL && + tc->session != NULL && + (~tc->flags & CLIENT_DEAD)) + tty_set_selection(&tc->tty, copy, bsize); + if (tc != NULL) + server_client_unref(tc); } cmdq_continue(item); @@ -84,16 +91,19 @@ static enum cmd_retval cmd_load_buffer_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); + struct client *tc = cmdq_get_target_client(item); struct cmd_load_buffer_data *cdata; const char *bufname = args_get(args, 'b'); char *path; - cdata = xmalloc(sizeof *cdata); + cdata = xcalloc(1, sizeof *cdata); cdata->item = item; if (bufname != NULL) cdata->name = xstrdup(bufname); - else - cdata->name = NULL; + if (args_has(args, 'w') && tc != NULL) { + cdata->client = tc; + cdata->client->references++; + } path = format_single_from_target(item, args->argv[0]); file_read(cmdq_get_client(item), path, cmd_load_buffer_done, cdata); diff --git a/cmd-queue.c b/cmd-queue.c index 693f7d90..36f1c9be 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -858,7 +858,7 @@ cmdq_error(struct cmdq_item *item, const char *fmt, ...) c->retval = 1; } else { *msg = toupper((u_char) *msg); - status_message_set(c, 1, "%s", msg); + status_message_set(c, -1, 1, "%s", msg); } free(msg); diff --git a/cmd-select-pane.c b/cmd-select-pane.c index b0c78d74..30529722 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -198,6 +198,7 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) if (args_has(args, 'T')) { title = format_single_from_target(item, args_get(args, 'T')); if (screen_set_title(&wp->base, title)) { + notify_pane("pane-title-changed", wp); server_redraw_window_borders(wp->window); server_status_window(wp->window); } diff --git a/cmd-set-buffer.c b/cmd-set-buffer.c index 0f3fffce..94d8cd52 100644 --- a/cmd-set-buffer.c +++ b/cmd-set-buffer.c @@ -33,10 +33,11 @@ const struct cmd_entry cmd_set_buffer_entry = { .name = "set-buffer", .alias = "setb", - .args = { "ab:n:", 0, 1 }, - .usage = "[-a] " CMD_BUFFER_USAGE " [-n new-buffer-name] data", + .args = { "ab:t:n:w", 0, 1 }, + .usage = "[-aw] " CMD_BUFFER_USAGE " [-n new-buffer-name] " + CMD_TARGET_CLIENT_USAGE " data", - .flags = CMD_AFTERHOOK, + .flags = CMD_AFTERHOOK|CMD_CLIENT_TFLAG|CMD_CLIENT_CANFAIL, .exec = cmd_set_buffer_exec }; @@ -55,6 +56,7 @@ static enum cmd_retval cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); + struct client *tc = cmdq_get_target_client(item); struct paste_buffer *pb; char *bufdata, *cause; const char *bufname, *olddata; @@ -118,6 +120,8 @@ cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item) free(cause); return (CMD_RETURN_ERROR); } + if (args_has(args, 'w') && tc != NULL) + tty_set_selection(&tc->tty, bufdata, bufsize); return (CMD_RETURN_NORMAL); } diff --git a/cmd-set-environment.c b/cmd-set-environment.c index 3c43b635..f142df53 100644 --- a/cmd-set-environment.c +++ b/cmd-set-environment.c @@ -34,8 +34,8 @@ const struct cmd_entry cmd_set_environment_entry = { .name = "set-environment", .alias = "setenv", - .args = { "hgrt:u", 1, 2 }, - .usage = "[-hgru] " CMD_TARGET_SESSION_USAGE " name [value]", + .args = { "Fhgrt:u", 1, 2 }, + .usage = "[-Fhgru] " CMD_TARGET_SESSION_USAGE " name [value]", .target = { 't', CMD_FIND_SESSION, CMD_FIND_CANFAIL }, @@ -50,6 +50,8 @@ cmd_set_environment_exec(struct cmd *self, struct cmdq_item *item) struct cmd_find_state *target = cmdq_get_target(item); struct environ *env; const char *name, *value, *tflag; + char *expand = NULL; + enum cmd_retval retval = CMD_RETURN_NORMAL; name = args->argv[0]; if (*name == '\0') { @@ -63,6 +65,8 @@ cmd_set_environment_exec(struct cmd *self, struct cmdq_item *item) if (args->argc < 2) value = NULL; + else if (args_has(args, 'F')) + value = expand = format_single_from_target(item, args->argv[1]); else value = args->argv[1]; @@ -75,7 +79,8 @@ cmd_set_environment_exec(struct cmd *self, struct cmdq_item *item) cmdq_error(item, "no such session: %s", tflag); else cmdq_error(item, "no current session"); - return (CMD_RETURN_ERROR); + retval = CMD_RETURN_ERROR; + goto out; } env = target->s->environ; } @@ -83,25 +88,31 @@ cmd_set_environment_exec(struct cmd *self, struct cmdq_item *item) if (args_has(args, 'u')) { if (value != NULL) { cmdq_error(item, "can't specify a value with -u"); - return (CMD_RETURN_ERROR); + retval = CMD_RETURN_ERROR; + goto out; } environ_unset(env, name); } else if (args_has(args, 'r')) { if (value != NULL) { cmdq_error(item, "can't specify a value with -r"); - return (CMD_RETURN_ERROR); + retval = CMD_RETURN_ERROR; + goto out; } environ_clear(env, name); } else { if (value == NULL) { cmdq_error(item, "no value specified"); - return (CMD_RETURN_ERROR); + retval = CMD_RETURN_ERROR; + goto out; } + if (args_has(args, 'h')) environ_set(env, name, ENVIRON_HIDDEN, "%s", value); else environ_set(env, name, 0, "%s", value); } - return (CMD_RETURN_NORMAL); +out: + free(expand); + return (retval); } diff --git a/cmd-source-file.c b/cmd-source-file.c index f5a0ca4b..1da59193 100644 --- a/cmd-source-file.c +++ b/cmd-source-file.c @@ -35,8 +35,8 @@ const struct cmd_entry cmd_source_file_entry = { .name = "source-file", .alias = "source", - .args = { "nqv", 1, -1 }, - .usage = "[-nqv] path ...", + .args = { "Fnqv", 1, -1 }, + .usage = "[-Fnqv] path ...", .flags = 0, .exec = cmd_source_file_exec @@ -126,7 +126,7 @@ cmd_source_file_exec(struct cmd *self, struct cmdq_item *item) struct cmd_source_file_data *cdata; struct client *c = cmdq_get_client(item); enum cmd_retval retval = CMD_RETURN_NORMAL; - char *pattern, *cwd; + char *pattern, *cwd, *expand = NULL; const char *path, *error; glob_t g; int i, result; @@ -145,7 +145,12 @@ cmd_source_file_exec(struct cmd *self, struct cmdq_item *item) utf8_stravis(&cwd, server_client_get_cwd(c, NULL), VIS_GLOB); for (i = 0; i < args->argc; i++) { - path = args->argv[i]; + if (args_has(args, 'F')) { + free(expand); + expand = format_single_from_target(item, args->argv[i]); + path = expand; + } else + path = args->argv[i]; if (strcmp(path, "-") == 0) { cmd_source_file_add(cdata, "-"); continue; @@ -172,6 +177,7 @@ cmd_source_file_exec(struct cmd *self, struct cmdq_item *item) free(pattern); continue; } + free(expand); free(pattern); for (j = 0; j < g.gl_pathc; j++) diff --git a/cmd-unbind-key.c b/cmd-unbind-key.c index 4b9f39a6..a29831af 100644 --- a/cmd-unbind-key.c +++ b/cmd-unbind-key.c @@ -32,8 +32,8 @@ const struct cmd_entry cmd_unbind_key_entry = { .name = "unbind-key", .alias = "unbind", - .args = { "anT:", 0, 1 }, - .usage = "[-an] [-T key-table] key", + .args = { "anqT:", 0, 1 }, + .usage = "[-anq] [-T key-table] key", .flags = CMD_AFTERHOOK, .exec = cmd_unbind_key_exec @@ -45,44 +45,54 @@ cmd_unbind_key_exec(struct cmd *self, struct cmdq_item *item) struct args *args = cmd_get_args(self); key_code key; const char *tablename; + int quiet = args_has(args, 'q'); - if (!args_has(args, 'a')) { - if (args->argc != 1) { - cmdq_error(item, "missing key"); - return (CMD_RETURN_ERROR); - } - key = key_string_lookup_string(args->argv[0]); - if (key == KEYC_NONE || key == KEYC_UNKNOWN) { - cmdq_error(item, "unknown key: %s", args->argv[0]); - return (CMD_RETURN_ERROR); - } - } else { + if (args_has(args, 'a')) { if (args->argc != 0) { - cmdq_error(item, "key given with -a"); + if (!quiet) + cmdq_error(item, "key given with -a"); return (CMD_RETURN_ERROR); } - key = KEYC_UNKNOWN; - } - if (key == KEYC_UNKNOWN) { tablename = args_get(args, 'T'); if (tablename == NULL) { - key_bindings_remove_table("root"); - key_bindings_remove_table("prefix"); - return (CMD_RETURN_NORMAL); + if (args_has(args, 'n')) + tablename = "root"; + else + tablename = "prefix"; } if (key_bindings_get_table(tablename, 0) == NULL) { - cmdq_error(item, "table %s doesn't exist", tablename); + if (!quiet) { + cmdq_error(item, "table %s doesn't exist" , + tablename); + } return (CMD_RETURN_ERROR); } + key_bindings_remove_table(tablename); return (CMD_RETURN_NORMAL); } + if (args->argc != 1) { + if (!quiet) + cmdq_error(item, "missing key"); + return (CMD_RETURN_ERROR); + } + + key = key_string_lookup_string(args->argv[0]); + if (key == KEYC_NONE || key == KEYC_UNKNOWN) { + if (!quiet) + cmdq_error(item, "unknown key: %s", args->argv[0]); + return (CMD_RETURN_ERROR); + } + if (args_has(args, 'T')) { tablename = args_get(args, 'T'); if (key_bindings_get_table(tablename, 0) == NULL) { - cmdq_error(item, "table %s doesn't exist", tablename); + if (!quiet) { + cmdq_error(item, "table %s doesn't exist" , + tablename); + } return (CMD_RETURN_ERROR); } } else if (args_has(args, 'n')) diff --git a/colour.c b/colour.c index c7972878..ee4b95db 100644 --- a/colour.c +++ b/colour.c @@ -189,6 +189,12 @@ colour_fromstring(const char *s) return (-1); return (n | COLOUR_FLAG_256); } + if (strncasecmp(s, "color", (sizeof "color") - 1) == 0) { + n = strtonum(s + (sizeof "color") - 1, 0, 255, &errstr); + if (errstr != NULL) + return (-1); + return (n | COLOUR_FLAG_256); + } if (strcasecmp(s, "default") == 0) return (8); diff --git a/compat.h b/compat.h index 5b23b178..b213336f 100644 --- a/compat.h +++ b/compat.h @@ -27,6 +27,10 @@ #include #include +#ifdef HAVE_MALLOC_TRIM +#include +#endif + #ifdef HAVE_UTF8PROC #include #endif @@ -35,6 +39,10 @@ #define __attribute__(a) #endif +#ifdef BROKEN___DEAD +#undef __dead +#endif + #ifndef __unused #define __unused __attribute__ ((__unused__)) #endif diff --git a/compat/getdtablesize.c b/compat/getdtablesize.c new file mode 100644 index 00000000..fa82577f --- /dev/null +++ b/compat/getdtablesize.c @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2020 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#include "compat.h" + +#ifdef HAVE_SYSCONF +int +getdtablesize(void) +{ + return (sysconf(_SC_OPEN_MAX)); +} +#endif diff --git a/compat/imsg-buffer.c b/compat/imsg-buffer.c index 814591f4..67d4c705 100644 --- a/compat/imsg-buffer.c +++ b/compat/imsg-buffer.c @@ -1,4 +1,4 @@ -/* $OpenBSD: imsg-buffer.c,v 1.11 2017/12/14 09:27:44 kettenis Exp $ */ +/* $OpenBSD: imsg-buffer.c,v 1.12 2019/01/20 02:50:03 bcook Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer @@ -70,7 +70,7 @@ ibuf_dynamic(size_t len, size_t max) static int ibuf_realloc(struct ibuf *buf, size_t len) { - u_char *b; + unsigned char *b; /* on static buffers max is eq size and so the following fails */ if (buf->wpos + len > buf->max) { diff --git a/compat/imsg.h b/compat/imsg.h index 8bf94147..5b092cfc 100644 --- a/compat/imsg.h +++ b/compat/imsg.h @@ -1,4 +1,4 @@ -/* $OpenBSD: imsg.h,v 1.4 2017/03/24 09:34:12 nicm Exp $ */ +/* $OpenBSD: imsg.h,v 1.5 2019/01/20 02:50:03 bcook Exp $ */ /* * Copyright (c) 2006, 2007 Pierre-Yves Ritschard @@ -21,13 +21,15 @@ #ifndef _IMSG_H_ #define _IMSG_H_ +#include + #define IBUF_READ_SIZE 65535 #define IMSG_HEADER_SIZE sizeof(struct imsg_hdr) #define MAX_IMSGSIZE 16384 struct ibuf { TAILQ_ENTRY(ibuf) entry; - u_char *buf; + unsigned char *buf; size_t size; size_t max; size_t wpos; @@ -42,8 +44,8 @@ struct msgbuf { }; struct ibuf_read { - u_char buf[IBUF_READ_SIZE]; - u_char *rptr; + unsigned char buf[IBUF_READ_SIZE]; + unsigned char *rptr; size_t wpos; }; diff --git a/configure.ac b/configure.ac index 22a48c8d..93246fc8 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ # configure.ac -AC_INIT([tmux], 3.2-rc2) +AC_INIT([tmux], next-3.3) AC_PREREQ([2.60]) AC_CONFIG_AUX_DIR(etc) @@ -113,6 +113,7 @@ AC_REPLACE_FUNCS([ \ fgetln \ freezero \ getdtablecount \ + getdtablesize \ getline \ getprogname \ memmem \ @@ -328,6 +329,31 @@ AC_SEARCH_LIBS(inet_ntoa, nsl) AC_SEARCH_LIBS(socket, socket) AC_CHECK_LIB(xnet, socket) +# Check if using glibc and have malloc_trim(3). The glibc free(3) is pretty bad +# about returning memory to the kernel unless the application tells it when to +# with malloc_trim(3). +AC_MSG_CHECKING(if free doesn't work very well) +AC_LINK_IFELSE([AC_LANG_SOURCE( + [ + #include + #ifdef __GLIBC__ + #include + int main(void) { + malloc_trim (0); + exit(0); + } + #else + no + #endif + ])], + found_malloc_trim=yes, + found_malloc_trim=no +) +AC_MSG_RESULT($found_malloc_trim) +if test "x$found_malloc_trim" = xyes; then + AC_DEFINE(HAVE_MALLOC_TRIM) +fi + # Check for CMSG_DATA. On some platforms like HP-UX this requires UNIX 95 # (_XOPEN_SOURCE and _XOPEN_SOURCE_EXTENDED) (see xopen_networking(7)). On # others, UNIX 03 (_XOPEN_SOURCE 600, see standards(7) on Solaris). @@ -570,6 +596,12 @@ case "$host_os" in AC_MSG_RESULT(darwin) PLATFORM=darwin # + # OS X uses __dead2 instead of __dead, like FreeBSD. But it + # defines __dead away so it needs to be removed before we can + # replace it. + # + AC_DEFINE(BROKEN___DEAD) + # # OS X CMSG_FIRSTHDR is broken, so redefine it with a working # one. daemon works but has some stupid side effects, so use # our internal version which has a workaround. diff --git a/control.c b/control.c index c52f2020..e86429cf 100644 --- a/control.c +++ b/control.c @@ -688,8 +688,8 @@ control_write_pending(struct client *c, struct control_pane *cp, size_t limit) else age = 0; log_debug("%s: %s: output block %zu (age %llu) for %%%u " - "(used %zu/%zu)", __func__, c->name, cb->size, age, - cp->pane, used, limit); + "(used %zu/%zu)", __func__, c->name, cb->size, + (unsigned long long)age, cp->pane, used, limit); size = cb->size; if (size > limit - used) diff --git a/environ.c b/environ.c index 940109b0..74d672e0 100644 --- a/environ.c +++ b/environ.c @@ -18,6 +18,7 @@ #include +#include #include #include #include @@ -191,7 +192,11 @@ 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); - if ((envent = environ_find(src, ov->string)) == NULL) + RB_FOREACH(envent, environ, src) { + if (fnmatch(ov->string, envent->name, 0) == 0) + break; + } + if (envent == NULL) environ_clear(dst, ov->string); else environ_set(dst, envent->name, 0, "%s", envent->value); diff --git a/format.c b/format.c index ced7b515..dd3af400 100644 --- a/format.c +++ b/format.c @@ -38,15 +38,18 @@ * string. */ -static char *format_job_get(struct format_tree *, const char *); -static void format_job_timer(int, short, void *); +struct format_expand_state; -static int format_replace(struct format_tree *, const char *, size_t, - char **, size_t *, size_t *); +static char *format_job_get(struct format_expand_state *, const char *); +static void format_job_timer(int, short, void *); +static char *format_expand1(struct format_expand_state *, const char *); +static int format_replace(struct format_expand_state *, const char *, + size_t, char **, size_t *, size_t *); static void format_defaults_session(struct format_tree *, struct session *); static void format_defaults_client(struct format_tree *, struct client *); -static void format_defaults_winlink(struct format_tree *, struct winlink *); +static void format_defaults_winlink(struct format_tree *, + struct winlink *); /* Entry in format job tree. */ struct format_job { @@ -94,15 +97,20 @@ format_job_cmp(struct format_job *fj1, struct format_job *fj2) #define FORMAT_WINDOWS 0x100 #define FORMAT_PANES 0x200 #define FORMAT_PRETTY 0x400 +#define FORMAT_LENGTH 0x800 /* Limit on recursion. */ #define FORMAT_LOOP_LIMIT 10 +/* Format expand flags. */ +#define FORMAT_EXPAND_TIME 0x1 +#define FORMAT_EXPAND_NOJOBS 0x2 + /* Entry in format tree. */ struct format_entry { char *key; char *value; - time_t t; + time_t time; format_cb cb; RB_ENTRY(format_entry) entry; }; @@ -119,8 +127,6 @@ struct format_tree { struct client *client; int flags; u_int tag; - time_t time; - u_int loop; struct mouse_event m; @@ -129,6 +135,14 @@ struct format_tree { static int format_entry_cmp(struct format_entry *, struct format_entry *); RB_GENERATE_STATIC(format_entry_tree, format_entry, entry, format_entry_cmp); +/* Format expand state. */ +struct format_expand_state { + struct format_tree *ft; + u_int loop; + time_t time; + int flags; +}; + /* Format modifier. */ struct format_modifier { char modifier[3]; @@ -214,8 +228,10 @@ format_logging(struct format_tree *ft) /* Log a message if verbose. */ static void printflike(3, 4) -format_log1(struct format_tree *ft, const char *from, const char *fmt, ...) +format_log1(struct format_expand_state *es, const char *from, const char *fmt, + ...) { + struct format_tree *ft = es->ft; va_list ap; char *s; static const char spaces[] = " "; @@ -229,11 +245,22 @@ format_log1(struct format_tree *ft, const char *from, const char *fmt, ...) log_debug("%s: %s", from, s); if (ft->item != NULL && (ft->flags & FORMAT_VERBOSE)) - cmdq_print(ft->item, "#%.*s%s", ft->loop, spaces, s); + cmdq_print(ft->item, "#%.*s%s", es->loop, spaces, s); free(s); } -#define format_log(ft, fmt, ...) format_log1(ft, __func__, fmt, ##__VA_ARGS__) +#define format_log(es, fmt, ...) format_log1(es, __func__, fmt, ##__VA_ARGS__) + +/* Copy expand state. */ +static void +format_copy_state(struct format_expand_state *to, + struct format_expand_state *from, int flags) +{ + to->ft = from->ft; + to->loop = from->loop; + to->time = from->time; + to->flags = from->flags|flags; +} /* Format job update callback. */ static void @@ -303,13 +330,15 @@ format_job_complete(struct job *job) /* Find a job. */ static char * -format_job_get(struct format_tree *ft, const char *cmd) +format_job_get(struct format_expand_state *es, const char *cmd) { - struct format_job_tree *jobs; - struct format_job fj0, *fj; - time_t t; - char *expanded; - int force; + struct format_tree *ft = es->ft; + struct format_job_tree *jobs; + struct format_job fj0, *fj; + time_t t; + char *expanded; + int force; + struct format_expand_state next; if (ft->client == NULL) jobs = &format_jobs; @@ -334,7 +363,7 @@ format_job_get(struct format_tree *ft, const char *cmd) RB_INSERT(format_job_tree, jobs, fj); } - expanded = format_expand(ft, cmd); + expanded = format_expand1(es, cmd); if (fj->expanded == NULL || strcmp(expanded, fj->expanded) != 0) { free((void *)fj->expanded); fj->expanded = xstrdup(expanded); @@ -356,12 +385,12 @@ format_job_get(struct format_tree *ft, const char *cmd) fj->last = t; fj->updated = 0; } + free(expanded); if (ft->flags & FORMAT_STATUS) fj->status = 1; - - free(expanded); - return (format_expand(ft, fj->out)); + format_copy_state(&next, es, FORMAT_EXPAND_NOJOBS); + return (format_expand1(&next, fj->out)); } /* Remove old jobs. */ @@ -1210,7 +1239,6 @@ format_create(struct client *c, struct cmdq_item *item, int tag, int flags) ft->tag = tag; ft->flags = flags; - ft->time = time(NULL); format_add(ft, "version", "%s", getversion()); format_add_cb(ft, "host", format_cb_host); @@ -1260,8 +1288,8 @@ format_each(struct format_tree *ft, void (*cb)(const char *, const char *, char s[64]; RB_FOREACH(fe, format_entry_tree, &ft->tree) { - if (fe->t != 0) { - xsnprintf(s, sizeof s, "%lld", (long long)fe->t); + if (fe->time != 0) { + xsnprintf(s, sizeof s, "%lld", (long long)fe->time); cb(fe->key, s, arg); } else { if (fe->value == NULL && fe->cb != NULL) { @@ -1294,7 +1322,7 @@ format_add(struct format_tree *ft, const char *key, const char *fmt, ...) } fe->cb = NULL; - fe->t = 0; + fe->time = 0; va_start(ap, fmt); xvasprintf(&fe->value, fmt, ap); @@ -1319,7 +1347,7 @@ format_add_tv(struct format_tree *ft, const char *key, struct timeval *tv) } fe->cb = NULL; - fe->t = tv->tv_sec; + fe->time = tv->tv_sec; fe->value = NULL; } @@ -1343,7 +1371,7 @@ format_add_cb(struct format_tree *ft, const char *key, format_cb cb) } fe->cb = cb; - fe->t = 0; + fe->time = 0; fe->value = NULL; } @@ -1439,8 +1467,8 @@ format_find(struct format_tree *ft, const char *key, int modifiers, fe_find.key = (char *)key; fe = RB_FIND(format_entry_tree, &ft->tree, &fe_find); if (fe != NULL) { - if (fe->t != 0) { - t = fe->t; + if (fe->time != 0) { + t = fe->time; goto found; } if (fe->value == NULL && fe->cb != NULL) { @@ -1562,8 +1590,8 @@ format_skip(const char *s, const char *end) /* Return left and right alternatives separated by commas. */ static int -format_choose(struct format_tree *ft, const char *s, char **left, char **right, - int expand) +format_choose(struct format_expand_state *es, const char *s, char **left, + char **right, int expand) { const char *cp; char *left0, *right0; @@ -1575,9 +1603,9 @@ format_choose(struct format_tree *ft, const char *s, char **left, char **right, right0 = xstrdup(cp + 1); if (expand) { - *left = format_expand(ft, left0); + *left = format_expand1(es, left0); free(left0); - *right = format_expand(ft, right0); + *right = format_expand1(es, right0); free(right0); } else { *left = left0; @@ -1633,7 +1661,8 @@ format_free_modifiers(struct format_modifier *list, u_int count) /* Build modifier list. */ static struct format_modifier * -format_build_modifiers(struct format_tree *ft, const char **s, u_int *count) +format_build_modifiers(struct format_expand_state *es, const char **s, + u_int *count) { const char *cp = *s, *end; struct format_modifier *list = NULL; @@ -1642,7 +1671,7 @@ format_build_modifiers(struct format_tree *ft, const char **s, u_int *count) /* * Modifiers are a ; separated list of the forms: - * l,m,C,b,d,t,q,E,T,S,W,P,<,> + * l,m,C,b,d,n,t,q,E,T,S,W,P,<,> * =a * =/a * =/a/ @@ -1659,7 +1688,7 @@ format_build_modifiers(struct format_tree *ft, const char **s, u_int *count) cp++; /* Check single character modifiers with no arguments. */ - if (strchr("lbdqETSWP<>", cp[0]) != NULL && + if (strchr("lbdnqETSWP<>", cp[0]) != NULL && format_is_end(cp[1])) { format_add_modifier(&list, count, cp, 1, NULL, 0); cp++; @@ -1701,7 +1730,7 @@ format_build_modifiers(struct format_tree *ft, const char **s, u_int *count) argv = xcalloc(1, sizeof *argv); value = xstrndup(cp + 1, end - (cp + 1)); - argv[0] = format_expand(ft, value); + argv[0] = format_expand1(es, value); free(value); argc = 1; @@ -1725,7 +1754,7 @@ format_build_modifiers(struct format_tree *ft, const char **s, u_int *count) argv = xreallocarray (argv, argc + 1, sizeof *argv); value = xstrndup(cp, end - cp); - argv[argc++] = format_expand(ft, value); + argv[argc++] = format_expand1(es, value); free(value); cp = end; @@ -1806,25 +1835,28 @@ format_search(struct format_modifier *fm, struct window_pane *wp, const char *s) /* Loop over sessions. */ static char * -format_loop_sessions(struct format_tree *ft, const char *fmt) +format_loop_sessions(struct format_expand_state *es, const char *fmt) { - struct client *c = ft->client; - struct cmdq_item *item = ft->item; - struct format_tree *nft; - char *expanded, *value; - size_t valuelen; - struct session *s; + struct format_tree *ft = es->ft; + struct client *c = ft->client; + struct cmdq_item *item = ft->item; + struct format_tree *nft; + struct format_expand_state next; + char *expanded, *value; + size_t valuelen; + struct session *s; value = xcalloc(1, 1); valuelen = 1; RB_FOREACH(s, sessions, &sessions) { - format_log(ft, "session loop: $%u", s->id); + format_log(es, "session loop: $%u", s->id); nft = format_create(c, item, FORMAT_NONE, ft->flags); - nft->loop = ft->loop; - format_defaults(nft, ft->c, s, NULL, NULL); - expanded = format_expand(nft, fmt); - format_free(nft); + format_defaults(next.ft, ft->c, s, NULL, NULL); + format_copy_state(&next, es, 0); + next.ft = nft; + expanded = format_expand1(&next, fmt); + format_free(next.ft); valuelen += strlen(expanded); value = xrealloc(value, valuelen); @@ -1838,22 +1870,24 @@ format_loop_sessions(struct format_tree *ft, const char *fmt) /* Loop over windows. */ static char * -format_loop_windows(struct format_tree *ft, const char *fmt) +format_loop_windows(struct format_expand_state *es, const char *fmt) { - struct client *c = ft->client; - struct cmdq_item *item = ft->item; - struct format_tree *nft; - char *all, *active, *use, *expanded, *value; - size_t valuelen; - struct winlink *wl; - struct window *w; + struct format_tree *ft = es->ft; + struct client *c = ft->client; + struct cmdq_item *item = ft->item; + struct format_tree *nft; + struct format_expand_state next; + char *all, *active, *use, *expanded, *value; + size_t valuelen; + struct winlink *wl; + struct window *w; if (ft->s == NULL) { - format_log(ft, "window loop but no session"); + format_log(es, "window loop but no session"); return (NULL); } - if (format_choose(ft, fmt, &all, &active, 0) != 0) { + if (format_choose(es, fmt, &all, &active, 0) != 0) { all = xstrdup(fmt); active = NULL; } @@ -1863,15 +1897,16 @@ format_loop_windows(struct format_tree *ft, const char *fmt) RB_FOREACH(wl, winlinks, &ft->s->windows) { w = wl->window; - format_log(ft, "window loop: %u @%u", wl->idx, w->id); + format_log(es, "window loop: %u @%u", wl->idx, w->id); if (active != NULL && wl == ft->s->curw) use = active; else use = all; nft = format_create(c, item, FORMAT_WINDOW|w->id, ft->flags); - nft->loop = ft->loop; format_defaults(nft, ft->c, ft->s, wl, NULL); - expanded = format_expand(nft, use); + format_copy_state(&next, es, 0); + next.ft = nft; + expanded = format_expand1(&next, use); format_free(nft); valuelen += strlen(expanded); @@ -1889,21 +1924,23 @@ format_loop_windows(struct format_tree *ft, const char *fmt) /* Loop over panes. */ static char * -format_loop_panes(struct format_tree *ft, const char *fmt) +format_loop_panes(struct format_expand_state *es, const char *fmt) { - struct client *c = ft->client; - struct cmdq_item *item = ft->item; - struct format_tree *nft; - char *all, *active, *use, *expanded, *value; - size_t valuelen; - struct window_pane *wp; + struct format_tree *ft = es->ft; + struct client *c = ft->client; + struct cmdq_item *item = ft->item; + struct format_tree *nft; + struct format_expand_state next; + char *all, *active, *use, *expanded, *value; + size_t valuelen; + struct window_pane *wp; if (ft->w == NULL) { - format_log(ft, "pane loop but no window"); + format_log(es, "pane loop but no window"); return (NULL); } - if (format_choose(ft, fmt, &all, &active, 0) != 0) { + if (format_choose(es, fmt, &all, &active, 0) != 0) { all = xstrdup(fmt); active = NULL; } @@ -1912,15 +1949,16 @@ format_loop_panes(struct format_tree *ft, const char *fmt) valuelen = 1; TAILQ_FOREACH(wp, &ft->w->panes, entry) { - format_log(ft, "pane loop: %%%u", wp->id); + format_log(es, "pane loop: %%%u", wp->id); if (active != NULL && wp == ft->w->active) use = active; else use = all; nft = format_create(c, item, FORMAT_PANE|wp->id, ft->flags); - nft->loop = ft->loop; format_defaults(nft, ft->c, ft->s, ft->wl, wp); - expanded = format_expand(nft, use); + format_copy_state(&next, es, 0); + next.ft = nft; + expanded = format_expand1(&next, use); format_free(nft); valuelen += strlen(expanded); @@ -1937,16 +1975,26 @@ format_loop_panes(struct format_tree *ft, const char *fmt) } static char * -format_replace_expression(struct format_modifier *mexp, struct format_tree *ft, - const char *copy) +format_replace_expression(struct format_modifier *mexp, + struct format_expand_state *es, const char *copy) { - int argc = mexp->argc; - const char *errstr; - char *endch, *value, *left = NULL, *right = NULL; - int use_fp = 0; - u_int prec = 0; - double mleft, mright, result; - enum { ADD, SUBTRACT, MULTIPLY, DIVIDE, MODULUS } operator; + int argc = mexp->argc; + const char *errstr; + char *endch, *value, *left = NULL, *right = NULL; + int use_fp = 0; + u_int prec = 0; + double mleft, mright, result; + enum { ADD, + SUBTRACT, + MULTIPLY, + DIVIDE, + MODULUS, + EQUAL, + NOT_EQUAL, + GREATER_THAN, + GREATER_THAN_EQUAL, + LESS_THAN, + LESS_THAN_EQUAL } operator; if (strcmp(mexp->argv[0], "+") == 0) operator = ADD; @@ -1959,8 +2007,20 @@ format_replace_expression(struct format_modifier *mexp, struct format_tree *ft, else if (strcmp(mexp->argv[0], "%") == 0 || strcmp(mexp->argv[0], "m") == 0) operator = MODULUS; + else if (strcmp(mexp->argv[0], "==") == 0) + operator = EQUAL; + else if (strcmp(mexp->argv[0], "!=") == 0) + operator = NOT_EQUAL; + else if (strcmp(mexp->argv[0], ">") == 0) + operator = GREATER_THAN; + else if (strcmp(mexp->argv[0], "<") == 0) + operator = LESS_THAN; + else if (strcmp(mexp->argv[0], ">=") == 0) + operator = GREATER_THAN_EQUAL; + else if (strcmp(mexp->argv[0], "<=") == 0) + operator = LESS_THAN_EQUAL; else { - format_log(ft, "expression has no valid operator: '%s'", + format_log(es, "expression has no valid operator: '%s'", mexp->argv[0]); goto fail; } @@ -1975,26 +2035,26 @@ format_replace_expression(struct format_modifier *mexp, struct format_tree *ft, if (argc >= 3) { prec = strtonum(mexp->argv[2], INT_MIN, INT_MAX, &errstr); if (errstr != NULL) { - format_log (ft, "expression precision %s: %s", errstr, + format_log(es, "expression precision %s: %s", errstr, mexp->argv[2]); goto fail; } } - if (format_choose(ft, copy, &left, &right, 1) != 0) { - format_log(ft, "expression syntax error"); + if (format_choose(es, copy, &left, &right, 1) != 0) { + format_log(es, "expression syntax error"); goto fail; } mleft = strtod(left, &endch); if (*endch != '\0') { - format_log(ft, "expression left side is invalid: %s", left); + format_log(es, "expression left side is invalid: %s", left); goto fail; } mright = strtod(right, &endch); if (*endch != '\0') { - format_log(ft, "expression right side is invalid: %s", right); + format_log(es, "expression right side is invalid: %s", right); goto fail; } @@ -2002,8 +2062,8 @@ format_replace_expression(struct format_modifier *mexp, struct format_tree *ft, mleft = (long long)mleft; mright = (long long)mright; } - format_log(ft, "expression left side is: %.*f", prec, mleft); - format_log(ft, "expression right side is: %.*f", prec, mright); + format_log(es, "expression left side is: %.*f", prec, mleft); + format_log(es, "expression right side is: %.*f", prec, mright); switch (operator) { case ADD: @@ -2021,12 +2081,30 @@ format_replace_expression(struct format_modifier *mexp, struct format_tree *ft, case MODULUS: result = fmod(mleft, mright); break; + case EQUAL: + result = fabs(mleft - mright) < 1e-9; + break; + case NOT_EQUAL: + result = fabs(mleft - mright) > 1e-9; + break; + case GREATER_THAN: + result = (mleft > mright); + break; + case GREATER_THAN_EQUAL: + result = (mleft >= mright); + break; + case LESS_THAN: + result = (mleft < mright); + break; + case LESS_THAN_EQUAL: + result = (mleft > mright); + break; } if (use_fp) xasprintf(&value, "%.*f", prec, result); else xasprintf(&value, "%.*f", prec, (double)(long long)result); - format_log(ft, "expression result is %s", value); + format_log(es, "expression result is %s", value); free(right); free(left); @@ -2040,31 +2118,34 @@ fail: /* Replace a key. */ static int -format_replace(struct format_tree *ft, const char *key, size_t keylen, +format_replace(struct format_expand_state *es, const char *key, size_t keylen, char **buf, size_t *len, size_t *off) { - struct window_pane *wp = ft->wp; - const char *errptr, *copy, *cp, *marker = NULL; - const char *time_format = NULL; - char *copy0, *condition, *found, *new; - char *value, *left, *right; - size_t valuelen; - int modifiers = 0, limit = 0, width = 0, j; - struct format_modifier *list, *fm, *cmp = NULL, *search = NULL; - struct format_modifier **sub = NULL, *mexp = NULL; - u_int i, count, nsub = 0; + struct format_tree *ft = es->ft; + struct window_pane *wp = ft->wp; + const char *errptr, *copy, *cp, *marker = NULL; + const char *time_format = NULL; + char *copy0, *condition, *found, *new; + char *value, *left, *right; + size_t valuelen; + int modifiers = 0, limit = 0, width = 0; + int j; + struct format_modifier *list, *cmp = NULL, *search = NULL; + struct format_modifier **sub = NULL, *mexp = NULL, *fm; + u_int i, count, nsub = 0; + struct format_expand_state next; /* Make a copy of the key. */ copy = copy0 = xstrndup(key, keylen); /* Process modifier list. */ - list = format_build_modifiers(ft, ©, &count); + list = format_build_modifiers(es, ©, &count); for (i = 0; i < count; i++) { fm = &list[i]; if (format_logging(ft)) { - format_log(ft, "modifier %u is %s", i, fm->modifier); + format_log(es, "modifier %u is %s", i, fm->modifier); for (j = 0; j < fm->argc; j++) { - format_log(ft, "modifier %u argument %d: %s", i, + format_log(es, "modifier %u argument %d: %s", i, j, fm->argv[j]); } } @@ -2117,6 +2198,9 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, case 'd': modifiers |= FORMAT_DIRNAME; break; + case 'n': + modifiers |= FORMAT_LENGTH; + break; case 't': modifiers |= FORMAT_TIMESTRING; if (fm->argc < 1) @@ -2165,37 +2249,37 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, /* Is this a loop, comparison or condition? */ if (modifiers & FORMAT_SESSIONS) { - value = format_loop_sessions(ft, copy); + value = format_loop_sessions(es, copy); if (value == NULL) goto fail; } else if (modifiers & FORMAT_WINDOWS) { - value = format_loop_windows(ft, copy); + value = format_loop_windows(es, copy); if (value == NULL) goto fail; } else if (modifiers & FORMAT_PANES) { - value = format_loop_panes(ft, copy); + value = format_loop_panes(es, copy); if (value == NULL) goto fail; } else if (search != NULL) { /* Search in pane. */ - new = format_expand(ft, copy); + new = format_expand1(es, copy); if (wp == NULL) { - format_log(ft, "search '%s' but no pane", new); + format_log(es, "search '%s' but no pane", new); value = xstrdup("0"); } else { - format_log(ft, "search '%s' pane %%%u", new, wp->id); + format_log(es, "search '%s' pane %%%u", new, wp->id); value = format_search(fm, wp, new); } free(new); } else if (cmp != NULL) { /* Comparison of left and right. */ - if (format_choose(ft, copy, &left, &right, 1) != 0) { - format_log(ft, "compare %s syntax error: %s", + if (format_choose(es, copy, &left, &right, 1) != 0) { + format_log(es, "compare %s syntax error: %s", cmp->modifier, copy); goto fail; } - format_log(ft, "compare %s left is: %s", cmp->modifier, left); - format_log(ft, "compare %s right is: %s", cmp->modifier, right); + format_log(es, "compare %s left is: %s", cmp->modifier, left); + format_log(es, "compare %s right is: %s", cmp->modifier, right); if (strcmp(cmp->modifier, "||") == 0) { if (format_true(left) || format_true(right)) @@ -2246,11 +2330,11 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, /* Conditional: check first and choose second or third. */ cp = format_skip(copy + 1, ","); if (cp == NULL) { - format_log(ft, "condition syntax error: %s", copy + 1); + format_log(es, "condition syntax error: %s", copy + 1); goto fail; } condition = xstrndup(copy + 1, cp - (copy + 1)); - format_log(ft, "condition is: %s", condition); + format_log(es, "condition is: %s", condition); found = format_find(ft, condition, modifiers, time_format); if (found == NULL) { @@ -2259,32 +2343,32 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, * the expansion doesn't have any effect, then assume * false. */ - found = format_expand(ft, condition); + found = format_expand1(es, condition); if (strcmp(found, condition) == 0) { free(found); found = xstrdup(""); - format_log(ft, "condition '%s' found: %s", + format_log(es, "condition '%s' found: %s", condition, found); } else { - format_log(ft, + format_log(es, "condition '%s' not found; assuming false", condition); } } else - format_log(ft, "condition '%s' found", condition); + format_log(es, "condition '%s' found", condition); - if (format_choose(ft, cp + 1, &left, &right, 0) != 0) { - format_log(ft, "condition '%s' syntax error: %s", + if (format_choose(es, cp + 1, &left, &right, 0) != 0) { + format_log(es, "condition '%s' syntax error: %s", condition, cp + 1); free(found); goto fail; } if (format_true(found)) { - format_log(ft, "condition '%s' is true", condition); - value = format_expand(ft, left); + format_log(es, "condition '%s' is true", condition); + value = format_expand1(es, left); } else { - format_log(ft, "condition '%s' is false", condition); - value = format_expand(ft, right); + format_log(es, "condition '%s' is false", condition); + value = format_expand1(es, right); } free(right); free(left); @@ -2292,38 +2376,44 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, free(condition); free(found); } else if (mexp != NULL) { - value = format_replace_expression(mexp, ft, copy); + value = format_replace_expression(mexp, es, copy); if (value == NULL) value = xstrdup(""); } else { - /* Neither: look up directly. */ - value = format_find(ft, copy, modifiers, time_format); - if (value == NULL) { - format_log(ft, "format '%s' not found", copy); - value = xstrdup(""); - } else - format_log(ft, "format '%s' found: %s", copy, value); + if (strstr(copy, "#{") != 0) { + format_log(es, "expanding inner format '%s'", copy); + value = format_expand1(es, copy); + } else { + value = format_find(ft, copy, modifiers, time_format); + if (value == NULL) { + format_log(es, "format '%s' not found", copy); + value = xstrdup(""); + } else { + format_log(es, "format '%s' found: %s", copy, + value); + } + } } done: /* Expand again if required. */ if (modifiers & FORMAT_EXPAND) { - new = format_expand(ft, value); + new = format_expand1(es, value); free(value); value = new; - } - else if (modifiers & FORMAT_EXPANDTIME) { - new = format_expand_time(ft, value); + } else if (modifiers & FORMAT_EXPANDTIME) { + format_copy_state(&next, es, FORMAT_EXPAND_TIME); + new = format_expand1(&next, value); free(value); value = new; } /* Perform substitution if any. */ for (i = 0; i < nsub; i++) { - left = format_expand(ft, sub[i]->argv[0]); - right = format_expand(ft, sub[i]->argv[1]); + left = format_expand1(es, sub[i]->argv[0]); + right = format_expand1(es, sub[i]->argv[1]); new = format_sub(sub[i], value, left, right); - format_log(ft, "substitute '%s' to '%s': %s", left, right, new); + format_log(es, "substitute '%s' to '%s': %s", left, right, new); free(value); value = new; free(right); @@ -2340,7 +2430,7 @@ done: free(value); value = new; } - format_log(ft, "applied length limit %d: %s", limit, value); + format_log(es, "applied length limit %d: %s", limit, value); } else if (limit < 0) { new = format_trim_right(value, -limit); if (marker != NULL && strcmp(new, value) != 0) { @@ -2350,7 +2440,7 @@ done: free(value); value = new; } - format_log(ft, "applied length limit %d: %s", limit, value); + format_log(es, "applied length limit %d: %s", limit, value); } /* Pad the value if needed. */ @@ -2358,12 +2448,20 @@ done: new = utf8_padcstr(value, width); free(value); value = new; - format_log(ft, "applied padding width %d: %s", width, value); + format_log(es, "applied padding width %d: %s", width, value); } else if (width < 0) { new = utf8_rpadcstr(value, -width); free(value); value = new; - format_log(ft, "applied padding width %d: %s", width, value); + format_log(es, "applied padding width %d: %s", width, value); + } + + /* Replace with the length if needed. */ + if (modifiers & FORMAT_LENGTH) { + xasprintf(&new, "%zu", strlen(value)); + free(value); + value = new; + format_log(es, "replacing with length: %s", new); } /* Expand the buffer and copy in the value. */ @@ -2375,7 +2473,7 @@ done: memcpy(*buf + *off, value, valuelen); *off += valuelen; - format_log(ft, "replaced '%s' with '%s'", copy0, value); + format_log(es, "replaced '%s' with '%s'", copy0, value); free(value); free(sub); @@ -2384,7 +2482,7 @@ done: return (0); fail: - format_log(ft, "failed %s", copy0); + format_log(es, "failed %s", copy0); free(sub); format_free_modifiers(list, count); @@ -2394,32 +2492,35 @@ fail: /* Expand keys in a template. */ static char * -format_expand1(struct format_tree *ft, const char *fmt, int time) +format_expand1(struct format_expand_state *es, const char *fmt) { - char *buf, *out, *name; - const char *ptr, *s; - size_t off, len, n, outlen; - int ch, brackets; - struct tm *tm; - char expanded[8192]; + struct format_tree *ft = es->ft; + char *buf, *out, *name; + const char *ptr, *s; + size_t off, len, n, outlen; + int ch, brackets; + struct tm *tm; + char expanded[8192]; if (fmt == NULL || *fmt == '\0') return (xstrdup("")); - if (ft->loop == FORMAT_LOOP_LIMIT) + if (es->loop == FORMAT_LOOP_LIMIT) return (xstrdup("")); - ft->loop++; + es->loop++; - format_log(ft, "expanding format: %s", fmt); + format_log(es, "expanding format: %s", fmt); - if (time) { - tm = localtime(&ft->time); + if (es->flags & FORMAT_EXPAND_TIME) { + if (es->time == 0) + es->time = time(NULL); + tm = localtime(&es->time); if (strftime(expanded, sizeof expanded, fmt, tm) == 0) { - format_log(ft, "format is too long"); + format_log(es, "format is too long"); return (xstrdup("")); } if (format_logging(ft) && strcmp(expanded, fmt) != 0) - format_log(ft, "after time expanded: %s", expanded); + format_log(es, "after time expanded: %s", expanded); fmt = expanded; } @@ -2453,14 +2554,15 @@ format_expand1(struct format_tree *ft, const char *fmt, int time) n = ptr - fmt; name = xstrndup(fmt, n); - format_log(ft, "found #(): %s", name); + format_log(es, "found #(): %s", name); - if (ft->flags & FORMAT_NOJOBS) { + if ((ft->flags & FORMAT_NOJOBS) || + (es->flags & FORMAT_EXPAND_NOJOBS)) { out = xstrdup(""); - format_log(ft, "#() is disabled"); + format_log(es, "#() is disabled"); } else { - out = format_job_get(ft, name); - format_log(ft, "#() result: %s", out); + out = format_job_get(es, name); + format_log(es, "#() result: %s", out); } free(name); @@ -2482,15 +2584,15 @@ format_expand1(struct format_tree *ft, const char *fmt, int time) break; n = ptr - fmt; - format_log(ft, "found #{}: %.*s", (int)n, fmt); - if (format_replace(ft, fmt, n, &buf, &len, &off) != 0) + format_log(es, "found #{}: %.*s", (int)n, fmt); + if (format_replace(es, fmt, n, &buf, &len, &off) != 0) break; fmt += n + 1; continue; case '}': case '#': case ',': - format_log(ft, "found #%c", ch); + format_log(es, "found #%c", ch); while (len - off < 2) { buf = xreallocarray(buf, 2, len); len *= 2; @@ -2513,8 +2615,8 @@ format_expand1(struct format_tree *ft, const char *fmt, int time) continue; } n = strlen(s); - format_log(ft, "found #%c: %s", ch, s); - if (format_replace(ft, s, n, &buf, &len, &off) != 0) + format_log(es, "found #%c: %s", ch, s); + if (format_replace(es, s, n, &buf, &len, &off) != 0) break; continue; } @@ -2523,8 +2625,8 @@ format_expand1(struct format_tree *ft, const char *fmt, int time) } buf[off] = '\0'; - format_log(ft, "result is: %s", buf); - ft->loop--; + format_log(es, "result is: %s", buf); + es->loop--; return (buf); } @@ -2533,14 +2635,24 @@ format_expand1(struct format_tree *ft, const char *fmt, int time) char * format_expand_time(struct format_tree *ft, const char *fmt) { - return (format_expand1(ft, fmt, 1)); + struct format_expand_state es; + + memset(&es, 0, sizeof es); + es.ft = ft; + es.flags = FORMAT_EXPAND_TIME; + return (format_expand1(&es, fmt)); } /* Expand keys in a template. */ char * format_expand(struct format_tree *ft, const char *fmt) { - return (format_expand1(ft, fmt, 0)); + struct format_expand_state es; + + memset(&es, 0, sizeof es); + es.ft = ft; + es.flags = 0; + return (format_expand1(&es, fmt)); } /* Expand a single string. */ @@ -2889,6 +3001,7 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp) format_add(ft, "pane_dead", "%d", wp->fd == -1); else format_add(ft, "pane_dead", "0"); + format_add(ft, "pane_last", "%d", wp == w->last); if (server_check_marked() && marked_pane.wp == wp) format_add(ft, "pane_marked", "1"); diff --git a/grid.c b/grid.c index 5ea5bf62..90e1a0a5 100644 --- a/grid.c +++ b/grid.c @@ -265,6 +265,9 @@ grid_free_lines(struct grid *gd, u_int py, u_int ny) for (yy = py; yy < py + ny; yy++) grid_free_line(gd, yy); +#ifdef HAVE_MALLOC_TRIM + malloc_trim(0); +#endif } /* Create a new grid. */ @@ -463,7 +466,7 @@ grid_expand_line(struct grid *gd, u_int py, u_int sx, u_int bg) sx = gd->sx / 4; else if (sx < gd->sx / 2) sx = gd->sx / 2; - else + else if (gd->sx > sx) sx = gd->sx; gl->celldata = xreallocarray(gl->celldata, sx, sizeof *gl->celldata); @@ -1277,7 +1280,7 @@ grid_reflow(struct grid *gd, u_int sx) struct grid *target; struct grid_line *gl; struct grid_cell gc; - u_int yy, width, i, at, first; + u_int yy, width, i, at; /* * Create a destination grid. This is just used as a container for the @@ -1294,13 +1297,12 @@ grid_reflow(struct grid *gd, u_int sx) continue; /* - * Work out the width of this line. first is the width of the - * first character, at is the point at which the available - * width is hit, and width is the full line width. + * Work out the width of this line. at is the point at which + * the available width is hit, and width is the full line + * width. */ - first = at = width = 0; + at = width = 0; if (~gl->flags & GRID_LINE_EXTENDED) { - first = 1; width = gl->cellused; if (width > sx) at = sx; @@ -1309,8 +1311,6 @@ grid_reflow(struct grid *gd, u_int sx) } else { for (i = 0; i < gl->cellused; i++) { grid_get_cell1(gl, i, &gc); - if (i == 0) - first = gc.data.width; if (at == 0 && width + gc.data.width > sx) at = i; width += gc.data.width; @@ -1318,10 +1318,10 @@ grid_reflow(struct grid *gd, u_int sx) } /* - * If the line is exactly right or the first character is wider - * than the target width, just move it across unchanged. + * If the line is exactly right, just move it across + * unchanged. */ - if (width == sx || first > sx) { + if (width == sx) { grid_reflow_move(target, gl); continue; } diff --git a/input-keys.c b/input-keys.c index ff402503..32ca4b97 100644 --- a/input-keys.c +++ b/input-keys.c @@ -555,6 +555,8 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key) case KEYC_SHIFT|KEYC_META|KEYC_CTRL: modifier = '8'; break; + default: + fatalx("invalid key modifiers: %llx", key); } xsnprintf(tmp, sizeof tmp, "\033[%llu;%cu", outkey, modifier); bufferevent_write(bev, tmp, strlen(tmp)); diff --git a/input.c b/input.c index 60bfa8e2..8b7ba08a 100644 --- a/input.c +++ b/input.c @@ -65,7 +65,7 @@ struct input_param { INPUT_MISSING, INPUT_NUMBER, INPUT_STRING - } type; + } type; union { int num; char *str; @@ -81,7 +81,7 @@ struct input_ctx { struct input_cell cell; struct input_cell old_cell; - u_int old_cx; + u_int old_cx; u_int old_cy; int old_mode; @@ -121,7 +121,7 @@ struct input_ctx { * All input received since we were last in the ground state. Sent to * control clients on connection. */ - struct evbuffer *since_ground; + struct evbuffer *since_ground; }; /* Helper functions. */ @@ -1545,6 +1545,10 @@ input_csi_dispatch(struct input_ctx *ictx) if (n == -1) break; + m = screen_size_x(s) - s->cx; + if (n > m) + n = m; + if (ictx->last == -1) break; ictx->ch = ictx->last; @@ -1867,6 +1871,7 @@ input_csi_dispatch_winops(struct input_ctx *ictx) case 2: screen_pop_title(sctx->s); if (wp != NULL) { + notify_pane("pane-title-changed", wp); server_redraw_window_borders(wp->window); server_status_window(wp->window); } @@ -2266,6 +2271,7 @@ input_exit_osc(struct input_ctx *ictx) case 0: case 2: if (screen_set_title(sctx->s, p) && wp != NULL) { + notify_pane("pane-title-changed", wp); server_redraw_window_borders(wp->window); server_status_window(wp->window); } @@ -2331,6 +2337,7 @@ input_exit_apc(struct input_ctx *ictx) log_debug("%s: \"%s\"", __func__, ictx->input_buf); if (screen_set_title(sctx->s, ictx->input_buf) && wp != NULL) { + notify_pane("pane-title-changed", wp); server_redraw_window_borders(wp->window); server_status_window(wp->window); } @@ -2461,13 +2468,31 @@ input_osc_parse_colour(const char *p, u_int *r, u_int *g, u_int *b) return (1); } +/* Reply to a colour request. */ +static void +input_osc_colour_reply(struct input_ctx *ictx, u_int n, int c) +{ + u_char r, g, b; + const char *end; + + if (c == 8 || (~c & COLOUR_FLAG_RGB)) + return; + colour_split_rgb(c, &r, &g, &b); + + if (ictx->input_end == INPUT_END_BEL) + end = "\007"; + else + end = "\033\\"; + input_reply(ictx, "\033]%u;rgb:%02hhx/%02hhx/%02hhx%s", n, r, g, b, end); +} + /* Handle the OSC 4 sequence for setting (multiple) palette entries. */ static void input_osc_4(struct input_ctx *ictx, const char *p) { struct window_pane *wp = ictx->wp; char *copy, *s, *next = NULL; - long idx; + long idx; u_int r, g, b; if (wp == NULL) @@ -2499,17 +2524,22 @@ bad: free(copy); } -/* Handle the OSC 10 sequence for setting foreground colour. */ +/* Handle the OSC 10 sequence for setting and querying foreground colour. */ static void input_osc_10(struct input_ctx *ictx, const char *p) { struct window_pane *wp = ictx->wp; + struct grid_cell defaults; u_int r, g, b; if (wp == NULL) return; - if (strcmp(p, "?") == 0) + + if (strcmp(p, "?") == 0) { + tty_default_colours(&defaults, wp); + input_osc_colour_reply(ictx, 10, defaults.fg); return; + } if (!input_osc_parse_colour(p, &r, &g, &b)) goto bad; @@ -2522,17 +2552,22 @@ bad: log_debug("bad OSC 10: %s", p); } -/* Handle the OSC 11 sequence for setting background colour. */ +/* Handle the OSC 11 sequence for setting and querying background colour. */ static void input_osc_11(struct input_ctx *ictx, const char *p) { struct window_pane *wp = ictx->wp; + struct grid_cell defaults; u_int r, g, b; if (wp == NULL) return; - if (strcmp(p, "?") == 0) + + if (strcmp(p, "?") == 0) { + tty_default_colours(&defaults, wp); + input_osc_colour_reply(ictx, 11, defaults.bg); return; + } if (!input_osc_parse_colour(p, &r, &g, &b)) goto bad; diff --git a/key-bindings.c b/key-bindings.c index 4c4d4ba5..b47f6ff7 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -191,6 +191,16 @@ key_bindings_add(const char *name, key_code key, const char *note, int repeat, table = key_bindings_get_table(name, 1); bd = key_bindings_get(table, key & ~KEYC_MASK_FLAGS); + if (cmdlist == NULL) { + if (bd != NULL) { + free((void *)bd->note); + if (note != NULL) + bd->note = xstrdup(note); + else + bd->note = NULL; + } + return; + } if (bd != NULL) { RB_REMOVE(key_bindings, &table->key_bindings, bd); key_bindings_free(bd); diff --git a/menu.c b/menu.c index 4fcf660a..48c9ed2f 100644 --- a/menu.c +++ b/menu.c @@ -203,16 +203,28 @@ menu_key_cb(struct client *c, struct key_event *event) m->x > md->px + 4 + menu->width || m->y < md->py + 1 || m->y > md->py + 1 + count - 1) { - if (MOUSE_RELEASE(m->b)) - return (1); + if (~md->flags & MENU_STAYOPEN) { + if (MOUSE_RELEASE(m->b)) + return (1); + } else { + if (!MOUSE_RELEASE(m->b) && + MOUSE_WHEEL(m->b) == 0 && + !MOUSE_DRAG(m->b)) + return (1); + } if (md->choice != -1) { md->choice = -1; c->flags |= CLIENT_REDRAWOVERLAY; } return (0); } - if (MOUSE_RELEASE(m->b)) - goto chosen; + if (~md->flags & MENU_STAYOPEN) { + if (MOUSE_RELEASE(m->b)) + goto chosen; + } else { + if (MOUSE_WHEEL(m->b) == 0 && !MOUSE_DRAG(m->b)) + goto chosen; + } md->choice = m->y - (md->py + 1); if (md->choice != old) c->flags |= CLIENT_REDRAWOVERLAY; @@ -303,8 +315,11 @@ chosen: if (md->choice == -1) return (1); item = &menu->items[md->choice]; - if (item->name == NULL || *item->name == '-') + if (item->name == NULL || *item->name == '-') { + if (md->flags & MENU_STAYOPEN) + return (0); return (1); + } if (md->cb != NULL) { md->cb(md->menu, md->choice, item->key, md->data); md->cb = NULL; diff --git a/mode-tree.c b/mode-tree.c index c4b776f9..a47c0c06 100644 --- a/mode-tree.c +++ b/mode-tree.c @@ -1176,7 +1176,7 @@ mode_tree_run_command(struct client *c, struct cmd_find_state *fs, if (status == CMD_PARSE_ERROR) { if (c != NULL) { *error = toupper((u_char)*error); - status_message_set(c, 1, "%s", error); + status_message_set(c, -1, 1, "%s", error); } free(error); } diff --git a/options-table.c b/options-table.c index 0555d43e..873f8d67 100644 --- a/options-table.c +++ b/options-table.c @@ -170,6 +170,14 @@ static const char *options_table_status_format_default[] = { .separator = "" \ } +/* Map of name conversions. */ +const struct options_name_map options_other_names[] = { + { "display-panes-color", "display-panes-colour" }, + { "display-panes-active-color", "display-panes-active-colour" }, + { "clock-mode-color", "clock-mode-colour" }, + { NULL, NULL } +}; + /* Top-level options. */ const struct options_table_entry options_table[] = { /* Server options. */ @@ -1120,6 +1128,7 @@ const struct options_table_entry options_table[] = { OPTIONS_TABLE_PANE_HOOK("pane-focus-out", ""), OPTIONS_TABLE_PANE_HOOK("pane-mode-changed", ""), OPTIONS_TABLE_PANE_HOOK("pane-set-clipboard", ""), + OPTIONS_TABLE_PANE_HOOK("pane-title-changed", ""), OPTIONS_TABLE_HOOK("session-closed", ""), OPTIONS_TABLE_HOOK("session-created", ""), OPTIONS_TABLE_HOOK("session-renamed", ""), diff --git a/options.c b/options.c index 336eb732..09850f7e 100644 --- a/options.c +++ b/options.c @@ -95,6 +95,18 @@ options_cmp(struct options_entry *lhs, struct options_entry *rhs) return (strcmp(lhs->name, rhs->name)); } +static const char * +options_map_name(const char *name) +{ + const struct options_name_map *map; + + for (map = options_other_names; map->from != NULL; map++) { + if (strcmp(map->from, name) == 0) + return (map->to); + } + return (name); +} + static const struct options_table_entry * options_parent_table_entry(struct options *oo, const char *s) { @@ -204,10 +216,14 @@ options_next(struct options_entry *o) struct options_entry * options_get_only(struct options *oo, const char *name) { - struct options_entry o; + struct options_entry o = { .name = name }, *found; - o.name = name; - return (RB_FIND(options_tree, &oo->tree, &o)); + found = RB_FIND(options_tree, &oo->tree, &o); + if (found == NULL) { + o.name = options_map_name(name); + return (RB_FIND(options_tree, &oo->tree, &o)); + } + return (found); } struct options_entry * @@ -608,19 +624,21 @@ char * options_match(const char *s, int *idx, int *ambiguous) { const struct options_table_entry *oe, *found; - char *name; + char *parsed; + const char *name; size_t namelen; - name = options_parse(s, idx); - if (name == NULL) + parsed = options_parse(s, idx); + if (parsed == NULL) return (NULL); - namelen = strlen(name); - - if (*name == '@') { + if (*parsed == '@') { *ambiguous = 0; - return (name); + return (parsed); } + name = options_map_name(parsed); + namelen = strlen(name); + found = NULL; for (oe = options_table; oe->name != NULL; oe++) { if (strcmp(oe->name, name) == 0) { @@ -630,13 +648,13 @@ options_match(const char *s, int *idx, int *ambiguous) if (strncmp(oe->name, name, namelen) == 0) { if (found != NULL) { *ambiguous = 1; - free(name); + free(parsed); return (NULL); } found = oe; } } - free(name); + free(parsed); if (found == NULL) { *ambiguous = 0; return (NULL); diff --git a/popup.c b/popup.c index 6f2ab101..0ad20c5f 100644 --- a/popup.c +++ b/popup.c @@ -261,7 +261,7 @@ popup_handle_drag(struct client *c, struct popup_data *pd, pd->sx = m->x - pd->px; pd->sy = m->y - pd->py; - screen_resize(&pd->s, pd->sx, pd->sy, 0); + screen_resize(&pd->s, pd->sx - 2, pd->sy - 2, 0); if (pd->ictx == NULL) popup_write_screen(c, pd); else if (pd->job != NULL) diff --git a/proc.c b/proc.c index a9aa070a..592d8e05 100644 --- a/proc.c +++ b/proc.c @@ -239,6 +239,7 @@ proc_set_signals(struct tmuxproc *tp, void (*signalcb)(int)) sigaction(SIGTSTP, &sa, NULL); sigaction(SIGTTIN, &sa, NULL); sigaction(SIGTTOU, &sa, NULL); + sigaction(SIGQUIT, &sa, NULL); signal_set(&tp->ev_sigint, SIGINT, proc_signal_cb, tp); signal_add(&tp->ev_sigint, NULL); diff --git a/regress/style-trim.sh b/regress/style-trim.sh new file mode 100644 index 00000000..56f93656 --- /dev/null +++ b/regress/style-trim.sh @@ -0,0 +1,93 @@ +#!/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 + +$TMUX2 -f/dev/null new -d "$TMUX -f/dev/null new" +sleep 2 +$TMUX set -g status-style fg=default,bg=default + +check() { + v=$($TMUX display -p "$1") + $TMUX set -g status-format[0] "$1" + sleep 1 + r=$($TMUX2 capturep -Cep|tail -1|sed 's|\\033\[||g') + + #printf "$1 = [$v = $2] [$r = $3]" + if [ "$v" = "$2" -a "$r" = "$3" ]; then + : #printf " good\n" + else + #printf " \033[31mbad\033[0m\n" + exit 1 + fi +} + +# drawn as #0 +$TMUX setenv -g V '#0' +check '#{V} #{w:V}' '#0 2' '#0 2' +check '#{=3:V}' '#0' '#0' +check '#{=-3:V}' '#0' '#0' + +# drawn as #0 +$TMUX setenv -g V '###[bg=yellow]0' +check '#{V} #{w:V}' '###[bg=yellow]0 2' '#43m0 249m' +check '#{=3:V}' '###[bg=yellow]0' '#43m049m' +check '#{=-3:V}' '###[bg=yellow]0' '#43m049m' + +# drawn as #0123456 +$TMUX setenv -g V '#0123456' +check '#{V} #{w:V}' '#0123456 8' '#0123456 8' +check '#{=3:V}' '#01' '#01' +check '#{=-3:V}' '456' '456' + +# drawn as ##0123456 +$TMUX setenv -g V '##0123456' +check '#{V} #{w:V}' '##0123456 9' '##0123456 9' +check '#{=3:V}' '##0' '##0' +check '#{=-3:V}' '456' '456' + +# drawn as ###0123456 +$TMUX setenv -g V '###0123456' +check '#{V} #{w:V}' '###0123456 10' '###0123456 10' +check '#{=3:V}' '###' '###' +check '#{=-3:V}' '456' '456' + +# drawn as 0123456 +$TMUX setenv -g V '#[bg=yellow]0123456' +check '#{V} #{w:V}' '#[bg=yellow]0123456 7' '43m0123456 749m' +check '#{=3:V}' '#[bg=yellow]012' '43m01249m' +check '#{=-3:V}' '#[bg=yellow]456' '43m45649m' + +# drawn as #[bg=yellow]0123456 +$TMUX setenv -g V '##[bg=yellow]0123456' +check '#{V} #{w:V}' '##[bg=yellow]0123456 19' '#[bg=yellow]0123456 19' +check '#{=3:V}' '##[b' '#[b' +check '#{=-3:V}' '456' '456' + +# drawn as #0123456 +$TMUX setenv -g V '###[bg=yellow]0123456' +check '#{V} #{w:V}' '###[bg=yellow]0123456 8' '#43m0123456 849m' +check '#{=3:V}' '###[bg=yellow]01' '#43m0149m' +check '#{=-3:V}' '#[bg=yellow]456' '43m45649m' + +# drawn as ##[bg=yellow]0123456 +$TMUX setenv -g V '####[bg=yellow]0123456' +check '#{V} #{w:V}' '####[bg=yellow]0123456 20' '##[bg=yellow]0123456 20' +check '#{=3:V}' '####[' '##[' +check '#{=-3:V}' '456' '456' + +# drawn as ###0123456 +$TMUX setenv -g V '#####[bg=yellow]0123456' +check '#{V} #{w:V}' '#####[bg=yellow]0123456 9' '##43m0123456 949m' +check '#{=3:V}' '#####[bg=yellow]0' '##43m049m' +check '#{=-3:V}' '#[bg=yellow]456' '43m45649m' + +$TMUX kill-server 2>/dev/null +$TMUX2 kill-server 2>/dev/null +exit 0 diff --git a/resize.c b/resize.c index d6e6dce2..172cbcb5 100644 --- a/resize.c +++ b/resize.c @@ -92,130 +92,156 @@ ignore_client_size(struct client *c) return (0); } -void -default_window_size(struct client *c, struct session *s, struct window *w, - u_int *sx, u_int *sy, u_int *xpixel, u_int *ypixel, int type) +static u_int +clients_with_window(struct window *w) { struct client *loop; - u_int cx, cy, n; - const char *value; + u_int n = 0; - if (type == -1) - type = options_get_number(global_w_options, "window-size"); - switch (type) { - case WINDOW_SIZE_LARGEST: + TAILQ_FOREACH(loop, &clients, entry) { + if (ignore_client_size(loop) || !session_has(loop->session, w)) + continue; + if (++n > 1) + break; + } + return (n); +} + +static int +clients_calculate_size(int type, int current, struct session *s, + struct window *w, int (*skip_client)(struct client *, int, int, + struct session *, struct window *), u_int *sx, u_int *sy, u_int *xpixel, + u_int *ypixel) +{ + struct client *loop; + u_int cx, cy, n = 0; + + /* Manual windows do not have their size changed based on a client. */ + if (type == WINDOW_SIZE_MANUAL) + return (0); + + /* + * Start comparing with 0 for largest and UINT_MAX for smallest or + * latest. + */ + if (type == WINDOW_SIZE_LARGEST) *sx = *sy = 0; - *xpixel = *ypixel = 0; - TAILQ_FOREACH(loop, &clients, entry) { - if (ignore_client_size(loop)) - continue; - if (w != NULL && !session_has(loop->session, w)) - continue; - if (w == NULL && loop->session != s) - continue; + else + *sx = *sy = UINT_MAX; + *xpixel = *ypixel = 0; - cx = loop->tty.sx; - cy = loop->tty.sy - status_line_size(loop); + /* + * For latest, count the number of clients with this window. We only + * care if there is more than one. + */ + if (type == WINDOW_SIZE_LATEST) + n = clients_with_window(w); + /* Loop over the clients and work out the size. */ + TAILQ_FOREACH(loop, &clients, entry) { + if (ignore_client_size(loop)) + continue; + if (skip_client(loop, type, current, s, w)) + continue; + + /* + * If there are multiple clients attached, only accept the + * latest client; otherwise let the only client be chosen as + * for smallest. + */ + if (type == WINDOW_SIZE_LATEST && n > 1 && loop != w->latest) + continue; + + /* Work out this client's size. */ + cx = loop->tty.sx; + cy = loop->tty.sy - status_line_size(loop); + + /* + * If it is larger or smaller than the best so far, update the + * new size. + */ + if (type == WINDOW_SIZE_LARGEST) { if (cx > *sx) *sx = cx; if (cy > *sy) *sy = cy; - - if (loop->tty.xpixel > *xpixel && - loop->tty.ypixel > *ypixel) { - *xpixel = loop->tty.xpixel; - *ypixel = loop->tty.ypixel; - } - } - if (*sx == 0 || *sy == 0) - goto manual; - break; - case WINDOW_SIZE_SMALLEST: - *sx = *sy = UINT_MAX; - *xpixel = *ypixel = 0; - TAILQ_FOREACH(loop, &clients, entry) { - if (ignore_client_size(loop)) - continue; - if (w != NULL && !session_has(loop->session, w)) - continue; - if (w == NULL && loop->session != s) - continue; - - cx = loop->tty.sx; - cy = loop->tty.sy - status_line_size(loop); - + } else { if (cx < *sx) *sx = cx; if (cy < *sy) *sy = cy; - - if (loop->tty.xpixel > *xpixel && - loop->tty.ypixel > *ypixel) { - *xpixel = loop->tty.xpixel; - *ypixel = loop->tty.ypixel; - } } - if (*sx == UINT_MAX || *sy == UINT_MAX) - goto manual; - break; - case WINDOW_SIZE_LATEST: + if (loop->tty.xpixel > *xpixel && loop->tty.ypixel > *ypixel) { + *xpixel = loop->tty.xpixel; + *ypixel = loop->tty.ypixel; + } + } + + /* Return whether a suitable size was found. */ + if (type == WINDOW_SIZE_LARGEST) + return (*sx != 0 && *sy != 0); + return (*sx != UINT_MAX && *sy != UINT_MAX); +} + +static int +default_window_size_skip_client (struct client *loop, int type, + __unused int current, struct session *s, struct window *w) +{ + /* + * Latest checks separately, so do not check here. Otherwise only + * include clients where the session contains the window or where the + * session is the given session. + */ + if (type == WINDOW_SIZE_LATEST) + return (0); + if (w != NULL && !session_has(loop->session, w)) + return (1); + if (w == NULL && loop->session != s) + return (1); + return (0); +} + +void +default_window_size(struct client *c, struct session *s, struct window *w, + u_int *sx, u_int *sy, u_int *xpixel, u_int *ypixel, int type) +{ + const char *value; + + /* Get type if not provided. */ + if (type == -1) + type = options_get_number(global_w_options, "window-size"); + + /* + * Latest clients can use the given client if suitable. If there is no + * client and no window, use the default size as for manual type. + */ + if (type == WINDOW_SIZE_LATEST) { if (c != NULL && !ignore_client_size(c)) { *sx = c->tty.sx; *sy = c->tty.sy - status_line_size(c); *xpixel = c->tty.xpixel; - *ypixel = c->tty.ypixel; - } else { - if (w == NULL) - goto manual; - n = 0; - TAILQ_FOREACH(loop, &clients, entry) { - if (!ignore_client_size(loop) && - session_has(loop->session, w)) { - if (++n > 1) - break; - } - } - *sx = *sy = UINT_MAX; - *xpixel = *ypixel = 0; - TAILQ_FOREACH(loop, &clients, entry) { - if (ignore_client_size(loop)) - continue; - if (n > 1 && loop != w->latest) - continue; - s = loop->session; - - cx = loop->tty.sx; - cy = loop->tty.sy - status_line_size(loop); - - if (cx < *sx) - *sx = cx; - if (cy < *sy) - *sy = cy; - - if (loop->tty.xpixel > *xpixel && - loop->tty.ypixel > *ypixel) { - *xpixel = loop->tty.xpixel; - *ypixel = loop->tty.ypixel; - } - } - if (*sx == UINT_MAX || *sy == UINT_MAX) - goto manual; + *ypixel = c->tty.ypixel; + goto done; } - break; - case WINDOW_SIZE_MANUAL: - goto manual; + if (w == NULL) + type = WINDOW_SIZE_MANUAL; } - goto done; -manual: - value = options_get_string(s->options, "default-size"); - if (sscanf(value, "%ux%u", sx, sy) != 2) { - *sx = 80; - *sy = 24; + /* + * Look for a client to base the size on. If none exists (or the type + * is manual), use the default-size option. + */ + if (!clients_calculate_size(type, 0, s, w, + default_window_size_skip_client, sx, sy, xpixel, ypixel)) { + value = options_get_string(s->options, "default-size"); + if (sscanf(value, "%ux%u", sx, sy) != 2) { + *sx = 80; + *sy = 24; + } } done: + /* Make sure the limits are enforced. */ if (*sx < WINDOW_MINIMUM) *sx = WINDOW_MINIMUM; if (*sx > WINDOW_MAXIMUM) @@ -226,127 +252,50 @@ done: *sy = WINDOW_MAXIMUM; } +static int +recalculate_size_skip_client(struct client *loop, __unused int type, + int current, __unused struct session *s, struct window *w) +{ + /* + * If the current flag is set, then skip any client where this window + * is not the current window - this is used for aggressive-resize. + * Otherwise skip any session that doesn't contain the window. + */ + if (current) + return (loop->session->curw->window != w); + return (session_has(loop->session, w) == 0); +} + void recalculate_size(struct window *w, int now) { - struct session *s; - struct client *c; - u_int sx, sy, cx, cy, xpixel = 0, ypixel = 0, n; - int type, current, has, changed; + u_int sx, sy, xpixel = 0, ypixel = 0; + int type, current, changed; + /* + * Do not attempt to resize windows which have no pane, they must be on + * the way to destruction. + */ if (w->active == NULL) return; log_debug("%s: @%u is %u,%u", __func__, w->id, w->sx, w->sy); + /* + * Type is manual, smallest, largest, latest. Current is the + * aggressive-resize option (do not resize based on clients where the + * window is not the current window). + */ type = options_get_number(w->options, "window-size"); current = options_get_number(w->options, "aggressive-resize"); - changed = 1; - switch (type) { - case WINDOW_SIZE_LARGEST: - sx = sy = 0; - TAILQ_FOREACH(c, &clients, entry) { - if (ignore_client_size(c)) - continue; - s = c->session; + /* Look for a suitable client and get the new size. */ + changed = clients_calculate_size(type, current, NULL, w, + recalculate_size_skip_client, &sx, &sy, &xpixel, &ypixel); - if (current) - has = (s->curw->window == w); - else - has = session_has(s, w); - if (!has) - continue; - - cx = c->tty.sx; - cy = c->tty.sy - status_line_size(c); - - if (cx > sx) - sx = cx; - if (cy > sy) - sy = cy; - - if (c->tty.xpixel > xpixel && c->tty.ypixel > ypixel) { - xpixel = c->tty.xpixel; - ypixel = c->tty.ypixel; - } - } - if (sx == 0 || sy == 0) - changed = 0; - break; - case WINDOW_SIZE_SMALLEST: - sx = sy = UINT_MAX; - TAILQ_FOREACH(c, &clients, entry) { - if (ignore_client_size(c)) - continue; - s = c->session; - - if (current) - has = (s->curw->window == w); - else - has = session_has(s, w); - if (!has) - continue; - - cx = c->tty.sx; - cy = c->tty.sy - status_line_size(c); - - if (cx < sx) - sx = cx; - if (cy < sy) - sy = cy; - - if (c->tty.xpixel > xpixel && c->tty.ypixel > ypixel) { - xpixel = c->tty.xpixel; - ypixel = c->tty.ypixel; - } - } - if (sx == UINT_MAX || sy == UINT_MAX) - changed = 0; - break; - case WINDOW_SIZE_LATEST: - n = 0; - TAILQ_FOREACH(c, &clients, entry) { - if (!ignore_client_size(c) && - session_has(c->session, w)) { - if (++n > 1) - break; - } - } - sx = sy = UINT_MAX; - TAILQ_FOREACH(c, &clients, entry) { - if (ignore_client_size(c)) - continue; - if (n > 1 && c != w->latest) - continue; - s = c->session; - - if (current) - has = (s->curw->window == w); - else - has = session_has(s, w); - if (!has) - continue; - - cx = c->tty.sx; - cy = c->tty.sy - status_line_size(c); - - if (cx < sx) - sx = cx; - if (cy < sy) - sy = cy; - - if (c->tty.xpixel > xpixel && c->tty.ypixel > ypixel) { - xpixel = c->tty.xpixel; - ypixel = c->tty.ypixel; - } - } - if (sx == UINT_MAX || sy == UINT_MAX) - changed = 0; - break; - case WINDOW_SIZE_MANUAL: - changed = 0; - break; - } + /* + * Make sure the size has actually changed. If the window has already + * got a resize scheduled, then use the new size; otherwise the old. + */ if (w->flags & WINDOW_RESIZE) { if (!now && changed && w->new_sx == sx && w->new_sy == sy) changed = 0; @@ -355,10 +304,20 @@ recalculate_size(struct window *w, int now) changed = 0; } + /* + * If the size hasn't changed, update the window offset but not the + * size. + */ if (!changed) { tty_update_window_offset(w); return; } + + /* + * If the now flag is set or if the window is sized manually, change + * the size immediately. Otherwise set the flag and it will be done + * later. + */ log_debug("%s: @%u new size %u,%u", __func__, w->id, sx, sy); if (now || type == WINDOW_SIZE_MANUAL) resize_window(w, sx, sy, xpixel, ypixel); diff --git a/screen.c b/screen.c index 95299306..b55c9f87 100644 --- a/screen.c +++ b/screen.c @@ -78,6 +78,7 @@ screen_init(struct screen *s, u_int sx, u_int sy, u_int hlimit) s->title = xstrdup(""); s->titles = NULL; + s->path = NULL; s->cstyle = 0; s->ccolour = xstrdup(""); @@ -120,6 +121,7 @@ screen_free(struct screen *s) { free(s->sel); free(s->tabs); + free(s->path); free(s->title); free(s->ccolour); diff --git a/server-client.c b/server-client.c index e9587e4c..66d0d577 100644 --- a/server-client.c +++ b/server-client.c @@ -1588,10 +1588,6 @@ server_client_check_pane_focus(struct window_pane *wp) if (wp->window->active != wp) goto not_focused; - /* If we're in a mode, we're not focused. */ - if (wp->screen != &wp->base) - goto not_focused; - /* * If our window is the current window in any focused clients with an * attached session, we're focused. @@ -1660,8 +1656,6 @@ server_client_reset_state(struct client *c) s = wp->screen; if (s != NULL) mode = s->mode; - if (c->prompt_string != NULL || c->message_string != NULL) - mode &= ~MODE_CURSOR; log_debug("%s: client %s mode %x", __func__, c->name, mode); /* Reset region and margin. */ @@ -1693,8 +1687,8 @@ server_client_reset_state(struct client *c) * mode. */ if (options_get_number(oo, "mouse")) { - mode &= ~ALL_MOUSE_MODES; if (c->overlay_draw == NULL) { + mode &= ~ALL_MOUSE_MODES; TAILQ_FOREACH(loop, &w->panes, entry) { if (loop->screen->mode & MODE_MOUSE_ALL) mode |= MODE_MOUSE_ALL; @@ -1985,6 +1979,7 @@ server_client_dispatch(struct imsg *imsg, void *arg) switch (imsg->hdr.type) { case MSG_IDENTIFY_FEATURES: case MSG_IDENTIFY_FLAGS: + case MSG_IDENTIFY_LONGFLAGS: case MSG_IDENTIFY_TERM: case MSG_IDENTIFY_TTYNAME: case MSG_IDENTIFY_CWD: @@ -2028,7 +2023,7 @@ server_client_dispatch(struct imsg *imsg, void *arg) break; c->flags &= ~CLIENT_SUSPENDED; - if (c->fd == -1) /* exited in the meantime */ + if (c->fd == -1 || c->session == NULL) /* exited already */ break; s = c->session; @@ -2143,6 +2138,7 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg) const char *data, *home; size_t datalen; int flags, feat; + uint64_t longflags; char *name; if (c->flags & CLIENT_IDENTIFIED) @@ -2167,6 +2163,14 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg) c->flags |= flags; log_debug("client %p IDENTIFY_FLAGS %#x", c, flags); break; + case MSG_IDENTIFY_LONGFLAGS: + if (datalen != sizeof longflags) + fatalx("bad MSG_IDENTIFY_LONGFLAGS size"); + memcpy(&longflags, data, sizeof longflags); + c->flags |= longflags; + log_debug("client %p IDENTIFY_LONGFLAGS %#llx", c, + (unsigned long long)longflags); + break; case MSG_IDENTIFY_TERM: if (datalen == 0 || data[datalen - 1] != '\0') fatalx("bad MSG_IDENTIFY_TERM string"); diff --git a/server-fn.c b/server-fn.c index d5e7cbd7..e3cc181f 100644 --- a/server-fn.c +++ b/server-fn.c @@ -342,6 +342,7 @@ server_destroy_pane(struct window_pane *wp, int notify) time(&t); ctime_r(&t, tim); + tim[strcspn(tim, "\n")] = '\0'; if (WIFEXITED(wp->status)) { screen_write_nputs(&ctx, -1, &gc, diff --git a/server.c b/server.c index ffbd28c1..9a1875f4 100644 --- a/server.c +++ b/server.c @@ -157,7 +157,7 @@ server_start(struct tmuxproc *client, int flags, struct event_base *base, { int pair[2]; sigset_t set, oldset; - struct client *c; + struct client *c = NULL; char *cause = NULL; sigfillset(&set); @@ -223,9 +223,11 @@ server_start(struct tmuxproc *client, int flags, struct event_base *base, } if (cause != NULL) { - cmdq_append(c, cmdq_get_error(cause)); + if (c != NULL) { + cmdq_append(c, cmdq_get_error(cause)); + c->flags |= CLIENT_EXIT; + } free(cause); - c->flags |= CLIENT_EXIT; } server_add_accept(0); diff --git a/status.c b/status.c index 7313d2a0..668c0a3b 100644 --- a/status.c +++ b/status.c @@ -423,11 +423,11 @@ status_redraw(struct client *c) /* Set a status line message. */ void -status_message_set(struct client *c, int ignore_styles, const char *fmt, ...) +status_message_set(struct client *c, int delay, int ignore_styles, + const char *fmt, ...) { struct timeval tv; va_list ap; - int delay; status_message_clear(c); status_push_screen(c); @@ -439,7 +439,12 @@ status_message_set(struct client *c, int ignore_styles, const char *fmt, ...) server_add_message("%s message: %s", c->name, c->message_string); - delay = options_get_number(c->session->options, "display-time"); + /* + * With delay -1, the display-time option is used; zero means wait for + * key press; more than zero is the actual delay time in milliseconds. + */ + if (delay == -1) + delay = options_get_number(c->session->options, "display-time"); if (delay > 0) { tv.tv_sec = delay / 1000; tv.tv_usec = (delay % 1000) * 1000L; @@ -447,6 +452,7 @@ status_message_set(struct client *c, int ignore_styles, const char *fmt, ...) if (event_initialized(&c->message_timer)) evtimer_del(&c->message_timer); evtimer_set(&c->message_timer, status_message_callback, c); + evtimer_add(&c->message_timer, &tv); } diff --git a/tmux.1 b/tmux.1 index 65ce211f..53687194 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1003,7 +1003,7 @@ wait for an empty line input before exiting in control mode .El .Pp A leading -.Ql ! +.Ql \&! turns a flag off if the client is already attached. .Fl r is an alias for @@ -1407,7 +1407,7 @@ and .Fl T show debugging information about jobs and terminals. .It Xo Ic source-file -.Op Fl nqv +.Op Fl Fnqv .Ar path .Ar ... .Xc @@ -1418,6 +1418,11 @@ Execute commands from one or more files specified by .Xr glob 7 patterns). If +.Fl F +is present, then +.Ar path +is expanded as a format. +If .Fl q is given, no error will be returned if .Ar path @@ -1853,7 +1858,7 @@ The .Fl P option prints information about the new window after it has been created. By default, it uses the format -.Ql #{session_name}:#{window_index} +.Ql #{session_name}:#{window_index}.#{pane_index} but a different format may be specified with .Fl F . .It Xo Ic capture-pane @@ -3034,7 +3039,7 @@ Send the prefix key, or with .Fl 2 the secondary prefix key, to a window as if it was pressed. .It Xo Ic unbind-key -.Op Fl an +.Op Fl anq .Op Fl T Ar key-table .Ar key .Xc @@ -3049,6 +3054,9 @@ are the same as for If .Fl a is present, all key bindings are removed. +The +.Fl q +option prevents errors being returned. .El .Sh OPTIONS The appearance and behaviour of @@ -3159,6 +3167,9 @@ flag unsets an option, so a session inherits the option from the global options (or with .Fl g , restores a global option to the default). +.Ar value +depends on the option and may be a number, a string, or a flag (on, off, or +omitted to toggle). .Pp The .Fl o @@ -3230,9 +3241,6 @@ includes hooks (omitted by default). .Fl A includes options inherited from a parent set of options, such options are marked with an asterisk. -.Ar value -depends on the option and may be a number, a string, or a flag (on, off, or -omitted to toggle). .El .Pp Available server options are: @@ -4536,7 +4544,7 @@ multiplication .Ql * , division .Ql / , -and modulus +modulus .Ql m or .Ql % @@ -4545,7 +4553,15 @@ or must be escaped as .Ql %% in formats which are also expanded by -.Xr strftime 3 ) . +.Xr strftime 3 ) +and numeric comparison operators +.Ql == , +.Ql != , +.Ql < , +.Ql <= , +.Ql > +and +.Ql >= . For example, .Ql #{e|*|f|4:5.5,3} multiplies 5.5 by 3 for a result with four decimal places and @@ -4574,6 +4590,9 @@ pads the string to a given width, for example .Ql #{p10:pane_title} will result in a width of at least 10 characters. A positive width pads on the left, a negative on the right. +.Ql n +expands to the length of the variable, for example +.Ql #{n:window_name} . .Pp Prefixing a time variable with .Ql t:\& @@ -4770,6 +4789,7 @@ The following variables are available, where appropriate: .It Li "pane_in_mode" Ta "" Ta "1 if pane is in a mode" .It Li "pane_index" Ta "#P" Ta "Index of pane" .It Li "pane_input_off" Ta "" Ta "1 if input to pane is disabled" +.It Li "pane_last" Ta "" Ta "1 if last pane" .It Li "pane_left" Ta "" Ta "Left of pane" .It Li "pane_marked" Ta "" Ta "1 if this is the marked pane" .It Li "pane_marked_set" Ta "" Ta "1 if a marked pane is set" @@ -5098,7 +5118,7 @@ section). Commands to alter and view the environment are: .Bl -tag -width Ds .It Xo Ic set-environment -.Op Fl hgru +.Op Fl Fhgru .Op Fl t Ar target-session .Ar name Op Ar value .Xc @@ -5109,6 +5129,11 @@ If is used, the change is made in the global environment; otherwise, it is applied to the session environment for .Ar target-session . +If +.Fl F +is present, then +.Ar value +is expanded as a format. The .Fl u flag unsets a variable. @@ -5314,6 +5339,7 @@ option. This command works only from inside .Nm . .It Xo Ic display-menu +.Op Fl O .Op Fl c Ar target-client .Op Fl t Ar target-pane .Op Fl T Ar title @@ -5365,8 +5391,14 @@ Both may be a row or column number, or one of the following special values: Each menu consists of items followed by a key shortcut shown in brackets. If the menu is too large to fit on the terminal, it is not displayed. Pressing the key shortcut chooses the corresponding item. -If the mouse is enabled and the menu is opened from a mouse key binding, releasing -the mouse button with an item selected will choose that item. +If the mouse is enabled and the menu is opened from a mouse key binding, +releasing the mouse button with an item selected chooses that item and +releasing the mouse button without an item selected closes the menu. +.Fl O +changes this behaviour so that the menu does not close when the mouse button is +released without an item selected the menu is not closed and a mouse button +must be clicked to choose an item. +.Pp The following keys are also available: .Bl -column "Key" "Function" -offset indent .It Sy "Key" Ta Sy "Function" @@ -5378,6 +5410,7 @@ The following keys are also available: .It Xo Ic display-message .Op Fl aIpv .Op Fl c Ar target-client +.Op Fl d Ar delay .Op Fl t Ar target-pane .Op Ar message .Xc @@ -5387,7 +5420,14 @@ If .Fl p is given, the output is printed to stdout, otherwise it is displayed in the .Ar target-client -status line. +status line for up to +.Ar delay +milliseconds. +If +.Ar delay +is not given, the +.Ic message-time +option is used; a delay of zero waits for a key press. The format of .Ar message is described in the @@ -5629,12 +5669,21 @@ See the .Sx FORMATS section. .It Xo Ic load-buffer +.Op Fl w .Op Fl b Ar buffer-name +.Op Fl t Ar target-client .Ar path .Xc .D1 (alias: Ic loadb ) Load the contents of the specified paste buffer from .Ar path . +If +.Fl w +is given, the buffer is also sent to the clipboard for +.Ar target-client +using the +.Xr xterm 1 +escape sequence, if possible. .It Xo Ic paste-buffer .Op Fl dpr .Op Fl b Ar buffer-name @@ -5671,14 +5720,22 @@ The .Fl a option appends to rather than overwriting the file. .It Xo Ic set-buffer -.Op Fl a +.Op Fl aw .Op Fl b Ar buffer-name +.Op Fl t Ar target-client .Op Fl n Ar new-buffer-name .Ar data .Xc .D1 (alias: Ic setb ) Set the contents of the specified buffer to .Ar data . +If +.Fl w +is given, the buffer is also sent to the clipboard for +.Ar target-client +using the +.Xr xterm 1 +escape sequence, if possible. The .Fl a option appends to rather than overwriting the buffer. @@ -5855,8 +5912,8 @@ Set a styled underscore. The single parameter is one of: 0 for no underscore, 1 for normal underscore, 2 for double underscore, 3 for curly underscore, 4 for dotted underscore and 5 for dashed underscore. -.It Em \&Setulc -Set the underscore colour. +.It Em \&Setulc , \&ol +Set the underscore colour or reset to the default. The argument is (red * 65536) + (green * 256) + blue where each is between 0 and 255. .It Em \&Ss , Se diff --git a/tmux.c b/tmux.c index b9a676a2..066714df 100644 --- a/tmux.c +++ b/tmux.c @@ -322,8 +322,8 @@ main(int argc, char **argv) char *path = NULL, *label = NULL; char *cause, **var; const char *s, *shell, *cwd; - int opt, flags = 0, keys; - int feat = 0; + int opt, keys, feat = 0; + uint64_t flags = 0; const struct options_table_entry *oe; if (setlocale(LC_CTYPE, "en_US.UTF-8") == NULL && diff --git a/tmux.h b/tmux.h index a17ae5c6..44ba53f5 100644 --- a/tmux.h +++ b/tmux.h @@ -450,6 +450,7 @@ enum tty_code_code { TTYC_KUP6, TTYC_KUP7, TTYC_MS, + TTYC_OL, TTYC_OP, TTYC_REV, TTYC_RGB, @@ -461,6 +462,7 @@ enum tty_code_code { TTYC_SE, TTYC_SETAB, TTYC_SETAF, + TTYC_SETAL, TTYC_SETRGBB, TTYC_SETRGBF, TTYC_SETULC, @@ -498,6 +500,7 @@ enum msgtype { MSG_IDENTIFY_CWD, MSG_IDENTIFY_FEATURES, MSG_IDENTIFY_STDOUT, + MSG_IDENTIFY_LONGFLAGS, MSG_COMMAND = 200, MSG_DETACH, @@ -1790,6 +1793,7 @@ enum options_table_type { struct options_table_entry { const char *name; + const char *alternative_name; enum options_table_type type; int scope; int flags; @@ -1809,6 +1813,11 @@ struct options_table_entry { const char *unit; }; +struct options_name_map { + const char *from; + const char *to; +}; + /* Common command usages. */ #define CMD_TARGET_PANE_USAGE "[-t target-pane]" #define CMD_TARGET_WINDOW_USAGE "[-t target-window]" @@ -2041,7 +2050,8 @@ int options_remove_or_default(struct options_entry *, int, char **); /* options-table.c */ -extern const struct options_table_entry options_table[]; +extern const struct options_table_entry options_table[]; +extern const struct options_name_map options_other_names[]; /* job.c */ typedef void (*job_update_cb) (struct job *); @@ -2121,6 +2131,7 @@ int tty_open(struct tty *, char **); void tty_close(struct tty *); void tty_free(struct tty *); void tty_update_features(struct tty *); +void tty_set_selection(struct tty *, const char *, size_t); void tty_write(void (*)(struct tty *, const struct tty_ctx *), struct tty_ctx *); void tty_cmd_alignmenttest(struct tty *, const struct tty_ctx *); @@ -2325,7 +2336,7 @@ void printflike(2, 3) cmdq_error(struct cmdq_item *, const char *, ...); void cmd_wait_for_flush(void); /* client.c */ -int client_main(struct event_base *, int, char **, int, int); +int client_main(struct event_base *, int, char **, uint64_t, int); /* key-bindings.c */ struct key_table *key_bindings_get_table(const char *, int); @@ -2452,8 +2463,7 @@ struct style_range *status_get_range(struct client *, u_int, u_int); void status_init(struct client *); void status_free(struct client *); int status_redraw(struct client *); -void printflike(3, 4) status_message_set(struct client *, int, const char *, - ...); +void status_message_set(struct client *, int, int, const char *, ...); void status_message_clear(struct client *); int status_message_redraw(struct client *); void status_prompt_set(struct client *, struct cmd_find_state *, @@ -2971,6 +2981,7 @@ __dead void printflike(1, 2) fatalx(const char *, ...); /* menu.c */ #define MENU_NOMOUSE 0x1 #define MENU_TAB 0x2 +#define MENU_STAYOPEN 0x4 struct menu *menu_create(const char *); void menu_add_items(struct menu *, const struct menu_item *, struct cmdq_item *, struct client *, diff --git a/tty-features.c b/tty-features.c index 5891e2c3..f167a2d3 100644 --- a/tty-features.c +++ b/tty-features.c @@ -112,6 +112,7 @@ static const struct tty_feature tty_feature_overline = { static const char *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", NULL }; static const struct tty_feature tty_feature_usstyle = { @@ -336,7 +337,7 @@ tty_default_features(int *feat, const char *name, u_int version) "256,RGB,bpaste,clipboard,strikethrough,title" { .name = "mintty", .features = TTY_FEATURES_BASE_MODERN_XTERM - ",ccolour,cstyle,extkeys,margins,overline" + ",ccolour,cstyle,extkeys,margins,overline,usstyle" }, { .name = "tmux", .features = TTY_FEATURES_BASE_MODERN_XTERM diff --git a/tty-term.c b/tty-term.c index f320db89..ec8302a6 100644 --- a/tty-term.c +++ b/tty-term.c @@ -248,6 +248,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_OL] = { TTYCODE_STRING, "ol" }, [TTYC_OP] = { TTYCODE_STRING, "op" }, [TTYC_REV] = { TTYCODE_STRING, "rev" }, [TTYC_RGB] = { TTYCODE_FLAG, "RGB" }, @@ -258,6 +259,7 @@ static const struct tty_term_code_entry tty_term_codes[] = { [TTYC_RMKX] = { TTYCODE_STRING, "rmkx" }, [TTYC_SETAB] = { TTYCODE_STRING, "setab" }, [TTYC_SETAF] = { TTYCODE_STRING, "setaf" }, + [TTYC_SETAL] = { TTYCODE_STRING, "setal" }, [TTYC_SETRGBB] = { TTYCODE_STRING, "setrgbb" }, [TTYC_SETRGBF] = { TTYCODE_STRING, "setrgbf" }, [TTYC_SETULC] = { TTYCODE_STRING, "Setulc" }, @@ -305,6 +307,8 @@ tty_term_strip(const char *s) ptr++; if (*ptr == '>') ptr++; + if (*ptr == '\0') + break; } buf[len++] = *ptr; diff --git a/tty.c b/tty.c index 14b770e2..bcbccca6 100644 --- a/tty.c +++ b/tty.c @@ -1896,19 +1896,27 @@ tty_cmd_cells(struct tty *tty, const struct tty_ctx *ctx) void tty_cmd_setselection(struct tty *tty, const struct tty_ctx *ctx) { - char *buf; - size_t off; + tty_set_selection(tty, ctx->ptr, ctx->num); +} +void +tty_set_selection(struct tty *tty, const char *buf, size_t len) +{ + char *encoded; + size_t size; + + if (~tty->flags & TTY_STARTED) + return; if (!tty_term_has(tty->term, TTYC_MS)) return; - off = 4 * ((ctx->num + 2) / 3) + 1; /* storage for base64 */ - buf = xmalloc(off); + size = 4 * ((len + 2) / 3) + 1; /* storage for base64 */ + encoded = xmalloc(size); - b64_ntop(ctx->ptr, ctx->num, buf, off); - tty_putcode_ptr2(tty, TTYC_MS, "", buf); + b64_ntop(buf, len, encoded, size); + tty_putcode_ptr2(tty, TTYC_MS, "", encoded); - free(buf); + free(encoded); } void @@ -2535,7 +2543,7 @@ tty_colours_fg(struct tty *tty, const struct grid_cell *gc) /* Is this a 24-bit or 256-colour colour? */ if (gc->fg & COLOUR_FLAG_RGB || gc->fg & COLOUR_FLAG_256) { if (tty_try_colour(tty, gc->fg, "38") == 0) - goto save_fg; + goto save; /* Should not get here, already converted in tty_check_fg. */ return; } @@ -2547,13 +2555,13 @@ tty_colours_fg(struct tty *tty, const struct grid_cell *gc) tty_puts(tty, s); } else tty_putcode1(tty, TTYC_SETAF, gc->fg - 90 + 8); - goto save_fg; + goto save; } /* Otherwise set the foreground colour. */ tty_putcode1(tty, TTYC_SETAF, gc->fg); -save_fg: +save: /* Save the new values in the terminal current cell. */ tc->fg = gc->fg; } @@ -2567,7 +2575,7 @@ tty_colours_bg(struct tty *tty, const struct grid_cell *gc) /* Is this a 24-bit or 256-colour colour? */ if (gc->bg & COLOUR_FLAG_RGB || gc->bg & COLOUR_FLAG_256) { if (tty_try_colour(tty, gc->bg, "48") == 0) - goto save_bg; + goto save; /* Should not get here, already converted in tty_check_bg. */ return; } @@ -2579,13 +2587,13 @@ tty_colours_bg(struct tty *tty, const struct grid_cell *gc) tty_puts(tty, s); } else tty_putcode1(tty, TTYC_SETAB, gc->bg - 90 + 8); - goto save_bg; + goto save; } /* Otherwise set the background colour. */ tty_putcode1(tty, TTYC_SETAB, gc->bg); -save_bg: +save: /* Save the new values in the terminal current cell. */ tc->bg = gc->bg; } @@ -2597,20 +2605,34 @@ tty_colours_us(struct tty *tty, const struct grid_cell *gc) u_int c; u_char r, g, b; + /* Clear underline colour. */ + if (gc->us == 0) { + tty_putcode(tty, TTYC_OL); + goto save; + } + /* Must be an RGB colour - this should never happen. */ if (~gc->us & COLOUR_FLAG_RGB) return; /* - * Setulc follows the ncurses(3) one argument "direct colour" + * Setulc and setal follows the ncurses(3) one argument "direct colour" * capability format. Calculate the colour value. */ colour_split_rgb(gc->us, &r, &g, &b); c = (65536 * r) + (256 * g) + b; - /* Write the colour. */ - tty_putcode1(tty, TTYC_SETULC, c); + /* + * Write the colour. Only use setal if the RGB flag is set because the + * non-RGB version may be wrong. + */ + if (tty_term_has(tty->term, TTYC_SETULC)) + tty_putcode1(tty, TTYC_SETULC, c); + else if (tty_term_has(tty->term, TTYC_SETAL) && + tty_term_has(tty->term, TTYC_RGB)) + tty_putcode1(tty, TTYC_SETAL, c); +save: /* Save the new values in the terminal current cell. */ tc->us = gc->us; } diff --git a/window-copy.c b/window-copy.c index 7103131d..1dc0c293 100644 --- a/window-copy.c +++ b/window-copy.c @@ -72,14 +72,13 @@ static int window_copy_search_marks(struct window_mode_entry *, struct screen *, int, int); static void window_copy_clear_marks(struct window_mode_entry *); static void window_copy_move_left(struct screen *, u_int *, u_int *, int); -static void window_copy_move_right(struct screen *, u_int *, u_int *, int); static int window_copy_is_lowercase(const char *); static int window_copy_search_jump(struct window_mode_entry *, struct grid *, struct grid *, u_int, u_int, u_int, int, int, - int, int); -static int window_copy_search(struct window_mode_entry *, int, int); -static int window_copy_search_up(struct window_mode_entry *, int); -static int window_copy_search_down(struct window_mode_entry *, int); + int, int, u_int *); +static int window_copy_search(struct window_mode_entry *, int, int, int); +static int window_copy_search_up(struct window_mode_entry *, int, int); +static int window_copy_search_down(struct window_mode_entry *, int, int); static void window_copy_goto_line(struct window_mode_entry *, const char *); static void window_copy_update_cursor(struct window_mode_entry *, u_int, u_int); @@ -110,7 +109,7 @@ static void window_copy_cursor_back_to_indentation( static void window_copy_cursor_end_of_line(struct window_mode_entry *); static void window_copy_other_end(struct window_mode_entry *); static void window_copy_cursor_left(struct window_mode_entry *); -static void window_copy_cursor_right(struct window_mode_entry *); +static void window_copy_cursor_right(struct window_mode_entry *, int); static void window_copy_cursor_up(struct window_mode_entry *, int); static void window_copy_cursor_down(struct window_mode_entry *, int); static void window_copy_cursor_jump(struct window_mode_entry *); @@ -1094,7 +1093,7 @@ window_copy_cmd_cursor_right(struct window_copy_cmd_state *cs) u_int np = wme->prefix; for (; np != 0; np--) - window_copy_cursor_right(wme); + window_copy_cursor_right(wme, 0); return (WINDOW_COPY_CMD_NOTHING); } @@ -1685,10 +1684,10 @@ window_copy_cmd_search_again(struct window_copy_cmd_state *cs) if (data->searchtype == WINDOW_COPY_SEARCHUP) { for (; np != 0; np--) - window_copy_search_up(wme, data->searchregex); + window_copy_search_up(wme, data->searchregex, 1); } else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) { for (; np != 0; np--) - window_copy_search_down(wme, data->searchregex); + window_copy_search_down(wme, data->searchregex, 1); } return (WINDOW_COPY_CMD_NOTHING); } @@ -1702,10 +1701,10 @@ window_copy_cmd_search_reverse(struct window_copy_cmd_state *cs) if (data->searchtype == WINDOW_COPY_SEARCHUP) { for (; np != 0; np--) - window_copy_search_down(wme, data->searchregex); + window_copy_search_down(wme, data->searchregex, 1); } else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) { for (; np != 0; np--) - window_copy_search_up(wme, data->searchregex); + window_copy_search_up(wme, data->searchregex, 1); } return (WINDOW_COPY_CMD_NOTHING); } @@ -1953,7 +1952,7 @@ window_copy_cmd_search_backward(struct window_copy_cmd_state *cs) data->searchregex = 1; data->timeout = 0; for (; np != 0; np--) - window_copy_search_up(wme, 1); + window_copy_search_up(wme, 1, 0); } return (WINDOW_COPY_CMD_NOTHING); } @@ -1973,7 +1972,7 @@ window_copy_cmd_search_backward_text(struct window_copy_cmd_state *cs) data->searchregex = 0; data->timeout = 0; for (; np != 0; np--) - window_copy_search_up(wme, 0); + window_copy_search_up(wme, 0, 0); } return (WINDOW_COPY_CMD_NOTHING); } @@ -1993,7 +1992,7 @@ window_copy_cmd_search_forward(struct window_copy_cmd_state *cs) data->searchregex = 1; data->timeout = 0; for (; np != 0; np--) - window_copy_search_down(wme, 1); + window_copy_search_down(wme, 1, 0); } return (WINDOW_COPY_CMD_NOTHING); } @@ -2013,7 +2012,7 @@ window_copy_cmd_search_forward_text(struct window_copy_cmd_state *cs) data->searchregex = 0; data->timeout = 0; for (; np != 0; np--) - window_copy_search_down(wme, 0); + window_copy_search_down(wme, 0, 0); } return (WINDOW_COPY_CMD_NOTHING); } @@ -2052,7 +2051,7 @@ window_copy_cmd_search_backward_incremental(struct window_copy_cmd_state *cs) data->searchregex = 0; free(data->searchstr); data->searchstr = xstrdup(argument); - if (!window_copy_search_up(wme, 0)) { + if (!window_copy_search_up(wme, 0, 1)) { window_copy_clear_marks(wme); return (WINDOW_COPY_CMD_REDRAW); } @@ -2062,7 +2061,7 @@ window_copy_cmd_search_backward_incremental(struct window_copy_cmd_state *cs) data->searchregex = 0; free(data->searchstr); data->searchstr = xstrdup(argument); - if (!window_copy_search_down(wme, 0)) { + if (!window_copy_search_down(wme, 0, 0)) { window_copy_clear_marks(wme); return (WINDOW_COPY_CMD_REDRAW); } @@ -2105,7 +2104,7 @@ window_copy_cmd_search_forward_incremental(struct window_copy_cmd_state *cs) data->searchregex = 0; free(data->searchstr); data->searchstr = xstrdup(argument); - if (!window_copy_search_down(wme, 0)) { + if (!window_copy_search_down(wme, 0, 1)) { window_copy_clear_marks(wme); return (WINDOW_COPY_CMD_REDRAW); } @@ -2115,7 +2114,7 @@ window_copy_cmd_search_forward_incremental(struct window_copy_cmd_state *cs) data->searchregex = 0; free(data->searchstr); data->searchstr = xstrdup(argument); - if (!window_copy_search_up(wme, 0)) { + if (!window_copy_search_up(wme, 0, 1)) { window_copy_clear_marks(wme); return (WINDOW_COPY_CMD_REDRAW); } @@ -2406,8 +2405,8 @@ window_copy_search_compare(struct grid *gd, u_int px, u_int py, } static int -window_copy_search_lr(struct grid *gd, - struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last, int cis) +window_copy_search_lr(struct grid *gd, struct grid *sgd, u_int *ppx, u_int py, + u_int first, u_int last, int cis) { u_int ax, bx, px, pywrap, endline; int matched; @@ -2817,23 +2816,6 @@ window_copy_move_left(struct screen *s, u_int *fx, u_int *fy, int wrapflag) *fx = *fx - 1; } -static void -window_copy_move_right(struct screen *s, u_int *fx, u_int *fy, int wrapflag) -{ - if (*fx == screen_size_x(s) - 1) { /* right */ - if (*fy == screen_hsize(s) + screen_size_y(s) - 1) { /* bottom */ - if (wrapflag) { - *fx = 0; - *fy = 0; - } - return; - } - *fx = 0; - *fy = *fy + 1; - } else - *fx = *fx + 1; -} - static int window_copy_is_lowercase(const char *ptr) { @@ -2854,7 +2836,7 @@ window_copy_is_lowercase(const char *ptr) static int window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd, struct grid *sgd, u_int fx, u_int fy, u_int endline, int cis, int wrap, - int direction, int regex) + int direction, int regex, u_int *foundlen) { u_int i, px, sx, ssize = 1; int found = 0, cflags = REG_EXTENDED; @@ -2871,6 +2853,7 @@ window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd, free(sbuf); return (0); } + free(sbuf); } if (direction) { @@ -2878,15 +2861,20 @@ window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd, if (regex) { found = window_copy_search_lr_regex(gd, &px, &sx, i, fx, gd->sx, ®); + if (found) + *foundlen = sx; } else { found = window_copy_search_lr(gd, sgd, &px, i, fx, gd->sx, cis); + if (found) + *foundlen = sgd->sx; } if (found) break; fx = 0; } } else { + *foundlen = 0; for (i = fy + 1; endline < i; i--) { if (regex) { found = window_copy_search_rl_regex(gd, @@ -2902,10 +2890,8 @@ window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd, fx = gd->sx - 1; } } - if (regex) { - free(sbuf); + if (regex) regfree(®); - } if (found) { window_copy_scroll_to(wme, px, i, 1); @@ -2915,7 +2901,7 @@ window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd, return (window_copy_search_jump(wme, gd, sgd, direction ? 0 : gd->sx - 1, direction ? 0 : gd->hsize + gd->sy - 1, fy, cis, 0, - direction, regex)); + direction, regex, foundlen)); } return (0); } @@ -2925,7 +2911,8 @@ window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd, * down. */ static int -window_copy_search(struct window_mode_entry *wme, int direction, int regex) +window_copy_search(struct window_mode_entry *wme, int direction, int regex, + int again) { struct window_pane *wp = wme->wp; struct window_copy_mode_data *data = wme->data; @@ -2933,7 +2920,7 @@ window_copy_search(struct window_mode_entry *wme, int direction, int regex) struct screen_write_ctx ctx; struct grid *gd = s->grid; const char *str = data->searchstr; - u_int fx, fy, endline; + u_int fx, fy, endline, i, foundlen; int wrapflag, cis, found, visible_only; if (regex && str[strcspn(str, "^$*+()?[].\\")] == '\0') @@ -2961,18 +2948,23 @@ window_copy_search(struct window_mode_entry *wme, int direction, int regex) wrapflag = options_get_number(wp->window->options, "wrap-search"); cis = window_copy_is_lowercase(str); - if (direction) { - window_copy_move_right(s, &fx, &fy, wrapflag); + if (direction) endline = gd->hsize + gd->sy - 1; - } else { - window_copy_move_left(s, &fx, &fy, wrapflag); + else { + if (again) + window_copy_move_left(s, &fx, &fy, wrapflag); endline = 0; } found = window_copy_search_jump(wme, gd, ss.grid, fx, fy, endline, cis, - wrapflag, direction, regex); - if (found) + wrapflag, direction, regex, &foundlen); + if (found) { window_copy_search_marks(wme, &ss, regex, visible_only); + if (foundlen != 0) { + for (i = 0; i < foundlen; i++) + window_copy_cursor_right(wme, 1); + } + } window_copy_redraw_screen(wme); screen_free(&ss); @@ -2995,8 +2987,8 @@ window_copy_visible_lines(struct window_copy_mode_data *data, u_int *start, } static int -window_copy_search_mark_at(struct window_copy_mode_data *data, u_int px, u_int py, - u_int *at) +window_copy_search_mark_at(struct window_copy_mode_data *data, u_int px, + u_int py, u_int *at) { struct screen *s = data->backing; struct grid *gd = s->grid; @@ -3049,6 +3041,7 @@ window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp, free(sbuf); return (0); } + free(sbuf); } tstart = get_timer(); @@ -3096,7 +3089,7 @@ again: data->searchgen++; } - px++; + px += width; } t = get_timer(); @@ -3146,10 +3139,8 @@ again: out: if (ssp == &ss) screen_free(&ss); - if (regex) { - free(sbuf); + if (regex) regfree(®); - } return (1); } @@ -3163,15 +3154,15 @@ window_copy_clear_marks(struct window_mode_entry *wme) } static int -window_copy_search_up(struct window_mode_entry *wme, int regex) +window_copy_search_up(struct window_mode_entry *wme, int regex, int again) { - return (window_copy_search(wme, 0, regex)); + return (window_copy_search(wme, 0, regex, again)); } static int -window_copy_search_down(struct window_mode_entry *wme, int regex) +window_copy_search_down(struct window_mode_entry *wme, int regex, int again) { - return (window_copy_search(wme, 1, regex)); + return (window_copy_search(wme, 1, regex, again)); } static void @@ -3256,7 +3247,7 @@ window_copy_update_style(struct window_mode_entry *wme, u_int fx, u_int fy, { struct window_copy_mode_data *data = wme->data; u_int mark, start, end, cy, cursor, current; - int inv = 0; + int inv = 0, found = 0; if (data->showmark && fy == data->my) { gc->attr = mkgc->attr; @@ -3282,20 +3273,28 @@ window_copy_update_style(struct window_mode_entry *wme, u_int fx, u_int fy, return; cy = screen_hsize(data->backing) - data->oy + data->cy; - if (window_copy_search_mark_at(data, data->cx, cy, &cursor) == 0 && - data->searchmark[cursor] == mark) { - window_copy_match_start_end(data, cursor, &start, &end); - if (current >= start && current <= end) { - gc->attr = cgc->attr; - if (inv) { - gc->fg = cgc->bg; - gc->bg = cgc->fg; + if (window_copy_search_mark_at(data, data->cx, cy, &cursor) == 0) { + if (data->searchmark[cursor] == mark) + found = 1; + else if (cursor != 0) { + cursor--; + if (data->searchmark[cursor] == mark) + found = 1; + } + if (found) { + window_copy_match_start_end(data, cursor, &start, &end); + if (current >= start && current <= end) { + gc->attr = cgc->attr; + if (inv) { + gc->fg = cgc->bg; + gc->bg = cgc->fg; + } + else { + gc->fg = cgc->fg; + gc->bg = cgc->bg; + } + return; } - else { - gc->fg = cgc->fg; - gc->bg = cgc->bg; - } - return; } } @@ -3370,7 +3369,8 @@ window_copy_write_line(struct window_mode_entry *wme, } else if (data->searchthis == -1) { size = xsnprintf(hdr, sizeof hdr, "(%d%s results) [%u/%u]", data->searchcount, - data->searchmore ? "+" : "", data->oy, hsize); + data->searchmore ? "+" : "", data->oy, + hsize); } else { size = xsnprintf(hdr, sizeof hdr, "(%d/%d results) [%u/%u]", data->searchthis, @@ -4142,7 +4142,7 @@ window_copy_cursor_left(struct window_mode_entry *wme) } static void -window_copy_cursor_right(struct window_mode_entry *wme) +window_copy_cursor_right(struct window_mode_entry *wme, int all) { struct window_copy_mode_data *data = wme->data; u_int px, py, yy, cx, cy; @@ -4150,7 +4150,7 @@ window_copy_cursor_right(struct window_mode_entry *wme) py = screen_hsize(data->backing) + data->cy - data->oy; yy = screen_hsize(data->backing) + screen_size_y(data->backing) - 1; - if (data->screen.sel != NULL && data->rectflag) + if (all || (data->screen.sel != NULL && data->rectflag)) px = screen_size_x(&data->screen); else px = window_copy_find_length(wme, py); diff --git a/window-customize.c b/window-customize.c index ecafc776..1dad07cd 100644 --- a/window-customize.c +++ b/window-customize.c @@ -380,7 +380,7 @@ window_customize_build_options(struct window_customize_modedata *data, struct format_tree *ft, const char *filter, struct cmd_find_state *fs) { struct mode_tree_item *top; - struct options_entry *o, *loop; + struct options_entry *o = NULL, *loop; const char **list = NULL, *name; u_int size = 0, i; enum window_customize_scope scope; @@ -1003,7 +1003,7 @@ window_customize_set_option_callback(struct client *c, void *itemdata, fail: *cause = toupper((u_char)*cause); - status_message_set(c, 1, "%s", cause); + status_message_set(c, -1, 1, "%s", cause); free(cause); return (0); } @@ -1018,7 +1018,7 @@ window_customize_set_option(struct client *c, struct options *oo; struct window_customize_itemdata *new_item; int flag, idx = item->idx; - enum window_customize_scope scope; + enum window_customize_scope scope = WINDOW_CUSTOMIZE_NONE; u_int choice; const char *name = item->name, *space = ""; char *prompt, *value, *text; @@ -1031,7 +1031,7 @@ window_customize_set_option(struct client *c, return; oe = options_table_entry(o); - if (~oe->scope & OPTIONS_TABLE_PANE) + if (oe != NULL && ~oe->scope & OPTIONS_TABLE_PANE) pane = 0; if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY)) { scope = item->scope; @@ -1209,7 +1209,7 @@ window_customize_set_command_callback(struct client *c, void *itemdata, fail: *error = toupper((u_char)*error); - status_message_set(c, 1, "%s", error); + status_message_set(c, -1, 1, "%s", error); free(error); return (0); }