diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 89c6549b..0c517a52 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -29,7 +29,7 @@ uname -sp && tmux -V && echo $TERM Also include: -- Your platform (Linux, OS X, or whatever). +- Your platform (Linux, macOS, or whatever). - A brief description of the problem with steps to reproduce. diff --git a/.github/README.md b/.github/README.md index 5590d0b2..353f10ec 100644 --- a/.github/README.md +++ b/.github/README.md @@ -4,7 +4,7 @@ tmux is a terminal multiplexer: it enables a number of terminals to be created, accessed, and controlled from a single screen. tmux may be detached from a screen and continue running in the background, then later reattached. -This release runs on OpenBSD, FreeBSD, NetBSD, Linux, OS X and Solaris. +This release runs on OpenBSD, FreeBSD, NetBSD, Linux, macOS and Solaris. ## Dependencies diff --git a/README b/README index 8732a027..57329629 100644 --- a/README +++ b/README @@ -4,7 +4,7 @@ tmux is a terminal multiplexer: it enables a number of terminals to be created, accessed, and controlled from a single screen. tmux may be detached from a screen and continue running in the background, then later reattached. -This release runs on OpenBSD, FreeBSD, NetBSD, Linux, OS X and Solaris. +This release runs on OpenBSD, FreeBSD, NetBSD, Linux, macOS and Solaris. * Dependencies diff --git a/README.ja b/README.ja index 1580df52..3c944738 100644 --- a/README.ja +++ b/README.ja @@ -3,7 +3,7 @@ tmuxへようこそ! tmuxはターミナルマルチプレクサーです。複数のターミナルを一つのスクリーン内に作成し、操作することができます。 バックグラウンドで処理を実行中に一度スクリーンから離れて後から復帰することも可能です。 -OpenBSD、FreeBSD、NetBSD、Linux、OS X、Solarisで実行できます。 +OpenBSD、FreeBSD、NetBSD、Linux、macOS、Solarisで実行できます。 tmuxはlibevent 2.x.に依存します。 下記からダウンロードしてください。 diff --git a/arguments.c b/arguments.c index 4b08de2c..a27d8e0a 100644 --- a/arguments.c +++ b/arguments.c @@ -108,6 +108,7 @@ args_value_as_string(struct args_value *value) case ARGS_STRING: return (value->string); } + fatalx("unexpected argument type"); } /* Create an empty arguments set. */ @@ -753,6 +754,7 @@ args_make_commands(struct args_command_state *state, int argc, char **argv, case CMD_PARSE_SUCCESS: return (pr->cmdlist); } + fatalx("invalid parse return state"); } /* Free commands state. */ diff --git a/cmd-confirm-before.c b/cmd-confirm-before.c index 95841962..ce8c95e0 100644 --- a/cmd-confirm-before.c +++ b/cmd-confirm-before.c @@ -72,7 +72,7 @@ cmd_confirm_before_exec(struct cmd *self, struct cmdq_item *item) int wait = !args_has(args, 'b'); cdata = xcalloc(1, sizeof *cdata); - cdata->cmdlist = args_make_commands_now(self, item, 0, 0); + cdata->cmdlist = args_make_commands_now(self, item, 0, 1); if (cdata->cmdlist == NULL) return (CMD_RETURN_ERROR); diff --git a/cmd-display-menu.c b/cmd-display-menu.c index 1a11bd01..e6a503b1 100644 --- a/cmd-display-menu.c +++ b/cmd-display-menu.c @@ -52,10 +52,12 @@ const struct cmd_entry cmd_display_popup_entry = { .name = "display-popup", .alias = "popup", - .args = { "BCc:d:Eh:t:w:x:y:", 0, -1, NULL }, - .usage = "[-BCE] [-c target-client] [-d start-directory] [-h height] " - CMD_TARGET_PANE_USAGE " [-w width] " - "[-x position] [-y position] [shell-command]", + .args = { "Bb:Cc:d:e:Eh:s:S:t:T:w:x:y:", 0, -1, NULL }, + .usage = "[-BCE] [-b border-lines] [-c target-client] " + "[-d start-directory] [-e environment] [-h height] " + "[-s style] [-S border-style] " CMD_TARGET_PANE_USAGE + "[-T title] [-w width] [-x position] [-y position] " + "[shell-command]", .target = { 't', CMD_FIND_PANE, 0 }, @@ -353,9 +355,16 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item) struct client *tc = cmdq_get_target_client(item); struct tty *tty = &tc->tty; const char *value, *shell, *shellcmd = NULL; - char *cwd, *cause, **argv = NULL; + const char *style = args_get(args, 's'); + const char *border_style = args_get(args, 'S'); + char *cwd, *cause = NULL, **argv = NULL, *title; int flags = 0, argc = 0; + enum box_lines lines = BOX_LINES_DEFAULT; u_int px, py, w, h, count = args_count(args); + struct args_value *av; + struct environ *env = NULL; + struct options *o = s->curw->window->options; + struct options_entry *oe; if (args_has(args, 'C')) { server_client_clear_overlay(tc); @@ -391,6 +400,20 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item) if (!cmd_display_menu_get_position(tc, item, args, &px, &py, w, h)) return (CMD_RETURN_NORMAL); + value = args_get(args, 'b'); + if (args_has(args, 'B')) + lines = BOX_LINES_NONE; + else if (value != NULL) { + oe = options_get(o, "popup-border-lines"); + lines = options_find_choice(options_table_entry(oe), value, + &cause); + if (cause != NULL) { + cmdq_error(item, "popup-border-lines %s", cause); + free(cause); + return (CMD_RETURN_ERROR); + } + } + value = args_get(args, 'd'); if (value != NULL) cwd = format_single_from_target(item, value); @@ -409,17 +432,34 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item) } else args_to_vector(args, &argc, &argv); + if (args_has(args, 'e') >= 1) { + env = environ_create(); + av = args_first_value(args, 'e'); + while (av != NULL) { + environ_put(env, av->string, 0); + av = args_next_value(av); + } + } + + if (args_has(args, 'T')) + title = format_single_from_target(item, args_get(args, 'T')); + else + title = xstrdup(""); if (args_has(args, 'E') > 1) flags |= POPUP_CLOSEEXITZERO; else if (args_has(args, 'E')) flags |= POPUP_CLOSEEXIT; - if (args_has(args, 'B')) - flags |= POPUP_NOBORDER; - if (popup_display(flags, item, px, py, w, h, shellcmd, argc, argv, cwd, - tc, s, NULL, NULL) != 0) { + if (popup_display(flags, lines, item, px, py, w, h, env, shellcmd, argc, + argv, cwd, title, tc, s, style, border_style, NULL, NULL) != 0) { cmd_free_argv(argc, argv); + if (env != NULL) + environ_free(env); + free(title); return (CMD_RETURN_NORMAL); } + if (env != NULL) + environ_free(env); + free(title); cmd_free_argv(argc, argv); return (CMD_RETURN_WAIT); } diff --git a/cmd-if-shell.c b/cmd-if-shell.c index 211e08d6..205a8ce1 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -118,7 +118,7 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) if (cdata->client != NULL) cdata->client->references++; - if (job_run(shellcmd, 0, NULL, s, + if (job_run(shellcmd, 0, NULL, NULL, s, server_client_get_cwd(cmdq_get_client(item), s), NULL, cmd_if_shell_callback, cmd_if_shell_free, cdata, 0, -1, -1) == NULL) { diff --git a/cmd-pipe-pane.c b/cmd-pipe-pane.c index 20318492..aac23a0f 100644 --- a/cmd-pipe-pane.c +++ b/cmd-pipe-pane.c @@ -130,7 +130,7 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmdq_item *item) sigprocmask(SIG_SETMASK, &oldset, NULL); close(pipe_fd[0]); - null_fd = open(_PATH_DEVNULL, O_WRONLY, 0); + null_fd = open(_PATH_DEVNULL, O_WRONLY); if (out) { if (dup2(pipe_fd[1], STDIN_FILENO) == -1) _exit(1); diff --git a/cmd-run-shell.c b/cmd-run-shell.c index bf43d313..5e914e65 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -188,7 +188,7 @@ cmd_run_shell_timer(__unused int fd, __unused short events, void* arg) cmd_run_shell_free(cdata); return; } - if (job_run(cmd, 0, NULL, cdata->s, cdata->cwd, NULL, + if (job_run(cmd, 0, NULL, NULL, cdata->s, cdata->cwd, NULL, cmd_run_shell_callback, cmd_run_shell_free, cdata, cdata->flags, -1, -1) == NULL) cmd_run_shell_free(cdata); diff --git a/cmd-send-keys.c b/cmd-send-keys.c index 47fa1caa..d6a95434 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -203,6 +203,8 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item) } if (count == 0) { + if (args_has(args, 'N') || args_has(args, 'R')) + return (CMD_RETURN_NORMAL); for (; np != 0; np--) cmd_send_keys_inject_key(item, NULL, event->key); return (CMD_RETURN_NORMAL); diff --git a/cmd-show-options.c b/cmd-show-options.c index 0e973ea0..90226ad3 100644 --- a/cmd-show-options.c +++ b/cmd-show-options.c @@ -126,6 +126,12 @@ cmd_show_options_exec(struct cmd *self, struct cmdq_item *item) parent = 0; if (o != NULL) cmd_show_options_print(self, item, o, idx, parent); + else if (*name == '@') { + if (args_has(args, 'q')) + goto fail; + cmdq_error(item, "invalid option: %s", argument); + goto fail; + } free(name); free(argument); diff --git a/colour.c b/colour.c index 9ac07415..6ede25da 100644 --- a/colour.c +++ b/colour.c @@ -105,6 +105,21 @@ colour_split_rgb(int c, u_char *r, u_char *g, u_char *b) *b = c & 0xff; } +/* Force colour to RGB if not already. */ +int +colour_force_rgb(int c) +{ + if (c & COLOUR_FLAG_RGB) + return (c); + if (c & COLOUR_FLAG_256) + return (colour_256toRGB(c)); + if (c >= 0 && c <= 7) + return (colour_256toRGB(c)); + if (c >= 90 && c <= 97) + return (colour_256toRGB(8 + c - 90)); + return (-1); +} + /* Convert colour to a string. */ const char * colour_tostring(int c) diff --git a/format-draw.c b/format-draw.c index 6164cc44..1110535f 100644 --- a/format-draw.c +++ b/format-draw.c @@ -677,7 +677,8 @@ format_draw_many(struct screen_write_ctx *ctx, struct style *sy, char ch, /* Draw a format to a screen. */ void format_draw(struct screen_write_ctx *octx, const struct grid_cell *base, - u_int available, const char *expanded, struct style_ranges *srs) + u_int available, const char *expanded, struct style_ranges *srs, + int default_colours) { enum { LEFT, CENTRE, @@ -819,6 +820,10 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base, log_debug("%s: style '%s' -> '%s'", __func__, tmp, style_tostring(&sy)); free(tmp); + if (default_colours) { + sy.gc.bg = base->bg; + sy.gc.fg = base->fg; + } /* If this style has a fill colour, store it for later. */ if (sy.fill != 8) diff --git a/format.c b/format.c index 9af6e0a0..2fc0961e 100644 --- a/format.c +++ b/format.c @@ -101,6 +101,7 @@ format_job_cmp(struct format_job *fj1, struct format_job *fj2) #define FORMAT_WINDOW_NAME 0x4000 #define FORMAT_SESSION_NAME 0x8000 #define FORMAT_CHARACTER 0x10000 +#define FORMAT_COLOUR 0x20000 /* Limit on recursion. */ #define FORMAT_LOOP_LIMIT 100 @@ -390,7 +391,7 @@ format_job_get(struct format_expand_state *es, const char *cmd) if (force && fj->job != NULL) job_free(fj->job); if (force || (fj->job == NULL && fj->last != t)) { - fj->job = job_run(expanded, 0, NULL, NULL, + fj->job = job_run(expanded, 0, NULL, NULL, NULL, server_client_get_cwd(ft->client, NULL), format_job_update, format_job_complete, NULL, fj, JOB_NOWAIT, -1, -1); if (fj->job == NULL) { @@ -3555,7 +3556,7 @@ format_build_modifiers(struct format_expand_state *es, const char **s, /* * Modifiers are a ; separated list of the forms: - * l,m,C,a,b,d,n,t,w,q,E,T,S,W,P,<,> + * l,m,C,a,b,c,d,n,t,w,q,E,T,S,W,P,<,> * =a * =/a * =/a/ @@ -3572,7 +3573,7 @@ format_build_modifiers(struct format_expand_state *es, const char **s, cp++; /* Check single character modifiers with no arguments. */ - if (strchr("labdnwETSWP<>", cp[0]) != NULL && + if (strchr("labcdnwETSWP<>", cp[0]) != NULL && format_is_end(cp[1])) { format_add_modifier(&list, count, cp, 1, NULL, 0); cp++; @@ -4052,10 +4053,10 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen, const char *errstr, *copy, *cp, *marker = NULL; const char *time_format = NULL; char *copy0, *condition, *found, *new; - char *value, *left, *right, c; + char *value, *left, *right; size_t valuelen; int modifiers = 0, limit = 0, width = 0; - int j; + int j, c; struct format_modifier *list, *cmp = NULL, *search = NULL; struct format_modifier **sub = NULL, *mexp = NULL, *fm; u_int i, count, nsub = 0; @@ -4126,6 +4127,9 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen, case 'b': modifiers |= FORMAT_BASENAME; break; + case 'c': + modifiers |= FORMAT_COLOUR; + break; case 'd': modifiers |= FORMAT_DIRNAME; break; @@ -4201,6 +4205,18 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen, goto done; } + /* Is this a colour? */ + if (modifiers & FORMAT_COLOUR) { + new = format_expand1(es, copy); + c = colour_fromstring(new); + if (c == -1 || (c = colour_force_rgb(c)) == -1) + value = xstrdup(""); + else + xasprintf(&value, "%06x", c & 0xffffff); + free(new); + goto done; + } + /* Is this a loop, comparison or condition? */ if (modifiers & FORMAT_SESSIONS) { value = format_loop_sessions(es, copy); diff --git a/input.c b/input.c index 88e1c3c7..faad5b59 100644 --- a/input.c +++ b/input.c @@ -137,10 +137,12 @@ static void input_reset_cell(struct input_ctx *); static void input_osc_4(struct input_ctx *, const char *); static void input_osc_10(struct input_ctx *, const char *); static void input_osc_11(struct input_ctx *, const char *); +static void input_osc_12(struct input_ctx *, const char *); static void input_osc_52(struct input_ctx *, const char *); static void input_osc_104(struct input_ctx *, const char *); static void input_osc_110(struct input_ctx *, const char *); static void input_osc_111(struct input_ctx *, const char *); +static void input_osc_112(struct input_ctx *, const char *); /* Transition entry/exit handlers. */ static void input_clear(struct input_ctx *); @@ -1617,7 +1619,7 @@ input_csi_dispatch(struct input_ctx *ictx) case INPUT_CSI_DECSCUSR: n = input_get(ictx, 0, 0, 0); if (n != -1) - screen_set_cursor_style(s, n); + screen_set_cursor_style(n, &s->cstyle, &s->mode); break; case INPUT_CSI_XDA: n = input_get(ictx, 0, 0, 0); @@ -1683,6 +1685,7 @@ input_csi_dispatch_rm_private(struct input_ctx *ictx) break; case 12: screen_write_mode_clear(sctx, MODE_CURSOR_BLINKING); + screen_write_mode_set(sctx, MODE_CURSOR_BLINKING_SET); break; case 25: /* TCEM */ screen_write_mode_clear(sctx, MODE_CURSOR); @@ -1772,6 +1775,7 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx) break; case 12: screen_write_mode_set(sctx, MODE_CURSOR_BLINKING); + screen_write_mode_set(sctx, MODE_CURSOR_BLINKING_SET); break; case 25: /* TCEM */ screen_write_mode_set(sctx, MODE_CURSOR); @@ -2322,8 +2326,7 @@ input_exit_osc(struct input_ctx *ictx) input_osc_11(ictx, p); break; case 12: - if (utf8_isvalid(p) && *p != '?') /* ? is colour request */ - screen_set_cursor_colour(sctx->s, p); + input_osc_12(ictx, p); break; case 52: input_osc_52(ictx, p); @@ -2338,8 +2341,7 @@ input_exit_osc(struct input_ctx *ictx) input_osc_111(ictx, p); break; case 112: - if (*p == '\0') /* no arguments allowed */ - screen_set_cursor_colour(sctx->s, ""); + input_osc_112(ictx, p); break; default: log_debug("%s: unknown '%u'", __func__, option); @@ -2392,6 +2394,7 @@ static void input_exit_rename(struct input_ctx *ictx) { struct window_pane *wp = ictx->wp; + struct window *w; struct options_entry *o; if (wp == NULL) @@ -2404,17 +2407,20 @@ input_exit_rename(struct input_ctx *ictx) if (!utf8_isvalid(ictx->input_buf)) return; + w = wp->window; if (ictx->input_len == 0) { - o = options_get_only(wp->window->options, "automatic-rename"); + o = options_get_only(w->options, "automatic-rename"); if (o != NULL) options_remove_or_default(o, -1, NULL); - return; + if (!options_get_number(w->options, "automatic-rename")) + window_set_name(w, ""); + } else { + options_set_number(w->options, "automatic-rename", 0); + window_set_name(w, ictx->input_buf); } - window_set_name(wp->window, ictx->input_buf); - options_set_number(wp->window->options, "automatic-rename", 0); - server_redraw_window_borders(wp->window); - server_status_window(wp->window); + server_redraw_window_borders(w); + server_status_window(w); } /* Open UTF-8 character. */ @@ -2501,7 +2507,9 @@ 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)) + if (c != -1) + c = colour_force_rgb(c); + if (c == -1) return; colour_split_rgb(c, &r, &g, &b); @@ -2509,7 +2517,8 @@ input_osc_colour_reply(struct input_ctx *ictx, u_int n, int c) end = "\007"; else end = "\033\\"; - input_reply(ictx, "\033]%u;rgb:%02hhx/%02hhx/%02hhx%s", n, r, g, b, end); + input_reply(ictx, "\033]%u;rgb:%02hhx%02hhx/%02hhx%02hhx/%02hhx%02hhx%s", + n, r, r, g, g, b, b, end); } /* Handle the OSC 4 sequence for setting (multiple) palette entries. */ @@ -2533,6 +2542,12 @@ input_osc_4(struct input_ctx *ictx, const char *p) } s = strsep(&next, ";"); + if (strcmp(s, "?") == 0) { + c = colour_palette_get(ictx->palette, idx); + if (c != -1) + input_osc_colour_reply(ictx, 4, c); + continue; + } if ((c = input_osc_parse_colour(s)) == -1) { s = next; continue; @@ -2576,7 +2591,7 @@ input_osc_10(struct input_ctx *ictx, const char *p) } } -/* Handle the OSC 110 sequence for resetting background colour. */ +/* Handle the OSC 110 sequence for resetting foreground colour. */ static void input_osc_110(struct input_ctx *ictx, const char *p) { @@ -2636,6 +2651,39 @@ input_osc_111(struct input_ctx *ictx, const char *p) } } +/* Handle the OSC 12 sequence for setting and querying cursor colour. */ +static void +input_osc_12(struct input_ctx *ictx, const char *p) +{ + struct window_pane *wp = ictx->wp; + int c; + + if (strcmp(p, "?") == 0) { + if (wp != NULL) { + c = ictx->ctx.s->ccolour; + if (c == -1) + c = ictx->ctx.s->default_ccolour; + input_osc_colour_reply(ictx, 12, c); + } + return; + } + + if ((c = input_osc_parse_colour(p)) == -1) { + log_debug("bad OSC 12: %s", p); + return; + } + screen_set_cursor_colour(ictx->ctx.s, c); +} + +/* Handle the OSC 112 sequence for resetting cursor colour. */ +static void +input_osc_112(struct input_ctx *ictx, const char *p) +{ + if (*p == '\0') /* no arguments allowed */ + screen_set_cursor_colour(ictx->ctx.s, -1); +} + + /* Handle the OSC 52 sequence for setting the clipboard. */ static void input_osc_52(struct input_ctx *ictx, const char *p) diff --git a/job.c b/job.c index 3624c679..b312fdc2 100644 --- a/job.c +++ b/job.c @@ -69,7 +69,7 @@ static LIST_HEAD(joblist, job) all_jobs = LIST_HEAD_INITIALIZER(all_jobs); /* Start a job running. */ struct job * -job_run(const char *cmd, int argc, char **argv, struct session *s, +job_run(const char *cmd, int argc, char **argv, struct environ *e, struct session *s, const char *cwd, job_update_cb updatecb, job_complete_cb completecb, job_free_cb freecb, void *data, int flags, int sx, int sy) { @@ -87,6 +87,9 @@ job_run(const char *cmd, int argc, char **argv, struct session *s, * if-shell to decide on default-terminal based on outside TERM. */ env = environ_for_session(s, !cfg_finished); + if (e != NULL) { + environ_copy(e, env); + } sigfillset(&set); sigprocmask(SIG_BLOCK, &set, &oldset); @@ -137,7 +140,7 @@ job_run(const char *cmd, int argc, char **argv, struct session *s, close(out[1]); close(out[0]); - nullfd = open(_PATH_DEVNULL, O_RDWR, 0); + nullfd = open(_PATH_DEVNULL, O_RDWR); if (nullfd == -1) fatal("open failed"); if (dup2(nullfd, STDERR_FILENO) == -1) diff --git a/menu.c b/menu.c index 043dafdd..aaa1287e 100644 --- a/menu.c +++ b/menu.c @@ -55,10 +55,11 @@ menu_add_item(struct menu *menu, const struct menu_item *item, struct cmdq_item *qitem, struct client *c, struct cmd_find_state *fs) { struct menu_item *new_item; - const char *key, *cmd; + const char *key = NULL, *cmd, *suffix = ""; char *s, *name; - u_int width; + u_int width, max_width; int line; + size_t keylen, slen; line = (item == NULL || item->name == NULL || *item->name == '\0'); if (line && menu->count == 0) @@ -80,11 +81,34 @@ menu_add_item(struct menu *menu, const struct menu_item *item, menu->count--; return; } + max_width = c->tty.sx - 4; + + slen = strlen(s); if (*s != '-' && item->key != KEYC_UNKNOWN && item->key != KEYC_NONE) { key = key_string_lookup_key(item->key, 0); - xasprintf(&name, "%s#[default] #[align=right](%s)", s, key); - } else - xasprintf(&name, "%s", s); + keylen = strlen(key) + 3; /* 3 = space and two brackets */ + + /* + * Add the key if it is shorter than a quarter of the available + * space or there is space for the entire item text and the + * key. + */ + if (keylen <= max_width / 4) + max_width -= keylen; + else if (keylen >= max_width || slen >= max_width - keylen) + key = NULL; + } + + if (slen > max_width) { + max_width--; + suffix = ">"; + } + if (key != NULL) + xasprintf(&name, "%.*s%s#[default] #[align=right](%s)", + (int)max_width, s, suffix, key); + else + xasprintf(&name, "%.*s%s", (int)max_width, s, suffix); + new_item->name = name; free(s); @@ -100,6 +124,8 @@ menu_add_item(struct menu *menu, const struct menu_item *item, new_item->key = item->key; width = format_width(new_item->name); + if (*new_item->name == '-') + width--; if (width > menu->width) menu->width = width; } @@ -140,17 +166,16 @@ menu_mode_cb(__unused struct client *c, void *data, __unused u_int *cx, return (&md->s); } -int -menu_check_cb(__unused struct client *c, void *data, u_int px, u_int py) +/* Return parts of the input range which are not obstructed by the menu. */ +void +menu_check_cb(__unused struct client *c, void *data, u_int px, u_int py, + u_int nx, struct overlay_ranges *r) { struct menu_data *md = data; struct menu *menu = md->menu; - if (px < md->px || px > md->px + menu->width + 3) - return (1); - if (py < md->py || py > md->py + menu->count + 1) - return (1); - return (0); + server_client_overlay_range(md->px, md->py, menu->width + 4, + menu->count + 2, px, py, nx, r); } void diff --git a/mode-tree.c b/mode-tree.c index c92f7cff..1eb496fe 100644 --- a/mode-tree.c +++ b/mode-tree.c @@ -716,14 +716,14 @@ mode_tree_draw(struct mode_tree_data *mtd) screen_write_nputs(&ctx, w, &gc0, "%s", text); if (mti->text != NULL) { format_draw(&ctx, &gc0, w - width, mti->text, - NULL); + NULL, 0); } } else { screen_write_clearendofline(&ctx, gc.bg); screen_write_nputs(&ctx, w, &gc, "%s", text); if (mti->text != NULL) { format_draw(&ctx, &gc, w - width, mti->text, - NULL); + NULL, 0); } } free(text); @@ -736,10 +736,8 @@ mode_tree_draw(struct mode_tree_data *mtd) } sy = screen_size_y(s); - if (!mtd->preview || sy <= 4 || h <= 4 || sy - h <= 4 || w <= 4) { - screen_write_stop(&ctx); - return; - } + if (!mtd->preview || sy <= 4 || h <= 4 || sy - h <= 4 || w <= 4) + goto done; line = &mtd->line_list[mtd->current]; mti = line->item; @@ -747,7 +745,7 @@ mode_tree_draw(struct mode_tree_data *mtd) mti = mti->parent; screen_write_cursormove(&ctx, 0, h, 0); - screen_write_box(&ctx, w, sy - h); + screen_write_box(&ctx, w, sy - h, BOX_LINES_DEFAULT, NULL, NULL); if (mtd->sort_list != NULL) { xasprintf(&text, " %s (sort: %s%s)", mti->name, @@ -783,6 +781,8 @@ mode_tree_draw(struct mode_tree_data *mtd) mtd->drawcb(mtd->modedata, mti->itemdata, &ctx, box_x, box_y); } +done: + screen_write_cursormove(&ctx, 0, mtd->current - mtd->offset, 0); screen_write_stop(&ctx); } @@ -1055,7 +1055,6 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key, case '\016': /* C-n */ mode_tree_down(mtd, 1); break; - case 'g': case KEYC_PPAGE: case '\002': /* C-b */ for (i = 0; i < mtd->height; i++) { @@ -1064,7 +1063,6 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key, mode_tree_up(mtd, 1); } break; - case 'G': case KEYC_NPAGE: case '\006': /* C-f */ for (i = 0; i < mtd->height; i++) { @@ -1073,10 +1071,12 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key, mode_tree_down(mtd, 1); } break; + case 'g': case KEYC_HOME: mtd->current = 0; mtd->offset = 0; break; + case 'G': case KEYC_END: mtd->current = mtd->line_size - 1; if (mtd->current > mtd->height - 1) diff --git a/options-table.c b/options-table.c index 76c2b053..3e0ff198 100644 --- a/options-table.c +++ b/options-table.c @@ -56,12 +56,19 @@ static const char *options_table_bell_action_list[] = { static const char *options_table_visual_bell_list[] = { "off", "on", "both", NULL }; +static const char *options_table_cursor_style_list[] = { + "default", "blinking-block", "block", "blinking-underline", "underline", + "blinking-bar", "bar", NULL +}; static const char *options_table_pane_status_list[] = { "off", "top", "bottom", NULL }; -static const char *options_table_pane_lines_list[] = { +static const char *options_table_pane_border_lines_list[] = { "single", "double", "heavy", "simple", "number", NULL }; +static const char *options_table_popup_border_lines_list[] = { + "single", "double", "heavy", "simple", "rounded", "padded", "none", NULL +}; static const char *options_table_set_clipboard_list[] = { "off", "external", "on", NULL }; @@ -184,6 +191,7 @@ 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" }, + { "cursor-color", "cursor-colour" }, { "pane-colors", "pane-colours" }, { NULL, NULL } }; @@ -231,6 +239,21 @@ const struct options_table_entry options_table[] = { "If empty, no command is run." }, + { .name = "cursor-colour", + .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, + .default_num = -1, + .text = "Colour of the cursor." + }, + + { .name = "cursor-style", + .type = OPTIONS_TABLE_CHOICE, + .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, + .choices = options_table_cursor_style_list, + .default_num = 0, + .text = "Style of the cursor." + }, + { .name = "default-terminal", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SERVER, @@ -950,7 +973,7 @@ const struct options_table_entry options_table[] = { { .name = "pane-border-lines", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_WINDOW, - .choices = options_table_pane_lines_list, + .choices = options_table_pane_border_lines_list, .default_num = PANE_LINES_SINGLE, .text = "Type of characters used to draw pane border lines. Some of " "these are only supported on terminals with UTF-8 support." @@ -981,6 +1004,33 @@ const struct options_table_entry options_table[] = { .text = "The default colour palette for colours zero to 255." }, + { .name = "popup-style", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_WINDOW, + .default_str = "default", + .flags = OPTIONS_TABLE_IS_STYLE, + .separator = ",", + .text = "Default style of popups." + }, + + { .name = "popup-border-style", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_WINDOW, + .default_str = "default", + .flags = OPTIONS_TABLE_IS_STYLE, + .separator = ",", + .text = "Default style of popup borders." + }, + + { .name = "popup-border-lines", + .type = OPTIONS_TABLE_CHOICE, + .scope = OPTIONS_TABLE_WINDOW, + .choices = options_table_popup_border_lines_list, + .default_num = BOX_LINES_SINGLE, + .text = "Type of characters used to draw popup border lines. Some of " + "these are only supported on terminals with UTF-8 support." + }, + { .name = "remain-on-exit", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, diff --git a/options.c b/options.c index e32db774..865ab01f 100644 --- a/options.c +++ b/options.c @@ -989,28 +989,39 @@ options_from_string_flag(struct options *oo, const char *name, return (0); } +int +options_find_choice(const struct options_table_entry *oe, const char *value, + char **cause) +{ + const char **cp; + int n = 0, choice = -1; + + for (cp = oe->choices; *cp != NULL; cp++) { + if (strcmp(*cp, value) == 0) + choice = n; + n++; + } + if (choice == -1) { + xasprintf(cause, "unknown value: %s", value); + return (-1); + } + return (choice); +} + static int options_from_string_choice(const struct options_table_entry *oe, struct options *oo, const char *name, const char *value, char **cause) { - const char **cp; - int n, choice = -1; + int choice = -1; if (value == NULL) { choice = options_get_number(oo, name); if (choice < 2) choice = !choice; } else { - n = 0; - for (cp = oe->choices; *cp != NULL; cp++) { - if (strcmp(*cp, value) == 0) - choice = n; - n++; - } - if (choice == -1) { - xasprintf(cause, "unknown value: %s", value); + choice = options_find_choice(oe, value, cause); + if (choice < 0) return (-1); - } } options_set_number(oo, name, choice); return (0); @@ -1095,15 +1106,30 @@ options_push_changes(const char *name) struct session *s; struct window *w; struct window_pane *wp; + int c; if (strcmp(name, "automatic-rename") == 0) { RB_FOREACH(w, windows, &windows) { if (w->active == NULL) continue; - if (options_get_number(w->options, "automatic-rename")) + if (options_get_number(w->options, name)) w->active->flags |= PANE_CHANGED; } } + if (strcmp(name, "cursor-colour") == 0) { + RB_FOREACH(wp, window_pane_tree, &all_window_panes) { + c = options_get_number(wp->options, name); + wp->screen->default_ccolour = c; + } + } + if (strcmp(name, "cursor-style") == 0) { + RB_FOREACH(wp, window_pane_tree, &all_window_panes) { + wp->screen->default_mode = 0; + screen_set_cursor_style(options_get_number(wp->options, + name), &wp->screen->default_cstyle, + &wp->screen->default_mode); + } + } if (strcmp(name, "key-table") == 0) { TAILQ_FOREACH(loop, &clients, entry) server_client_set_key_table(loop, NULL); diff --git a/osdep-openbsd.c b/osdep-openbsd.c index f5c61372..54464753 100644 --- a/osdep-openbsd.c +++ b/osdep-openbsd.c @@ -18,6 +18,7 @@ #include /* MAXCOMLEN */ #include +#include #include #include #include diff --git a/popup.c b/popup.c index 3f64a852..0418ea7e 100644 --- a/popup.c +++ b/popup.c @@ -30,9 +30,15 @@ struct popup_data { struct client *c; struct cmdq_item *item; int flags; + char *title; + + struct grid_cell border_cell; + enum box_lines border_lines; struct screen s; + struct grid_cell defaults; struct colour_palette palette; + struct job *job; struct input_ctx *ictx; int status; @@ -116,7 +122,7 @@ popup_set_client_cb(struct tty_ctx *ttyctx, struct client *c) ttyctx->wsx = c->tty.sx; ttyctx->wsy = c->tty.sy; - if (pd->flags & POPUP_NOBORDER) { + if (pd->border_lines == BOX_LINES_NONE) { ttyctx->xoff = ttyctx->rxoff = pd->px; ttyctx->yoff = ttyctx->ryoff = pd->py; } else { @@ -132,6 +138,7 @@ popup_init_ctx_cb(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx) { struct popup_data *pd = ctx->arg; + memcpy(&ttyctx->defaults, &pd->defaults, sizeof ttyctx->defaults); ttyctx->palette = &pd->palette; ttyctx->redraw_cb = popup_redraw_cb; ttyctx->set_client_cb = popup_set_client_cb; @@ -146,7 +153,7 @@ popup_mode_cb(__unused struct client *c, void *data, u_int *cx, u_int *cy) if (pd->md != NULL) return (menu_mode_cb(c, pd->md, cx, cy)); - if (pd->flags & POPUP_NOBORDER) { + if (pd->border_lines == BOX_LINES_NONE) { *cx = pd->px + pd->s.cx; *cy = pd->py + pd->s.cy; } else { @@ -156,18 +163,49 @@ popup_mode_cb(__unused struct client *c, void *data, u_int *cx, u_int *cy) return (&pd->s); } -static int -popup_check_cb(struct client *c, void *data, u_int px, u_int py) +/* Return parts of the input range which are not obstructed by the popup. */ +static void +popup_check_cb(struct client* c, void *data, u_int px, u_int py, u_int nx, + struct overlay_ranges *r) { struct popup_data *pd = data; + struct overlay_ranges or[2]; + u_int i, j, k = 0; - if (pd->md != NULL && menu_check_cb(c, pd->md, px, py) == 0) - return (0); - if (px < pd->px || px > pd->px + pd->sx - 1) - return (1); - if (py < pd->py || py > pd->py + pd->sy - 1) - return (1); - return (0); + if (pd->md != NULL) { + /* Check each returned range for the menu against the popup. */ + menu_check_cb(c, pd->md, px, py, nx, r); + for (i = 0; i < 2; i++) { + server_client_overlay_range(pd->px, pd->py, pd->sx, + pd->sy, r->px[i], py, r->nx[i], &or[i]); + } + + /* + * or has up to OVERLAY_MAX_RANGES non-overlapping ranges, + * ordered from left to right. Collect them in the output. + */ + for (i = 0; i < 2; i++) { + /* Each or[i] only has 2 ranges. */ + for (j = 0; j < 2; j++) { + if (or[i].nx[j] > 0) { + r->px[k] = or[i].px[j]; + r->nx[k] = or[i].nx[j]; + k++; + } + } + } + + /* Zero remaining ranges if any. */ + for (i = k; i < OVERLAY_MAX_RANGES; i++) { + r->px[i] = 0; + r->nx[i] = 0; + } + + return; + } + + server_client_overlay_range(pd->px, pd->py, pd->sx, pd->sy, px, py, nx, + r); } static void @@ -179,26 +217,29 @@ popup_draw_cb(struct client *c, void *data, struct screen_redraw_ctx *rctx) struct screen_write_ctx ctx; u_int i, px = pd->px, py = pd->py; struct colour_palette *palette = &pd->palette; - struct grid_cell gc; + struct grid_cell defaults; screen_init(&s, pd->sx, pd->sy, 0); screen_write_start(&ctx, &s); screen_write_clearscreen(&ctx, 8); - if (pd->flags & POPUP_NOBORDER) { + if (pd->border_lines == BOX_LINES_NONE) { screen_write_cursormove(&ctx, 0, 0, 0); screen_write_fast_copy(&ctx, &pd->s, 0, 0, pd->sx, pd->sy); } else if (pd->sx > 2 && pd->sy > 2) { - screen_write_box(&ctx, pd->sx, pd->sy); + screen_write_box(&ctx, pd->sx, pd->sy, pd->border_lines, + &pd->border_cell, pd->title); screen_write_cursormove(&ctx, 1, 1, 0); screen_write_fast_copy(&ctx, &pd->s, 0, 0, pd->sx - 2, pd->sy - 2); } screen_write_stop(&ctx); - memcpy(&gc, &grid_default_cell, sizeof gc); - gc.fg = pd->palette.fg; - gc.bg = pd->palette.bg; + memcpy(&defaults, &pd->defaults, sizeof defaults); + if (defaults.fg == 8) + defaults.fg = palette->fg; + if (defaults.bg == 8) + defaults.bg = palette->bg; if (pd->md != NULL) { c->overlay_check = menu_check_cb; @@ -207,8 +248,10 @@ popup_draw_cb(struct client *c, void *data, struct screen_redraw_ctx *rctx) c->overlay_check = NULL; c->overlay_data = NULL; } - for (i = 0; i < pd->sy; i++) - tty_draw_line(tty, &s, 0, i, pd->sx, px, py + i, &gc, palette); + for (i = 0; i < pd->sy; i++) { + tty_draw_line(tty, &s, 0, i, pd->sx, px, py + i, &defaults, + palette); + } if (pd->md != NULL) { c->overlay_check = NULL; c->overlay_data = NULL; @@ -245,6 +288,7 @@ popup_free_cb(struct client *c, void *data) screen_free(&pd->s); colour_palette_free(&pd->palette); + free(pd->title); free(pd); } @@ -278,7 +322,7 @@ popup_resize_cb(__unused struct client *c, void *data) pd->px = pd->ppx; /* Avoid zero size screens. */ - if (pd->flags & POPUP_NOBORDER) { + if (pd->border_lines == BOX_LINES_NONE) { screen_resize(&pd->s, pd->sx, pd->sy, 0); if (pd->job != NULL) job_resize(pd->job, pd->sx, pd->sy ); @@ -404,7 +448,7 @@ popup_handle_drag(struct client *c, struct popup_data *pd, pd->ppy = py; server_redraw_client(c); } else if (pd->dragging == SIZE) { - if (pd->flags & POPUP_NOBORDER) { + if (pd->border_lines == BOX_LINES_NONE) { if (m->x < pd->px + 1) return; if (m->y < pd->py + 1) @@ -420,7 +464,7 @@ popup_handle_drag(struct client *c, struct popup_data *pd, pd->psx = pd->sx; pd->psy = pd->sy; - if (pd->flags & POPUP_NOBORDER) { + if (pd->border_lines == BOX_LINES_NONE) { screen_resize(&pd->s, pd->sx, pd->sy, 0); if (pd->job != NULL) job_resize(pd->job, pd->sx, pd->sy); @@ -468,7 +512,7 @@ popup_key_cb(struct client *c, void *data, struct key_event *event) goto menu; return (0); } - if (~pd->flags & POPUP_NOBORDER) { + if (pd->border_lines != BOX_LINES_NONE) { if (m->x == pd->px) border = LEFT; else if (m->x == pd->px + pd->sx - 1) @@ -502,7 +546,7 @@ popup_key_cb(struct client *c, void *data, struct key_event *event) if (pd->job != NULL) { if (KEYC_IS_MOUSE(event->key)) { /* Must be inside, checked already. */ - if (pd->flags & POPUP_NOBORDER) { + if (pd->border_lines == BOX_LINES_NONE) { px = m->x - pd->px; py = m->y - pd->py; } else { @@ -588,14 +632,25 @@ popup_job_complete_cb(struct job *job) } int -popup_display(int flags, struct cmdq_item *item, u_int px, u_int py, u_int sx, - u_int sy, const char *shellcmd, int argc, char **argv, const char *cwd, - struct client *c, struct session *s, popup_close_cb cb, void *arg) +popup_display(int flags, enum box_lines lines, struct cmdq_item *item, u_int px, + u_int py, u_int sx, u_int sy, struct environ *env, const char *shellcmd, + int argc, char **argv, const char *cwd, const char *title, struct client *c, + struct session *s, const char* style, const char* border_style, + popup_close_cb cb, void *arg) { struct popup_data *pd; u_int jx, jy; + struct options *o; + struct style sytmp; - if (flags & POPUP_NOBORDER) { + if (s != NULL) + o = s->curw->window->options; + else + o = c->session->curw->window->options; + + if (lines == BOX_LINES_DEFAULT) + lines = options_get_number(o, "popup-border-lines"); + if (lines == BOX_LINES_NONE) { if (sx < 1 || sy < 1) return (-1); jx = sx; @@ -612,6 +667,7 @@ popup_display(int flags, struct cmdq_item *item, u_int px, u_int py, u_int sx, pd = xcalloc(1, sizeof *pd); pd->item = item; pd->flags = flags; + pd->title = xstrdup(title); pd->c = c; pd->c->references++; @@ -620,10 +676,33 @@ popup_display(int flags, struct cmdq_item *item, u_int px, u_int py, u_int sx, pd->arg = arg; pd->status = 128 + SIGHUP; + pd->border_lines = lines; + memcpy(&pd->border_cell, &grid_default_cell, sizeof pd->border_cell); + style_apply(&pd->border_cell, o, "popup-border-style", NULL); + if (border_style != NULL) { + style_set(&sytmp, &grid_default_cell); + if (style_parse(&sytmp, &pd->border_cell, border_style) == 0) { + pd->border_cell.fg = sytmp.gc.fg; + pd->border_cell.bg = sytmp.gc.bg; + } + } + pd->border_cell.attr = 0; + screen_init(&pd->s, sx - 2, sy - 2, 0); colour_palette_init(&pd->palette); colour_palette_from_option(&pd->palette, global_w_options); + memcpy(&pd->defaults, &grid_default_cell, sizeof pd->defaults); + style_apply(&pd->defaults, o, "popup-style", NULL); + if (style != NULL) { + style_set(&sytmp, &grid_default_cell); + if (style_parse(&sytmp, &pd->defaults, style) == 0) { + pd->defaults.fg = sytmp.gc.fg; + pd->defaults.bg = sytmp.gc.bg; + } + } + pd->defaults.attr = 0; + pd->px = px; pd->py = py; pd->sx = sx; @@ -634,7 +713,7 @@ popup_display(int flags, struct cmdq_item *item, u_int px, u_int py, u_int sx, pd->psx = sx; pd->psy = sy; - pd->job = job_run(shellcmd, argc, argv, s, cwd, + pd->job = job_run(shellcmd, argc, argv, env, s, cwd, popup_job_update_cb, popup_job_complete_cb, NULL, pd, JOB_NOWAIT|JOB_PTY|JOB_KEEPWRITE, jx, jy); pd->ictx = input_init(NULL, job_get_event(pd->job), &pd->palette); @@ -723,8 +802,9 @@ popup_editor(struct client *c, const char *buf, size_t len, py = (c->tty.sy / 2) - (sy / 2); xasprintf(&cmd, "%s %s", editor, path); - if (popup_display(POPUP_INTERNAL|POPUP_CLOSEEXIT, NULL, px, py, sx, sy, - cmd, 0, NULL, _PATH_TMP, c, NULL, popup_editor_close_cb, pe) != 0) { + if (popup_display(POPUP_INTERNAL|POPUP_CLOSEEXIT, BOX_LINES_DEFAULT, + NULL, px, py, sx, sy, NULL, cmd, 0, NULL, _PATH_TMP, NULL, c, NULL, + NULL, NULL, popup_editor_close_cb, pe) != 0) { popup_editor_free(pe); free(cmd); return (-1); diff --git a/resize.c b/resize.c index 8416ad6a..175dd740 100644 --- a/resize.c +++ b/resize.c @@ -348,6 +348,8 @@ recalculate_size_skip_client(struct client *loop, __unused int type, * is not the current window - this is used for aggressive-resize. * Otherwise skip any session that doesn't contain the window. */ + if (loop->session->curw == NULL) + return (1); if (current) return (loop->session->curw->window != w); return (session_has(loop->session, w) == 0); diff --git a/screen-redraw.c b/screen-redraw.c index 9860c0de..bffbe8b5 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -31,57 +31,9 @@ static void screen_redraw_draw_pane(struct screen_redraw_ctx *, static void screen_redraw_set_context(struct client *, struct screen_redraw_ctx *); -#define CELL_INSIDE 0 -#define CELL_TOPBOTTOM 1 -#define CELL_LEFTRIGHT 2 -#define CELL_TOPLEFT 3 -#define CELL_TOPRIGHT 4 -#define CELL_BOTTOMLEFT 5 -#define CELL_BOTTOMRIGHT 6 -#define CELL_TOPJOIN 7 -#define CELL_BOTTOMJOIN 8 -#define CELL_LEFTJOIN 9 -#define CELL_RIGHTJOIN 10 -#define CELL_JOIN 11 -#define CELL_OUTSIDE 12 - -#define CELL_BORDERS " xqlkmjwvtun~" - #define START_ISOLATE "\342\201\246" #define END_ISOLATE "\342\201\251" -static const struct utf8_data screen_redraw_double_borders[] = { - { "", 0, 0, 0 }, - { "\342\225\221", 0, 3, 1 }, /* U+2551 */ - { "\342\225\220", 0, 3, 1 }, /* U+2550 */ - { "\342\225\224", 0, 3, 1 }, /* U+2554 */ - { "\342\225\227", 0, 3, 1 }, /* U+2557 */ - { "\342\225\232", 0, 3, 1 }, /* U+255A */ - { "\342\225\235", 0, 3, 1 }, /* U+255D */ - { "\342\225\246", 0, 3, 1 }, /* U+2566 */ - { "\342\225\251", 0, 3, 1 }, /* U+2569 */ - { "\342\225\240", 0, 3, 1 }, /* U+2560 */ - { "\342\225\243", 0, 3, 1 }, /* U+2563 */ - { "\342\225\254", 0, 3, 1 }, /* U+256C */ - { "\302\267", 0, 2, 1 } /* U+00B7 */ -}; - -static const struct utf8_data screen_redraw_heavy_borders[] = { - { "", 0, 0, 0 }, - { "\342\224\203", 0, 3, 1 }, /* U+2503 */ - { "\342\224\201", 0, 3, 1 }, /* U+2501 */ - { "\342\224\223", 0, 3, 1 }, /* U+2513 */ - { "\342\224\217", 0, 3, 1 }, /* U+250F */ - { "\342\224\227", 0, 3, 1 }, /* U+2517 */ - { "\342\224\233", 0, 3, 1 }, /* U+251B */ - { "\342\224\263", 0, 3, 1 }, /* U+2533 */ - { "\342\224\273", 0, 3, 1 }, /* U+253B */ - { "\342\224\243", 0, 3, 1 }, /* U+2523 */ - { "\342\224\253", 0, 3, 1 }, /* U+252B */ - { "\342\225\213", 0, 3, 1 }, /* U+254B */ - { "\302\267", 0, 2, 1 } /* U+00B7 */ -}; - enum screen_redraw_border_type { SCREEN_REDRAW_OUTSIDE, SCREEN_REDRAW_INSIDE, @@ -90,8 +42,8 @@ enum screen_redraw_border_type { /* Get cell border character. */ static void -screen_redraw_border_set(struct window_pane *wp, int pane_lines, int cell_type, - struct grid_cell *gc) +screen_redraw_border_set(struct window_pane *wp, enum pane_lines pane_lines, + int cell_type, struct grid_cell *gc) { u_int idx; @@ -110,15 +62,15 @@ screen_redraw_border_set(struct window_pane *wp, int pane_lines, int cell_type, break; case PANE_LINES_DOUBLE: gc->attr &= ~GRID_ATTR_CHARSET; - utf8_copy(&gc->data, &screen_redraw_double_borders[cell_type]); + utf8_copy(&gc->data, tty_acs_double_borders(cell_type)); break; case PANE_LINES_HEAVY: gc->attr &= ~GRID_ATTR_CHARSET; - utf8_copy(&gc->data, &screen_redraw_heavy_borders[cell_type]); + utf8_copy(&gc->data, tty_acs_heavy_borders(cell_type)); break; case PANE_LINES_SIMPLE: gc->attr &= ~GRID_ATTR_CHARSET; - utf8_set(&gc->data, " |-+++++++++."[cell_type]); + utf8_set(&gc->data, SIMPLE_BORDERS[cell_type]); break; default: gc->attr |= GRID_ATTR_CHARSET; @@ -402,7 +354,7 @@ screen_redraw_check_is(u_int px, u_int py, int pane_status, /* Update pane status. */ static int screen_redraw_make_pane_status(struct client *c, struct window_pane *wp, - struct screen_redraw_ctx *rctx, int pane_lines) + struct screen_redraw_ctx *rctx, enum pane_lines pane_lines) { struct window *w = wp->window; struct grid_cell gc; @@ -448,7 +400,7 @@ screen_redraw_make_pane_status(struct client *c, struct window_pane *wp, gc.attr &= ~GRID_ATTR_CHARSET; screen_write_cursormove(&ctx, 0, 0, 0); - format_draw(&ctx, &gc, width, expanded, NULL); + format_draw(&ctx, &gc, width, expanded, NULL, 0); screen_write_stop(&ctx); free(expanded); @@ -527,11 +479,12 @@ screen_redraw_draw_pane_status(struct screen_redraw_ctx *ctx) static int screen_redraw_update(struct client *c, int flags) { - struct window *w = c->session->curw->window; - struct window_pane *wp; - struct options *wo = w->options; - int redraw, lines; - struct screen_redraw_ctx ctx; + struct window *w = c->session->curw->window; + struct window_pane *wp; + struct options *wo = w->options; + int redraw; + enum pane_lines lines; + struct screen_redraw_ctx ctx; if (c->message_string != NULL) redraw = status_message_redraw(c); @@ -685,14 +638,17 @@ screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j) struct tty *tty = &c->tty; struct format_tree *ft; struct window_pane *wp; - u_int cell_type, x = ctx->ox + i, y = ctx->oy + j; - int pane_status = ctx->pane_status, isolates; struct grid_cell gc; const struct grid_cell *tmp; + struct overlay_ranges r; + u_int cell_type, x = ctx->ox + i, y = ctx->oy + j; + int pane_status = ctx->pane_status, isolates; - if (c->overlay_check != NULL && - !c->overlay_check(c, c->overlay_data, x, y)) - return; + if (c->overlay_check != NULL) { + c->overlay_check(c, c->overlay_data, x, y, 1, &r); + if (r.nx[0] + r.nx[1] == 0) + return; + } cell_type = screen_redraw_check_cell(c, x, y, pane_status, &wp); if (cell_type == CELL_INSIDE) diff --git a/screen-write.c b/screen-write.c index 6286f11a..7bdba327 100644 --- a/screen-write.c +++ b/screen-write.c @@ -184,8 +184,10 @@ screen_write_initctx(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx, if (ctx->init_ctx_cb != NULL) { ctx->init_ctx_cb(ctx, ttyctx); if (ttyctx->palette != NULL) { - ttyctx->defaults.fg = ttyctx->palette->fg; - ttyctx->defaults.bg = ttyctx->palette->bg; + if (ttyctx->defaults.fg == 8) + ttyctx->defaults.fg = ttyctx->palette->fg; + if (ttyctx->defaults.bg == 8) + ttyctx->defaults.bg = ttyctx->palette->bg; } } else { ttyctx->redraw_cb = screen_write_redraw_cb; @@ -645,9 +647,8 @@ screen_write_menu(struct screen_write_ctx *ctx, struct menu *menu, memcpy(&default_gc, &grid_default_cell, sizeof default_gc); - screen_write_box(ctx, menu->width + 4, menu->count + 2); - screen_write_cursormove(ctx, cx + 2, cy, 0); - format_draw(ctx, &default_gc, menu->width, menu->title, NULL); + screen_write_box(ctx, menu->width + 4, menu->count + 2, + BOX_LINES_DEFAULT, &default_gc, menu->title); for (i = 0; i < menu->count; i++) { name = menu->items[i].name; @@ -664,10 +665,12 @@ screen_write_menu(struct screen_write_ctx *ctx, struct menu *menu, if (*name == '-') { name++; default_gc.attr |= GRID_ATTR_DIM; - format_draw(ctx, gc, menu->width, name, NULL); + format_draw(ctx, gc, menu->width, name, NULL, + 0); default_gc.attr &= ~GRID_ATTR_DIM; } else - format_draw(ctx, gc, menu->width, name, NULL); + format_draw(ctx, gc, menu->width, name, NULL, + gc == choice_gc); gc = &default_gc; } } @@ -675,39 +678,95 @@ screen_write_menu(struct screen_write_ctx *ctx, struct menu *menu, screen_write_set_cursor(ctx, cx, cy); } +static void +screen_write_box_border_set(enum box_lines box_lines, int cell_type, + struct grid_cell *gc) +{ + switch (box_lines) { + case BOX_LINES_NONE: + break; + case BOX_LINES_DOUBLE: + gc->attr &= ~GRID_ATTR_CHARSET; + utf8_copy(&gc->data, tty_acs_double_borders(cell_type)); + break; + case BOX_LINES_HEAVY: + gc->attr &= ~GRID_ATTR_CHARSET; + utf8_copy(&gc->data, tty_acs_heavy_borders(cell_type)); + break; + case BOX_LINES_ROUNDED: + gc->attr &= ~GRID_ATTR_CHARSET; + utf8_copy(&gc->data, tty_acs_rounded_borders(cell_type)); + break; + case BOX_LINES_SIMPLE: + gc->attr &= ~GRID_ATTR_CHARSET; + utf8_set(&gc->data, SIMPLE_BORDERS[cell_type]); + break; + case BOX_LINES_PADDED: + gc->attr &= ~GRID_ATTR_CHARSET; + utf8_set(&gc->data, PADDED_BORDERS[cell_type]); + break; + case BOX_LINES_SINGLE: + case BOX_LINES_DEFAULT: + gc->attr |= GRID_ATTR_CHARSET; + utf8_set(&gc->data, CELL_BORDERS[cell_type]); + break; + } +} + /* Draw a box on screen. */ void -screen_write_box(struct screen_write_ctx *ctx, u_int nx, u_int ny) +screen_write_box(struct screen_write_ctx *ctx, u_int nx, u_int ny, + enum box_lines lines, const struct grid_cell *gcp, const char *title) { struct screen *s = ctx->s; - struct grid_cell gc; + struct grid_cell gc; u_int cx, cy, i; cx = s->cx; cy = s->cy; - memcpy(&gc, &grid_default_cell, sizeof gc); + if (gcp != NULL) + memcpy(&gc, gcp, sizeof gc); + else + memcpy(&gc, &grid_default_cell, sizeof gc); + gc.attr |= GRID_ATTR_CHARSET; gc.flags |= GRID_FLAG_NOPALETTE; - screen_write_putc(ctx, &gc, 'l'); + /* Draw top border */ + screen_write_box_border_set(lines, CELL_TOPLEFT, &gc); + screen_write_cell(ctx, &gc); + screen_write_box_border_set(lines, CELL_LEFTRIGHT, &gc); for (i = 1; i < nx - 1; i++) - screen_write_putc(ctx, &gc, 'q'); - screen_write_putc(ctx, &gc, 'k'); + screen_write_cell(ctx, &gc); + screen_write_box_border_set(lines, CELL_TOPRIGHT, &gc); + screen_write_cell(ctx, &gc); + /* Draw bottom border */ screen_write_set_cursor(ctx, cx, cy + ny - 1); - screen_write_putc(ctx, &gc, 'm'); + screen_write_box_border_set(lines, CELL_BOTTOMLEFT, &gc); + screen_write_cell(ctx, &gc); + screen_write_box_border_set(lines, CELL_LEFTRIGHT, &gc); for (i = 1; i < nx - 1; i++) - screen_write_putc(ctx, &gc, 'q'); - screen_write_putc(ctx, &gc, 'j'); + screen_write_cell(ctx, &gc); + screen_write_box_border_set(lines, CELL_BOTTOMRIGHT, &gc); + screen_write_cell(ctx, &gc); + /* Draw sides */ + screen_write_box_border_set(lines, CELL_TOPBOTTOM, &gc); for (i = 1; i < ny - 1; i++) { + /* left side */ screen_write_set_cursor(ctx, cx, cy + i); - screen_write_putc(ctx, &gc, 'x'); - } - for (i = 1; i < ny - 1; i++) { + screen_write_cell(ctx, &gc); + /* right side */ screen_write_set_cursor(ctx, cx + nx - 1, cy + i); - screen_write_putc(ctx, &gc, 'x'); + screen_write_cell(ctx, &gc); + } + + if (title != NULL) { + gc.attr &= ~GRID_ATTR_CHARSET; + screen_write_cursormove(ctx, cx + 2, cy, 0); + format_draw(ctx, &gc, nx - 4, title, NULL, 0); } screen_write_set_cursor(ctx, cx, cy); diff --git a/screen.c b/screen.c index 77b511a9..ca5f8574 100644 --- a/screen.c +++ b/screen.c @@ -81,7 +81,10 @@ screen_init(struct screen *s, u_int sx, u_int sy, u_int hlimit) s->path = NULL; s->cstyle = SCREEN_CURSOR_DEFAULT; - s->ccolour = xstrdup(""); + s->default_cstyle = SCREEN_CURSOR_DEFAULT; + s->default_mode = 0; + s->ccolour = -1; + s->default_ccolour = -1; s->tabs = NULL; s->sel = NULL; @@ -127,7 +130,6 @@ screen_free(struct screen *s) free(s->tabs); free(s->path); free(s->title); - free(s->ccolour); if (s->write_list != NULL) screen_write_free_list(s); @@ -154,48 +156,47 @@ screen_reset_tabs(struct screen *s) bit_set(s->tabs, i); } -/* Set screen cursor style. */ +/* Set screen cursor style and mode. */ void -screen_set_cursor_style(struct screen *s, u_int style) +screen_set_cursor_style(u_int style, enum screen_cursor_style *cstyle, + int *mode) { - log_debug("%s: new %u, was %u", __func__, style, s->cstyle); switch (style) { case 0: - s->cstyle = SCREEN_CURSOR_DEFAULT; + *cstyle = SCREEN_CURSOR_DEFAULT; break; case 1: - s->cstyle = SCREEN_CURSOR_BLOCK; - s->mode |= MODE_CURSOR_BLINKING; + *cstyle = SCREEN_CURSOR_BLOCK; + *mode |= MODE_CURSOR_BLINKING; break; case 2: - s->cstyle = SCREEN_CURSOR_BLOCK; - s->mode &= ~MODE_CURSOR_BLINKING; + *cstyle = SCREEN_CURSOR_BLOCK; + *mode &= ~MODE_CURSOR_BLINKING; break; case 3: - s->cstyle = SCREEN_CURSOR_UNDERLINE; - s->mode |= MODE_CURSOR_BLINKING; + *cstyle = SCREEN_CURSOR_UNDERLINE; + *mode |= MODE_CURSOR_BLINKING; break; case 4: - s->cstyle = SCREEN_CURSOR_UNDERLINE; - s->mode &= ~MODE_CURSOR_BLINKING; + *cstyle = SCREEN_CURSOR_UNDERLINE; + *mode &= ~MODE_CURSOR_BLINKING; break; case 5: - s->cstyle = SCREEN_CURSOR_BAR; - s->mode |= MODE_CURSOR_BLINKING; + *cstyle = SCREEN_CURSOR_BAR; + *mode |= MODE_CURSOR_BLINKING; break; case 6: - s->cstyle = SCREEN_CURSOR_BAR; - s->mode &= ~MODE_CURSOR_BLINKING; + *cstyle = SCREEN_CURSOR_BAR; + *mode &= ~MODE_CURSOR_BLINKING; break; } } /* Set screen cursor colour. */ void -screen_set_cursor_colour(struct screen *s, const char *colour) +screen_set_cursor_colour(struct screen *s, int colour) { - free(s->ccolour); - s->ccolour = xstrdup(colour); + s->ccolour = colour; } /* Set screen title. */ diff --git a/server-client.c b/server-client.c index 5c93f8c7..56848a15 100644 --- a/server-client.c +++ b/server-client.c @@ -144,6 +144,54 @@ server_client_clear_overlay(struct client *c) server_redraw_client(c); } +/* + * Given overlay position and dimensions, return parts of the input range which + * are visible. + */ +void +server_client_overlay_range(u_int x, u_int y, u_int sx, u_int sy, u_int px, + u_int py, u_int nx, struct overlay_ranges *r) +{ + u_int ox, onx; + + /* Return up to 2 ranges. */ + r->px[2] = 0; + r->nx[2] = 0; + + /* Trivial case of no overlap in the y direction. */ + if (py < y || py > y + sy - 1) { + r->px[0] = px; + r->nx[0] = nx; + r->px[1] = 0; + r->nx[1] = 0; + return; + } + + /* Visible bit to the left of the popup. */ + if (px < x) { + r->px[0] = px; + r->nx[0] = x - px; + if (r->nx[0] > nx) + r->nx[0] = nx; + } else { + r->px[0] = 0; + r->nx[0] = 0; + } + + /* Visible bit to the right of the popup. */ + ox = x + sx; + if (px > ox) + ox = px; + onx = px + nx; + if (onx > ox) { + r->px[1] = ox; + r->nx[1] = onx - ox; + } else { + r->px[1] = 0; + r->nx[1] = 0; + } +} + /* Check if this client is inside this server. */ int server_client_check_nested(struct client *c) @@ -465,7 +513,7 @@ server_client_detach(struct client *c, enum msgtype msgtype) { struct session *s = c->session; - if (s == NULL || (c->flags & CLIENT_UNATTACHEDFLAGS)) + if (s == NULL || (c->flags & CLIENT_NODETACHFLAGS)) return; c->flags |= CLIENT_EXIT; @@ -1655,7 +1703,7 @@ server_client_reset_state(struct client *c) struct window_pane *wp = server_client_get_pane(c), *loop; struct screen *s = NULL; struct options *oo = c->session->options; - int mode = 0, cursor, flags; + int mode = 0, cursor, flags, n; u_int cx = 0, cy = 0, ox, oy, sx, sy; if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED)) @@ -1683,7 +1731,20 @@ server_client_reset_state(struct client *c) tty_margin_off(tty); /* Move cursor to pane cursor and offset. */ - if (c->overlay_draw == NULL) { + if (c->prompt_string != NULL) { + n = options_get_number(c->session->options, "status-position"); + if (n == 0) + cy = 0; + else { + n = status_line_size(c); + if (n == 0) + cy = tty->sy - 1; + else + cy = tty->sy - n; + } + cx = c->prompt_cursor; + mode &= ~MODE_CURSOR; + } else if (c->overlay_draw == NULL) { cursor = 0; tty_window_offset(tty, &ox, &oy, &sx, &sy); if (wp->xoff + s->cx >= ox && wp->xoff + s->cx <= ox + sx && diff --git a/status.c b/status.c index b442e85d..bb57b3d6 100644 --- a/status.c +++ b/status.c @@ -439,7 +439,8 @@ status_redraw(struct client *c) screen_write_cursormove(&ctx, 0, i, 0); status_free_ranges(&sle->ranges); - format_draw(&ctx, &gc, width, expanded, &sle->ranges); + format_draw(&ctx, &gc, width, expanded, &sle->ranges, + 0); free(sle->expanded); sle->expanded = expanded; @@ -562,7 +563,7 @@ status_message_redraw(struct client *c) if (c->message_ignore_styles) screen_write_nputs(&ctx, len, &gc, "%s", c->message_string); else - format_draw(&ctx, &gc, c->tty.sx, c->message_string, NULL); + format_draw(&ctx, &gc, c->tty.sx, c->message_string, NULL, 0); screen_write_stop(&ctx); if (grid_compare(sl->active->grid, old_screen.grid) == 0) { @@ -747,6 +748,7 @@ status_prompt_redraw(struct client *c) offset = 0; if (pwidth > left) pwidth = left; + c->prompt_cursor = start + c->prompt_index - offset; width = 0; for (i = 0; c->prompt_buffer[i].size != 0; i++) { @@ -809,14 +811,23 @@ status_prompt_translate_key(struct client *c, key_code key, key_code *new_key) { if (c->prompt_mode == PROMPT_ENTRY) { switch (key) { + case '\001': /* C-a */ case '\003': /* C-c */ + case '\005': /* C-e */ case '\007': /* C-g */ case '\010': /* C-h */ case '\011': /* Tab */ + case '\013': /* C-k */ + case '\016': /* C-n */ + case '\020': /* C-p */ + case '\024': /* C-t */ case '\025': /* C-u */ case '\027': /* C-w */ + case '\031': /* C-y */ case '\n': case '\r': + case KEYC_LEFT|KEYC_CTRL: + case KEYC_RIGHT|KEYC_CTRL: case KEYC_BSPACE: case KEYC_DC: case KEYC_DOWN: @@ -837,6 +848,9 @@ status_prompt_translate_key(struct client *c, key_code key, key_code *new_key) } switch (key) { + case KEYC_BSPACE: + *new_key = KEYC_LEFT; + return (1); case 'A': case 'I': case 'C': @@ -882,7 +896,7 @@ status_prompt_translate_key(struct client *c, key_code key, key_code *new_key) *new_key = 'B'|KEYC_VI; return (1); case 'd': - *new_key = '\025'; + *new_key = '\025'; /* C-u */ return (1); case 'e': *new_key = 'e'|KEYC_VI; @@ -1714,7 +1728,7 @@ status_prompt_complete_list_menu(struct client *c, char **list, u_int size, item.name = list[i]; item.key = '0' + (i - spm->start); item.command = NULL; - menu_add_item(menu, &item, NULL, NULL, NULL); + menu_add_item(menu, &item, NULL, c, NULL); } if (options_get_number(c->session->options, "status-position") == 0) diff --git a/tmux.1 b/tmux.1 index 80fde996..8433d9f2 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1027,13 +1027,14 @@ section. .Pp The following commands are available to manage clients and sessions: .Bl -tag -width Ds +.Tg attach .It Xo Ic attach-session .Op Fl dErx .Op Fl c Ar working-directory .Op Fl f Ar flags .Op Fl t Ar target-session .Xc -.D1 (alias: Ic attach ) +.D1 Pq alias: Ic attach If run from outside .Nm , create a new client in the current terminal and attach it to @@ -1113,13 +1114,14 @@ If is used, the .Ic update-environment option will not be applied. +.Tg detach .It Xo Ic detach-client .Op Fl aP .Op Fl E Ar shell-command .Op Fl s Ar target-session .Op Fl t Ar target-client .Xc -.D1 (alias: Ic detach ) +.D1 Pq alias: Ic detach Detach the current client if bound to a key, the client specified with .Fl t , or all clients currently attached to the session specified by @@ -1139,8 +1141,9 @@ With run .Ar shell-command to replace the client. +.Tg has .It Ic has-session Op Fl t Ar target-session -.D1 (alias: Ic has ) +.D1 Pq alias: Ic has Report an error and exit with 1 if the specified session does not exist. If it does exist, exit with 0. .It Ic kill-server @@ -1160,11 +1163,12 @@ The .Fl C flag clears alerts (bell, activity, or silence) in all windows linked to the session. +.Tg lsc .It Xo Ic list-clients .Op Fl F Ar format .Op Fl t Ar target-session .Xc -.D1 (alias: Ic lsc ) +.D1 Pq alias: Ic lsc List all clients attached to the server. For the meaning of the .Fl F @@ -1174,20 +1178,22 @@ section. If .Ar target-session is specified, list only clients connected to that session. +.Tg lscm .It Xo Ic list-commands .Op Fl F Ar format .Op Ar command .Xc -.D1 (alias: Ic lscm ) +.D1 Pq alias: Ic lscm List the syntax of .Ar command or - if omitted - of all commands supported by .Nm . +.Tg ls .It Xo Ic list-sessions .Op Fl F Ar format .Op Fl f Ar filter .Xc -.D1 (alias: Ic ls ) +.D1 Pq alias: Ic ls List all sessions managed by the server. .Fl F specifies the format of each line and @@ -1197,17 +1203,20 @@ Only sessions for which the filter is true are shown. See the .Sx FORMATS section. +.Tg lockc .It Ic lock-client Op Fl t Ar target-client -.D1 (alias: Ic lockc ) +.D1 Pq alias: Ic lockc Lock .Ar target-client , see the .Ic lock-server command. +.Tg locks .It Ic lock-session Op Fl t Ar target-session -.D1 (alias: Ic locks ) +.D1 Pq alias: Ic locks Lock all clients attached to .Ar target-session . +.Tg new .It Xo Ic new-session .Op Fl AdDEPX .Op Fl c Ar start-directory @@ -1221,7 +1230,7 @@ Lock all clients attached to .Op Fl y Ar height .Op Ar shell-command .Xc -.D1 (alias: Ic new ) +.D1 Pq alias: Ic new Create a new session with name .Ar session-name . .Pp @@ -1327,6 +1336,7 @@ takes the form .Ql VARIABLE=value and sets an environment variable for the newly created session; it may be specified multiple times. +.Tg refresh .It Xo Ic refresh-client .Op Fl cDlLRSU .Op Fl A Ar pane:state @@ -1336,7 +1346,7 @@ specified multiple times. .Op Fl t Ar target-client .Op Ar adjustment .Xc -.D1 (alias: Ic refresh ) +.D1 Pq alias: Ic refresh Refresh the current client if bound to a key, or a single client if one is given with .Fl t . @@ -1462,18 +1472,20 @@ resets so that the position follows the cursor. See the .Ic window-size option. +.Tg rename .It Xo Ic rename-session .Op Fl t Ar target-session .Ar new-name .Xc -.D1 (alias: Ic rename ) +.D1 Pq alias: Ic rename Rename the session to .Ar new-name . +.Tg showmsgs .It Xo Ic show-messages .Op Fl JT .Op Fl t Ar target-client .Xc -.D1 (alias: Ic showmsgs ) +.D1 Pq alias: Ic showmsgs Show server messages or information. Messages are stored, up to a maximum of the limit set by the .Ar message-limit @@ -1482,12 +1494,13 @@ server option. and .Fl T show debugging information about jobs and terminals. +.Tg source .It Xo Ic source-file .Op Fl Fnqv .Ar path .Ar ... .Xc -.D1 (alias: Ic source ) +.D1 Pq alias: Ic source Execute commands from one or more files specified by .Ar path (which may be @@ -1508,8 +1521,9 @@ With the file is parsed but no commands are executed. .Fl v shows the parsed commands and line numbers if possible. +.Tg start .It Ic start-server -.D1 (alias: Ic start ) +.D1 Pq alias: Ic start Start the .Nm server, if not already running, without creating any sessions. @@ -1524,20 +1538,22 @@ For example: .Bd -literal -offset indent $ tmux start \\; show -g .Ed +.Tg suspendc .It Xo Ic suspend-client .Op Fl t Ar target-client .Xc -.D1 (alias: Ic suspendc ) +.D1 Pq alias: Ic suspendc Suspend a client by sending .Dv SIGTSTP (tty stop). +.Tg switchc .It Xo Ic switch-client .Op Fl ElnprZ .Op Fl c Ar target-client .Op Fl t Ar target-session .Op Fl T Ar key-table .Xc -.D1 (alias: Ic switchc ) +.D1 Pq alias: Ic switchc Switch the current session for client .Ar target-client to @@ -1921,6 +1937,7 @@ from which the layout was originally defined. .Pp Commands related to windows and panes are as follows: .Bl -tag -width Ds +.Tg breakp .It Xo Ic break-pane .Op Fl abdP .Op Fl F Ar format @@ -1928,7 +1945,7 @@ Commands related to windows and panes are as follows: .Op Fl s Ar src-pane .Op Fl t Ar dst-window .Xc -.D1 (alias: Ic breakp ) +.D1 Pq alias: Ic breakp Break .Ar src-pane off from its containing window to make it the only pane in @@ -1949,6 +1966,7 @@ By default, it uses the format .Ql #{session_name}:#{window_index}.#{pane_index} but a different format may be specified with .Fl F . +.Tg capturep .It Xo Ic capture-pane .Op Fl aepPqCJN .Op Fl b Ar buffer-name @@ -1956,7 +1974,7 @@ but a different format may be specified with .Op Fl S Ar start-line .Op Fl t Ar target-pane .Xc -.D1 (alias: Ic capturep ) +.D1 Pq alias: Ic capturep Capture the contents of a pane. If .Fl p @@ -2201,13 +2219,14 @@ specifies the format for each item in the tree. starts without the option information. This command works only if at least one client is attached. .It Xo +.Tg displayp .Ic display-panes .Op Fl bN .Op Fl d Ar duration .Op Fl t Ar target-client .Op Ar template .Xc -.D1 (alias: Ic displayp ) +.D1 Pq alias: Ic displayp Display a visible indicator of each pane shown by .Ar target-client . See the @@ -2241,12 +2260,13 @@ is "select-pane -t '%%'". With .Fl b , other commands are not blocked from running until the indicator is closed. +.Tg findw .It Xo Ic find-window .Op Fl iCNrTZ .Op Fl t Ar target-pane .Ar match-string .Xc -.D1 (alias: Ic findw ) +.D1 Pq alias: Ic findw Search for a .Xr fnmatch 3 pattern or, with @@ -2269,13 +2289,14 @@ The default is zooms the pane. .Pp This command works only if at least one client is attached. +.Tg joinp .It Xo Ic join-pane .Op Fl bdfhv .Op Fl l Ar size .Op Fl s Ar src-pane .Op Fl t Ar dst-pane .Xc -.D1 (alias: Ic joinp ) +.D1 Pq alias: Ic joinp Like .Ic split-window , but instead of splitting @@ -2298,22 +2319,24 @@ is omitted and a marked pane is present (see .Ic select-pane .Fl m ) , the marked pane is used rather than the current pane. +.Tg killp .It Xo Ic kill-pane .Op Fl a .Op Fl t Ar target-pane .Xc -.D1 (alias: Ic killp ) +.D1 Pq alias: Ic killp Destroy the given pane. If no panes remain in the containing window, it is also destroyed. The .Fl a option kills all but the pane given with .Fl t . +.Tg killw .It Xo Ic kill-window .Op Fl a .Op Fl t Ar target-window .Xc -.D1 (alias: Ic killw ) +.D1 Pq alias: Ic killw Kill the current window or the window at .Ar target-window , removing it from any sessions to which it is linked. @@ -2321,11 +2344,12 @@ The .Fl a option kills all but the window given with .Fl t . +.Tg lastp .It Xo Ic last-pane .Op Fl deZ .Op Fl t Ar target-window .Xc -.D1 (alias: Ic lastp ) +.D1 Pq alias: Ic lastp Select the last (previously selected) pane. .Fl Z keeps the window zoomed if it was zoomed. @@ -2333,18 +2357,20 @@ keeps the window zoomed if it was zoomed. enables or .Fl d disables input to the pane. +.Tg last .It Ic last-window Op Fl t Ar target-session -.D1 (alias: Ic last ) +.D1 Pq alias: Ic last Select the last (previously selected) window. If no .Ar target-session is specified, select the last window of the current session. +.Tg link .It Xo Ic link-window .Op Fl abdk .Op Fl s Ar src-window .Op Fl t Ar dst-window .Xc -.D1 (alias: Ic linkw ) +.D1 Pq alias: Ic linkw Link the window at .Ar src-window to the specified @@ -2369,13 +2395,14 @@ exists, it is killed, otherwise an error is generated. If .Fl d is given, the newly linked window is not selected. +.Tg lsp .It Xo Ic list-panes .Op Fl as .Op Fl F Ar format .Op Fl f Ar filter .Op Fl t Ar target .Xc -.D1 (alias: Ic lsp ) +.D1 Pq alias: Ic lsp If .Fl a is given, @@ -2397,13 +2424,14 @@ Only panes for which the filter is true are shown. See the .Sx FORMATS section. +.Tg lsw .It Xo Ic list-windows .Op Fl a .Op Fl F Ar format .Op Fl f Ar filter .Op Fl t Ar target-session .Xc -.D1 (alias: Ic lsw ) +.D1 Pq alias: Ic lsw If .Fl a is given, list all windows on the server. @@ -2417,21 +2445,23 @@ Only windows for which the filter is true are shown. See the .Sx FORMATS section. +.Tg movep .It Xo Ic move-pane .Op Fl bdfhv .Op Fl l Ar size .Op Fl s Ar src-pane .Op Fl t Ar dst-pane .Xc -.D1 (alias: Ic movep ) +.D1 Pq alias: Ic movep Does the same as .Ic join-pane . +.Tg movew .It Xo Ic move-window .Op Fl abrdk .Op Fl s Ar src-window .Op Fl t Ar dst-window .Xc -.D1 (alias: Ic movew ) +.D1 Pq alias: Ic movew This is similar to .Ic link-window , except the window at @@ -2444,6 +2474,7 @@ all windows in the session are renumbered in sequential order, respecting the .Ic base-index option. +.Tg neww .It Xo Ic new-window .Op Fl abdkPS .Op Fl c Ar start-directory @@ -2453,7 +2484,7 @@ option. .Op Fl t Ar target-window .Op Ar shell-command .Xc -.D1 (alias: Ic neww ) +.D1 Pq alias: Ic neww Create a new window. With .Fl a @@ -2526,24 +2557,27 @@ By default, it uses the format .Ql #{session_name}:#{window_index} but a different format may be specified with .Fl F . +.Tg nextl .It Ic next-layout Op Fl t Ar target-window -.D1 (alias: Ic nextl ) +.D1 Pq alias: Ic nextl Move a window to the next layout and rearrange the panes to fit. +.Tg next .It Xo Ic next-window .Op Fl a .Op Fl t Ar target-session .Xc -.D1 (alias: Ic next ) +.D1 Pq alias: Ic next Move to the next window in the session. If .Fl a is used, move to the next window with an alert. +.Tg pipep .It Xo Ic pipe-pane .Op Fl IOo .Op Fl t Ar target-pane .Op Ar shell-command .Xc -.D1 (alias: Ic pipep ) +.D1 Pq alias: Ic pipep Pipe output sent by the program in .Ar target-pane to a shell command or vice versa. @@ -2586,29 +2620,33 @@ be toggled with a single key, for example: .Bd -literal -offset indent bind-key C-p pipe-pane -o 'cat >>~/output.#I-#P' .Ed +.Tg prevl .It Xo Ic previous-layout .Op Fl t Ar target-window .Xc -.D1 (alias: Ic prevl ) +.D1 Pq alias: Ic prevl Move to the previous layout in the session. +.Tg prev .It Xo Ic previous-window .Op Fl a .Op Fl t Ar target-session .Xc -.D1 (alias: Ic prev ) +.D1 Pq alias: Ic prev Move to the previous window in the session. With .Fl a , move to the previous window with an alert. +.Tg renamew .It Xo Ic rename-window .Op Fl t Ar target-window .Ar new-name .Xc -.D1 (alias: Ic renamew ) +.D1 Pq alias: Ic renamew Rename the current window, or the window at .Ar target-window if specified, to .Ar new-name . +.Tg resizep .It Xo Ic resize-pane .Op Fl DLMRTUZ .Op Fl t Ar target-pane @@ -2616,7 +2654,7 @@ if specified, to .Op Fl y Ar height .Op Ar adjustment .Xc -.D1 (alias: Ic resizep ) +.D1 Pq alias: Ic resizep Resize a pane, up, down, left or right by .Ar adjustment with @@ -2653,6 +2691,7 @@ begins mouse resizing (only valid if bound to a mouse key binding, see .Fl T trims all lines below the current cursor position and moves lines out of the history to replace them. +.Tg resizew .It Xo Ic resize-window .Op Fl aADLRU .Op Fl t Ar target-window @@ -2660,7 +2699,7 @@ history to replace them. .Op Fl y Ar height .Op Ar adjustment .Xc -.D1 (alias: Ic resizew ) +.D1 Pq alias: Ic resizew Resize a window, up, down, left or right by .Ar adjustment with @@ -2685,6 +2724,7 @@ the size of the smallest. This command will automatically set .Ic window-size to manual in the window options. +.Tg respawnp .It Xo Ic respawn-pane .Op Fl k .Op Fl c Ar start-directory @@ -2692,7 +2732,7 @@ to manual in the window options. .Op Fl t Ar target-pane .Op Ar shell-command .Xc -.D1 (alias: Ic respawnp ) +.D1 Pq alias: Ic respawnp Reactivate a pane in which the command has exited (see the .Ic remain-on-exit window option). @@ -2710,6 +2750,7 @@ The option has the same meaning as for the .Ic new-window command. +.Tg respawnw .It Xo Ic respawn-window .Op Fl k .Op Fl c Ar start-directory @@ -2717,7 +2758,7 @@ command. .Op Fl t Ar target-window .Op Ar shell-command .Xc -.D1 (alias: Ic respawnw ) +.D1 Pq alias: Ic respawnw Reactivate a window in which the command has exited (see the .Ic remain-on-exit window option). @@ -2735,23 +2776,25 @@ The option has the same meaning as for the .Ic new-window command. +.Tg rotatew .It Xo Ic rotate-window .Op Fl DUZ .Op Fl t Ar target-window .Xc -.D1 (alias: Ic rotatew ) +.D1 Pq alias: Ic rotatew Rotate the positions of the panes within a window, either upward (numerically lower) with .Fl U or downward (numerically higher). .Fl Z keeps the window zoomed if it was zoomed. +.Tg selectl .It Xo Ic select-layout .Op Fl Enop .Op Fl t Ar target-pane .Op Ar layout-name .Xc -.D1 (alias: Ic selectl ) +.D1 Pq alias: Ic selectl Choose a specific layout for a window. If .Ar layout-name @@ -2768,12 +2811,13 @@ commands. applies the last set layout if possible (undoes the most recent layout change). .Fl E spreads the current pane and any panes next to it out evenly. +.Tg selectp .It Xo Ic select-pane .Op Fl DdeLlMmRUZ .Op Fl T Ar title .Op Fl t Ar target-pane .Xc -.D1 (alias: Ic selectp ) +.D1 Pq alias: Ic selectp Make pane .Ar target-pane the active pane in its window. @@ -2812,11 +2856,12 @@ to .Ic swap-pane and .Ic swap-window . +.Tg selectw .It Xo Ic select-window .Op Fl lnpT .Op Fl t Ar target-window .Xc -.D1 (alias: Ic selectw ) +.D1 Pq alias: Ic selectw Select the window at .Ar target-window . .Fl l , @@ -2834,6 +2879,7 @@ If is given and the selected window is already the current window, the command behaves like .Ic last-window . +.Tg splitw .It Xo Ic split-window .Op Fl bdfhIvPZ .Op Fl c Ar start-directory @@ -2843,7 +2889,7 @@ the command behaves like .Op Ar shell-command .Op Fl F Ar format .Xc -.D1 (alias: Ic splitw ) +.D1 Pq alias: Ic splitw Create a new pane by splitting .Ar target-pane : .Fl h @@ -2894,12 +2940,13 @@ $ make 2>&1|tmux splitw -dI & All other options have the same meaning as for the .Ic new-window command. +.Tg swapp .It Xo Ic swap-pane .Op Fl dDUZ .Op Fl s Ar src-pane .Op Fl t Ar dst-pane .Xc -.D1 (alias: Ic swapp ) +.D1 Pq alias: Ic swapp Swap two panes. If .Fl U @@ -2922,12 +2969,13 @@ is omitted and a marked pane is present (see .Ic select-pane .Fl m ) , the marked pane is used rather than the current pane. +.Tg swapw .It Xo Ic swap-window .Op Fl d .Op Fl s Ar src-window .Op Fl t Ar dst-window .Xc -.D1 (alias: Ic swapw ) +.D1 Pq alias: Ic swapw This is similar to .Ic link-window , except the source and destination windows are swapped. @@ -2943,11 +2991,12 @@ is omitted and a marked pane is present (see .Ic select-pane .Fl m ) , the window containing the marked pane is used rather than the current window. +.Tg unlinkw .It Xo Ic unlink-window .Op Fl k .Op Fl t Ar target-window .Xc -.D1 (alias: Ic unlinkw ) +.D1 Pq alias: Ic unlinkw Unlink .Ar target-window . Unless @@ -3013,13 +3062,14 @@ key will execute for all keys which do not have a more specific binding. .Pp Commands related to key bindings are as follows: .Bl -tag -width Ds +.Tg bind .It Xo Ic bind-key .Op Fl nr .Op Fl N Ar note .Op Fl T Ar key-table .Ar key command Op Ar arguments .Xc -.D1 (alias: Ic bind ) +.D1 Pq alias: Ic bind Bind key .Ar key to @@ -3071,12 +3121,13 @@ attaches a note to the key (shown with To view the default bindings and possible commands, see the .Ic list-keys command. +.Tg lsk .It Xo Ic list-keys .Op Fl 1aN .Op Fl P Ar prefix-string Fl T Ar key-table .Op Ar key .Xc -.D1 (alias: Ic lsk ) +.D1 Pq alias: Ic lsk List key bindings. There are two forms: the default lists keys as .Ic bind-key @@ -3106,13 +3157,14 @@ specifies a prefix to print before each key and lists only the first matching key. .Fl a lists the command for keys that do not have a note rather than skipping them. +.Tg send .It Xo Ic send-keys .Op Fl FHlMRX .Op Fl N Ar repeat-count .Op Fl t Ar target-pane .Ar key Ar ... .Xc -.D1 (alias: Ic send ) +.D1 Pq alias: Ic send Send a key or keys to a window. Each argument .Ar key @@ -3157,12 +3209,13 @@ expands formats in arguments where appropriate. Send the prefix key, or with .Fl 2 the secondary prefix key, to a window as if it was pressed. +.Tg unbind .It Xo Ic unbind-key .Op Fl anq .Op Fl T Ar key-table .Ar key .Xc -.D1 (alias: Ic unbind ) +.D1 Pq alias: Ic unbind Unbind the command bound to .Ar key . .Fl n @@ -3252,12 +3305,13 @@ abc123 .Pp Commands which set options are as follows: .Bl -tag -width Ds +.Tg set .It Xo Ic set-option .Op Fl aFgopqsuUw .Op Fl t Ar target-pane .Ar option Ar value .Xc -.D1 (alias: Ic set ) +.D1 Pq alias: Ic set Set a pane option with .Fl p , a window option with @@ -3326,12 +3380,13 @@ blue foreground. Without .Fl a , the result would be the default background and a blue foreground. +.Tg show .It Xo Ic show-options .Op Fl AgHpqsvw .Op Fl t Ar target-pane .Op Ar option .Xc -.D1 (alias: Ic show ) +.D1 Pq alias: Ic show Show the pane options (or a single option if .Ar option is provided) with @@ -4159,6 +4214,7 @@ see the .Sx STYLES section. Attributes are ignored. +.Pp .It Ic pane-base-index Ar index Like .Ic base-index , @@ -4203,6 +4259,50 @@ see the section. Attributes are ignored. .Pp +.It Ic popup-style Ar style +Set the popup style. +For how to specify +.Ar style , +see the +.Sx STYLES +section. +Attributes are ignored. +.Pp +.It Ic popup-border-style Ar style +Set the popup border style. +For how to specify +.Ar style , +see the +.Sx STYLES +section. +Attributes are ignored. +.Pp +.It Ic popup-border-lines Ar type +Set the type of characters used for drawing popup borders. +.Ar type +may be one of: +.Bl -tag -width Ds +.It single +single lines using ACS or UTF-8 characters (default) +.It rounded +variation of single with rounded corners using UTF-8 characters +.It double +double lines using UTF-8 characters +.It heavy +heavy lines using UTF-8 characters +.It simple +simple ASCII characters +.It padded +simple ASCII space character +.It none +no border +.El +.Pp +.Ql double +and +.Ql heavy +will fall back to standard ACS line drawing when UTF-8 is not supported. +.Pp .It Ic window-status-activity-style Ar style Set status line style for windows with an activity alert. For how to specify @@ -4316,6 +4416,9 @@ The alternate screen feature preserves the contents of the window when an interactive application starts and restores it on exit, so that any output visible before the application starts reappears unchanged after it exits. .Pp +.It Ic cursor-colour Ar colour +Set the colour of the cursor. +.Pp .It Ic pane-colours[] Ar colour The default colour palette. Each entry in the array defines the colour @@ -4323,6 +4426,17 @@ Each entry in the array defines the colour uses when the colour with that index is requested. The index may be from zero to 255. .Pp +.It Ic cursor-style Ar style +Set the style of the cursor. +Available styles are: +.Ic default , +.Ic blinking-block , +.Ic block , +.Ic blinking-underline , +.Ic underline , +.Ic blinking-bar , +.Ic bar . +.Pp .It Xo Ic remain-on-exit .Op Ic on | off | failed .Xc @@ -4357,7 +4471,6 @@ see the .Sx STYLES section. .El -.El .Sh HOOKS .Nm allows commands to run on various triggers, called @@ -4732,6 +4845,10 @@ replaces a numeric argument by its ASCII equivalent, so .Ql #{a:98} results in .Ql b . +.Ql c +replaces a +.Nm +colour by its six-digit hexadecimal RGB value. .Pp A limit may be placed on the length of the resultant string by prefixing it by an @@ -5303,12 +5420,13 @@ section). .Pp Commands to alter and view the environment are: .Bl -tag -width Ds +.Tg setenv .It Xo Ic set-environment .Op Fl Fhgru .Op Fl t Ar target-session .Ar name Op Ar value .Xc -.D1 (alias: Ic setenv ) +.D1 Pq alias: Ic setenv Set or unset an environment variable. If .Fl g @@ -5328,12 +5446,13 @@ indicates the variable is to be removed from the environment before starting a new process. .Fl h marks the variable as hidden. +.Tg showenv .It Xo Ic show-environment .Op Fl hgs .Op Fl t Ar target-session .Op Ar variable .Xc -.D1 (alias: Ic showenv ) +.D1 Pq alias: Ic showenv Display the environment for .Ar target-session or the global environment with @@ -5412,10 +5531,11 @@ session option. .Pp Commands related to the status line are as follows: .Bl -tag -width Ds +.Tg clrphist .It Xo Ic clear-prompt-history .Op Fl T Ar prompt-type .Xc -.D1 (alias: Ic clrphist) +.D1 Pq alias: Ic clrphist Clear status prompt history for prompt type .Ar prompt-type . If @@ -5534,13 +5654,14 @@ With .Fl b , the prompt is shown in the background and the invoking client does not exit until it is dismissed. +.Tg confirm .It Xo Ic confirm-before .Op Fl b .Op Fl p Ar prompt .Op Fl t Ar target-client .Ar command .Xc -.D1 (alias: Ic confirm ) +.D1 Pq alias: Ic confirm Ask for confirmation before executing .Ar command . If @@ -5556,6 +5677,7 @@ With .Fl b , the prompt is shown in the background and the invoking client does not exit until it is dismissed. +.Tg menu .It Xo Ic display-menu .Op Fl O .Op Fl c Ar target-client @@ -5568,7 +5690,7 @@ until it is dismissed. .Ar command .Ar ... .Xc -.D1 (alias: Ic menu ) +.D1 Pq alias: Ic menu Display a menu on .Ar target-client . .Ar target-pane @@ -5647,6 +5769,7 @@ The following keys are also available: .It Li "Down" Ta "Select next item" .It Li "q" Ta "Exit menu" .El +.Tg display .It Xo Ic display-message .Op Fl aINpv .Op Fl c Ar target-client @@ -5654,7 +5777,7 @@ The following keys are also available: .Op Fl t Ar target-pane .Op Ar message .Xc -.D1 (alias: Ic display ) +.D1 Pq alias: Ic display Display a message. If .Fl p @@ -5688,18 +5811,24 @@ lists the format variables and their values. .Fl I forwards any input read from stdin to the empty pane given by .Ar target-pane . +.Tg popup .It Xo Ic display-popup .Op Fl BCE +.Op Fl b Ar border-lines .Op Fl c Ar target-client .Op Fl d Ar start-directory +.Op Fl e Ar environment .Op Fl h Ar height +.Op Fl s Ar style +.Op Fl S Ar border-style .Op Fl t Ar target-pane +.Op Fl T Ar title .Op Fl w Ar width .Op Fl x Ar position .Op Fl y Ar position .Op Ar shell-command .Xc -.D1 (alias: Ic popup ) +.D1 Pq alias: Ic popup Display a popup running .Ar shell-command on @@ -5729,16 +5858,50 @@ and give the width and height - both may be a percentage (followed by .Ql % ) . If omitted, half of the terminal size is used. +.Pp .Fl B does not surround the popup by a border. .Pp +.Fl b +sets the type of border line for the popup. +When +.Fl B +is specified the +.Fl b +option is ignored. +See +.Ic popup-border-lines +for possible values for +.Ar border-lines . +.Pp +.Fl s +sets the style for the popup and +.Fl S +sets the style for the popup border. +For how to specify +.Ar style , +see the +.Sx STYLES +section. +.Pp +.Fl e +takes the form +.Ql VARIABLE=value +and sets an environment variable for the popup; it may be specified multiple +times. +.Pp +.Fl T +is a format for the popup title (see +.Sx FORMATS ) . +.Pp The .Fl C flag closes any popup on the client. +.Tg showphist .It Xo Ic show-prompt-history .Op Fl T Ar prompt-type .Xc -.D1 (alias: Ic showphist) +.D1 Pq alias: Ic showphist Display status prompt history for prompt type .Ar prompt-type . If @@ -5864,19 +6027,22 @@ a format for each shortcut key; both are evaluated once for each line. .Fl N starts without the preview. This command works only if at least one client is attached. +.Tg clearhist .It Ic clear-history Op Fl t Ar target-pane -.D1 (alias: Ic clearhist ) +.D1 Pq alias: Ic clearhist Remove and free the history for the specified pane. +.Tg deleteb .It Ic delete-buffer Op Fl b Ar buffer-name -.D1 (alias: Ic deleteb ) +.D1 Pq alias: Ic deleteb Delete the buffer named .Ar buffer-name , or the most recently added automatically named buffer if not specified. +.Tg lsb .It Xo Ic list-buffers .Op Fl F Ar format .Op Fl f Ar filter .Xc -.D1 (alias: Ic lsb ) +.D1 Pq alias: Ic lsb List the global buffers. .Fl F specifies the format of each line and @@ -5892,7 +6058,8 @@ section. .Op Fl t Ar target-client .Ar path .Xc -.D1 (alias: Ic loadb ) +.Tg loadb +.D1 Pq alias: Ic loadb Load the contents of the specified paste buffer from .Ar path . If @@ -5902,13 +6069,14 @@ is given, the buffer is also sent to the clipboard for using the .Xr xterm 1 escape sequence, if possible. +.Tg pasteb .It Xo Ic paste-buffer .Op Fl dpr .Op Fl b Ar buffer-name .Op Fl s Ar separator .Op Fl t Ar target-pane .Xc -.D1 (alias: Ic pasteb ) +.D1 Pq alias: Ic pasteb Insert the contents of a paste buffer into the specified pane. If not specified, paste into the current one. With @@ -5926,12 +6094,13 @@ If .Fl p is specified, paste bracket control codes are inserted around the buffer if the application has requested bracketed paste mode. +.Tg saveb .It Xo Ic save-buffer .Op Fl a .Op Fl b Ar buffer-name .Ar path .Xc -.D1 (alias: Ic saveb ) +.D1 Pq alias: Ic saveb Save the contents of the specified paste buffer to .Ar path . The @@ -5941,10 +6110,11 @@ option appends to rather than overwriting the file. .Op Fl aw .Op Fl b Ar buffer-name .Op Fl t Ar target-client +.Tg setb .Op Fl n Ar new-buffer-name .Ar data .Xc -.D1 (alias: Ic setb ) +.D1 Pq alias: Ic setb Set the contents of the specified buffer to .Ar data . If @@ -5961,10 +6131,11 @@ The .Fl n option renames the buffer to .Ar new-buffer-name . +.Tg showb .It Xo Ic show-buffer .Op Fl b Ar buffer-name .Xc -.D1 (alias: Ic showb ) +.D1 Pq alias: Ic showb Display the contents of the specified buffer. .El .Sh MISCELLANEOUS @@ -5972,13 +6143,14 @@ Miscellaneous commands are as follows: .Bl -tag -width Ds .It Ic clock-mode Op Fl t Ar target-pane Display a large clock. +.Tg if .It Xo Ic if-shell .Op Fl bF .Op Fl t Ar target-pane .Ar shell-command command .Op Ar command .Xc -.D1 (alias: Ic if ) +.D1 Pq alias: Ic if Execute the first .Ar command if @@ -6003,18 +6175,20 @@ is given, .Ar shell-command is not executed but considered success if neither empty nor zero (after formats are expanded). +.Tg lock .It Ic lock-server -.D1 (alias: Ic lock ) +.D1 Pq alias: Ic lock Lock each client individually by running the command specified by the .Ic lock-command option. +.Tg run .It Xo Ic run-shell .Op Fl bC .Op Fl d Ar delay .Op Fl t Ar target-pane .Op Ar shell-command .Xc -.D1 (alias: Ic run ) +.D1 Pq alias: Ic run Execute .Ar shell-command or (with @@ -6041,11 +6215,12 @@ specified by .Fl t or the current pane if omitted) after the command finishes. If the command fails, the exit status is also displayed. +.Tg wait .It Xo Ic wait-for .Op Fl L | S | U .Ar channel .Xc -.D1 (alias: Ic wait ) +.D1 Pq alias: Ic wait When used without options, prevents the client from exiting until woken using .Ic wait-for .Fl S diff --git a/tmux.h b/tmux.h index 4182ce07..12d1d9d5 100644 --- a/tmux.h +++ b/tmux.h @@ -533,6 +533,7 @@ enum tty_code_code { #define MODE_CRLF 0x4000 #define MODE_KEXTENDED 0x8000 #define MODE_CURSOR_VERY_VISIBLE 0x10000 +#define MODE_CURSOR_BLINKING_SET 0x20000 #define ALL_MODES 0xffffff #define ALL_MOUSE_MODES (MODE_MOUSE_STANDARD|MODE_MOUSE_BUTTON|MODE_MOUSE_ALL) @@ -616,6 +617,24 @@ struct colour_palette { #define GRID_LINE_EXTENDED 0x2 #define GRID_LINE_DEAD 0x4 +#define CELL_INSIDE 0 +#define CELL_TOPBOTTOM 1 +#define CELL_LEFTRIGHT 2 +#define CELL_TOPLEFT 3 +#define CELL_TOPRIGHT 4 +#define CELL_BOTTOMLEFT 5 +#define CELL_BOTTOMRIGHT 6 +#define CELL_TOPJOIN 7 +#define CELL_BOTTOMJOIN 8 +#define CELL_LEFTJOIN 9 +#define CELL_RIGHTJOIN 10 +#define CELL_JOIN 11 +#define CELL_OUTSIDE 12 + +#define CELL_BORDERS " xqlkmjwvtun~" +#define SIMPLE_BORDERS " |-+++++++++." +#define PADDED_BORDERS " " + /* Grid cell data. */ struct grid_cell { struct utf8_data data; @@ -779,11 +798,16 @@ struct screen { u_int cy; /* cursor y */ enum screen_cursor_style cstyle; /* cursor style */ - char *ccolour; /* cursor colour */ + enum screen_cursor_style default_cstyle; + int ccolour; /* cursor colour */ + int default_ccolour; u_int rupper; /* scroll region top */ u_int rlower; /* scroll region bottom */ + int mode; + int default_mode; + u_int saved_cx; u_int saved_cy; struct grid *saved_grid; @@ -817,6 +841,27 @@ struct screen_write_ctx { u_int bg; }; +/* Box border lines option. */ +enum box_lines { + BOX_LINES_DEFAULT = -1, + BOX_LINES_SINGLE, + BOX_LINES_DOUBLE, + BOX_LINES_HEAVY, + BOX_LINES_SIMPLE, + BOX_LINES_ROUNDED, + BOX_LINES_PADDED, + BOX_LINES_NONE +}; + +/* Pane border lines option. */ +enum pane_lines { + PANE_LINES_SINGLE, + PANE_LINES_DOUBLE, + PANE_LINES_HEAVY, + PANE_LINES_SIMPLE, + PANE_LINES_NUMBER +}; + /* Screen redraw context. */ struct screen_redraw_ctx { struct client *c; @@ -825,7 +870,7 @@ struct screen_redraw_ctx { int statustop; int pane_status; - int pane_lines; + enum pane_lines pane_lines; struct grid_cell no_pane_gc; int no_pane_gc_set; @@ -1079,13 +1124,6 @@ TAILQ_HEAD(winlink_stack, winlink); #define PANE_STATUS_TOP 1 #define PANE_STATUS_BOTTOM 2 -/* Pane border lines option. */ -#define PANE_LINES_SINGLE 0 -#define PANE_LINES_DOUBLE 1 -#define PANE_LINES_HEAVY 2 -#define PANE_LINES_SIMPLE 3 -#define PANE_LINES_NUMBER 4 - /* Layout direction. */ enum layout_type { LAYOUT_LEFTRIGHT, @@ -1263,7 +1301,7 @@ struct tty { u_int cx; u_int cy; enum screen_cursor_style cstyle; - char *ccolour; + int ccolour; int oflag; u_int oox; @@ -1588,10 +1626,18 @@ struct client_window { }; RB_HEAD(client_windows, client_window); +/* Visible areas not obstructed by overlays. */ +#define OVERLAY_MAX_RANGES 3 +struct overlay_ranges { + u_int px[OVERLAY_MAX_RANGES]; + u_int nx[OVERLAY_MAX_RANGES]; +}; + /* Client connection. */ typedef int (*prompt_input_cb)(struct client *, void *, const char *, int); typedef void (*prompt_free_cb)(void *); -typedef int (*overlay_check_cb)(struct client *, void *, u_int, u_int); +typedef void (*overlay_check_cb)(struct client*, void *, u_int, u_int, u_int, + struct overlay_ranges *); typedef struct screen *(*overlay_mode_cb)(struct client *, void *, u_int *, u_int *); typedef void (*overlay_draw_cb)(struct client *, void *, @@ -1691,6 +1737,9 @@ struct client { (CLIENT_DEAD| \ CLIENT_SUSPENDED| \ CLIENT_EXIT) +#define CLIENT_NODETACHFLAGS \ + (CLIENT_DEAD| \ + CLIENT_EXIT) #define CLIENT_NOSIZEFLAGS \ (CLIENT_DEAD| \ CLIENT_SUSPENDED| \ @@ -1732,6 +1781,7 @@ struct client { #define PROMPT_KEY 0x10 int prompt_flags; enum prompt_type prompt_type; + int prompt_cursor; struct session *session; struct session *last_session; @@ -2013,7 +2063,7 @@ char *format_grid_line(struct grid *, u_int); /* format-draw.c */ void format_draw(struct screen_write_ctx *, const struct grid_cell *, u_int, const char *, - struct style_ranges *); + struct style_ranges *, int); u_int format_width(const char *); char *format_trim_left(const char *, u_int); char *format_trim_right(const char *, u_int); @@ -2079,6 +2129,8 @@ struct style *options_string_to_style(struct options *, const char *, int options_from_string(struct options *, const struct options_table_entry *, const char *, const char *, int, char **); +int options_find_choice(const struct options_table_entry *, + const char *, char **); void options_push_changes(const char *); int options_remove_or_default(struct options_entry *, int, char **); @@ -2094,9 +2146,9 @@ typedef void (*job_free_cb) (void *); #define JOB_NOWAIT 0x1 #define JOB_KEEPWRITE 0x2 #define JOB_PTY 0x4 -struct job *job_run(const char *, int, char **, struct session *, - const char *, job_update_cb, job_complete_cb, job_free_cb, - void *, int, int, int); +struct job *job_run(const char *, int, char **, struct environ *, + struct session *, const char *, job_update_cb, + job_complete_cb, job_free_cb, void *, int, int, int); void job_free(struct job *); int job_transfer(struct job *, pid_t *, char *, size_t); void job_resize(struct job *, u_int, u_int); @@ -2231,6 +2283,9 @@ void tty_default_features(int *, const char *, u_int); int tty_acs_needed(struct tty *); const char *tty_acs_get(struct tty *, u_char); int tty_acs_reverse_get(struct tty *, const char *, size_t); +const struct utf8_data *tty_acs_double_borders(int); +const struct utf8_data *tty_acs_heavy_borders(int); +const struct utf8_data *tty_acs_rounded_borders(int); /* tty-keys.c */ void tty_keys_build(struct tty *); @@ -2485,6 +2540,8 @@ void server_client_set_overlay(struct client *, u_int, overlay_check_cb, overlay_mode_cb, overlay_draw_cb, overlay_key_cb, overlay_free_cb, overlay_resize_cb, void *); void server_client_clear_overlay(struct client *); +void server_client_overlay_range(u_int, u_int, u_int, u_int, u_int, u_int, + u_int, struct overlay_ranges *); void server_client_set_key_table(struct client *, const char *); const char *server_client_get_key_table(struct client *); int server_client_check_nested(struct client *); @@ -2592,6 +2649,7 @@ int input_key_get_mouse(struct screen *, struct mouse_event *, u_int, int colour_find_rgb(u_char, u_char, u_char); int colour_join_rgb(u_char, u_char, u_char); void colour_split_rgb(int, u_char *, u_char *, u_char *); +int colour_force_rgb(int); const char *colour_tostring(int); int colour_fromstring(const char *s); int colour_256toRGB(int); @@ -2712,7 +2770,8 @@ void screen_write_hline(struct screen_write_ctx *, u_int, int, int); void screen_write_vline(struct screen_write_ctx *, u_int, int, int); void screen_write_menu(struct screen_write_ctx *, struct menu *, int, const struct grid_cell *); -void screen_write_box(struct screen_write_ctx *, u_int, u_int); +void screen_write_box(struct screen_write_ctx *, u_int, u_int, int, + const struct grid_cell *, const char *); void screen_write_preview(struct screen_write_ctx *, struct screen *, u_int, u_int); void screen_write_backspace(struct screen_write_ctx *); @@ -2765,8 +2824,8 @@ void screen_init(struct screen *, u_int, u_int, u_int); void screen_reinit(struct screen *); void screen_free(struct screen *); void screen_reset_tabs(struct screen *); -void screen_set_cursor_style(struct screen *, u_int); -void screen_set_cursor_colour(struct screen *, const char *); +void screen_set_cursor_style(u_int, enum screen_cursor_style *, int *); +void screen_set_cursor_colour(struct screen *, int); int screen_set_title(struct screen *, const char *); void screen_set_path(struct screen *, const char *); void screen_push_title(struct screen *); @@ -3117,7 +3176,8 @@ int menu_display(struct menu *, int, struct cmdq_item *, u_int, u_int, struct client *, struct cmd_find_state *, menu_choice_cb, void *); struct screen *menu_mode_cb(struct client *, void *, u_int *, u_int *); -int menu_check_cb(struct client *, void *, u_int, u_int); +void menu_check_cb(struct client *, void *, u_int, u_int, u_int, + struct overlay_ranges *); void menu_draw_cb(struct client *, void *, struct screen_redraw_ctx *); void menu_free_cb(struct client *, void *); @@ -3126,13 +3186,14 @@ int menu_key_cb(struct client *, void *, struct key_event *); /* popup.c */ #define POPUP_CLOSEEXIT 0x1 #define POPUP_CLOSEEXITZERO 0x2 -#define POPUP_NOBORDER 0x4 -#define POPUP_INTERNAL 0x8 +#define POPUP_INTERNAL 0x4 typedef void (*popup_close_cb)(int, void *); typedef void (*popup_finish_edit_cb)(char *, size_t, void *); -int popup_display(int, struct cmdq_item *, u_int, u_int, u_int, - u_int, const char *, int, char **, const char *, - struct client *, struct session *, popup_close_cb, void *); +int popup_display(int, int, struct cmdq_item *, u_int, u_int, + u_int, u_int, struct environ *, const char *, int, char **, + const char *, const char *, struct client *, + struct session *, const char *, const char *, + popup_close_cb, void *); int popup_editor(struct client *, const char *, size_t, popup_finish_edit_cb, void *); diff --git a/tools/ansicode.txt b/tools/ansicode.txt index 8767b9e7..5a9c79e0 100644 --- a/tools/ansicode.txt +++ b/tools/ansicode.txt @@ -184,7 +184,7 @@ Oct Hex Name * (* marks function used in DEC VT series or LA series terminals) 230 98 X Reserved for for future standard 231 99 Y Reserved 232 9A Z * Reserved, but causes DEC terminals to respond with DA codes -233 9B [ CSI * Control Sequence Introducer (described in a seperate table) +233 9B [ CSI * Control Sequence Introducer (described in a separate table) 234 9C \ ST * String Terminator (VT125 exits graphics) 235 9D ] OSC Operating System Command (reprograms intelligent terminal) 236 9E ^ PM Privacy Message (password verification), terminated by ST diff --git a/tty-acs.c b/tty-acs.c index 63eccb93..64ba367e 100644 --- a/tty-acs.c +++ b/tty-acs.c @@ -25,7 +25,7 @@ /* Table mapping ACS entries to UTF-8. */ struct tty_acs_entry { - u_char key; + u_char key; const char *string; }; static const struct tty_acs_entry tty_acs_table[] = { @@ -61,7 +61,7 @@ static const struct tty_acs_entry tty_acs_table[] = { { 'x', "\342\224\202" }, /* vertical line */ { 'y', "\342\211\244" }, /* less-than-or-equal-to */ { 'z', "\342\211\245" }, /* greater-than-or-equal-to */ - { '{', "\317\200" }, /* greek pi */ + { '{', "\317\200" }, /* greek pi */ { '|', "\342\211\240" }, /* not-equal */ { '}', "\302\243" }, /* UK pound sign */ { '~', "\302\267" } /* bullet */ @@ -110,6 +110,78 @@ static const struct tty_acs_reverse_entry tty_acs_reverse3[] = { { "\342\225\254", 'n' }, }; +/* UTF-8 double borders. */ +static const struct utf8_data tty_acs_double_borders_list[] = { + { "", 0, 0, 0 }, + { "\342\225\221", 0, 3, 1 }, /* U+2551 */ + { "\342\225\220", 0, 3, 1 }, /* U+2550 */ + { "\342\225\224", 0, 3, 1 }, /* U+2554 */ + { "\342\225\227", 0, 3, 1 }, /* U+2557 */ + { "\342\225\232", 0, 3, 1 }, /* U+255A */ + { "\342\225\235", 0, 3, 1 }, /* U+255D */ + { "\342\225\246", 0, 3, 1 }, /* U+2566 */ + { "\342\225\251", 0, 3, 1 }, /* U+2569 */ + { "\342\225\240", 0, 3, 1 }, /* U+2560 */ + { "\342\225\243", 0, 3, 1 }, /* U+2563 */ + { "\342\225\254", 0, 3, 1 }, /* U+256C */ + { "\302\267", 0, 2, 1 } /* U+00B7 */ +}; + +/* UTF-8 heavy borders. */ +static const struct utf8_data tty_acs_heavy_borders_list[] = { + { "", 0, 0, 0 }, + { "\342\224\203", 0, 3, 1 }, /* U+2503 */ + { "\342\224\201", 0, 3, 1 }, /* U+2501 */ + { "\342\224\217", 0, 3, 1 }, /* U+250F */ + { "\342\224\223", 0, 3, 1 }, /* U+2513 */ + { "\342\224\227", 0, 3, 1 }, /* U+2517 */ + { "\342\224\233", 0, 3, 1 }, /* U+251B */ + { "\342\224\263", 0, 3, 1 }, /* U+2533 */ + { "\342\224\273", 0, 3, 1 }, /* U+253B */ + { "\342\224\243", 0, 3, 1 }, /* U+2523 */ + { "\342\224\253", 0, 3, 1 }, /* U+252B */ + { "\342\225\213", 0, 3, 1 }, /* U+254B */ + { "\302\267", 0, 2, 1 } /* U+00B7 */ +}; + +/* UTF-8 rounded borders. */ +static const struct utf8_data tty_acs_rounded_borders_list[] = { + { "", 0, 0, 0 }, + { "\342\224\202", 0, 3, 1 }, /* U+2502 */ + { "\342\224\200", 0, 3, 1 }, /* U+2500 */ + { "\342\225\255", 0, 3, 1 }, /* U+256D */ + { "\342\225\256", 0, 3, 1 }, /* U+256E */ + { "\342\225\260", 0, 3, 1 }, /* U+2570 */ + { "\342\225\257", 0, 3, 1 }, /* U+256F */ + { "\342\224\263", 0, 3, 1 }, /* U+2533 */ + { "\342\224\273", 0, 3, 1 }, /* U+253B */ + { "\342\224\243", 0, 3, 1 }, /* U+2523 */ + { "\342\224\253", 0, 3, 1 }, /* U+252B */ + { "\342\225\213", 0, 3, 1 }, /* U+254B */ + { "\302\267", 0, 2, 1 } /* U+00B7 */ +}; + +/* Get cell border character for double style. */ +const struct utf8_data * +tty_acs_double_borders(int cell_type) +{ + return (&tty_acs_double_borders_list[cell_type]); +} + +/* Get cell border character for heavy style. */ +const struct utf8_data * +tty_acs_heavy_borders(int cell_type) +{ + return (&tty_acs_heavy_borders_list[cell_type]); +} + +/* Get cell border character for rounded style. */ +const struct utf8_data * +tty_acs_rounded_borders(int cell_type) +{ + return (&tty_acs_rounded_borders_list[cell_type]); +} + static int tty_acs_cmp(const void *key, const void *value) { diff --git a/tty-keys.c b/tty-keys.c index edc1e558..22c64a5d 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -1204,6 +1204,9 @@ tty_keys_clipboard(__unused struct tty *tty, const char *buf, size_t len, buf += 5; end -= 5; + /* Adjust end so that it points to the start of the terminator. */ + end -= terminator - 1; + /* Get the second argument. */ while (end != 0 && *buf != ';') { buf++; diff --git a/tty.c b/tty.c index 6b2d917e..fb1276de 100644 --- a/tty.c +++ b/tty.c @@ -38,7 +38,7 @@ static int tty_client_ready(struct client *); static void tty_set_italics(struct tty *); static int tty_try_colour(struct tty *, int, const char *); -static void tty_force_cursor_colour(struct tty *, const char *); +static void tty_force_cursor_colour(struct tty *, int); static void tty_cursor_pane(struct tty *, const struct tty_ctx *, u_int, u_int); static void tty_cursor_pane_unless_wrap(struct tty *, @@ -71,6 +71,8 @@ static void tty_draw_pane(struct tty *, const struct tty_ctx *, u_int); static void tty_default_attributes(struct tty *, const struct grid_cell *, struct colour_palette *, u_int); static int tty_check_overlay(struct tty *, u_int, u_int); +static void tty_check_overlay_range(struct tty *, u_int, u_int, u_int, + struct overlay_ranges *); #define tty_use_margin(tty) \ (tty->term->flags & TERM_DECSLRM) @@ -103,7 +105,7 @@ tty_init(struct tty *tty, struct client *c) tty->client = c; tty->cstyle = SCREEN_CURSOR_DEFAULT; - tty->ccolour = xstrdup(""); + tty->ccolour = -1; if (tcgetattr(c->fd, &tty->tio) != 0) return (-1); @@ -305,7 +307,7 @@ tty_start_tty(struct tty *tty) { struct client *c = tty->client; struct termios tio; - struct timeval tv = { .tv_sec = 1 }; + struct timeval tv = { .tv_sec = 3 }; setblocking(c->fd, 0); event_add(&tty->event_in, NULL); @@ -344,8 +346,8 @@ tty_start_tty(struct tty *tty) tty->flags |= TTY_STARTED; tty_invalidate(tty); - if (*tty->ccolour != '\0') - tty_force_cursor_colour(tty, ""); + if (tty->ccolour != -1) + tty_force_cursor_colour(tty, -1); tty->mouse_drag_flag = 0; tty->mouse_drag_update = NULL; @@ -409,7 +411,7 @@ tty_stop_tty(struct tty *tty) } if (tty->mode & MODE_BRACKETPASTE) tty_raw(tty, tty_term_string(tty->term, TTYC_DSBP)); - if (*tty->ccolour != '\0') + if (tty->ccolour != -1) tty_raw(tty, tty_term_string(tty->term, TTYC_CR)); tty_raw(tty, tty_term_string(tty->term, TTYC_CNORM)); @@ -454,7 +456,6 @@ void tty_free(struct tty *tty) { tty_close(tty); - free(tty->ccolour); } void @@ -653,39 +654,66 @@ tty_set_title(struct tty *tty, const char *title) } static void -tty_force_cursor_colour(struct tty *tty, const char *ccolour) +tty_force_cursor_colour(struct tty *tty, int c) { - if (*ccolour == '\0') + u_char r, g, b; + char s[13] = ""; + + if (c != -1) + c = colour_force_rgb(c); + if (c == tty->ccolour) + return; + if (c == -1) tty_putcode(tty, TTYC_CR); - else - tty_putcode_ptr1(tty, TTYC_CS, ccolour); - free(tty->ccolour); - tty->ccolour = xstrdup(ccolour); + else { + colour_split_rgb(c, &r, &g, &b); + xsnprintf(s, sizeof s, "rgb:%02hhx/%02hhx/%02hhx", r, g, b); + tty_putcode_ptr1(tty, TTYC_CS, s); + } + tty->ccolour = c; } -static void -tty_update_cursor(struct tty *tty, int mode, int changed, struct screen *s) +static int +tty_update_cursor(struct tty *tty, int mode, struct screen *s) { - enum screen_cursor_style cstyle; + enum screen_cursor_style cstyle; + int ccolour, changed, cmode = mode; /* Set cursor colour if changed. */ - if (s != NULL && strcmp(s->ccolour, tty->ccolour) != 0) - tty_force_cursor_colour(tty, s->ccolour); + if (s != NULL) { + ccolour = s->ccolour; + if (s->ccolour == -1) + ccolour = s->default_ccolour; + tty_force_cursor_colour(tty, ccolour); + } /* If cursor is off, set as invisible. */ - if (~mode & MODE_CURSOR) { - if (changed & MODE_CURSOR) + if (~cmode & MODE_CURSOR) { + if (tty->mode & MODE_CURSOR) tty_putcode(tty, TTYC_CIVIS); - return; + return (cmode); } /* Check if blinking or very visible flag changed or style changed. */ if (s == NULL) cstyle = tty->cstyle; - else + else { cstyle = s->cstyle; + if (cstyle == SCREEN_CURSOR_DEFAULT) { + if (~cmode & MODE_CURSOR_BLINKING_SET) { + if (s->default_mode & MODE_CURSOR_BLINKING) + cmode |= MODE_CURSOR_BLINKING; + else + cmode &= ~MODE_CURSOR_BLINKING; + } + cstyle = s->default_cstyle; + } + } + + /* If nothing changed, do nothing. */ + changed = cmode ^ tty->mode; if ((changed & CURSOR_MODES) == 0 && cstyle == tty->cstyle) - return; + return (cmode); /* * Set cursor style. If an explicit style has been set with DECSCUSR, @@ -703,49 +731,56 @@ tty_update_cursor(struct tty *tty, int mode, int changed, struct screen *s) else tty_putcode1(tty, TTYC_SS, 0); } - if (mode & (MODE_CURSOR_BLINKING|MODE_CURSOR_VERY_VISIBLE)) + if (cmode & (MODE_CURSOR_BLINKING|MODE_CURSOR_VERY_VISIBLE)) tty_putcode(tty, TTYC_CVVIS); break; case SCREEN_CURSOR_BLOCK: if (tty_term_has(tty->term, TTYC_SS)) { - if (mode & MODE_CURSOR_BLINKING) + if (cmode & MODE_CURSOR_BLINKING) tty_putcode1(tty, TTYC_SS, 1); else tty_putcode1(tty, TTYC_SS, 2); - } else if (mode & MODE_CURSOR_BLINKING) + } else if (cmode & MODE_CURSOR_BLINKING) tty_putcode(tty, TTYC_CVVIS); break; case SCREEN_CURSOR_UNDERLINE: if (tty_term_has(tty->term, TTYC_SS)) { - if (mode & MODE_CURSOR_BLINKING) + if (cmode & MODE_CURSOR_BLINKING) tty_putcode1(tty, TTYC_SS, 3); else tty_putcode1(tty, TTYC_SS, 4); - } else if (mode & MODE_CURSOR_BLINKING) + } else if (cmode & MODE_CURSOR_BLINKING) tty_putcode(tty, TTYC_CVVIS); break; case SCREEN_CURSOR_BAR: if (tty_term_has(tty->term, TTYC_SS)) { - if (mode & MODE_CURSOR_BLINKING) + if (cmode & MODE_CURSOR_BLINKING) tty_putcode1(tty, TTYC_SS, 5); else tty_putcode1(tty, TTYC_SS, 6); - } else if (mode & MODE_CURSOR_BLINKING) + } else if (cmode & MODE_CURSOR_BLINKING) tty_putcode(tty, TTYC_CVVIS); break; } tty->cstyle = cstyle; + return (cmode); } void tty_update_mode(struct tty *tty, int mode, struct screen *s) { + struct tty_term *term = tty->term; struct client *c = tty->client; int changed; if (tty->flags & TTY_NOCURSOR) mode &= ~MODE_CURSOR; + if (tty_update_cursor(tty, mode, s) & MODE_CURSOR_BLINKING) + mode |= MODE_CURSOR_BLINKING; + else + mode &= ~MODE_CURSOR_BLINKING; + changed = mode ^ tty->mode; if (log_get_level() != 0 && changed != 0) { log_debug("%s: current mode %s", c->name, @@ -754,9 +789,7 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s) screen_mode_to_string(mode)); } - tty_update_cursor(tty, mode, changed, s); - if ((changed & ALL_MOUSE_MODES) && - tty_term_has(tty->term, TTYC_KMOUS)) { + if ((changed & ALL_MOUSE_MODES) && tty_term_has(term, TTYC_KMOUS)) { /* * If the mouse modes have changed, clear any that are set and * apply again. There are differences in how terminals track @@ -909,7 +942,9 @@ tty_update_window_offset(struct window *w) struct client *c; TAILQ_FOREACH(c, &clients, entry) { - if (c->session != NULL && c->session->curw->window == w) + if (c->session != NULL && + c->session->curw != NULL && + c->session->curw->window == w) tty_update_client_offset(c); } } @@ -1051,8 +1086,9 @@ static void tty_clear_line(struct tty *tty, const struct grid_cell *defaults, u_int py, u_int px, u_int nx, u_int bg) { - struct client *c = tty->client; - u_int i; + struct client *c = tty->client; + struct overlay_ranges r; + u_int i; log_debug("%s: %s, %u at %u,%u", __func__, c->name, nx, px, py); @@ -1088,18 +1124,13 @@ tty_clear_line(struct tty *tty, const struct grid_cell *defaults, u_int py, * Couldn't use an escape sequence, use spaces. Clear only the visible * bit if there is an overlay. */ - for (i = 0; i < nx; i++) { - if (!tty_check_overlay(tty, px + i, py)) - break; + tty_check_overlay_range(tty, px, py, nx, &r); + for (i = 0; i < OVERLAY_MAX_RANGES; i++) { + if (r.nx[i] == 0) + continue; + tty_cursor(tty, r.px[i], py); + tty_repeat_space(tty, r.nx[i]); } - tty_cursor(tty, px, py); - tty_repeat_space(tty, i); - for (; i < nx; i++) { - if (tty_check_overlay(tty, px + i, py)) - break; - } - tty_cursor(tty, px + i, py); - tty_repeat_space(tty, nx - i); } /* Clear a line, adjusting to visible part of pane. */ @@ -1311,14 +1342,44 @@ tty_check_codeset(struct tty *tty, const struct grid_cell *gc) return (&new); } +/* + * Check if a single character is obstructed by the overlay and return a + * boolean. + */ static int tty_check_overlay(struct tty *tty, u_int px, u_int py) +{ + struct overlay_ranges r; + + /* + * A unit width range will always return nx[2] == 0 from a check, even + * with multiple overlays, so it's sufficient to check just the first + * two entries. + */ + tty_check_overlay_range(tty, px, py, 1, &r); + if (r.nx[0] + r.nx[1] == 0) + return (0); + return (1); +} + +/* Return parts of the input range which are visible. */ +static void +tty_check_overlay_range(struct tty *tty, u_int px, u_int py, u_int nx, + struct overlay_ranges *r) { struct client *c = tty->client; - if (c->overlay_check == NULL) - return (1); - return (c->overlay_check(c, c->overlay_data, px, py)); + if (c->overlay_check == NULL) { + r->px[0] = px; + r->nx[0] = nx; + r->px[1] = 0; + r->nx[1] = 0; + r->px[2] = 0; + r->nx[2] = 0; + return; + } + + c->overlay_check(c, c->overlay_data, px, py, nx, r); } void @@ -1331,11 +1392,12 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx, const struct grid_cell *gcp; struct grid_line *gl; struct client *c = tty->client; - u_int i, j, ux, sx, width, hidden; + struct overlay_ranges r; + u_int i, j, ux, sx, width, hidden, eux, nxx; + u_int cellsize; int flags, cleared = 0, wrapped = 0; char buf[512]; size_t len; - u_int cellsize; log_debug("%s: px=%u py=%u nx=%u atx=%u aty=%u", __func__, px, py, nx, atx, aty); @@ -1435,29 +1497,31 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx, else memcpy(&last, gcp, sizeof last); + tty_check_overlay_range(tty, atx + ux, aty, gcp->data.width, + &r); hidden = 0; - for (j = 0; j < gcp->data.width; j++) { - if (!tty_check_overlay(tty, atx + ux + j, aty)) - hidden++; - } + for (j = 0; j < OVERLAY_MAX_RANGES; j++) + hidden += r.nx[j]; + hidden = gcp->data.width - hidden; if (hidden != 0 && hidden == gcp->data.width) { if (~gcp->flags & GRID_FLAG_PADDING) ux += gcp->data.width; } else if (hidden != 0 || ux + gcp->data.width > nx) { if (~gcp->flags & GRID_FLAG_PADDING) { tty_attributes(tty, &last, defaults, palette); - tty_cursor(tty, atx + ux, aty); - for (j = 0; j < gcp->data.width; j++) { - if (ux > nx) - break; - if (tty_check_overlay(tty, atx + ux, - aty)) - tty_putc(tty, ' '); - else { - tty_cursor(tty, atx + ux + 1, - aty); + for (j = 0; j < OVERLAY_MAX_RANGES; j++) { + if (r.nx[j] == 0) + continue; + /* Effective width drawn so far. */ + eux = r.px[j] - atx; + if (eux < nx) { + tty_cursor(tty, r.px[j], aty); + nxx = nx - eux; + if (r.nx[j] > nxx) + r.nx[j] = nxx; + tty_repeat_space(tty, r.nx[j]); + ux = eux + r.nx[j]; } - ux++; } } } else if (gcp->attr & GRID_ATTR_CHARSET) { @@ -1963,7 +2027,8 @@ tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx) { const struct grid_cell *gcp = ctx->cell; struct screen *s = ctx->s; - u_int i, px, py; + struct overlay_ranges r; + u_int px, py, i, vis = 0; px = ctx->xoff + ctx->ocx - ctx->wox; py = ctx->yoff + ctx->ocy - ctx->woy; @@ -1973,13 +2038,13 @@ tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx) /* Handle partially obstructed wide characters. */ if (gcp->data.width > 1) { - for (i = 0; i < gcp->data.width; i++) { - if (!tty_check_overlay(tty, px + i, py)) { - tty_draw_line(tty, s, s->cx, s->cy, - gcp->data.width, px, py, &ctx->defaults, - ctx->palette); - return; - } + tty_check_overlay_range(tty, px, py, gcp->data.width, &r); + for (i = 0; i < OVERLAY_MAX_RANGES; i++) + vis += r.nx[i]; + if (vis < gcp->data.width) { + tty_draw_line(tty, s, s->cx, s->cy, gcp->data.width, + px, py, &ctx->defaults, ctx->palette); + return; } } @@ -1997,7 +2062,9 @@ tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx) void tty_cmd_cells(struct tty *tty, const struct tty_ctx *ctx) { - u_int i, hide = 0; + struct overlay_ranges r; + u_int i, px, py, cx; + char *cp = ctx->ptr; if (!tty_is_visible(tty, ctx, ctx->ocx, ctx->ocy, ctx->num, 1)) return; @@ -2020,20 +2087,21 @@ tty_cmd_cells(struct tty *tty, const struct tty_ctx *ctx) tty_margin_off(tty); tty_cursor_pane_unless_wrap(tty, ctx, ctx->ocx, ctx->ocy); - tty_attributes(tty, ctx->cell, &ctx->defaults, ctx->palette); - for (i = 0; i < ctx->num; i++) { - if (!tty_check_overlay(tty, tty->cx + i, tty->cy)) - break; + + /* Get tty position from pane position for overlay check. */ + px = ctx->xoff + ctx->ocx - ctx->wox; + py = ctx->yoff + ctx->ocy - ctx->woy; + + tty_check_overlay_range(tty, px, py, ctx->num, &r); + for (i = 0; i < OVERLAY_MAX_RANGES; i++) { + if (r.nx[i] == 0) + continue; + /* Convert back to pane position for printing. */ + cx = r.px[i] - ctx->xoff + ctx->wox; + tty_cursor_pane_unless_wrap(tty, ctx, cx, ctx->ocy); + tty_putn(tty, cp + r.px[i] - px, r.nx[i], r.nx[i]); } - tty_putn(tty, ctx->ptr, i, i); - for (; i < ctx->num; i++) { - if (tty_check_overlay(tty, tty->cx + hide, tty->cy)) - break; - hide++; - } - tty_cursor(tty, tty->cx + hide, tty->cy); - tty_putn(tty, (char *)ctx->ptr + i, ctx->num - i, ctx->num - i); } void @@ -2321,17 +2389,25 @@ tty_cursor(struct tty *tty, u_int cx, u_int cy) if (tty->flags & TTY_BLOCK) return; - if (cx > tty->sx - 1) - cx = tty->sx - 1; - thisx = tty->cx; thisy = tty->cy; + /* + * If in the automargin space, and want to be there, do not move. + * Otherwise, force the cursor to be in range (and complain). + */ + if (cx == thisx && cy == thisy && cx == tty->sx) + return; + if (cx > tty->sx - 1) { + log_debug("%s: x too big %u > %u", __func__, cx, tty->sx - 1); + cx = tty->sx - 1; + } + /* No change. */ if (cx == thisx && cy == thisy) return; - /* Very end of the line, just use absolute movement. */ + /* Currently at the very end of the line - use absolute movement. */ if (thisx > tty->sx - 1) goto absolute; diff --git a/utf8.c b/utf8.c index 56f20cbb..df75a769 100644 --- a/utf8.c +++ b/utf8.c @@ -237,21 +237,6 @@ utf8_width(struct utf8_data *ud, int *width) if (*width >= 0 && *width <= 0xff) return (UTF8_DONE); log_debug("UTF-8 %.*s, wcwidth() %d", (int)ud->size, ud->data, *width); - -#ifndef __OpenBSD__ - /* - * Many platforms (particularly and inevitably OS X) have no width for - * relatively common characters (wcwidth() returns -1); assume width 1 - * in this case. This will be wrong for genuinely nonprintable - * characters, but they should be rare. We may pass through stuff that - * ideally we would block, but this is no worse than sending the same - * to the terminal without tmux. - */ - if (*width < 0) { - *width = 1; - return (UTF8_DONE); - } -#endif return (UTF8_ERROR); } diff --git a/window-copy.c b/window-copy.c index c1a31b48..ec6a2f4e 100644 --- a/window-copy.c +++ b/window-copy.c @@ -4531,8 +4531,8 @@ window_copy_pipe_run(struct window_mode_entry *wme, struct session *s, if (cmd == NULL || *cmd == '\0') cmd = options_get_string(global_options, "copy-command"); if (cmd != NULL && *cmd != '\0') { - job = job_run(cmd, 0, NULL, s, NULL, NULL, NULL, NULL, NULL, - JOB_NOWAIT, -1, -1); + job = job_run(cmd, 0, NULL, NULL, s, NULL, NULL, NULL, NULL, + NULL, JOB_NOWAIT, -1, -1); bufferevent_write(job_get_event(job), buf, *len); } return (buf); diff --git a/window-customize.c b/window-customize.c index 0f09eba8..98387e50 100644 --- a/window-customize.c +++ b/window-customize.c @@ -398,11 +398,11 @@ window_customize_build_options(struct window_customize_modedata *data, for (i = 0; i < size; i++) { if (oo2 != NULL) - o = options_get(oo0, list[i]); + o = options_get(oo2, list[i]); if (o == NULL && oo1 != NULL) o = options_get(oo1, list[i]); if (o == NULL) - o = options_get(oo2, list[i]); + o = options_get(oo0, list[i]); if (options_owner(o) == oo2) scope = scope2; else if (options_owner(o) == oo1) diff --git a/window-tree.c b/window-tree.c index 4998e1aa..77011ea2 100644 --- a/window-tree.c +++ b/window-tree.c @@ -519,7 +519,8 @@ window_tree_draw_label(struct screen_write_ctx *ctx, u_int px, u_int py, if (ox > 1 && ox + len < sx - 1 && sy >= 3) { screen_write_cursormove(ctx, px + ox - 1, py + oy - 1, 0); - screen_write_box(ctx, len + 2, 3); + screen_write_box(ctx, len + 2, 3, BOX_LINES_DEFAULT, NULL, + NULL); } screen_write_cursormove(ctx, px + ox, py + oy, 0); screen_write_puts(ctx, gc, "%s", label);