From 759efe1b3327a7244c03ecc7b90e0e3c49712d06 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 11 Oct 2021 10:55:30 +0000 Subject: Add -e flag to set environment for popup, from Alexis Hildebrandt in GitHub issue 2924. --- cmd-display-menu.c | 27 +++++++++++++++++++++------ cmd-if-shell.c | 2 +- cmd-run-shell.c | 2 +- format.c | 2 +- job.c | 5 ++++- popup.c | 9 +++++---- tmux.1 | 7 +++++++ tmux.h | 11 ++++++----- window-copy.c | 4 ++-- 9 files changed, 48 insertions(+), 21 deletions(-) diff --git a/cmd-display-menu.c b/cmd-display-menu.c index 5df01c7a..87871f68 100644 --- a/cmd-display-menu.c +++ b/cmd-display-menu.c @@ -53,10 +53,10 @@ 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 = { "BCc:d:e:Eh:t:w:x:y:", 0, -1, NULL }, + .usage = "[-BCE] [-c target-client] [-d start-directory] " + "[-e environment] [-h height] " CMD_TARGET_PANE_USAGE " " + "[-w width] [-x position] [-y position] [shell-command]", .target = { 't', CMD_FIND_PANE, 0 }, @@ -357,6 +357,8 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item) char *cwd, *cause, **argv = NULL; int flags = 0, argc = 0; u_int px, py, w, h, count = args_count(args); + struct args_value *av; + struct environ *env = NULL; if (args_has(args, 'C')) { server_client_clear_overlay(tc); @@ -410,17 +412,30 @@ 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, '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, item, px, py, w, h, env, shellcmd, argc, argv, + cwd, tc, s, NULL, NULL) != 0) { cmd_free_argv(argc, argv); + if (env != NULL) + environ_free(env); return (CMD_RETURN_NORMAL); } + if (env != NULL) + environ_free(env); 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-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/format.c b/format.c index 37b9123b..f5d2c5f6 100644 --- a/format.c +++ b/format.c @@ -390,7 +390,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) { diff --git a/job.c b/job.c index 66a25557..4c13f943 100644 --- a/job.c +++ b/job.c @@ -71,7 +71,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) { @@ -89,6 +89,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); diff --git a/popup.c b/popup.c index d4fc3833..635559fa 100644 --- a/popup.c +++ b/popup.c @@ -590,8 +590,9 @@ 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) + u_int sy, struct environ *env, const char *shellcmd, int argc, char **argv, + const char *cwd, struct client *c, struct session *s, popup_close_cb cb, + void *arg) { struct popup_data *pd; u_int jx, jy; @@ -635,7 +636,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); @@ -725,7 +726,7 @@ popup_editor(struct client *c, const char *buf, size_t len, 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) { + NULL, cmd, 0, NULL, _PATH_TMP, c, NULL, popup_editor_close_cb, pe) != 0) { popup_editor_free(pe); free(cmd); return (-1); diff --git a/tmux.1 b/tmux.1 index 027db664..14556ba3 100644 --- a/tmux.1 +++ b/tmux.1 @@ -5753,6 +5753,7 @@ forwards any input read from stdin to the empty pane given by .Op Fl BCE .Op Fl c Ar target-client .Op Fl d Ar start-directory +.Op Fl e Ar environment .Op Fl h Ar height .Op Fl t Ar target-pane .Op Fl w Ar width @@ -5793,6 +5794,12 @@ If omitted, half of the terminal size is used. .Fl B does not surround the popup by a border. .Pp +.Fl e +takes the form +.Ql VARIABLE=value +and sets an environment variable for the popup; it may be specified multiple +times. +.Pp The .Fl C flag closes any popup on the client. diff --git a/tmux.h b/tmux.h index 29a532aa..57d3c909 100644 --- a/tmux.h +++ b/tmux.h @@ -2073,9 +2073,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); @@ -3105,8 +3105,9 @@ int menu_key_cb(struct client *, void *, struct key_event *); 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 *); + u_int, struct environ *, const char *, int, char **, + const char *, struct client *, struct session *, + popup_close_cb, void *); int popup_editor(struct client *, const char *, size_t, popup_finish_edit_cb, void *); 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); -- cgit From b8581ec80e5339be5e2c08cfec70a77f21ba06b2 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 11 Oct 2021 13:27:50 +0000 Subject: Make positions hidden by overlays range-based rather than character-based, from Anindya Mukherjee. --- menu.c | 13 +++--- popup.c | 49 +++++++++++++++++---- screen-redraw.c | 13 +++--- server-client.c | 48 +++++++++++++++++++++ tmux.h | 15 ++++++- tty.c | 131 +++++++++++++++++++++++++++++++++++--------------------- 6 files changed, 196 insertions(+), 73 deletions(-) diff --git a/menu.c b/menu.c index 043dafdd..4c6403a0 100644 --- a/menu.c +++ b/menu.c @@ -140,17 +140,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/popup.c b/popup.c index 635559fa..106e4ee5 100644 --- a/popup.c +++ b/popup.c @@ -157,18 +157,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 diff --git a/screen-redraw.c b/screen-redraw.c index 82e390cd..1d736531 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -685,14 +685,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/server-client.c b/server-client.c index 51988799..dc5074e7 100644 --- a/server-client.c +++ b/server-client.c @@ -147,6 +147,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) diff --git a/tmux.h b/tmux.h index 57d3c909..f4083a71 100644 --- a/tmux.h +++ b/tmux.h @@ -1567,10 +1567,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 *, @@ -2462,6 +2470,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 *); @@ -3091,7 +3101,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 *); diff --git a/tty.c b/tty.c index 0f295f6c..9a8265ef 100644 --- a/tty.c +++ b/tty.c @@ -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) @@ -1046,8 +1048,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); @@ -1083,18 +1086,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_cursor(tty, px, py); - tty_repeat_space(tty, i); - for (; 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 + i, py); - tty_repeat_space(tty, nx - i); } /* Clear a line, adjusting to visible part of pane. */ @@ -1306,14 +1304,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 @@ -1326,11 +1354,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); @@ -1430,29 +1459,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) { @@ -1926,7 +1957,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; @@ -1936,13 +1968,14 @@ 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, + 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; - } } } @@ -1960,7 +1993,8 @@ 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; if (!tty_is_visible(tty, ctx, ctx->ocx, ctx->ocy, ctx->num, 1)) return; @@ -1985,18 +2019,15 @@ tty_cmd_cells(struct tty *tty, const struct tty_ctx *ctx) 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; - } - tty_putn(tty, ctx->ptr, i, i); - for (; i < ctx->num; i++) { - if (tty_check_overlay(tty, tty->cx + hide, tty->cy)) - break; - hide++; + px = tty->cx; + tty_check_overlay_range(tty, px, tty->cy, ctx->num, &r); + for (i = 0; i < OVERLAY_MAX_RANGES; i++) { + if (r.nx[i] == 0) + continue; + tty_cursor(tty, r.px[i], tty->cy); + tty_putn(tty, (char *)ctx->ptr + r.px[i] - px, r.nx[i], + r.nx[i]); } - tty_cursor(tty, tty->cx + hide, tty->cy); - tty_putn(tty, (char *)ctx->ptr + i, ctx->num - i, ctx->num - i); } void -- cgit From 837ca176d1874273f3de615c75b506e1b1787a1b Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 13 Oct 2021 09:28:36 +0000 Subject: Add popup-style and popup-border-style options, from Alexis Hildebrandt in GitHub issue 2927. --- mode-tree.c | 2 +- options-table.c | 18 ++++++++++++++++++ popup.c | 14 +++++++++++--- screen-write.c | 12 ++++++++---- tmux.1 | 18 ++++++++++++++++++ tmux.h | 3 ++- window-tree.c | 2 +- 7 files changed, 59 insertions(+), 10 deletions(-) diff --git a/mode-tree.c b/mode-tree.c index c92f7cff..2d2d8d5c 100644 --- a/mode-tree.c +++ b/mode-tree.c @@ -747,7 +747,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, NULL); if (mtd->sort_list != NULL) { xasprintf(&text, " %s (sort: %s%s)", mti->name, diff --git a/options-table.c b/options-table.c index 607a90b6..e728dbe9 100644 --- a/options-table.c +++ b/options-table.c @@ -982,6 +982,24 @@ 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 = "remain-on-exit", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, diff --git a/popup.c b/popup.c index 106e4ee5..5eea46ef 100644 --- a/popup.c +++ b/popup.c @@ -212,16 +212,22 @@ popup_draw_cb(struct client *c, void *data, struct screen_redraw_ctx *rctx) u_int i, px = pd->px, py = pd->py; struct colour_palette *palette = &pd->palette; struct grid_cell gc; + struct grid_cell bgc; + struct options *o = c->session->curw->window->options; screen_init(&s, pd->sx, pd->sy, 0); screen_write_start(&ctx, &s); screen_write_clearscreen(&ctx, 8); + memcpy(&bgc, &grid_default_cell, sizeof bgc); + style_apply(&bgc, o, "popup-border-style", NULL); + bgc.attr = 0; + if (pd->flags & POPUP_NOBORDER) { 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, &bgc); screen_write_cursormove(&ctx, 1, 1, 0); screen_write_fast_copy(&ctx, &pd->s, 0, 0, pd->sx - 2, pd->sy - 2); @@ -229,8 +235,10 @@ popup_draw_cb(struct client *c, void *data, struct screen_redraw_ctx *rctx) screen_write_stop(&ctx); memcpy(&gc, &grid_default_cell, sizeof gc); - gc.fg = pd->palette.fg; - gc.bg = pd->palette.bg; + style_apply(&gc, o, "popup-style", NULL); + gc.attr = 0; + palette->fg = gc.fg; + palette->bg = gc.bg; if (pd->md != NULL) { c->overlay_check = menu_check_cb; diff --git a/screen-write.c b/screen-write.c index c09d09ab..e3b2b977 100644 --- a/screen-write.c +++ b/screen-write.c @@ -645,7 +645,7 @@ 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_box(ctx, menu->width + 4, menu->count + 2, NULL); screen_write_cursormove(ctx, cx + 2, cy, 0); format_draw(ctx, &default_gc, menu->width, menu->title, NULL); @@ -677,16 +677,20 @@ screen_write_menu(struct screen_write_ctx *ctx, struct menu *menu, /* 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, + const struct grid_cell *gcp) { 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; diff --git a/tmux.1 b/tmux.1 index 14556ba3..f222e907 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4258,6 +4258,24 @@ 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 window-status-activity-style Ar style Set status line style for windows with an activity alert. For how to specify diff --git a/tmux.h b/tmux.h index f4083a71..885093e0 100644 --- a/tmux.h +++ b/tmux.h @@ -2699,7 +2699,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, + const struct grid_cell *gc); void screen_write_preview(struct screen_write_ctx *, struct screen *, u_int, u_int); void screen_write_backspace(struct screen_write_ctx *); diff --git a/window-tree.c b/window-tree.c index e7029d33..a721f1b5 100644 --- a/window-tree.c +++ b/window-tree.c @@ -519,7 +519,7 @@ 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, NULL); } screen_write_cursormove(ctx, px + ox, py + oy, 0); screen_write_puts(ctx, gc, "%s", label); -- cgit From d0ab1a837a0ab3e26fe7195f14672f6feb43c4c4 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 14 Oct 2021 09:54:51 +0000 Subject: When checking ranges in tty_cmd_cells, cannot use the tty cursor position and tty_cursor because it may be at the final invisible cursor position on automargin terminals. The text to be drawn is confined to the pane, so use the pane cursor position for the checks instead. Fix from Anindya Mukherjee, redraw problem reported by naddy@. --- tty.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/tty.c b/tty.c index 9a8265ef..809289e0 100644 --- a/tty.c +++ b/tty.c @@ -1993,8 +1993,9 @@ tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx) void tty_cmd_cells(struct tty *tty, const struct tty_ctx *ctx) { - struct overlay_ranges r; - u_int i, px; + 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; @@ -2017,16 +2018,20 @@ 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); - px = tty->cx; - tty_check_overlay_range(tty, px, tty->cy, ctx->num, &r); + + /* 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; - tty_cursor(tty, r.px[i], tty->cy); - tty_putn(tty, (char *)ctx->ptr + r.px[i] - px, r.nx[i], - r.nx[i]); + /* 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]); } } -- cgit From add20637f256c0118d3c687d5d1446612d14389a Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 14 Oct 2021 13:19:01 +0000 Subject: Add popup-border-lines option to set popup line style, from Alexis Hildebrandt, GitHub issue 2930. --- cmd-display-menu.c | 32 ++++++++++++++++++------ mode-tree.c | 2 +- options-table.c | 16 ++++++++++-- options.c | 33 ++++++++++++++++--------- popup.c | 44 +++++++++++++++++++++------------ screen-redraw.c | 71 +++++++++-------------------------------------------- screen-write.c | 70 +++++++++++++++++++++++++++++++++++++++++++--------- tmux.1 | 58 +++++++++++++++++++++++++++++++++++++++++++ tmux.h | 64 +++++++++++++++++++++++++++++++++++++----------- tty-acs.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ window-tree.c | 2 +- 11 files changed, 340 insertions(+), 124 deletions(-) diff --git a/cmd-display-menu.c b/cmd-display-menu.c index 87871f68..f6531ede 100644 --- a/cmd-display-menu.c +++ b/cmd-display-menu.c @@ -53,9 +53,10 @@ const struct cmd_entry cmd_display_popup_entry = { .name = "display-popup", .alias = "popup", - .args = { "BCc:d:e:Eh:t:w:x:y:", 0, -1, NULL }, - .usage = "[-BCE] [-c target-client] [-d start-directory] " - "[-e environment] [-h height] " CMD_TARGET_PANE_USAGE " " + .args = { "Bb:Cc:d:e:Eh:t:w:x:y:", 0, -1, NULL }, + .usage = "[-BCE] [-b border-lines] [-c target-client] " + "[-d start-directory] [-e environment] [-h height] " + CMD_TARGET_PANE_USAGE " " "[-w width] [-x position] [-y position] [shell-command]", .target = { 't', CMD_FIND_PANE, 0 }, @@ -354,11 +355,14 @@ 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; + char *cwd, *cause = NULL, **argv = NULL; 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); @@ -394,6 +398,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); @@ -425,10 +443,8 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item) 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, env, shellcmd, argc, argv, - cwd, tc, s, NULL, NULL) != 0) { + if (popup_display(flags, lines, item, px, py, w, h, env, shellcmd, argc, + argv, cwd, tc, s, NULL, NULL) != 0) { cmd_free_argv(argc, argv); if (env != NULL) environ_free(env); diff --git a/mode-tree.c b/mode-tree.c index 2d2d8d5c..2e029691 100644 --- a/mode-tree.c +++ b/mode-tree.c @@ -747,7 +747,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, NULL); + screen_write_box(&ctx, w, sy - h, BOX_LINES_DEFAULT, NULL); if (mtd->sort_list != NULL) { xasprintf(&text, " %s (sort: %s%s)", mti->name, diff --git a/options-table.c b/options-table.c index e728dbe9..6c57e3ff 100644 --- a/options-table.c +++ b/options-table.c @@ -60,9 +60,12 @@ static const char *options_table_visual_bell_list[] = { 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 }; @@ -951,7 +954,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." @@ -1000,6 +1003,15 @@ const struct options_table_entry options_table[] = { .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..65263fd0 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); diff --git a/popup.c b/popup.c index 5eea46ef..328deba6 100644 --- a/popup.c +++ b/popup.c @@ -31,6 +31,7 @@ struct popup_data { struct client *c; struct cmdq_item *item; int flags; + enum box_lines lines; struct screen s; struct colour_palette palette; @@ -117,7 +118,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->lines == BOX_LINES_NONE) { ttyctx->xoff = ttyctx->rxoff = pd->px; ttyctx->yoff = ttyctx->ryoff = pd->py; } else { @@ -147,7 +148,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->lines == BOX_LINES_NONE) { *cx = pd->px + pd->s.cx; *cy = pd->py + pd->s.cy; } else { @@ -220,14 +221,15 @@ popup_draw_cb(struct client *c, void *data, struct screen_redraw_ctx *rctx) screen_write_clearscreen(&ctx, 8); memcpy(&bgc, &grid_default_cell, sizeof bgc); + bgc.attr = 0; style_apply(&bgc, o, "popup-border-style", NULL); bgc.attr = 0; - if (pd->flags & POPUP_NOBORDER) { + if (pd->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, &bgc); + screen_write_box(&ctx, pd->sx, pd->sy, pd->lines, &bgc); screen_write_cursormove(&ctx, 1, 1, 0); screen_write_fast_copy(&ctx, &pd->s, 0, 0, pd->sx - 2, pd->sy - 2); @@ -318,7 +320,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->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 ); @@ -444,7 +446,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->lines == BOX_LINES_NONE) { if (m->x < pd->px + 1) return; if (m->y < pd->py + 1) @@ -460,7 +462,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->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); @@ -508,7 +510,7 @@ popup_key_cb(struct client *c, void *data, struct key_event *event) goto menu; return (0); } - if (~pd->flags & POPUP_NOBORDER) { + if (pd->lines != BOX_LINES_NONE) { if (m->x == pd->px) border = LEFT; else if (m->x == pd->px + pd->sx - 1) @@ -542,7 +544,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->lines == BOX_LINES_NONE) { px = m->x - pd->px; py = m->y - pd->py; } else { @@ -628,15 +630,23 @@ 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, struct environ *env, 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, struct client *c, struct session *s, + popup_close_cb cb, void *arg) { struct popup_data *pd; u_int jx, jy; + struct options *o; - if (flags & POPUP_NOBORDER) { + if (lines == BOX_LINES_DEFAULT) { + if (s != NULL) + o = s->curw->window->options; + else + o = c->session->curw->window->options; + lines = options_get_number(o, "popup-border-lines"); + } + if (lines == BOX_LINES_NONE) { if (sx < 1 || sy < 1) return (-1); jx = sx; @@ -653,6 +663,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->lines = lines; pd->c = c; pd->c->references++; @@ -764,8 +775,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, - NULL, 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, c, NULL, + popup_editor_close_cb, pe) != 0) { popup_editor_free(pe); free(cmd); return (-1); diff --git a/screen-redraw.c b/screen-redraw.c index 1d736531..11900b4f 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; @@ -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); diff --git a/screen-write.c b/screen-write.c index e3b2b977..3cd0fb55 100644 --- a/screen-write.c +++ b/screen-write.c @@ -645,7 +645,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, NULL); + screen_write_box(ctx, menu->width + 4, menu->count + 2, + BOX_LINES_DEFAULT, NULL); screen_write_cursormove(ctx, cx + 2, cy, 0); format_draw(ctx, &default_gc, menu->width, menu->title, NULL); @@ -675,10 +676,45 @@ 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, - const struct grid_cell *gcp) + enum box_lines l, const struct grid_cell *gcp) { struct screen *s = ctx->s; struct grid_cell gc; @@ -694,24 +730,34 @@ screen_write_box(struct screen_write_ctx *ctx, u_int nx, u_int ny, gc.attr |= GRID_ATTR_CHARSET; gc.flags |= GRID_FLAG_NOPALETTE; - screen_write_putc(ctx, &gc, 'l'); + /* Draw top border */ + screen_write_box_border_set(l, CELL_TOPLEFT, &gc); + screen_write_cell(ctx, &gc); + screen_write_box_border_set(l, 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(l, 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(l, CELL_BOTTOMLEFT, &gc); + screen_write_cell(ctx, &gc); + screen_write_box_border_set(l, 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(l, CELL_BOTTOMRIGHT, &gc); + screen_write_cell(ctx, &gc); + /* Draw sides */ + screen_write_box_border_set(l, 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); } screen_write_set_cursor(ctx, cx, cy); diff --git a/tmux.1 b/tmux.1 index f222e907..cb930c9c 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4276,6 +4276,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 @@ -5769,6 +5813,7 @@ forwards any input read from stdin to the empty pane given by .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 @@ -5809,9 +5854,22 @@ 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 e takes the form .Ql VARIABLE=value diff --git a/tmux.h b/tmux.h index 885093e0..65a3a571 100644 --- a/tmux.h +++ b/tmux.h @@ -613,6 +613,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; @@ -799,6 +817,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; @@ -807,7 +846,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; @@ -1061,13 +1100,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, @@ -2066,6 +2098,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 **); @@ -2216,6 +2250,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 *); @@ -2699,8 +2736,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, - const struct grid_cell *gc); +void screen_write_box(struct screen_write_ctx *, u_int, u_int, int, + const struct grid_cell *); void screen_write_preview(struct screen_write_ctx *, struct screen *, u_int, u_int); void screen_write_backspace(struct screen_write_ctx *); @@ -3112,12 +3149,11 @@ 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, struct environ *, const char *, int, char **, +int popup_display(int, int, struct cmdq_item *, u_int, u_int, + u_int, u_int, struct environ *, const char *, int, char **, const char *, struct client *, struct session *, popup_close_cb, void *); int popup_editor(struct client *, const char *, size_t, diff --git a/tty-acs.c b/tty-acs.c index 63eccb93..1a57826c 100644 --- a/tty-acs.c +++ b/tty-acs.c @@ -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/window-tree.c b/window-tree.c index a721f1b5..d22a2b58 100644 --- a/window-tree.c +++ b/window-tree.c @@ -519,7 +519,7 @@ 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, NULL); + screen_write_box(ctx, len + 2, 3, BOX_LINES_DEFAULT, NULL); } screen_write_cursormove(ctx, px + ox, py + oy, 0); screen_write_puts(ctx, gc, "%s", label); -- cgit From 537302f2c17849058cf6080f5735efe86d21a239 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 15 Oct 2021 10:39:22 +0000 Subject: Do not send any key if -N flag is given even if no other arguments, fixes problem with repeat in copy mode reported by tb@. --- cmd-send-keys.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmd-send-keys.c b/cmd-send-keys.c index 47fa1caa..46cb37f0 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')) + return (CMD_RETURN_NORMAL); for (; np != 0; np--) cmd_send_keys_inject_key(item, NULL, event->key); return (CMD_RETURN_NORMAL); -- cgit From cc27a43c402b412509f4bd63ecf4c263ea65ec81 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 18 Oct 2021 09:09:46 +0000 Subject: Remove duplicate options, spotted by Ricky Cintron. --- tmux.1 | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/tmux.1 b/tmux.1 index cb930c9c..ab92be2e 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4276,24 +4276,6 @@ 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 -- cgit From 2c188ee0c5dbe2633920b0c2a341264335734767 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 18 Oct 2021 09:15:56 +0000 Subject: Spacing fixes from Alexis Hildebrandt. --- tty-acs.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/tty-acs.c b/tty-acs.c index 1a57826c..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 */ @@ -124,7 +124,7 @@ static const struct utf8_data tty_acs_double_borders_list[] = { { "\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 */ + { "\302\267", 0, 2, 1 } /* U+00B7 */ }; /* UTF-8 heavy borders. */ @@ -141,24 +141,24 @@ static const struct utf8_data tty_acs_heavy_borders_list[] = { { "\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 */ + { "\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 */ + { "", 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. */ -- cgit From 51ff77d47be80d59b1e30e55bd75788fcc22e4bf Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 18 Oct 2021 09:48:35 +0000 Subject: Fix menu width containing disabled items, from Alexis Hildebrandt in GitHub issue 2935. --- menu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/menu.c b/menu.c index 4c6403a0..a1c81816 100644 --- a/menu.c +++ b/menu.c @@ -100,6 +100,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; } -- cgit From f26b8c57ffb253db08072629b7c969c021580492 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 19 Oct 2021 12:51:43 +0000 Subject: Same as -N, don't send if 0 arguments and -R. --- cmd-send-keys.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd-send-keys.c b/cmd-send-keys.c index 46cb37f0..d6a95434 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -203,7 +203,7 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item) } if (count == 0) { - if (args_has(args, 'N')) + 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); -- cgit From 8a9bfd0cddd783436e842495fc3039aa56571ed1 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 20 Oct 2021 09:50:40 +0000 Subject: Add -T to set a popup title, from Alexis Hildebrandt in GitHub issue 2941. --- cmd-display-menu.c | 14 ++++++++++---- mode-tree.c | 2 +- screen-write.c | 27 ++++++++++++++++----------- tmux.1 | 5 +++++ tmux.h | 6 +++--- window-tree.c | 3 ++- 6 files changed, 37 insertions(+), 20 deletions(-) diff --git a/cmd-display-menu.c b/cmd-display-menu.c index f6531ede..b350ca15 100644 --- a/cmd-display-menu.c +++ b/cmd-display-menu.c @@ -53,10 +53,10 @@ const struct cmd_entry cmd_display_popup_entry = { .name = "display-popup", .alias = "popup", - .args = { "Bb:Cc:d:e:Eh:t:w:x:y:", 0, -1, NULL }, + .args = { "Bb:Cc:d:e:Eh:t:T:w:x:y:", 0, -1, NULL }, .usage = "[-BCE] [-b border-lines] [-c target-client] " "[-d start-directory] [-e environment] [-h height] " - CMD_TARGET_PANE_USAGE " " + CMD_TARGET_PANE_USAGE " [-T title] " "[-w width] [-x position] [-y position] [shell-command]", .target = { 't', CMD_FIND_PANE, 0 }, @@ -355,7 +355,7 @@ 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 = NULL, **argv = NULL; + 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); @@ -439,19 +439,25 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item) } } + 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 (popup_display(flags, lines, item, px, py, w, h, env, shellcmd, argc, - argv, cwd, tc, s, NULL, NULL) != 0) { + argv, cwd, title, tc, s, 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/mode-tree.c b/mode-tree.c index 2e029691..d4e93208 100644 --- a/mode-tree.c +++ b/mode-tree.c @@ -747,7 +747,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, BOX_LINES_DEFAULT, NULL); + 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, diff --git a/screen-write.c b/screen-write.c index 3cd0fb55..e2904ef6 100644 --- a/screen-write.c +++ b/screen-write.c @@ -646,9 +646,7 @@ 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, - BOX_LINES_DEFAULT, NULL); - screen_write_cursormove(ctx, cx + 2, cy, 0); - format_draw(ctx, &default_gc, menu->width, menu->title, NULL); + BOX_LINES_DEFAULT, &default_gc, menu->title); for (i = 0; i < menu->count; i++) { name = menu->items[i].name; @@ -714,7 +712,7 @@ screen_write_box_border_set(enum box_lines box_lines, int cell_type, /* Draw a box on screen. */ void screen_write_box(struct screen_write_ctx *ctx, u_int nx, u_int ny, - enum box_lines l, const struct grid_cell *gcp) + enum box_lines lines, const struct grid_cell *gcp, const char *title) { struct screen *s = ctx->s; struct grid_cell gc; @@ -727,30 +725,31 @@ screen_write_box(struct screen_write_ctx *ctx, u_int nx, u_int ny, memcpy(&gc, gcp, sizeof gc); else memcpy(&gc, &grid_default_cell, sizeof gc); + gc.attr |= GRID_ATTR_CHARSET; gc.flags |= GRID_FLAG_NOPALETTE; /* Draw top border */ - screen_write_box_border_set(l, CELL_TOPLEFT, &gc); + screen_write_box_border_set(lines, CELL_TOPLEFT, &gc); screen_write_cell(ctx, &gc); - screen_write_box_border_set(l, CELL_LEFTRIGHT, &gc); + screen_write_box_border_set(lines, CELL_LEFTRIGHT, &gc); for (i = 1; i < nx - 1; i++) screen_write_cell(ctx, &gc); - screen_write_box_border_set(l, CELL_TOPRIGHT, &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_box_border_set(l, CELL_BOTTOMLEFT, &gc); + screen_write_box_border_set(lines, CELL_BOTTOMLEFT, &gc); screen_write_cell(ctx, &gc); - screen_write_box_border_set(l, CELL_LEFTRIGHT, &gc); + screen_write_box_border_set(lines, CELL_LEFTRIGHT, &gc); for (i = 1; i < nx - 1; i++) screen_write_cell(ctx, &gc); - screen_write_box_border_set(l, CELL_BOTTOMRIGHT, &gc); + screen_write_box_border_set(lines, CELL_BOTTOMRIGHT, &gc); screen_write_cell(ctx, &gc); /* Draw sides */ - screen_write_box_border_set(l, CELL_TOPBOTTOM, &gc); + 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); @@ -760,6 +759,12 @@ screen_write_box(struct screen_write_ctx *ctx, u_int nx, u_int ny, 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); + } + screen_write_set_cursor(ctx, cx, cy); } diff --git a/tmux.1 b/tmux.1 index ab92be2e..bf615895 100644 --- a/tmux.1 +++ b/tmux.1 @@ -5801,6 +5801,7 @@ forwards any input read from stdin to the empty pane given by .Op Fl e Ar environment .Op Fl h Ar height .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 @@ -5858,6 +5859,10 @@ takes the form 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. diff --git a/tmux.h b/tmux.h index 65a3a571..fc4ad70d 100644 --- a/tmux.h +++ b/tmux.h @@ -2737,7 +2737,7 @@ 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, int, - const struct grid_cell *); + 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 *); @@ -3154,8 +3154,8 @@ typedef void (*popup_close_cb)(int, void *); typedef void (*popup_finish_edit_cb)(char *, size_t, 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 *, struct client *, struct session *, - popup_close_cb, void *); + const char *, const char *, struct client *, + struct session *, popup_close_cb, void *); int popup_editor(struct client *, const char *, size_t, popup_finish_edit_cb, void *); diff --git a/window-tree.c b/window-tree.c index d22a2b58..b594edd9 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, BOX_LINES_DEFAULT, NULL); + 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); -- cgit From acba07629ebf2dc2f0c316f110493e720b30757c Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 20 Oct 2021 09:52:27 +0000 Subject: Remove a TODO comment. --- popup.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/popup.c b/popup.c index 328deba6..549c26e1 100644 --- a/popup.c +++ b/popup.c @@ -32,6 +32,7 @@ struct popup_data { struct cmdq_item *item; int flags; enum box_lines lines; + char *title; struct screen s; struct colour_palette palette; @@ -229,7 +230,8 @@ popup_draw_cb(struct client *c, void *data, struct screen_redraw_ctx *rctx) 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, pd->lines, &bgc); + screen_write_box(&ctx, pd->sx, pd->sy, pd->lines, &bgc, + pd->title); screen_write_cursormove(&ctx, 1, 1, 0); screen_write_fast_copy(&ctx, &pd->s, 0, 0, pd->sx - 2, pd->sy - 2); @@ -287,6 +289,7 @@ popup_free_cb(struct client *c, void *data) screen_free(&pd->s); colour_palette_free(&pd->palette); + free(pd->title); free(pd); } @@ -632,8 +635,8 @@ popup_job_complete_cb(struct job *job) int 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, struct client *c, struct session *s, - popup_close_cb cb, void *arg) + int argc, char **argv, const char *cwd, const char *title, struct client *c, + struct session *s, popup_close_cb cb, void *arg) { struct popup_data *pd; u_int jx, jy; @@ -664,6 +667,7 @@ popup_display(int flags, enum box_lines lines, struct cmdq_item *item, u_int px, pd->item = item; pd->flags = flags; pd->lines = lines; + pd->title = xstrdup(title); pd->c = c; pd->c->references++; @@ -776,7 +780,7 @@ popup_editor(struct client *c, const char *buf, size_t len, xasprintf(&cmd, "%s %s", editor, path); if (popup_display(POPUP_INTERNAL|POPUP_CLOSEEXIT, BOX_LINES_DEFAULT, - NULL, px, py, sx, sy, NULL, cmd, 0, NULL, _PATH_TMP, c, NULL, + NULL, px, py, sx, sy, NULL, cmd, 0, NULL, _PATH_TMP, NULL, c, NULL, popup_editor_close_cb, pe) != 0) { popup_editor_free(pe); free(cmd); -- cgit From 7bd9cdf6fcf43e0edc8ab3a4accf2009ca5aa35e Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 21 Oct 2021 08:23:48 +0000 Subject: Show error if user option doesn't exist, GitHub issue 2938. --- cmd-show-options.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cmd-show-options.c b/cmd-show-options.c index bdcd3e78..7ac7e455 100644 --- a/cmd-show-options.c +++ b/cmd-show-options.c @@ -127,6 +127,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); -- cgit From 289ac55ebde18df237ad21734ba4056896d11022 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 21 Oct 2021 08:36:51 +0000 Subject: Correctly adjust the end pointer for a two character terminator before decoding OSC 52 response, from Daniel Ekloef in GitHub issue 2942. --- tty-keys.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tty-keys.c b/tty-keys.c index 6dfa70f3..65b600c0 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++; -- cgit From 8235957eaae0aacb1ad0c9b145a1f9d3abc93dfd Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 22 Oct 2021 17:12:50 +0000 Subject: Remove key and trim text if menu cannot fit in available space, based on a change from Alexis Hildebrandt. --- menu.c | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/menu.c b/menu.c index a1c81816..45645147 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,30 @@ 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); + keylen = strlen(key) + 3; /* 3 = space and two brackets */ + + /* + * Only add the key if there is space for the entire item text + * and the key. + */ + if (keylen >= max_width || slen >= max_width - keylen) + key = NULL; + } + + if (key != NULL) xasprintf(&name, "%s#[default] #[align=right](%s)", s, key); - } else - xasprintf(&name, "%s", s); + else { + if (slen > max_width) { + max_width--; + suffix = ">"; + } + xasprintf(&name, "%.*s%s", (int)max_width, s, suffix); + } new_item->name = name; free(s); -- cgit From 9b4148b12ca631b9cb8f48a03adb1fad146ee53d Mon Sep 17 00:00:00 2001 From: deraadt Date: Sun, 24 Oct 2021 21:24:17 +0000 Subject: For open/openat, if the flags parameter does not contain O_CREAT, the 3rd (variadic) mode_t parameter is irrelevant. Many developers in the past have passed mode_t (0, 044, 0644, or such), which might lead future people to copy this broken idiom, and perhaps even believe this parameter has some meaning or implication or application. Delete them all. This comes out of a conversation where tb@ noticed that a strange (but intentional) pledge behaviour is to always knock-out high-bits from mode_t on a number of system calls as a safety factor, and his bewilderment that this appeared to be happening against valid modes (at least visually), but no sorry, they are all irrelevant junk. They could all be 0xdeafbeef. ok millert --- cmd-pipe-pane.c | 2 +- job.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd-pipe-pane.c b/cmd-pipe-pane.c index ed08e618..1a8c851b 100644 --- a/cmd-pipe-pane.c +++ b/cmd-pipe-pane.c @@ -131,7 +131,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/job.c b/job.c index 4c13f943..dad211f4 100644 --- a/job.c +++ b/job.c @@ -142,7 +142,7 @@ job_run(const char *cmd, int argc, char **argv, struct environ *e, struct sessio 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) -- cgit From 0cca695d6e75426e295e03668a4ed35ee62afe7c Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 25 Oct 2021 09:22:17 +0000 Subject: Instead of setting the popup default colours in the draw callback, set it up in popup_display and follow the same routine as panes in the draw and init_ctx callbacks - use the palette if the option value is default. Allows application-set fg and bg to work in panes again. --- popup.c | 74 ++++++++++++++++++++++++++++++++-------------------------- screen-write.c | 6 +++-- tty.c | 7 +++--- 3 files changed, 48 insertions(+), 39 deletions(-) diff --git a/popup.c b/popup.c index 549c26e1..dd83d608 100644 --- a/popup.c +++ b/popup.c @@ -31,11 +31,15 @@ struct popup_data { struct client *c; struct cmdq_item *item; int flags; - enum box_lines lines; 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; @@ -119,7 +123,7 @@ popup_set_client_cb(struct tty_ctx *ttyctx, struct client *c) ttyctx->wsx = c->tty.sx; ttyctx->wsy = c->tty.sy; - if (pd->lines == BOX_LINES_NONE) { + if (pd->border_lines == BOX_LINES_NONE) { ttyctx->xoff = ttyctx->rxoff = pd->px; ttyctx->yoff = ttyctx->ryoff = pd->py; } else { @@ -135,6 +139,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; @@ -149,7 +154,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->lines == BOX_LINES_NONE) { + if (pd->border_lines == BOX_LINES_NONE) { *cx = pd->px + pd->s.cx; *cy = pd->py + pd->s.cy; } else { @@ -213,36 +218,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 bgc; - struct options *o = c->session->curw->window->options; + struct grid_cell defaults; screen_init(&s, pd->sx, pd->sy, 0); screen_write_start(&ctx, &s); screen_write_clearscreen(&ctx, 8); - memcpy(&bgc, &grid_default_cell, sizeof bgc); - bgc.attr = 0; - style_apply(&bgc, o, "popup-border-style", NULL); - bgc.attr = 0; - - if (pd->lines == BOX_LINES_NONE) { + 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, pd->lines, &bgc, - pd->title); + 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); - style_apply(&gc, o, "popup-style", NULL); - gc.attr = 0; - palette->fg = gc.fg; - palette->bg = gc.bg; + memcpy(&defaults, &pd->defaults, sizeof defaults); + if (COLOUR_DEFAULT(defaults.fg)) + defaults.fg = palette->fg; + if (COLOUR_DEFAULT(defaults.bg)) + defaults.bg = palette->bg; if (pd->md != NULL) { c->overlay_check = menu_check_cb; @@ -251,8 +249,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; @@ -323,7 +323,7 @@ popup_resize_cb(__unused struct client *c, void *data) pd->px = pd->ppx; /* Avoid zero size screens. */ - if (pd->lines == BOX_LINES_NONE) { + 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 ); @@ -449,7 +449,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->lines == BOX_LINES_NONE) { + if (pd->border_lines == BOX_LINES_NONE) { if (m->x < pd->px + 1) return; if (m->y < pd->py + 1) @@ -465,7 +465,7 @@ popup_handle_drag(struct client *c, struct popup_data *pd, pd->psx = pd->sx; pd->psy = pd->sy; - if (pd->lines == BOX_LINES_NONE) { + 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); @@ -513,7 +513,7 @@ popup_key_cb(struct client *c, void *data, struct key_event *event) goto menu; return (0); } - if (pd->lines != BOX_LINES_NONE) { + if (pd->border_lines != BOX_LINES_NONE) { if (m->x == pd->px) border = LEFT; else if (m->x == pd->px + pd->sx - 1) @@ -547,7 +547,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->lines == BOX_LINES_NONE) { + if (pd->border_lines == BOX_LINES_NONE) { px = m->x - pd->px; py = m->y - pd->py; } else { @@ -642,13 +642,13 @@ popup_display(int flags, enum box_lines lines, struct cmdq_item *item, u_int px, u_int jx, jy; struct options *o; - if (lines == BOX_LINES_DEFAULT) { - if (s != NULL) - o = s->curw->window->options; - else - o = c->session->curw->window->options; + 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); @@ -666,7 +666,6 @@ popup_display(int flags, enum box_lines lines, struct cmdq_item *item, u_int px, pd = xcalloc(1, sizeof *pd); pd->item = item; pd->flags = flags; - pd->lines = lines; pd->title = xstrdup(title); pd->c = c; @@ -676,10 +675,19 @@ popup_display(int flags, enum box_lines lines, struct cmdq_item *item, u_int px, 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); + 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); + pd->defaults.attr = 0; + pd->px = px; pd->py = py; pd->sx = sx; diff --git a/screen-write.c b/screen-write.c index e2904ef6..251ca823 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 (COLOUR_DEFAULT(ttyctx->defaults.fg)) + ttyctx->defaults.fg = ttyctx->palette->fg; + if (COLOUR_DEFAULT(ttyctx->defaults.bg)) + ttyctx->defaults.bg = ttyctx->palette->bg; } } else { ttyctx->redraw_cb = screen_write_redraw_cb; diff --git a/tty.c b/tty.c index 809289e0..243eae56 100644 --- a/tty.c +++ b/tty.c @@ -1972,10 +1972,9 @@ tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx) 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; + tty_draw_line(tty, s, s->cx, s->cy, gcp->data.width, + px, py, &ctx->defaults, ctx->palette); + return; } } -- cgit From ef46eb91a5e0b6e2b023544f45dbc98c8fe1377f Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 25 Oct 2021 09:38:36 +0000 Subject: Add -s and -S to display-popup to set popup and border style, from Alexis Hildebrandt in GitHub issue 2931. --- cmd-display-menu.c | 11 +++++++---- popup.c | 24 ++++++++++++++++++++---- screen-write.c | 4 ++-- tmux.1 | 12 ++++++++++++ tmux.h | 3 ++- 5 files changed, 43 insertions(+), 11 deletions(-) diff --git a/cmd-display-menu.c b/cmd-display-menu.c index b350ca15..6ecfad29 100644 --- a/cmd-display-menu.c +++ b/cmd-display-menu.c @@ -53,11 +53,12 @@ const struct cmd_entry cmd_display_popup_entry = { .name = "display-popup", .alias = "popup", - .args = { "Bb:Cc:d:e:Eh:t:T:w:x:y:", 0, -1, NULL }, + .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] " - CMD_TARGET_PANE_USAGE " [-T title] " - "[-w width] [-x position] [-y position] [shell-command]", + "[-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 }, @@ -355,6 +356,8 @@ 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; + 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; @@ -448,7 +451,7 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item) else if (args_has(args, 'E')) flags |= POPUP_CLOSEEXIT; if (popup_display(flags, lines, item, px, py, w, h, env, shellcmd, argc, - argv, cwd, title, tc, s, NULL, NULL) != 0) { + argv, cwd, title, tc, s, style, border_style, NULL, NULL) != 0) { cmd_free_argv(argc, argv); if (env != NULL) environ_free(env); diff --git a/popup.c b/popup.c index dd83d608..ed6a6bb9 100644 --- a/popup.c +++ b/popup.c @@ -237,9 +237,9 @@ popup_draw_cb(struct client *c, void *data, struct screen_redraw_ctx *rctx) screen_write_stop(&ctx); memcpy(&defaults, &pd->defaults, sizeof defaults); - if (COLOUR_DEFAULT(defaults.fg)) + if (defaults.fg == 8) defaults.fg = palette->fg; - if (COLOUR_DEFAULT(defaults.bg)) + if (defaults.bg == 8) defaults.bg = palette->bg; if (pd->md != NULL) { @@ -636,11 +636,13 @@ int 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, popup_close_cb cb, void *arg) + 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 (s != NULL) o = s->curw->window->options; @@ -678,6 +680,13 @@ popup_display(int flags, enum box_lines lines, struct cmdq_item *item, u_int px, 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); @@ -686,6 +695,13 @@ popup_display(int flags, enum box_lines lines, struct cmdq_item *item, u_int px, 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; @@ -789,7 +805,7 @@ popup_editor(struct client *c, const char *buf, size_t len, xasprintf(&cmd, "%s %s", editor, path); if (popup_display(POPUP_INTERNAL|POPUP_CLOSEEXIT, BOX_LINES_DEFAULT, NULL, px, py, sx, sy, NULL, cmd, 0, NULL, _PATH_TMP, NULL, c, NULL, - popup_editor_close_cb, pe) != 0) { + NULL, NULL, popup_editor_close_cb, pe) != 0) { popup_editor_free(pe); free(cmd); return (-1); diff --git a/screen-write.c b/screen-write.c index 251ca823..cead12d4 100644 --- a/screen-write.c +++ b/screen-write.c @@ -184,9 +184,9 @@ 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) { - if (COLOUR_DEFAULT(ttyctx->defaults.fg)) + if (ttyctx->defaults.fg == 8) ttyctx->defaults.fg = ttyctx->palette->fg; - if (COLOUR_DEFAULT(ttyctx->defaults.bg)) + if (ttyctx->defaults.bg == 8) ttyctx->defaults.bg = ttyctx->palette->bg; } } else { diff --git a/tmux.1 b/tmux.1 index bf615895..9bedb20b 100644 --- a/tmux.1 +++ b/tmux.1 @@ -5800,6 +5800,8 @@ forwards any input read from stdin to the empty pane given by .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 @@ -5853,6 +5855,16 @@ See 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 diff --git a/tmux.h b/tmux.h index fc4ad70d..f2957de9 100644 --- a/tmux.h +++ b/tmux.h @@ -3155,7 +3155,8 @@ typedef void (*popup_finish_edit_cb)(char *, size_t, 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 *, popup_close_cb, void *); + struct session *, const char *, const char *, + popup_close_cb, void *); int popup_editor(struct client *, const char *, size_t, popup_finish_edit_cb, void *); -- cgit From eb82ad5216f1538e13f4ad7c2d36f28fedd88310 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 25 Oct 2021 20:32:42 +0000 Subject: Missing Pp, from Alexis Hildebrandt. --- tmux.1 | 1 + 1 file changed, 1 insertion(+) diff --git a/tmux.1 b/tmux.1 index 9bedb20b..c270309f 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4214,6 +4214,7 @@ see the .Sx STYLES section. Attributes are ignored. +.Pp .It Ic pane-base-index Ar index Like .Ic base-index , -- cgit From 197a116f5a2146309c4c6fecbd9d08d36f2be750 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 25 Oct 2021 21:21:16 +0000 Subject: Add a way to force a colour to RGB and a format to display it. --- colour.c | 15 +++++++++++++++ format.c | 24 ++++++++++++++++++++---- tmux.1 | 4 ++++ tmux.h | 1 + 4 files changed, 40 insertions(+), 4 deletions(-) diff --git a/colour.c b/colour.c index 9ac07415..92bbd71e 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.c b/format.c index f5d2c5f6..b34fc341 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 @@ -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/tmux.1 b/tmux.1 index c270309f..35a87bd0 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4831,6 +4831,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 diff --git a/tmux.h b/tmux.h index f2957de9..e3896132 100644 --- a/tmux.h +++ b/tmux.h @@ -2616,6 +2616,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); -- cgit From 5745bd27fdfd0e806fb3f734966f66f302c76c48 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 26 Oct 2021 12:22:23 +0000 Subject: Do not allow inline styles to replace mode-style for the selected item, from Alexis Hildebrandt in GitHub issue 2946. --- format-draw.c | 7 ++++++- mode-tree.c | 4 ++-- screen-redraw.c | 2 +- screen-write.c | 8 +++++--- tmux.h | 2 +- 5 files changed, 15 insertions(+), 8 deletions(-) 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/mode-tree.c b/mode-tree.c index d4e93208..85027caf 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); diff --git a/screen-redraw.c b/screen-redraw.c index 11900b4f..0326c12d 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -400,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); diff --git a/screen-write.c b/screen-write.c index cead12d4..0d70f668 100644 --- a/screen-write.c +++ b/screen-write.c @@ -665,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; } } @@ -764,7 +766,7 @@ screen_write_box(struct screen_write_ctx *ctx, u_int nx, u_int ny, if (title != NULL) { gc.attr &= ~GRID_ATTR_CHARSET; screen_write_cursormove(ctx, cx + 2, cy, 0); - format_draw(ctx, &gc, nx - 4, title, NULL); + format_draw(ctx, &gc, nx - 4, title, NULL, 0); } screen_write_set_cursor(ctx, cx, cy); diff --git a/tmux.h b/tmux.h index e3896132..0d3c923f 100644 --- a/tmux.h +++ b/tmux.h @@ -2032,7 +2032,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); -- cgit From 9695114230613f57c11936ae0e1fe2cd6ba0a7b4 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 26 Oct 2021 12:29:41 +0000 Subject: Accept some emacs control keys in vi normal mode, from Alexis Hildebrandt in GitHub issue 2922. --- status.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/status.c b/status.c index b442e85d..65c81386 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) { @@ -809,14 +810,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 +847,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 +895,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; -- cgit From 76f5d3364c8d822989adbf0d3f7bc7c27981110b Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 28 Oct 2021 18:39:15 +0000 Subject: Expand command as a format, GitHub issue 2920. --- cmd-confirm-before.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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); -- cgit From 49d33a4282dad9245cc644b9259b40ae94bc0063 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 28 Oct 2021 18:54:33 +0000 Subject: Allow detach even if suspend flag set, GitHub issue 2932. --- server-client.c | 2 +- tmux.h | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/server-client.c b/server-client.c index dc5074e7..7cea32c0 100644 --- a/server-client.c +++ b/server-client.c @@ -516,7 +516,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; diff --git a/tmux.h b/tmux.h index 0d3c923f..59e1687d 100644 --- a/tmux.h +++ b/tmux.h @@ -1710,6 +1710,9 @@ struct client { (CLIENT_DEAD| \ CLIENT_SUSPENDED| \ CLIENT_EXIT) +#define CLIENT_NODETACHFLAGS \ + (CLIENT_DEAD| \ + CLIENT_EXIT) #define CLIENT_NOSIZEFLAGS \ (CLIENT_DEAD| \ CLIENT_SUSPENDED| \ -- cgit From 4acad43013b7bb5f91103a68cfce591b1980ae17 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 28 Oct 2021 18:57:06 +0000 Subject: Do not force the cursor to move if it is in the automargin space at EOL and that is where we want it to be, GitHub issue 2956. --- tty.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/tty.c b/tty.c index 243eae56..62298a54 100644 --- a/tty.c +++ b/tty.c @@ -2278,17 +2278,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; -- cgit From c76904343a3ddfea3c58ec00d9904c16574a8af3 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 29 Oct 2021 08:36:40 +0100 Subject: Spelling, from someone in GitHub issue 2958. --- tools/ansicode.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 -- cgit From 4fe5aa99fb203ddb25089955d8814e1065e11a60 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 1 Nov 2021 07:48:04 +0000 Subject: Fix a comparison, from Ben Boeckel, and a crash when opening completion menu, from Anindya Mukherjee. --- colour.c | 2 +- status.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/colour.c b/colour.c index 92bbd71e..6ede25da 100644 --- a/colour.c +++ b/colour.c @@ -115,7 +115,7 @@ colour_force_rgb(int c) return (colour_256toRGB(c)); if (c >= 0 && c <= 7) return (colour_256toRGB(c)); - if (c >= 90 & c <= 97) + if (c >= 90 && c <= 97) return (colour_256toRGB(8 + c - 90)); return (-1); } diff --git a/status.c b/status.c index 65c81386..d499eab8 100644 --- a/status.c +++ b/status.c @@ -1727,7 +1727,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) -- cgit From 8d2286b76917debc4f6c3b0903ad2807ae254bb5 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 1 Nov 2021 09:34:49 +0000 Subject: Add a cursor-colour option, from Alexis Hildebrandt in GitHub issue 2959. --- input.c | 47 +++++++++++++++++++++++++++++++++++++++++------ options-table.c | 8 ++++++++ options.c | 9 ++++++++- screen.c | 9 ++++----- tmux.1 | 3 +++ tmux.h | 7 ++++--- tty.c | 43 ++++++++++++++++++++++++++++--------------- 7 files changed, 96 insertions(+), 30 deletions(-) diff --git a/input.c b/input.c index 8a16281c..b2769e31 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 *); @@ -2310,8 +2312,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); @@ -2326,8 +2327,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); @@ -2489,7 +2489,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); @@ -2564,7 +2566,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) { @@ -2624,6 +2626,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/options-table.c b/options-table.c index 6c57e3ff..d23f7d57 100644 --- a/options-table.c +++ b/options-table.c @@ -188,6 +188,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 } }; @@ -235,6 +236,13 @@ 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 = "default-terminal", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SERVER, diff --git a/options.c b/options.c index 65263fd0..4da1c408 100644 --- a/options.c +++ b/options.c @@ -1106,15 +1106,22 @@ 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, "key-table") == 0) { TAILQ_FOREACH(loop, &clients, entry) server_client_set_key_table(loop, NULL); diff --git a/screen.c b/screen.c index 4c13b693..bc95705a 100644 --- a/screen.c +++ b/screen.c @@ -82,7 +82,8 @@ 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->ccolour = -1; + s->default_ccolour = -1; s->tabs = NULL; s->sel = NULL; @@ -126,7 +127,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); @@ -190,10 +190,9 @@ screen_set_cursor_style(struct screen *s, u_int style) /* 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/tmux.1 b/tmux.1 index 35a87bd0..eb2340e3 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4416,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 diff --git a/tmux.h b/tmux.h index 59e1687d..62c84cc7 100644 --- a/tmux.h +++ b/tmux.h @@ -779,7 +779,8 @@ struct screen { u_int cy; /* cursor y */ enum screen_cursor_style cstyle; /* cursor style */ - char *ccolour; /* cursor colour */ + int ccolour; /* cursor colour */ + int default_ccolour; u_int rupper; /* scroll region top */ u_int rlower; /* scroll region bottom */ @@ -1276,7 +1277,7 @@ struct tty { u_int cx; u_int cy; enum screen_cursor_style cstyle; - char *ccolour; + int ccolour; int oflag; u_int oox; @@ -2793,7 +2794,7 @@ 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_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 *); diff --git a/tty.c b/tty.c index 62298a54..38a7a752 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 *, @@ -105,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); @@ -341,8 +341,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; @@ -406,7 +406,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)); @@ -451,7 +451,6 @@ void tty_free(struct tty *tty) { tty_close(tty); - free(tty->ccolour); } void @@ -650,24 +649,38 @@ 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) { - enum screen_cursor_style cstyle; + enum screen_cursor_style cstyle; + int ccolour; /* 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) { -- cgit From 200b6536e13b41a6fed72c0ca4f364f270bfe382 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 2 Nov 2021 10:57:04 +0000 Subject: fatalx on unknown enum members in a couple of places, from Ben Boeckel. --- arguments.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arguments.c b/arguments.c index 985aadc0..17bc12ec 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. */ -- cgit From 57100376cc70739f53a1f8a4bacf192b8cdcd124 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 3 Nov 2021 13:37:17 +0000 Subject: Add a cursor-style option, from Alexis Hildebrandt in GitHub issue 2960. --- input.c | 4 +++- options-table.c | 12 ++++++++++++ options.c | 8 ++++++++ screen.c | 34 ++++++++++++++++++---------------- tmux.1 | 10 ++++++++++ tmux.h | 5 ++++- tty.c | 54 ++++++++++++++++++++++++++++++++++++------------------ 7 files changed, 91 insertions(+), 36 deletions(-) diff --git a/input.c b/input.c index b2769e31..3626c4b2 100644 --- a/input.c +++ b/input.c @@ -1619,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); @@ -1685,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); @@ -1774,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); diff --git a/options-table.c b/options-table.c index d23f7d57..bad22bfb 100644 --- a/options-table.c +++ b/options-table.c @@ -57,6 +57,10 @@ 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 }; @@ -243,6 +247,14 @@ const struct options_table_entry options_table[] = { .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, diff --git a/options.c b/options.c index 4da1c408..865ab01f 100644 --- a/options.c +++ b/options.c @@ -1122,6 +1122,14 @@ options_push_changes(const char *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/screen.c b/screen.c index bc95705a..da108866 100644 --- a/screen.c +++ b/screen.c @@ -82,6 +82,8 @@ screen_init(struct screen *s, u_int sx, u_int sy, u_int hlimit) s->path = NULL; s->cstyle = SCREEN_CURSOR_DEFAULT; + s->default_cstyle = SCREEN_CURSOR_DEFAULT; + s->default_mode = 0; s->ccolour = -1; s->default_ccolour = -1; s->tabs = NULL; @@ -152,38 +154,38 @@ 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; } } diff --git a/tmux.1 b/tmux.1 index eb2340e3..4131719c 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4426,6 +4426,16 @@ 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 diff --git a/tmux.h b/tmux.h index 62c84cc7..07e649f2 100644 --- a/tmux.h +++ b/tmux.h @@ -530,6 +530,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) @@ -779,6 +780,7 @@ struct screen { u_int cy; /* cursor y */ enum screen_cursor_style cstyle; /* cursor style */ + enum screen_cursor_style default_cstyle; int ccolour; /* cursor colour */ int default_ccolour; @@ -786,6 +788,7 @@ struct screen { u_int rlower; /* scroll region bottom */ int mode; + int default_mode; u_int saved_cx; u_int saved_cy; @@ -2793,7 +2796,7 @@ 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_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 *); diff --git a/tty.c b/tty.c index 38a7a752..70470a08 100644 --- a/tty.c +++ b/tty.c @@ -668,11 +668,11 @@ tty_force_cursor_colour(struct tty *tty, int c) 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; - int ccolour; + int ccolour, changed, cmode = mode; /* Set cursor colour if changed. */ if (s != NULL) { @@ -683,19 +683,32 @@ tty_update_cursor(struct tty *tty, int mode, int changed, struct screen *s) } /* 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, @@ -713,49 +726,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, @@ -764,9 +784,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 -- cgit From 8f1cc0e9fa78a6200ba6bddf7c8958225d9fdd6d Mon Sep 17 00:00:00 2001 From: kn Date: Thu, 4 Nov 2021 13:15:13 +0000 Subject: Fix mandoc HTML rendering for command aliases Replace hand-rolled parentheses with the proper mdoc(7) macro, otherwise the closing ")" ends up inside the command description. Reported by Josh Rickmar, thanks! --- tmux.1 | 150 ++++++++++++++++++++++++++++++++--------------------------------- 1 file changed, 75 insertions(+), 75 deletions(-) diff --git a/tmux.1 b/tmux.1 index 4131719c..20723308 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1034,7 +1034,7 @@ The following commands are available to manage clients and sessions: .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 @@ -1121,7 +1121,7 @@ option will not be applied. .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 @@ -1143,7 +1143,7 @@ run 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 @@ -1168,7 +1168,7 @@ session. .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 @@ -1183,7 +1183,7 @@ is specified, list only clients connected to that session. .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 @@ -1193,7 +1193,7 @@ or - if omitted - of all commands supported by .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 @@ -1205,7 +1205,7 @@ See the 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 @@ -1213,7 +1213,7 @@ see the 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 @@ -1230,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 @@ -1346,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 . @@ -1477,7 +1477,7 @@ option. .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 @@ -1485,7 +1485,7 @@ Rename the session to .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 @@ -1500,7 +1500,7 @@ show debugging information about jobs and terminals. .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 @@ -1523,7 +1523,7 @@ the file is parsed but no commands are executed. 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. @@ -1542,7 +1542,7 @@ $ tmux start \\; show -g .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). @@ -1553,7 +1553,7 @@ Suspend a client by sending .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 @@ -1945,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 @@ -1974,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 @@ -2226,7 +2226,7 @@ This command works only if at least one client is attached. .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 @@ -2266,7 +2266,7 @@ other commands are not blocked from running until the indicator is closed. .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 @@ -2296,7 +2296,7 @@ This command works only if at least one client is attached. .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 @@ -2324,7 +2324,7 @@ the marked pane is used rather than the current 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 @@ -2336,7 +2336,7 @@ option kills all but the pane given with .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. @@ -2349,7 +2349,7 @@ option kills all but the window given with .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. @@ -2359,7 +2359,7 @@ enables or 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 @@ -2370,7 +2370,7 @@ is specified, select the last window of the current session. .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 @@ -2402,7 +2402,7 @@ is given, the newly linked window is not selected. .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, @@ -2431,7 +2431,7 @@ section. .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. @@ -2452,7 +2452,7 @@ section. .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 @@ -2461,7 +2461,7 @@ Does the same as .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 @@ -2484,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 @@ -2559,14 +2559,14 @@ 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 @@ -2577,7 +2577,7 @@ is used, move to the next window with an alert. .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. @@ -2624,14 +2624,14 @@ bind-key C-p pipe-pane -o 'cat >>~/output.#I-#P' .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 , @@ -2641,7 +2641,7 @@ move to the previous window with an alert. .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 @@ -2654,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 @@ -2699,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 @@ -2732,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). @@ -2758,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). @@ -2781,7 +2781,7 @@ command. .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 @@ -2794,7 +2794,7 @@ keeps the window zoomed if it was zoomed. .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 @@ -2817,7 +2817,7 @@ spreads the current pane and any panes next to it out evenly. .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. @@ -2861,7 +2861,7 @@ and .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 , @@ -2889,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 @@ -2946,7 +2946,7 @@ command. .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 @@ -2975,7 +2975,7 @@ the marked pane is used rather than the current pane. .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. @@ -2996,7 +2996,7 @@ the window containing the marked pane is used rather than the current 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 @@ -3069,7 +3069,7 @@ Commands related to key bindings are as follows: .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 @@ -3127,7 +3127,7 @@ command. .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 @@ -3164,7 +3164,7 @@ lists the command for keys that do not have a note rather than skipping them. .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 @@ -3215,7 +3215,7 @@ the secondary prefix key, to a window as if it was pressed. .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 @@ -3311,7 +3311,7 @@ Commands which set options are as follows: .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 @@ -3386,7 +3386,7 @@ the result would be the default background and a blue foreground. .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 @@ -5425,7 +5425,7 @@ Commands to alter and view the environment are: .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 @@ -5451,7 +5451,7 @@ marks the variable as hidden. .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 @@ -5534,7 +5534,7 @@ Commands related to the status line are as follows: .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 @@ -5660,7 +5660,7 @@ until it is dismissed. .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 @@ -5689,7 +5689,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 @@ -5776,7 +5776,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 @@ -5827,7 +5827,7 @@ forwards any input read from stdin to the empty pane given by .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 @@ -5900,7 +5900,7 @@ flag closes any popup on the client. .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 @@ -6028,11 +6028,11 @@ 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. @@ -6041,7 +6041,7 @@ or the most recently added automatically named buffer if not specified. .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 @@ -6058,7 +6058,7 @@ section. .Ar path .Xc .Tg loadb -.D1 (alias: Ic loadb ) +.D1 Pq alias: Ic loadb Load the contents of the specified paste buffer from .Ar path . If @@ -6075,7 +6075,7 @@ escape sequence, if possible. .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 @@ -6099,7 +6099,7 @@ buffer if the application has requested bracketed paste mode. .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 @@ -6113,7 +6113,7 @@ option appends to rather than overwriting the file. .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 @@ -6134,7 +6134,7 @@ option renames the buffer to .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 @@ -6149,7 +6149,7 @@ Display a large clock. .Ar shell-command command .Op Ar command .Xc -.D1 (alias: Ic if ) +.D1 Pq alias: Ic if Execute the first .Ar command if @@ -6176,7 +6176,7 @@ 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. @@ -6187,7 +6187,7 @@ option. .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 @@ -6219,7 +6219,7 @@ If the command fails, the exit status is also displayed. .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 -- cgit From 10b3cd17fa94e3ac129080e68c69d2130b015fb2 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 11 Nov 2021 09:18:04 +0000 Subject: OS X -> macOS, from J Lewis Muir. --- .github/CONTRIBUTING.md | 2 +- .github/README.md | 2 +- README | 2 +- README.ja | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) 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.に依存します。 下記からダウンロードしてください。 -- cgit From 630c592ef8740a935ba6c12c957a359c94414219 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 11 Nov 2021 09:22:33 +0000 Subject: If trimming menu item text, show key if it would take up less than a quarter of the space; from Alexis Hildebrandt. Also new sentence, new line in tmux.1, from jmc. --- menu.c | 24 ++++++++++++++---------- tmux.1 | 3 ++- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/menu.c b/menu.c index 45645147..aaa1287e 100644 --- a/menu.c +++ b/menu.c @@ -89,22 +89,26 @@ menu_add_item(struct menu *menu, const struct menu_item *item, keylen = strlen(key) + 3; /* 3 = space and two brackets */ /* - * Only add the key if there is space for the entire item text - * and the key. + * 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 || slen >= max_width - keylen) + 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#[default] #[align=right](%s)", s, key); - else { - if (slen > max_width) { - max_width--; - suffix = ">"; - } + 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); diff --git a/tmux.1 b/tmux.1 index 20723308..7d5c7357 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4427,7 +4427,8 @@ 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: +Set the style of the cursor. +Available styles are: .Ic default , .Ic blinking-block , .Ic block , -- cgit From cb8a0d83fbaa2ae49c06105cb94d247ef20ed91e Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 11 Nov 2021 09:31:16 +0000 Subject: If automatic-rename is off, allow the escape sequence to set an empty window name, GitHub issue 2964. --- input.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/input.c b/input.c index 3626c4b2..73a58890 100644 --- a/input.c +++ b/input.c @@ -2382,6 +2382,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) @@ -2394,17 +2395,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. */ -- cgit From b55f0ac6b94449b3372f0d1737fcf967f4fa13a2 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 15 Nov 2021 10:58:13 +0000 Subject: Leave the hardware cursor at the position of the selected line in choose modes and current editing position and at the command prompt. It is invisible but this is helpful for people using screen readers. GitHub issue 2970. --- mode-tree.c | 8 ++++---- server-client.c | 17 +++++++++++++++-- status.c | 1 + tmux.h | 1 + 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/mode-tree.c b/mode-tree.c index 85027caf..21a73ef2 100644 --- a/mode-tree.c +++ b/mode-tree.c @@ -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; @@ -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); } diff --git a/server-client.c b/server-client.c index 7cea32c0..072c5589 100644 --- a/server-client.c +++ b/server-client.c @@ -1706,7 +1706,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)) @@ -1734,7 +1734,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 d499eab8..bb57b3d6 100644 --- a/status.c +++ b/status.c @@ -748,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++) { diff --git a/tmux.h b/tmux.h index 07e649f2..dfea1583 100644 --- a/tmux.h +++ b/tmux.h @@ -1758,6 +1758,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; -- cgit From add03dfb8dd4f2871b2cbc487ff5af95a63f48f4 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 29 Nov 2021 11:01:51 +0000 Subject: Fix user option lookup ordering. --- window-customize.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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) -- cgit From 333cf6429ae16da6fb3892978ea450bc62c3e873 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 29 Nov 2021 11:05:28 +0000 Subject: Bump response timer to three seconds, GitHub issue 2984. --- tty.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tty.c b/tty.c index 70470a08..233d52a8 100644 --- a/tty.c +++ b/tty.c @@ -302,7 +302,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); -- cgit From 8fccbbb02673bed71676412f1a313093a39c48ff Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 6 Dec 2021 10:08:42 +0000 Subject: Do not dereference NULL window when resizing client, GitHub issue 2982. --- resize.c | 2 ++ tty.c | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) 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/tty.c b/tty.c index 233d52a8..a9678eaf 100644 --- a/tty.c +++ b/tty.c @@ -937,7 +937,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); } } -- cgit From ecac73f66445311c58a28afc7d3f92c964eaae67 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 6 Dec 2021 10:10:52 +0000 Subject: Fix g/G keys to be in line with copy mode. --- mode-tree.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mode-tree.c b/mode-tree.c index 21a73ef2..1eb496fe 100644 --- a/mode-tree.c +++ b/mode-tree.c @@ -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) -- cgit From ef676e1202a4d5c423d5bba2f8ecba1f768d8364 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 6 Dec 2021 10:17:34 +0000 Subject: Remove fallback for wcwidth failure, GitHub issue 3003. --- utf8.c | 15 --------------- 1 file changed, 15 deletions(-) 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); } -- cgit From 71c3234dc78f6f59a66757a6e272e77823a820b3 Mon Sep 17 00:00:00 2001 From: deraadt Date: Tue, 7 Dec 2021 00:38:42 +0000 Subject: Use PATH_MAX (the standard name) rather than MAXPATHLEN (from BSD sys/param.h) --- procname.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/procname.c b/procname.c index 45e508ef..ac56bb48 100644 --- a/procname.c +++ b/procname.c @@ -138,7 +138,7 @@ char * get_proc_cwd(int fd) { int name[] = { CTL_KERN, KERN_PROC_CWD, 0 }; - static char path[MAXPATHLEN]; + static char path[PATH_MAX]; size_t pathlen = sizeof path; if ((name[2] = tcgetpgrp(fd)) == -1) -- cgit From 7532a5cf9574f8c5e102383329477e934992d2ea Mon Sep 17 00:00:00 2001 From: deraadt Date: Tue, 7 Dec 2021 00:40:03 +0000 Subject: sys/signal.h (or some master include) must happen before sys/proc.h, which is not standalone. This problem is being hidden by a sys/param.h which cannot be deleted yet. --- procname.c | 1 + 1 file changed, 1 insertion(+) diff --git a/procname.c b/procname.c index ac56bb48..b2cba1c2 100644 --- a/procname.c +++ b/procname.c @@ -18,6 +18,7 @@ #include /* MAXCOMLEN */ #include +#include #include #include #include -- cgit From d721fb2a9fd70c157abb8540d4c50fca654f9f4d Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 7 Dec 2021 07:28:44 +0000 Subject: Respond to OSC 4 query. --- input.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/input.c b/input.c index 73a58890..16f31ad7 100644 --- a/input.c +++ b/input.c @@ -2505,7 +2505,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. */ @@ -2529,6 +2530,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; -- cgit From db3aabcc34464940c286d7ce3abc8f3edc94b7bc Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 10 Dec 2021 12:42:37 +0000 Subject: Add a NOBLOCK flag rather than adding amount to wait for when dealing with potentially-long sequences. GitHub issue 3001. --- tmux.h | 2 +- tty.c | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/tmux.h b/tmux.h index dfea1583..e6a33104 100644 --- a/tmux.h +++ b/tmux.h @@ -1311,7 +1311,7 @@ struct tty { #define TTY_NOCURSOR 0x1 #define TTY_FREEZE 0x2 #define TTY_TIMER 0x4 -/* 0x8 unused */ +#define TTY_NOBLOCK 0x8 #define TTY_STARTED 0x10 #define TTY_OPENED 0x20 /* 0x40 unused */ diff --git a/tty.c b/tty.c index a9678eaf..3aefe784 100644 --- a/tty.c +++ b/tty.c @@ -206,6 +206,11 @@ tty_block_maybe(struct tty *tty) size_t size = EVBUFFER_LENGTH(tty->out); struct timeval tv = { .tv_usec = TTY_BLOCK_INTERVAL }; + if (size == 0) + tty->flags &= ~TTY_NOBLOCK; + else if (tty->flags & TTY_NOBLOCK) + return (0); + if (size < TTY_BLOCK_START(tty)) return (0); @@ -2088,8 +2093,8 @@ tty_set_selection(struct tty *tty, const char *buf, size_t len) encoded = xmalloc(size); b64_ntop(buf, len, encoded, size); + tty->flags |= TTY_NOBLOCK; tty_putcode_ptr2(tty, TTYC_MS, "", encoded); - tty->client->redraw = EVBUFFER_LENGTH(tty->out); free(encoded); } @@ -2097,6 +2102,7 @@ tty_set_selection(struct tty *tty, const char *buf, size_t len) void tty_cmd_rawstring(struct tty *tty, const struct tty_ctx *ctx) { + tty->flags |= TTY_NOBLOCK; tty_add(tty, ctx->ptr, ctx->num); tty_invalidate(tty); } -- cgit From 1f9aad2bb40fe90bec38288b08e8b152f13e04ad Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 10 Dec 2021 12:45:32 +0000 Subject: Mention XParseColor(3) for the cursor colour escape sequence. --- tmux.1 | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tmux.1 b/tmux.1 index 7d5c7357..00750dd6 100644 --- a/tmux.1 +++ b/tmux.1 @@ -6292,6 +6292,11 @@ to change the cursor colour from inside .Bd -literal -offset indent $ printf '\e033]12;red\e033\e\e' .Ed +.Pp +The colour is an +.Xr X 7 +colour, see +.Xr XParseColor 3 . .It Em \&Cmg, \&Clmg, \&Dsmg , \&Enmg Set, clear, disable or enable DECSLRM margins. These are set automatically if the terminal reports it is -- cgit From 9c1633a8659c4045b8303eb99d5f89b4f0dcb784 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 10 Dec 2021 12:51:11 +0000 Subject: Missed unlinked control notifications, GitHub issue 2996. --- tmux.1 | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tmux.1 b/tmux.1 index 00750dd6..708f5c75 100644 --- a/tmux.1 +++ b/tmux.1 @@ -6512,6 +6512,14 @@ are for future use and should be ignored. The window with ID .Ar window-id was created but is not linked to the current session. +.It Ic %unlinked-window-close Ar window-id +The window with ID +.Ar window-id , +which is not linked to the current session, was closed. +.It Ic %unlinked-window-renamed Ar window-id +The window with ID +.Ar window-id , +which is not linked to the current session, was renamed. .It Ic %window-add Ar window-id The window with ID .Ar window-id -- cgit From c6149adf55a1ab108a9336ba15a82bd11a809cb3 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 13 Dec 2021 09:42:20 +0000 Subject: Make pane-border-format a pane option, GitHub issue 2999. --- options-table.c | 2 +- screen-redraw.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/options-table.c b/options-table.c index bad22bfb..cfb8162e 100644 --- a/options-table.c +++ b/options-table.c @@ -965,7 +965,7 @@ const struct options_table_entry options_table[] = { { .name = "pane-border-format", .type = OPTIONS_TABLE_STRING, - .scope = OPTIONS_TABLE_WINDOW, + .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, .default_str = "#{?pane_active,#[reverse],}#{pane_index}#[default] " "\"#{pane_title}\"", .text = "Format of text in the pane status lines." diff --git a/screen-redraw.c b/screen-redraw.c index 0326c12d..5ef9e64c 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -373,7 +373,7 @@ screen_redraw_make_pane_status(struct client *c, struct window_pane *wp, style_apply(&gc, w->options, "pane-active-border-style", ft); else style_apply(&gc, w->options, "pane-border-style", ft); - fmt = options_get_string(w->options, "pane-border-format"); + fmt = options_get_string(wp->options, "pane-border-format"); expanded = format_expand_time(ft, fmt); if (wp->sx < 4) -- cgit From e4856de8bf23108ee9d335a22d7e16dd8ad855d8 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 20 Dec 2021 09:02:12 +0000 Subject: Do not crash on a zero size character. --- window-copy.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/window-copy.c b/window-copy.c index ec6a2f4e..9a0179a4 100644 --- a/window-copy.c +++ b/window-copy.c @@ -3148,6 +3148,11 @@ window_copy_cellstring(const struct grid_line *gl, u_int px, size_t *size, } utf8_to_data(gl->extddata[gce->offset].data, &ud); + if (ud.size == 0) { + *size = 0; + *allocated = 0; + return (NULL); + } *size = ud.size; *allocated = 1; -- cgit From ff64aafeabe0c9b51b33a10313e2027ae1686bc4 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 21 Dec 2021 13:07:53 +0000 Subject: ARM's Morello CHERI architecture does not support pointers in packed structures, so remove the packed attribute on struct grid_line and reorder the members to eliminate unnecessary padding. From Jessica Clarke in GitHub issue 3012. --- tmux.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tmux.h b/tmux.h index e6a33104..4f8016d3 100644 --- a/tmux.h +++ b/tmux.h @@ -668,15 +668,15 @@ struct grid_cell_entry { /* Grid line. */ struct grid_line { + struct grid_cell_entry *celldata; u_int cellused; u_int cellsize; - struct grid_cell_entry *celldata; - u_int extdsize; struct grid_extd_entry *extddata; + u_int extdsize; int flags; -} __packed; +}; /* Entire grid of cells. */ struct grid { -- cgit From 9aad945f7e58112c3f8819f658e379d672842d73 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 21 Dec 2021 14:57:28 +0000 Subject: Support underscore style with capture-pane -e, GitHub issue 2928. --- grid.c | 96 ++++++++++++++++++++++++++++++++++++++-------------------- tty-features.c | 2 +- 2 files changed, 64 insertions(+), 34 deletions(-) diff --git a/grid.c b/grid.c index 7744587a..91dc8f42 100644 --- a/grid.c +++ b/grid.c @@ -826,6 +826,56 @@ grid_string_cells_bg(const struct grid_cell *gc, int *values) return (n); } +/* Get underscore colour sequence. */ +static size_t +grid_string_cells_us(const struct grid_cell *gc, int *values) +{ + size_t n; + u_char r, g, b; + + n = 0; + if (gc->us & COLOUR_FLAG_256) { + values[n++] = 58; + values[n++] = 5; + values[n++] = gc->us & 0xff; + } else if (gc->us & COLOUR_FLAG_RGB) { + values[n++] = 58; + values[n++] = 2; + colour_split_rgb(gc->us, &r, &g, &b); + values[n++] = r; + values[n++] = g; + values[n++] = b; + } + return (n); +} + +/* Add on SGR code. */ +static void +grid_string_cells_add_code(char *buf, size_t len, u_int n, int *s, int *newc, + int *oldc, size_t nnewc, size_t noldc, int escape_c0) +{ + u_int i; + char tmp[64]; + + if (nnewc != 0 && + (nnewc != noldc || + memcmp(newc, oldc, nnewc * sizeof newc[0]) != 0 || + (n != 0 && s[0] == 0))) { + if (escape_c0) + strlcat(buf, "\\033[", len); + else + strlcat(buf, "\033[", len); + for (i = 0; i < nnewc; i++) { + if (i + 1 < nnewc) + xsnprintf(tmp, sizeof tmp, "%d;", newc[i]); + else + xsnprintf(tmp, sizeof tmp, "%d", newc[i]); + strlcat(buf, tmp, len); + } + strlcat(buf, "m", len); + } +} + /* * Returns ANSI code to set particular attributes (colour, bold and so on) * given a current state. @@ -861,7 +911,9 @@ grid_string_cells_code(const struct grid_cell *lastgc, /* If any attribute is removed, begin with 0. */ for (i = 0; i < nitems(attrs); i++) { - if (!(attr & attrs[i].mask) && (lastattr & attrs[i].mask)) { + if (((~attr & attrs[i].mask) && + (lastattr & attrs[i].mask)) || + (lastgc->us != 0 && gc->us == 0)) { s[n++] = 0; lastattr &= GRID_ATTR_CHARSET; break; @@ -897,42 +949,20 @@ grid_string_cells_code(const struct grid_cell *lastgc, /* If the foreground colour changed, write its parameters. */ nnewc = grid_string_cells_fg(gc, newc); noldc = grid_string_cells_fg(lastgc, oldc); - if (nnewc != noldc || - memcmp(newc, oldc, nnewc * sizeof newc[0]) != 0 || - (n != 0 && s[0] == 0)) { - if (escape_c0) - strlcat(buf, "\\033[", len); - else - strlcat(buf, "\033[", len); - for (i = 0; i < nnewc; i++) { - if (i + 1 < nnewc) - xsnprintf(tmp, sizeof tmp, "%d;", newc[i]); - else - xsnprintf(tmp, sizeof tmp, "%d", newc[i]); - strlcat(buf, tmp, len); - } - strlcat(buf, "m", len); - } + grid_string_cells_add_code(buf, len, n, s, newc, oldc, nnewc, noldc, + escape_c0); /* If the background colour changed, append its parameters. */ nnewc = grid_string_cells_bg(gc, newc); noldc = grid_string_cells_bg(lastgc, oldc); - if (nnewc != noldc || - memcmp(newc, oldc, nnewc * sizeof newc[0]) != 0 || - (n != 0 && s[0] == 0)) { - if (escape_c0) - strlcat(buf, "\\033[", len); - else - strlcat(buf, "\033[", len); - for (i = 0; i < nnewc; i++) { - if (i + 1 < nnewc) - xsnprintf(tmp, sizeof tmp, "%d;", newc[i]); - else - xsnprintf(tmp, sizeof tmp, "%d", newc[i]); - strlcat(buf, tmp, len); - } - strlcat(buf, "m", len); - } + grid_string_cells_add_code(buf, len, n, s, newc, oldc, nnewc, noldc, + escape_c0); + + /* If the underscore colour changed, append its parameters. */ + nnewc = grid_string_cells_us(gc, newc); + noldc = grid_string_cells_us(lastgc, oldc); + grid_string_cells_add_code(buf, len, n, s, newc, oldc, nnewc, noldc, + escape_c0); /* Append shift in/shift out if needed. */ if ((attr & GRID_ATTR_CHARSET) && !(lastattr & GRID_ATTR_CHARSET)) { diff --git a/tty-features.c b/tty-features.c index 48ac51be..3aca2520 100644 --- a/tty-features.c +++ b/tty-features.c @@ -363,7 +363,7 @@ tty_default_features(int *feat, const char *name, u_int version) }, { .name = "iTerm2", .features = TTY_FEATURES_BASE_MODERN_XTERM - ",cstyle,extkeys,margins,sync" + ",cstyle,extkeys,margins,usstyle,sync" }, { .name = "XTerm", /* -- cgit From e6e3c75ed70adece8b1cf93017f6f359d022bdb0 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 31 Dec 2021 11:35:49 +0000 Subject: Try to turn on less-capable mouse modes when turning on more-capable, to increase the chances we get something even if the terminal doesn't support the one we really want. GitHub issue 3020. --- tty.c | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/tty.c b/tty.c index 3aefe784..8410a64c 100644 --- a/tty.c +++ b/tty.c @@ -791,26 +791,19 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s) 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 - * the various bits. + * If the mouse modes have changed, clear then all and apply + * again. There are differences in how terminals track the + * various bits. */ - if (tty->mode & MODE_MOUSE_SGR) - tty_puts(tty, "\033[?1006l"); - if (tty->mode & MODE_MOUSE_STANDARD) - tty_puts(tty, "\033[?1000l"); - if (tty->mode & MODE_MOUSE_BUTTON) - tty_puts(tty, "\033[?1002l"); - if (tty->mode & MODE_MOUSE_ALL) - tty_puts(tty, "\033[?1003l"); + tty_puts(tty, "\033[?1006l\033[?1000l\033[?1002l\033[?1003l"); if (mode & ALL_MOUSE_MODES) tty_puts(tty, "\033[?1006h"); - if (mode & MODE_MOUSE_STANDARD) - tty_puts(tty, "\033[?1000h"); - if (mode & MODE_MOUSE_BUTTON) - tty_puts(tty, "\033[?1002h"); if (mode & MODE_MOUSE_ALL) - tty_puts(tty, "\033[?1003h"); + tty_puts(tty, "\033[?1000h\033[?1002h\033[?1003h"); + if (mode & MODE_MOUSE_BUTTON) + tty_puts(tty, "\033[?1000h\033[?1002h"); + else if (mode & MODE_MOUSE_STANDARD) + tty_puts(tty, "\033[?1000h"); } if (changed & MODE_BRACKETPASTE) { if (mode & MODE_BRACKETPASTE) -- cgit From b2b94dcba73a62e2949fb98aee097dbaea658760 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 6 Jan 2022 08:20:00 +0000 Subject: Ignore windows without a size set (may be used for pane only), from Anindya Mukherjee. --- resize.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resize.c b/resize.c index 175dd740..18a02adb 100644 --- a/resize.c +++ b/resize.c @@ -178,7 +178,7 @@ clients_calculate_size(int type, int current, struct client *c, cw = NULL; /* Work out this client's size. */ - if (cw != NULL) { + if (cw != NULL && cw->sx != 0 && cw->sy != 0) { cx = cw->sx; cy = cw->sy; } else { -- cgit From e5e4df7a2241298b7ea3ba77b5fe1fdef6df0e08 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 6 Jan 2022 08:23:42 +0000 Subject: Mention alternate config files, from Daniel Augusto in GitHub issue 3023. --- tmux.1 | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tmux.1 b/tmux.1 index 50a356a5..78a7d10b 100644 --- a/tmux.1 +++ b/tmux.1 @@ -140,7 +140,10 @@ By default, loads the system configuration file from .Pa @SYSCONFDIR@/tmux.conf , if present, then looks for a user configuration file at -.Pa ~/.tmux.conf . +.Pa ~/.tmux.conf, +.Pa $XDG_CONFIG_HOME/tmux/tmux.conf +or +.Pa ~/.config/tmux/tmux.conf . .Pp The configuration file is a set of .Nm @@ -6627,6 +6630,8 @@ options. .Sh FILES .Bl -tag -width "@SYSCONFDIR@/tmux.confXXX" -compact .It Pa ~/.tmux.conf +.It Pa $XDG_CONFIG_HOME/tmux/tmux.conf +.It Pa ~/.config/tmux/tmux.conf Default .Nm configuration file. -- cgit From a6b361e775e0bf6301284391e75dd9af7af340da Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 17 Jan 2022 10:40:03 +0000 Subject: Do not try to strdup NULL, from seL4 at disroot dot org in GitHub issue 3038. --- popup.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/popup.c b/popup.c index ed6a6bb9..bebc7cc6 100644 --- a/popup.c +++ b/popup.c @@ -668,7 +668,8 @@ popup_display(int flags, enum box_lines lines, struct cmdq_item *item, u_int px, pd = xcalloc(1, sizeof *pd); pd->item = item; pd->flags = flags; - pd->title = xstrdup(title); + if (title != NULL) + pd->title = xstrdup(title); pd->c = c; pd->c->references++; -- cgit From 770d28b8c517fd87621ea3e8d6d7bdc4f35b3a94 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 1 Feb 2022 11:52:08 +0000 Subject: Do not overflow width when not enough space. --- menu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/menu.c b/menu.c index aaa1287e..c770cd7d 100644 --- a/menu.c +++ b/menu.c @@ -81,6 +81,8 @@ menu_add_item(struct menu *menu, const struct menu_item *item, menu->count--; return; } + if (c->tty.sx <= 4) + return; max_width = c->tty.sx - 4; slen = strlen(s); -- cgit From 7a4ba6d4a5458f4e2450024d72f0e16905dd5c64 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 1 Feb 2022 12:05:42 +0000 Subject: Mention that if-shell and #() use /bin/sh. --- job.c | 3 +-- tmux.1 | 8 +++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/job.c b/job.c index dad211f4..fb33c0cf 100644 --- a/job.c +++ b/job.c @@ -89,9 +89,8 @@ job_run(const char *cmd, int argc, char **argv, struct environ *e, struct sessio * if-shell to decide on default-terminal based on outside TERM. */ env = environ_for_session(s, !cfg_finished); - if (e != NULL) { + if (e != NULL) environ_copy(e, env); - } sigfillset(&set); sigprocmask(SIG_BLOCK, &set, &oldset); diff --git a/tmux.1 b/tmux.1 index 708f5c75..5310b97f 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4989,7 +4989,9 @@ commands to finish; instead, the previous result from running the same command i or a placeholder if the command has not been run before. If the command hasn't exited, the most recent line of output will be used, but the status line will not be updated more than once a second. -Commands are executed with the +Commands are executed using +.Pa /bin/sh +and with the .Nm global environment set (see the .Sx GLOBAL AND SESSION ENVIRONMENT @@ -6155,6 +6157,8 @@ Execute the first .Ar command if .Ar shell-command +(run with +.Pa /bin/sh ) returns success or the second .Ar command otherwise. @@ -6191,6 +6195,8 @@ option. .D1 Pq alias: Ic run Execute .Ar shell-command +using +.Pa /bin/sh or (with .Fl C ) a -- cgit From 7e34645fcbe93984461343d67373a29e552fec20 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 1 Feb 2022 14:46:41 +0000 Subject: Add option to show arrows for active pane indicator, GitHub issue 3022 from Marcel Partap. --- options-table.c | 12 +++++++++ screen-redraw.c | 82 +++++++++++++++++++++++++++++++++++++++++++-------------- tmux.1 | 6 +++++ tmux.h | 6 +++++ 4 files changed, 86 insertions(+), 20 deletions(-) diff --git a/options-table.c b/options-table.c index cfb8162e..5cbec546 100644 --- a/options-table.c +++ b/options-table.c @@ -64,6 +64,9 @@ static const char *options_table_cursor_style_list[] = { static const char *options_table_pane_status_list[] = { "off", "top", "bottom", NULL }; +static const char *options_table_pane_border_indicators_list[] = { + "off", "colour", "arrows", "both", NULL +}; static const char *options_table_pane_border_lines_list[] = { "single", "double", "heavy", "simple", "number", NULL }; @@ -971,6 +974,15 @@ const struct options_table_entry options_table[] = { .text = "Format of text in the pane status lines." }, + { .name = "pane-border-indicators", + .type = OPTIONS_TABLE_CHOICE, + .scope = OPTIONS_TABLE_WINDOW, + .choices = options_table_pane_border_indicators_list, + .default_num = PANE_BORDER_COLOUR, + .text = "Whether to indicate the active pane by colouring border or " + "displaying arrow markers." + }, + { .name = "pane-border-lines", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_WINDOW, diff --git a/screen-redraw.c b/screen-redraw.c index 5ef9e64c..8dd75f40 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -34,11 +34,16 @@ static void screen_redraw_set_context(struct client *, #define START_ISOLATE "\342\201\246" #define END_ISOLATE "\342\201\251" +/* Border in relation to a pane. */ enum screen_redraw_border_type { SCREEN_REDRAW_OUTSIDE, SCREEN_REDRAW_INSIDE, - SCREEN_REDRAW_BORDER + SCREEN_REDRAW_BORDER_LEFT, + SCREEN_REDRAW_BORDER_RIGHT, + SCREEN_REDRAW_BORDER_TOP, + SCREEN_REDRAW_BORDER_BOTTOM }; +#define BORDER_MARKERS " +,.-" /* Get cell border character. */ static void @@ -102,64 +107,74 @@ static enum screen_redraw_border_type screen_redraw_pane_border(struct window_pane *wp, u_int px, u_int py, int pane_status) { - u_int ex = wp->xoff + wp->sx, ey = wp->yoff + wp->sy; + struct options *oo = wp->window->options; + int split = 0; + u_int ex = wp->xoff + wp->sx, ey = wp->yoff + wp->sy; /* Inside pane. */ if (px >= wp->xoff && px < ex && py >= wp->yoff && py < ey) return (SCREEN_REDRAW_INSIDE); + /* Get pane indicator. */ + switch (options_get_number(oo, "pane-border-indicators")) { + case PANE_BORDER_COLOUR: + case PANE_BORDER_BOTH: + split = 1; + break; + } + /* Left/right borders. */ if (pane_status == PANE_STATUS_OFF) { - if (screen_redraw_two_panes(wp->window, 0)) { + if (screen_redraw_two_panes(wp->window, 0) && split) { if (wp->xoff == 0 && px == wp->sx && py <= wp->sy / 2) - return (SCREEN_REDRAW_BORDER); + return (SCREEN_REDRAW_BORDER_RIGHT); if (wp->xoff != 0 && px == wp->xoff - 1 && py > wp->sy / 2) - return (SCREEN_REDRAW_BORDER); + return (SCREEN_REDRAW_BORDER_LEFT); } else { if ((wp->yoff == 0 || py >= wp->yoff - 1) && py <= ey) { if (wp->xoff != 0 && px == wp->xoff - 1) - return (SCREEN_REDRAW_BORDER); + return (SCREEN_REDRAW_BORDER_LEFT); if (px == ex) - return (SCREEN_REDRAW_BORDER); + return (SCREEN_REDRAW_BORDER_RIGHT); } } } else { if ((wp->yoff == 0 || py >= wp->yoff - 1) && py <= ey) { if (wp->xoff != 0 && px == wp->xoff - 1) - return (SCREEN_REDRAW_BORDER); + return (SCREEN_REDRAW_BORDER_LEFT); if (px == ex) - return (SCREEN_REDRAW_BORDER); + return (SCREEN_REDRAW_BORDER_RIGHT); } } /* Top/bottom borders. */ if (pane_status == PANE_STATUS_OFF) { - if (screen_redraw_two_panes(wp->window, 1)) { + if (screen_redraw_two_panes(wp->window, 1) && split) { if (wp->yoff == 0 && py == wp->sy && px <= wp->sx / 2) - return (SCREEN_REDRAW_BORDER); + return (SCREEN_REDRAW_BORDER_BOTTOM); if (wp->yoff != 0 && py == wp->yoff - 1 && px > wp->sx / 2) - return (SCREEN_REDRAW_BORDER); + return (SCREEN_REDRAW_BORDER_TOP); } else { if ((wp->xoff == 0 || px >= wp->xoff - 1) && px <= ex) { if (wp->yoff != 0 && py == wp->yoff - 1) - return (SCREEN_REDRAW_BORDER); + return (SCREEN_REDRAW_BORDER_TOP); if (py == ey) - return (SCREEN_REDRAW_BORDER); + return (SCREEN_REDRAW_BORDER_BOTTOM); } } } else if (pane_status == PANE_STATUS_TOP) { if ((wp->xoff == 0 || px >= wp->xoff - 1) && px <= ex) { if (wp->yoff != 0 && py == wp->yoff - 1) - return (SCREEN_REDRAW_BORDER); + return (SCREEN_REDRAW_BORDER_TOP); } } else { if ((wp->xoff == 0 || px >= wp->xoff - 1) && px <= ex) { if (py == ey) - return (SCREEN_REDRAW_BORDER); + return (SCREEN_REDRAW_BORDER_BOTTOM); } } @@ -189,10 +204,10 @@ screen_redraw_cell_border(struct client *c, u_int px, u_int py, int pane_status) switch (screen_redraw_pane_border(wp, px, py, pane_status)) { case SCREEN_REDRAW_INSIDE: return (0); - case SCREEN_REDRAW_BORDER: - return (1); case SCREEN_REDRAW_OUTSIDE: break; + default: + return (1); } } @@ -346,7 +361,7 @@ screen_redraw_check_is(u_int px, u_int py, int pane_status, enum screen_redraw_border_type border; border = screen_redraw_pane_border(wp, px, py, pane_status); - if (border == SCREEN_REDRAW_BORDER) + if (border != SCREEN_REDRAW_INSIDE && border != SCREEN_REDRAW_OUTSIDE) return (1); return (0); } @@ -637,11 +652,12 @@ screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j) struct options *oo = w->options; struct tty *tty = &c->tty; struct format_tree *ft; - struct window_pane *wp; + struct window_pane *wp, *active = server_client_get_pane(c); 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 arrows = 0, border; int pane_status = ctx->pane_status, isolates; if (c->overlay_check != NULL) { @@ -689,6 +705,32 @@ screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j) tty_cursor(tty, i, j); if (isolates) tty_puts(tty, END_ISOLATE); + + switch (options_get_number(oo, "pane-border-indicators")) { + case PANE_BORDER_ARROWS: + case PANE_BORDER_BOTH: + arrows = 1; + break; + } + + if (wp != NULL && arrows) { + border = screen_redraw_pane_border(active, x, y, pane_status); + if (((i == wp->xoff + 1 && + (cell_type == CELL_LEFTRIGHT || + (cell_type == CELL_TOPJOIN && + border == SCREEN_REDRAW_BORDER_BOTTOM) || + (cell_type == CELL_BOTTOMJOIN && + border == SCREEN_REDRAW_BORDER_TOP))) || + (j == wp->yoff + 1 && + (cell_type == CELL_TOPBOTTOM || + (cell_type == CELL_LEFTJOIN && + border == SCREEN_REDRAW_BORDER_RIGHT) || + (cell_type == CELL_RIGHTJOIN && + border == SCREEN_REDRAW_BORDER_LEFT)))) && + screen_redraw_check_is(x, y, pane_status, active)) + utf8_set(&gc.data, BORDER_MARKERS[border]); + } + tty_cell(tty, &gc, &grid_default_cell, NULL); if (isolates) tty_puts(tty, START_ISOLATE); diff --git a/tmux.1 b/tmux.1 index 5310b97f..7a8b9f24 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4223,6 +4223,12 @@ but set the starting index for pane numbers. .It Ic pane-border-format Ar format Set the text shown in pane border status lines. .Pp +.It Xo Ic pane-border-indicators +.Op Ic off | colour | arrows | both +.Xc +Indicate active pane by colouring only half of the border in windows with +exactly two panes, by displaying arrow markers, by drawing both or neither. +.Pp .It Ic pane-border-lines Ar type Set the type of characters used for drawing pane borders. .Ar type diff --git a/tmux.h b/tmux.h index 4f8016d3..6fc1490a 100644 --- a/tmux.h +++ b/tmux.h @@ -842,6 +842,12 @@ enum pane_lines { PANE_LINES_NUMBER }; +/* Pane border indicator option. */ +#define PANE_BORDER_OFF 0 +#define PANE_BORDER_COLOUR 1 +#define PANE_BORDER_ARROWS 2 +#define PANE_BORDER_BOTH 3 + /* Screen redraw context. */ struct screen_redraw_ctx { struct client *c; -- cgit From 97900d0442252aa4b76f89745718038f39717ecd Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 1 Feb 2022 18:12:20 +0000 Subject: A menu must be shown on a client, so always give the client when adding the items. Also fix mode menus. --- menu.c | 2 -- mode-tree.c | 12 +++--------- popup.c | 4 ++-- 3 files changed, 5 insertions(+), 13 deletions(-) diff --git a/menu.c b/menu.c index c770cd7d..aaa1287e 100644 --- a/menu.c +++ b/menu.c @@ -81,8 +81,6 @@ menu_add_item(struct menu *menu, const struct menu_item *item, menu->count--; return; } - if (c->tty.sx <= 4) - return; max_width = c->tty.sx - 4; slen = strlen(s); diff --git a/mode-tree.c b/mode-tree.c index 1eb496fe..c007e27f 100644 --- a/mode-tree.c +++ b/mode-tree.c @@ -104,7 +104,6 @@ struct mode_tree_menu { struct mode_tree_data *data; struct client *c; u_int line; - void *itemdata; }; static void mode_tree_free_items(struct mode_tree_list *); @@ -909,18 +908,14 @@ static void mode_tree_menu_callback(__unused struct menu *menu, __unused u_int idx, key_code key, void *data) { - struct mode_tree_menu *mtm = data; - struct mode_tree_data *mtd = mtm->data; - struct mode_tree_item *mti; + struct mode_tree_menu *mtm = data; + struct mode_tree_data *mtd = mtm->data; if (mtd->dead || key == KEYC_NONE) goto out; if (mtm->line >= mtd->line_size) goto out; - mti = mtd->line_list[mtm->line].item; - if (mti->itemdata != mtm->itemdata) - goto out; mtd->current = mtm->line; mtd->menucb(mtd->modedata, mtm->c, key); @@ -954,14 +949,13 @@ mode_tree_display_menu(struct mode_tree_data *mtd, struct client *c, u_int x, title = xstrdup(""); } menu = menu_create(title); - menu_add_items(menu, items, NULL, NULL, NULL); + menu_add_items(menu, items, NULL, c, NULL); free(title); mtm = xmalloc(sizeof *mtm); mtm->data = mtd; mtm->c = c; mtm->line = line; - mtm->itemdata = mti->itemdata; mtd->references++; if (x >= (menu->width + 4) / 2) diff --git a/popup.c b/popup.c index bebc7cc6..4b4c58ca 100644 --- a/popup.c +++ b/popup.c @@ -566,10 +566,10 @@ popup_key_cb(struct client *c, void *data, struct key_event *event) menu: pd->menu = menu_create(""); if (pd->flags & POPUP_INTERNAL) { - menu_add_items(pd->menu, popup_internal_menu_items, NULL, NULL, + menu_add_items(pd->menu, popup_internal_menu_items, NULL, c, NULL); } else - menu_add_items(pd->menu, popup_menu_items, NULL, NULL, NULL); + menu_add_items(pd->menu, popup_menu_items, NULL, c, NULL); if (m->x >= (pd->menu->width + 4) / 2) x = m->x - (pd->menu->width + 4) / 2; else -- cgit From 5080acc12714862ef7a66286d7c3bce538c6d74f Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 3 Feb 2022 07:26:43 +0000 Subject: Add a key in copy mode to toggle position indicator. --- key-bindings.c | 2 ++ tmux.1 | 1 + window-copy.c | 16 ++++++++++++++++ 3 files changed, 19 insertions(+) diff --git a/key-bindings.c b/key-bindings.c index 9f7e734a..95171966 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -492,6 +492,7 @@ key_bindings_init(void) "bind -Tcopy-mode \\; { send -X jump-again }", "bind -Tcopy-mode F { command-prompt -1p'(jump backward)' { send -X jump-backward '%%' } }", "bind -Tcopy-mode N { send -X search-reverse }", + "bind -Tcopy-mode P { send -X toggle-position }", "bind -Tcopy-mode R { send -X rectangle-toggle }", "bind -Tcopy-mode T { command-prompt -1p'(jump to backward)' { send -X jump-to-backward '%%' } }", "bind -Tcopy-mode X { send -X set-mark }", @@ -588,6 +589,7 @@ key_bindings_init(void) "bind -Tcopy-mode-vi L { send -X bottom-line }", "bind -Tcopy-mode-vi M { send -X middle-line }", "bind -Tcopy-mode-vi N { send -X search-reverse }", + "bind -Tcopy-mode-vi P { send -X toggle-position }", "bind -Tcopy-mode-vi T { command-prompt -1p'(jump to backward)' { send -X jump-to-backward '%%' } }", "bind -Tcopy-mode-vi V { send -X select-line }", "bind -Tcopy-mode-vi W { send -X next-space }", diff --git a/tmux.1 b/tmux.1 index 7a8b9f24..255f10a9 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1773,6 +1773,7 @@ The following commands are supported in copy mode: .It Li "set-mark" Ta "X" Ta "X" .It Li "start-of-line" Ta "0" Ta "C-a" .It Li "stop-selection" Ta "" Ta "" +.It Li "toggle-position" Ta "P" Ta "P" .It Li "top-line" Ta "H" Ta "M-R" .El .Pp diff --git a/window-copy.c b/window-copy.c index 9a0179a4..7c55b0f2 100644 --- a/window-copy.c +++ b/window-copy.c @@ -1272,6 +1272,16 @@ window_copy_cmd_halfpage_up(struct window_copy_cmd_state *cs) return (WINDOW_COPY_CMD_NOTHING); } +static enum window_copy_cmd_action +window_copy_cmd_toggle_position(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + struct window_copy_mode_data *data = wme->data; + + data->hide_position = !data->hide_position; + return (WINDOW_COPY_CMD_REDRAW); +} + static enum window_copy_cmd_action window_copy_cmd_history_bottom(struct window_copy_cmd_state *cs) { @@ -2817,6 +2827,12 @@ static const struct { .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, .f = window_copy_cmd_stop_selection }, + { .command = "toggle-position", + .minargs = 0, + .maxargs = 0, + .clear = WINDOW_COPY_CMD_CLEAR_NEVER, + .f = window_copy_cmd_toggle_position + }, { .command = "top-line", .minargs = 0, .maxargs = 0, -- cgit From c401c91ad9f1ef8ea50b11762856a458de525bf9 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 3 Feb 2022 07:38:17 +0000 Subject: Update focus when active pane changes after pane destroyed. --- window.c | 1 + 1 file changed, 1 insertion(+) diff --git a/window.c b/window.c index d7fc3bf4..2ca3833c 100644 --- a/window.c +++ b/window.c @@ -754,6 +754,7 @@ window_lost_pane(struct window *w, struct window_pane *wp) if (w->active != NULL) { w->active->flags |= PANE_CHANGED; notify_window("window-pane-changed", w); + window_update_focus(w); } } else if (wp == w->last) w->last = NULL; -- cgit From 57396b55a2a979925def85cf142f087c5ad340a6 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 3 Feb 2022 07:50:06 +0000 Subject: Update CHANGES. --- CHANGES | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/CHANGES b/CHANGES index d1dbb6ea..159ab905 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,45 @@ CHANGES FROM 3.2a TO 3.3 +* Add an option (pane-border-indicators) to select how the active pane is shown + on the pane border (colour, arrows or both). + +* Support underscore styles with capture-pane -e. + +* Make pane-border-format a pane option rather than window. + +* Respond to OSC 4 queries + +* Fix g/G keys in modes to do the same thing as copy mode (and vi). + +* Bump the time terminals have to respond to device attributes queries to three + seconds. + +* If automatic-rename is off, allow the rename escape sequence to set an empty + name. + +* Trim menu item text more intelligently. + +* Add cursor-style and cursor-colour options to set the default cursor style + and colour. + +* Accept some useful and non-conflicting emacs keys in vi normal mode at the + command prompt. + +* Add a format modifier (c) to force a colour to RGB. + +* Add -s and -S to display-popup to set styles, -b to set lines and -T to set + popup title. New popup-border-lines, popup-border-style and popup-style + options set the defaults. + +* Add -e flag to set an environment variable for a popup. + +* Make send-keys without arguments send the key it is bound to (if bound to a + key). + +* Try to leave terminal cursor at the right position even when tmux is drawing + its own cursor or selection (such as at the command prompt and in choose + mode) for people using screen readers and similar which can make use of it. + * Change so that {} is converted to tmux commands immediately when parsed. This means it must contain valid tmux commands. For commands which expand %% and %%%, this now only happens within string arguments. Use of nested aliases -- cgit From b46abeb34d775159b9c50618339d8da9fd8c0212 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 3 Feb 2022 07:50:35 +0000 Subject: 3.3-rc. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 26bd1a98..c71a54b0 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ # configure.ac -AC_INIT([tmux], next-3.4) +AC_INIT([tmux], 3.3-rc) AC_PREREQ([2.60]) AC_CONFIG_AUX_DIR(etc) -- cgit From 948d2fad0ae9723263554115164c412aa095fe7a Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 3 Feb 2022 10:07:11 +0000 Subject: Use format_draw for command prompt prefix to allow styles, GitHub issue 3054. --- status.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/status.c b/status.c index bb57b3d6..79033242 100644 --- a/status.c +++ b/status.c @@ -718,7 +718,7 @@ status_prompt_redraw(struct client *c) memcpy(&cursorgc, &gc, sizeof cursorgc); cursorgc.attr ^= GRID_ATTR_REVERSE; - start = screen_write_strlen("%s", c->prompt_string); + start = format_width(c->prompt_string); if (start > c->tty.sx) start = c->tty.sx; @@ -728,7 +728,7 @@ status_prompt_redraw(struct client *c) for (offset = 0; offset < c->tty.sx; offset++) screen_write_putc(&ctx, &gc, ' '); screen_write_cursormove(&ctx, 0, lines - 1, 0); - screen_write_nputs(&ctx, start, &gc, "%s", c->prompt_string); + format_draw(&ctx, &gc, start, c->prompt_string, NULL, 0); screen_write_cursormove(&ctx, start, lines - 1, 0); left = c->tty.sx - start; -- cgit From 3276f0c18473703a734d53495202f50a2c7d6b34 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 3 Feb 2022 11:06:11 +0000 Subject: Adjust size given to resize-pane for pane status line, GitHub issue 3050. --- cmd-resize-pane.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/cmd-resize-pane.c b/cmd-resize-pane.c index 81744f72..c9439441 100644 --- a/cmd-resize-pane.c +++ b/cmd-resize-pane.c @@ -60,7 +60,7 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item) const char *errstr; char *cause; u_int adjust; - int x, y; + int x, y, status; struct grid *gd = wp->base.grid; if (args_has(args, 'T')) { @@ -121,6 +121,17 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item) free(cause); return (CMD_RETURN_ERROR); } + status = options_get_number(w->options, "pane-border-status"); + switch (status) { + case PANE_STATUS_TOP: + if (y != INT_MAX && wp->yoff == 1) + y++; + break; + case PANE_STATUS_BOTTOM: + if (y != INT_MAX && wp->yoff + wp->sy == w->sy - 1) + y++; + break; + } layout_resize_pane_to(wp, LAYOUT_TOPBOTTOM, y); } -- cgit From 07e2d88c205db5620154c6124c8ec7686b8c9028 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 4 Feb 2022 11:57:22 +0000 Subject: Use ACS for pane border indicators so they work with different line types, from Thomas Adam. --- screen-redraw.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/screen-redraw.c b/screen-redraw.c index 8dd75f40..ef79d9aa 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -727,8 +727,10 @@ screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j) border == SCREEN_REDRAW_BORDER_RIGHT) || (cell_type == CELL_RIGHTJOIN && border == SCREEN_REDRAW_BORDER_LEFT)))) && - screen_redraw_check_is(x, y, pane_status, active)) + screen_redraw_check_is(x, y, pane_status, active)) { + gc.attr |= GRID_ATTR_CHARSET; utf8_set(&gc.data, BORDER_MARKERS[border]); + } } tty_cell(tty, &gc, &grid_default_cell, NULL); -- cgit From 040164555a0e41d23082b74a2a22ff370e8193c2 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 14 Feb 2022 09:10:48 +0000 Subject: Do not return error with -q, GitHub issue 3065. --- cmd-show-options.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cmd-show-options.c b/cmd-show-options.c index 7ac7e455..0e7e5192 100644 --- a/cmd-show-options.c +++ b/cmd-show-options.c @@ -103,7 +103,7 @@ cmd_show_options_exec(struct cmd *self, struct cmdq_item *item) name = options_match(argument, &idx, &ambiguous); if (name == NULL) { if (args_has(args, 'q')) - goto fail; + goto out; if (ambiguous) cmdq_error(item, "ambiguous option: %s", argument); else @@ -114,7 +114,7 @@ cmd_show_options_exec(struct cmd *self, struct cmdq_item *item) &cause); if (scope == OPTIONS_TABLE_NONE) { if (args_has(args, 'q')) - goto fail; + goto out; cmdq_error(item, "%s", cause); free(cause); goto fail; @@ -129,11 +129,12 @@ cmd_show_options_exec(struct cmd *self, struct cmdq_item *item) cmd_show_options_print(self, item, o, idx, parent); else if (*name == '@') { if (args_has(args, 'q')) - goto fail; + goto out; cmdq_error(item, "invalid option: %s", argument); goto fail; } +out: free(name); free(argument); return (CMD_RETURN_NORMAL); -- cgit From f85208602ddd43ba55f29beae8d7a05cbfedefc1 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 15 Feb 2022 13:03:02 +0000 Subject: Do not create a buffer from an OSC 52 response if we have not sent a query. --- cmd-refresh-client.c | 2 +- tmux.h | 4 +++- tty-keys.c | 5 +++++ tty.c | 27 ++++++++++++++++++++++++++- 4 files changed, 35 insertions(+), 3 deletions(-) diff --git a/cmd-refresh-client.c b/cmd-refresh-client.c index 821558ae..2af9cb46 100644 --- a/cmd-refresh-client.c +++ b/cmd-refresh-client.c @@ -225,7 +225,7 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item) } if (args_has(args, 'l')) { - tty_putcode_ptr2(&tc->tty, TTYC_MS, "", "?"); + tty_send_osc52_query(&tc->tty); return (CMD_RETURN_NORMAL); } diff --git a/tmux.h b/tmux.h index 6fc1490a..bc87d098 100644 --- a/tmux.h +++ b/tmux.h @@ -1277,6 +1277,7 @@ LIST_HEAD(tty_terms, tty_term); struct tty { struct client *client; struct event start_timer; + struct event query_timer; u_int sx; u_int sy; @@ -1320,7 +1321,7 @@ struct tty { #define TTY_NOBLOCK 0x8 #define TTY_STARTED 0x10 #define TTY_OPENED 0x20 -/* 0x40 unused */ +#define TTY_OSC52QUERY 0x40 #define TTY_BLOCK 0x80 #define TTY_HAVEDA 0x100 #define TTY_HAVEXDA 0x200 @@ -2173,6 +2174,7 @@ void tty_reset(struct tty *); void tty_region_off(struct tty *); void tty_margin_off(struct tty *); void tty_cursor(struct tty *, u_int, u_int); +void tty_send_osc52_query(struct tty *); void tty_putcode(struct tty *, enum tty_code_code); void tty_putcode1(struct tty *, enum tty_code_code, int); void tty_putcode2(struct tty *, enum tty_code_code, int, int); diff --git a/tty-keys.c b/tty-keys.c index 65b600c0..5d950078 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -1217,6 +1217,11 @@ tty_keys_clipboard(__unused struct tty *tty, const char *buf, size_t len, buf++; end--; + /* If we did not request this, ignore it. */ + if (~tty->flags & TTY_OSC52QUERY) + return (0); + tty->flags &= ~TTY_OSC52QUERY; + /* It has to be a string so copy it. */ copy = xmalloc(end + 1); memcpy(copy, buf, end); diff --git a/tty.c b/tty.c index 8410a64c..06b1bfb2 100644 --- a/tty.c +++ b/tty.c @@ -83,6 +83,8 @@ static void tty_check_overlay_range(struct tty *, u_int, u_int, u_int, #define TTY_BLOCK_START(tty) (1 + ((tty)->sx * (tty)->sy) * 8) #define TTY_BLOCK_STOP(tty) (1 + ((tty)->sx * (tty)->sy) / 8) +#define TTY_QUERY_TIMEOUT 5 + void tty_create_log(void) { @@ -307,7 +309,7 @@ tty_start_tty(struct tty *tty) { struct client *c = tty->client; struct termios tio; - struct timeval tv = { .tv_sec = 3 }; + struct timeval tv = { .tv_sec = TTY_QUERY_TIMEOUT }; setblocking(c->fd, 0); event_add(&tty->event_in, NULL); @@ -2917,3 +2919,26 @@ tty_default_attributes(struct tty *tty, const struct grid_cell *defaults, gc.bg = bg; tty_attributes(tty, &gc, defaults, palette); } + +static void +tty_query_timer_callback(__unused int fd, __unused short events, void *data) +{ + struct tty *tty = data; + + tty->flags &= ~TTY_OSC52QUERY; +} + +void +tty_send_osc52_query(struct tty *tty) +{ + struct timeval tv = { .tv_sec = TTY_QUERY_TIMEOUT }; + + if ((~tty->flags & TTY_STARTED) || (tty->flags & TTY_OSC52QUERY)) + return; + tty_putcode_ptr2(tty, TTYC_MS, "", "?"); + tty->flags |= TTY_OSC52QUERY; + + evtimer_set(&tty->query_timer, tty_query_timer_callback, tty); + evtimer_add(&tty->query_timer, &tv); +} + -- cgit From 5076beb009f761999a3b218a1a8d7cbfbc80ee03 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 15 Feb 2022 13:11:29 +0000 Subject: Add an option (default off) to control the passthrough escape sequence. Like set-clipboard and allow-rename it is safer to forbid this by default. --- input.c | 6 +++++- options-table.c | 20 ++++++++++++++------ tmux.1 | 7 +++++++ 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/input.c b/input.c index 16f31ad7..5adc694d 100644 --- a/input.c +++ b/input.c @@ -2235,15 +2235,19 @@ input_enter_dcs(struct input_ctx *ictx) static int input_dcs_dispatch(struct input_ctx *ictx) { + struct window_pane *wp = ictx->wp; struct screen_write_ctx *sctx = &ictx->ctx; u_char *buf = ictx->input_buf; size_t len = ictx->input_len; const char prefix[] = "tmux;"; const u_int prefixlen = (sizeof prefix) - 1; + if (wp == NULL) + return (0); if (ictx->flags & INPUT_DISCARD) return (0); - + if (!options_get_number(ictx->wp->options, "allow-passthrough")) + return (0); log_debug("%s: \"%s\"", __func__, buf); if (len >= prefixlen && strncmp(buf, prefix, prefixlen) == 0) diff --git a/options-table.c b/options-table.c index 5cbec546..62d37c39 100644 --- a/options-table.c +++ b/options-table.c @@ -302,7 +302,7 @@ const struct options_table_entry options_table[] = { .choices = options_table_extended_keys_list, .default_num = 0, .text = "Whether to request extended key sequences from terminals " - "that support it." + "that support it." }, { .name = "focus-events", @@ -801,6 +801,14 @@ const struct options_table_entry options_table[] = { "linked to ('off')." }, + { .name = "allow-passthrough", + .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, + .default_num = 0, + .text = "Whether applications are allowed to use the escape sequence " + "to bypass tmux." + }, + { .name = "allow-rename", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, @@ -989,7 +997,7 @@ const struct options_table_entry options_table[] = { .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." + "these are only supported on terminals with UTF-8 support." }, { .name = "pane-border-status", @@ -1041,7 +1049,7 @@ const struct options_table_entry options_table[] = { .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." + "these are only supported on terminals with UTF-8 support." }, { .name = "remain-on-exit", @@ -1171,7 +1179,7 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_WINDOW, .default_num = 1, .text = "Whether xterm-style function key sequences should be sent. " - "This option is no longer used." + "This option is no longer used." }, /* Hook options. */ @@ -1219,8 +1227,8 @@ const struct options_table_entry options_table[] = { OPTIONS_TABLE_HOOK("client-active", ""), OPTIONS_TABLE_HOOK("client-attached", ""), OPTIONS_TABLE_HOOK("client-detached", ""), - OPTIONS_TABLE_HOOK("client-focus-in", ""), - OPTIONS_TABLE_HOOK("client-focus-out", ""), + OPTIONS_TABLE_HOOK("client-focus-in", ""), + OPTIONS_TABLE_HOOK("client-focus-out", ""), OPTIONS_TABLE_HOOK("client-resized", ""), OPTIONS_TABLE_HOOK("client-session-changed", ""), OPTIONS_TABLE_PANE_HOOK("pane-died", ""), diff --git a/tmux.1 b/tmux.1 index 255f10a9..c9c9f221 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4403,6 +4403,13 @@ The default is on. Available pane options are: .Pp .Bl -tag -width Ds -compact +.It Xo Ic allow-passthrough +.Op Ic on | off +.Xc +Allow programs in the pane to bypass +.Nm +using a terminal escape sequence (\eePtmux;...\ee\e\e). +.Pp .It Xo Ic allow-rename .Op Ic on | off .Xc -- cgit From c7c7e875a0f71451564746a20db8d84c418ad7d5 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 16 Feb 2022 12:26:23 +0000 Subject: No not allow static linking on macOS. --- configure.ac | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/configure.ac b/configure.ac index 26bd1a98..edda0d6b 100644 --- a/configure.ac +++ b/configure.ac @@ -69,6 +69,11 @@ AC_ARG_ENABLE( AS_HELP_STRING(--enable-static, create a static build) ) if test "x$enable_static" = xyes; then + case "$host_os" in + *darwin*) + AC_MSG_ERROR([static linking is not supported on macOS]) + ;; + esac test "x$PKG_CONFIG" != x && PKG_CONFIG="$PKG_CONFIG --static" AM_LDFLAGS="-static $AM_LDFLAGS" LDFLAGS="$AM_LDFLAGS $SAVED_LDFLAGS" -- cgit From 0027ee13a089efe7d1db1a4cfedb9b801635ded8 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 16 Feb 2022 18:55:05 +0000 Subject: Support more mouse buttons when the terminal sends them, GitHub issue 3055. --- input-keys.c | 6 +- key-string.c | 42 ++++ menu.c | 6 +- popup.c | 8 +- server-client.c | 640 +++++++++++++++++++++++++++++++++++++++++++++++++++++--- tmux.h | 66 +++++- tty-keys.c | 4 +- 7 files changed, 728 insertions(+), 44 deletions(-) diff --git a/input-keys.c b/input-keys.c index c2f2fe19..0b618407 100644 --- a/input-keys.c +++ b/input-keys.c @@ -578,13 +578,13 @@ input_key_get_mouse(struct screen *s, struct mouse_event *m, u_int x, u_int y, */ if (m->sgr_type != ' ') { if (MOUSE_DRAG(m->sgr_b) && - MOUSE_BUTTONS(m->sgr_b) == 3 && + MOUSE_RELEASE(m->sgr_b) && (~s->mode & MODE_MOUSE_ALL)) return (0); } else { if (MOUSE_DRAG(m->b) && - MOUSE_BUTTONS(m->b) == 3 && - MOUSE_BUTTONS(m->lb) == 3 && + MOUSE_RELEASE(m->b) && + MOUSE_RELEASE(m->lb) && (~s->mode & MODE_MOUSE_ALL)) return (0); } diff --git a/key-string.c b/key-string.c index 4f7be858..0ca91306 100644 --- a/key-string.c +++ b/key-string.c @@ -91,26 +91,68 @@ static const struct { KEYC_MOUSE_STRING(MOUSEDOWN1, MouseDown1), KEYC_MOUSE_STRING(MOUSEDOWN2, MouseDown2), KEYC_MOUSE_STRING(MOUSEDOWN3, MouseDown3), + KEYC_MOUSE_STRING(MOUSEDOWN6, MouseDown6), + KEYC_MOUSE_STRING(MOUSEDOWN7, MouseDown7), + KEYC_MOUSE_STRING(MOUSEDOWN8, MouseDown8), + KEYC_MOUSE_STRING(MOUSEDOWN9, MouseDown9), + KEYC_MOUSE_STRING(MOUSEDOWN10, MouseDown10), + KEYC_MOUSE_STRING(MOUSEDOWN11, MouseDown11), KEYC_MOUSE_STRING(MOUSEUP1, MouseUp1), KEYC_MOUSE_STRING(MOUSEUP2, MouseUp2), KEYC_MOUSE_STRING(MOUSEUP3, MouseUp3), + KEYC_MOUSE_STRING(MOUSEUP6, MouseUp6), + KEYC_MOUSE_STRING(MOUSEUP7, MouseUp7), + KEYC_MOUSE_STRING(MOUSEUP8, MouseUp8), + KEYC_MOUSE_STRING(MOUSEUP9, MouseUp9), + KEYC_MOUSE_STRING(MOUSEUP10, MouseUp10), + KEYC_MOUSE_STRING(MOUSEUP11, MouseUp11), KEYC_MOUSE_STRING(MOUSEDRAG1, MouseDrag1), KEYC_MOUSE_STRING(MOUSEDRAG2, MouseDrag2), KEYC_MOUSE_STRING(MOUSEDRAG3, MouseDrag3), + KEYC_MOUSE_STRING(MOUSEDRAG6, MouseDrag6), + KEYC_MOUSE_STRING(MOUSEDRAG7, MouseDrag7), + KEYC_MOUSE_STRING(MOUSEDRAG8, MouseDrag8), + KEYC_MOUSE_STRING(MOUSEDRAG9, MouseDrag9), + KEYC_MOUSE_STRING(MOUSEDRAG10, MouseDrag10), + KEYC_MOUSE_STRING(MOUSEDRAG11, MouseDrag11), KEYC_MOUSE_STRING(MOUSEDRAGEND1, MouseDragEnd1), KEYC_MOUSE_STRING(MOUSEDRAGEND2, MouseDragEnd2), KEYC_MOUSE_STRING(MOUSEDRAGEND3, MouseDragEnd3), + KEYC_MOUSE_STRING(MOUSEDRAGEND6, MouseDragEnd6), + KEYC_MOUSE_STRING(MOUSEDRAGEND7, MouseDragEnd7), + KEYC_MOUSE_STRING(MOUSEDRAGEND8, MouseDragEnd8), + KEYC_MOUSE_STRING(MOUSEDRAGEND9, MouseDragEnd9), + KEYC_MOUSE_STRING(MOUSEDRAGEND10, MouseDragEnd10), + KEYC_MOUSE_STRING(MOUSEDRAGEND11, MouseDragEnd11), KEYC_MOUSE_STRING(WHEELUP, WheelUp), KEYC_MOUSE_STRING(WHEELDOWN, WheelDown), KEYC_MOUSE_STRING(SECONDCLICK1, SecondClick1), KEYC_MOUSE_STRING(SECONDCLICK2, SecondClick2), KEYC_MOUSE_STRING(SECONDCLICK3, SecondClick3), + KEYC_MOUSE_STRING(SECONDCLICK6, SecondClick6), + KEYC_MOUSE_STRING(SECONDCLICK7, SecondClick7), + KEYC_MOUSE_STRING(SECONDCLICK8, SecondClick8), + KEYC_MOUSE_STRING(SECONDCLICK9, SecondClick9), + KEYC_MOUSE_STRING(SECONDCLICK10, SecondClick10), + KEYC_MOUSE_STRING(SECONDCLICK11, SecondClick11), KEYC_MOUSE_STRING(DOUBLECLICK1, DoubleClick1), KEYC_MOUSE_STRING(DOUBLECLICK2, DoubleClick2), KEYC_MOUSE_STRING(DOUBLECLICK3, DoubleClick3), + KEYC_MOUSE_STRING(DOUBLECLICK6, DoubleClick6), + KEYC_MOUSE_STRING(DOUBLECLICK7, DoubleClick7), + KEYC_MOUSE_STRING(DOUBLECLICK8, DoubleClick8), + KEYC_MOUSE_STRING(DOUBLECLICK9, DoubleClick9), + KEYC_MOUSE_STRING(DOUBLECLICK10, DoubleClick10), + KEYC_MOUSE_STRING(DOUBLECLICK11, DoubleClick11), KEYC_MOUSE_STRING(TRIPLECLICK1, TripleClick1), KEYC_MOUSE_STRING(TRIPLECLICK2, TripleClick2), KEYC_MOUSE_STRING(TRIPLECLICK3, TripleClick3), + KEYC_MOUSE_STRING(TRIPLECLICK6, TripleClick6), + KEYC_MOUSE_STRING(TRIPLECLICK7, TripleClick7), + KEYC_MOUSE_STRING(TRIPLECLICK8, TripleClick8), + KEYC_MOUSE_STRING(TRIPLECLICK9, TripleClick9), + KEYC_MOUSE_STRING(TRIPLECLICK10, TripleClick10), + KEYC_MOUSE_STRING(TRIPLECLICK11, TripleClick11) }; /* Find key string in table. */ diff --git a/menu.c b/menu.c index aaa1287e..c17d10b1 100644 --- a/menu.c +++ b/menu.c @@ -235,7 +235,7 @@ menu_key_cb(struct client *c, void *data, struct key_event *event) if (KEYC_IS_MOUSE(event->key)) { if (md->flags & MENU_NOMOUSE) { - if (MOUSE_BUTTONS(m->b) != 0) + if (MOUSE_BUTTONS(m->b) != MOUSE_BUTTON_1) return (1); return (0); } @@ -248,7 +248,7 @@ menu_key_cb(struct client *c, void *data, struct key_event *event) return (1); } else { if (!MOUSE_RELEASE(m->b) && - MOUSE_WHEEL(m->b) == 0 && + !MOUSE_WHEEL(m->b) && !MOUSE_DRAG(m->b)) return (1); } @@ -262,7 +262,7 @@ menu_key_cb(struct client *c, void *data, struct key_event *event) if (MOUSE_RELEASE(m->b)) goto chosen; } else { - if (MOUSE_WHEEL(m->b) == 0 && !MOUSE_DRAG(m->b)) + if (!MOUSE_WHEEL(m->b) && !MOUSE_DRAG(m->b)) goto chosen; } md->choice = m->y - (md->py + 1); diff --git a/popup.c b/popup.c index 4b4c58ca..a3b33dce 100644 --- a/popup.c +++ b/popup.c @@ -509,7 +509,7 @@ popup_key_cb(struct client *c, void *data, struct key_event *event) m->x > pd->px + pd->sx - 1 || m->y < pd->py || m->y > pd->py + pd->sy - 1) { - if (MOUSE_BUTTONS(m->b) == 2) + if (MOUSE_BUTTONS(m->b) == MOUSE_BUTTON_3) goto menu; return (0); } @@ -524,16 +524,16 @@ popup_key_cb(struct client *c, void *data, struct key_event *event) border = BOTTOM; } if ((m->b & MOUSE_MASK_MODIFIERS) == 0 && - MOUSE_BUTTONS(m->b) == 2 && + MOUSE_BUTTONS(m->b) == MOUSE_BUTTON_3 && (border == LEFT || border == TOP)) goto menu; if (((m->b & MOUSE_MASK_MODIFIERS) == MOUSE_MASK_META) || border != NONE) { if (!MOUSE_DRAG(m->b)) goto out; - if (MOUSE_BUTTONS(m->lb) == 0) + if (MOUSE_BUTTONS(m->lb) == MOUSE_BUTTON_1) pd->dragging = MOVE; - else if (MOUSE_BUTTONS(m->lb) == 2) + else if (MOUSE_BUTTONS(m->lb) == MOUSE_BUTTON_3) pd->dragging = SIZE; pd->dx = m->lx - pd->px; pd->dy = m->ly - pd->py; diff --git a/server-client.c b/server-client.c index 072c5589..f95088f0 100644 --- a/server-client.c +++ b/server-client.c @@ -596,11 +596,11 @@ server_client_check_mouse(struct client *c, struct key_event *event) log_debug("double-click at %u,%u", x, y); } else if ((m->sgr_type != ' ' && MOUSE_DRAG(m->sgr_b) && - MOUSE_BUTTONS(m->sgr_b) == 3) || + MOUSE_RELEASE(m->sgr_b)) || (m->sgr_type == ' ' && MOUSE_DRAG(m->b) && - MOUSE_BUTTONS(m->b) == 3 && - MOUSE_BUTTONS(m->lb) == 3)) { + MOUSE_RELEASE(m->b) && + MOUSE_RELEASE(m->lb))) { type = MOVE; x = m->x, y = m->y, b = 0; log_debug("move at %u,%u", x, y); @@ -753,7 +753,7 @@ have_event: m->wp = -1; /* Stop dragging if needed. */ - if (type != DRAG && type != WHEEL && c->tty.mouse_drag_flag) { + if (type != DRAG && type != WHEEL && c->tty.mouse_drag_flag != 0) { if (c->tty.mouse_drag_release != NULL) c->tty.mouse_drag_release(c, m); @@ -764,8 +764,8 @@ have_event: * End a mouse drag by passing a MouseDragEnd key corresponding * to the button that started the drag. */ - switch (c->tty.mouse_drag_flag) { - case 1: + switch (c->tty.mouse_drag_flag - 1) { + case MOUSE_BUTTON_1: if (where == PANE) key = KEYC_MOUSEDRAGEND1_PANE; if (where == STATUS) @@ -779,7 +779,7 @@ have_event: if (where == BORDER) key = KEYC_MOUSEDRAGEND1_BORDER; break; - case 2: + case MOUSE_BUTTON_2: if (where == PANE) key = KEYC_MOUSEDRAGEND2_PANE; if (where == STATUS) @@ -793,7 +793,7 @@ have_event: if (where == BORDER) key = KEYC_MOUSEDRAGEND2_BORDER; break; - case 3: + case MOUSE_BUTTON_3: if (where == PANE) key = KEYC_MOUSEDRAGEND3_PANE; if (where == STATUS) @@ -807,6 +807,90 @@ have_event: if (where == BORDER) key = KEYC_MOUSEDRAGEND3_BORDER; break; + case MOUSE_BUTTON_6: + if (where == PANE) + key = KEYC_MOUSEDRAGEND6_PANE; + if (where == STATUS) + key = KEYC_MOUSEDRAGEND6_STATUS; + if (where == STATUS_LEFT) + key = KEYC_MOUSEDRAGEND6_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_MOUSEDRAGEND6_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_MOUSEDRAGEND6_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_MOUSEDRAGEND6_BORDER; + break; + case MOUSE_BUTTON_7: + if (where == PANE) + key = KEYC_MOUSEDRAGEND7_PANE; + if (where == STATUS) + key = KEYC_MOUSEDRAGEND7_STATUS; + if (where == STATUS_LEFT) + key = KEYC_MOUSEDRAGEND7_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_MOUSEDRAGEND7_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_MOUSEDRAGEND7_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_MOUSEDRAGEND7_BORDER; + break; + case MOUSE_BUTTON_8: + if (where == PANE) + key = KEYC_MOUSEDRAGEND8_PANE; + if (where == STATUS) + key = KEYC_MOUSEDRAGEND8_STATUS; + if (where == STATUS_LEFT) + key = KEYC_MOUSEDRAGEND8_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_MOUSEDRAGEND8_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_MOUSEDRAGEND8_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_MOUSEDRAGEND8_BORDER; + break; + case MOUSE_BUTTON_9: + if (where == PANE) + key = KEYC_MOUSEDRAGEND9_PANE; + if (where == STATUS) + key = KEYC_MOUSEDRAGEND9_STATUS; + if (where == STATUS_LEFT) + key = KEYC_MOUSEDRAGEND9_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_MOUSEDRAGEND9_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_MOUSEDRAGEND9_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_MOUSEDRAGEND9_BORDER; + break; + case MOUSE_BUTTON_10: + if (where == PANE) + key = KEYC_MOUSEDRAGEND10_PANE; + if (where == STATUS) + key = KEYC_MOUSEDRAGEND10_STATUS; + if (where == STATUS_LEFT) + key = KEYC_MOUSEDRAGEND10_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_MOUSEDRAGEND10_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_MOUSEDRAGEND10_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_MOUSEDRAGEND10_BORDER; + break; + case MOUSE_BUTTON_11: + if (where == PANE) + key = KEYC_MOUSEDRAGEND11_PANE; + if (where == STATUS) + key = KEYC_MOUSEDRAGEND11_STATUS; + if (where == STATUS_LEFT) + key = KEYC_MOUSEDRAGEND11_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_MOUSEDRAGEND11_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_MOUSEDRAGEND11_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_MOUSEDRAGEND11_BORDER; + break; default: key = KEYC_MOUSE; break; @@ -839,7 +923,7 @@ have_event: key = KEYC_DRAGGING; else { switch (MOUSE_BUTTONS(b)) { - case 0: + case MOUSE_BUTTON_1: if (where == PANE) key = KEYC_MOUSEDRAG1_PANE; if (where == STATUS) @@ -853,7 +937,7 @@ have_event: if (where == BORDER) key = KEYC_MOUSEDRAG1_BORDER; break; - case 1: + case MOUSE_BUTTON_2: if (where == PANE) key = KEYC_MOUSEDRAG2_PANE; if (where == STATUS) @@ -867,7 +951,7 @@ have_event: if (where == BORDER) key = KEYC_MOUSEDRAG2_BORDER; break; - case 2: + case MOUSE_BUTTON_3: if (where == PANE) key = KEYC_MOUSEDRAG3_PANE; if (where == STATUS) @@ -881,6 +965,90 @@ have_event: if (where == BORDER) key = KEYC_MOUSEDRAG3_BORDER; break; + case MOUSE_BUTTON_6: + if (where == PANE) + key = KEYC_MOUSEDRAG6_PANE; + if (where == STATUS) + key = KEYC_MOUSEDRAG6_STATUS; + if (where == STATUS_LEFT) + key = KEYC_MOUSEDRAG6_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_MOUSEDRAG6_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_MOUSEDRAG6_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_MOUSEDRAG6_BORDER; + break; + case MOUSE_BUTTON_7: + if (where == PANE) + key = KEYC_MOUSEDRAG7_PANE; + if (where == STATUS) + key = KEYC_MOUSEDRAG7_STATUS; + if (where == STATUS_LEFT) + key = KEYC_MOUSEDRAG7_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_MOUSEDRAG7_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_MOUSEDRAG7_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_MOUSEDRAG7_BORDER; + break; + case MOUSE_BUTTON_8: + if (where == PANE) + key = KEYC_MOUSEDRAG8_PANE; + if (where == STATUS) + key = KEYC_MOUSEDRAG8_STATUS; + if (where == STATUS_LEFT) + key = KEYC_MOUSEDRAG8_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_MOUSEDRAG8_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_MOUSEDRAG8_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_MOUSEDRAG8_BORDER; + break; + case MOUSE_BUTTON_9: + if (where == PANE) + key = KEYC_MOUSEDRAG9_PANE; + if (where == STATUS) + key = KEYC_MOUSEDRAG9_STATUS; + if (where == STATUS_LEFT) + key = KEYC_MOUSEDRAG9_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_MOUSEDRAG9_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_MOUSEDRAG9_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_MOUSEDRAG9_BORDER; + break; + case MOUSE_BUTTON_10: + if (where == PANE) + key = KEYC_MOUSEDRAG10_PANE; + if (where == STATUS) + key = KEYC_MOUSEDRAG10_STATUS; + if (where == STATUS_LEFT) + key = KEYC_MOUSEDRAG10_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_MOUSEDRAG10_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_MOUSEDRAG10_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_MOUSEDRAG10_BORDER; + break; + case MOUSE_BUTTON_11: + if (where == PANE) + key = KEYC_MOUSEDRAG11_PANE; + if (where == STATUS) + key = KEYC_MOUSEDRAG11_STATUS; + if (where == STATUS_LEFT) + key = KEYC_MOUSEDRAG11_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_MOUSEDRAG11_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_MOUSEDRAG11_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_MOUSEDRAG11_BORDER; + break; } } @@ -921,7 +1089,7 @@ have_event: break; case UP: switch (MOUSE_BUTTONS(b)) { - case 0: + case MOUSE_BUTTON_1: if (where == PANE) key = KEYC_MOUSEUP1_PANE; if (where == STATUS) @@ -935,7 +1103,7 @@ have_event: if (where == BORDER) key = KEYC_MOUSEUP1_BORDER; break; - case 1: + case MOUSE_BUTTON_2: if (where == PANE) key = KEYC_MOUSEUP2_PANE; if (where == STATUS) @@ -949,7 +1117,7 @@ have_event: if (where == BORDER) key = KEYC_MOUSEUP2_BORDER; break; - case 2: + case MOUSE_BUTTON_3: if (where == PANE) key = KEYC_MOUSEUP3_PANE; if (where == STATUS) @@ -963,11 +1131,95 @@ have_event: if (where == BORDER) key = KEYC_MOUSEUP3_BORDER; break; + case MOUSE_BUTTON_6: + if (where == PANE) + key = KEYC_MOUSEUP6_PANE; + if (where == STATUS) + key = KEYC_MOUSEUP6_STATUS; + if (where == STATUS_LEFT) + key = KEYC_MOUSEUP6_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_MOUSEUP6_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_MOUSEUP6_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_MOUSEUP6_BORDER; + break; + case MOUSE_BUTTON_7: + if (where == PANE) + key = KEYC_MOUSEUP7_PANE; + if (where == STATUS) + key = KEYC_MOUSEUP7_STATUS; + if (where == STATUS_LEFT) + key = KEYC_MOUSEUP7_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_MOUSEUP7_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_MOUSEUP7_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_MOUSEUP7_BORDER; + break; + case MOUSE_BUTTON_8: + if (where == PANE) + key = KEYC_MOUSEUP8_PANE; + if (where == STATUS) + key = KEYC_MOUSEUP8_STATUS; + if (where == STATUS_LEFT) + key = KEYC_MOUSEUP8_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_MOUSEUP8_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_MOUSEUP8_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_MOUSEUP8_BORDER; + break; + case MOUSE_BUTTON_9: + if (where == PANE) + key = KEYC_MOUSEUP9_PANE; + if (where == STATUS) + key = KEYC_MOUSEUP9_STATUS; + if (where == STATUS_LEFT) + key = KEYC_MOUSEUP9_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_MOUSEUP9_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_MOUSEUP9_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_MOUSEUP9_BORDER; + break; + case MOUSE_BUTTON_10: + if (where == PANE) + key = KEYC_MOUSEUP1_PANE; + if (where == STATUS) + key = KEYC_MOUSEUP1_STATUS; + if (where == STATUS_LEFT) + key = KEYC_MOUSEUP1_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_MOUSEUP1_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_MOUSEUP1_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_MOUSEUP1_BORDER; + break; + case MOUSE_BUTTON_11: + if (where == PANE) + key = KEYC_MOUSEUP11_PANE; + if (where == STATUS) + key = KEYC_MOUSEUP11_STATUS; + if (where == STATUS_LEFT) + key = KEYC_MOUSEUP11_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_MOUSEUP11_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_MOUSEUP11_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_MOUSEUP11_BORDER; + break; } break; case DOWN: switch (MOUSE_BUTTONS(b)) { - case 0: + case MOUSE_BUTTON_1: if (where == PANE) key = KEYC_MOUSEDOWN1_PANE; if (where == STATUS) @@ -981,7 +1233,7 @@ have_event: if (where == BORDER) key = KEYC_MOUSEDOWN1_BORDER; break; - case 1: + case MOUSE_BUTTON_2: if (where == PANE) key = KEYC_MOUSEDOWN2_PANE; if (where == STATUS) @@ -995,7 +1247,7 @@ have_event: if (where == BORDER) key = KEYC_MOUSEDOWN2_BORDER; break; - case 2: + case MOUSE_BUTTON_3: if (where == PANE) key = KEYC_MOUSEDOWN3_PANE; if (where == STATUS) @@ -1009,11 +1261,95 @@ have_event: if (where == BORDER) key = KEYC_MOUSEDOWN3_BORDER; break; + case MOUSE_BUTTON_6: + if (where == PANE) + key = KEYC_MOUSEDOWN6_PANE; + if (where == STATUS) + key = KEYC_MOUSEDOWN6_STATUS; + if (where == STATUS_LEFT) + key = KEYC_MOUSEDOWN6_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_MOUSEDOWN6_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_MOUSEDOWN6_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_MOUSEDOWN6_BORDER; + break; + case MOUSE_BUTTON_7: + if (where == PANE) + key = KEYC_MOUSEDOWN7_PANE; + if (where == STATUS) + key = KEYC_MOUSEDOWN7_STATUS; + if (where == STATUS_LEFT) + key = KEYC_MOUSEDOWN7_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_MOUSEDOWN7_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_MOUSEDOWN7_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_MOUSEDOWN7_BORDER; + break; + case MOUSE_BUTTON_8: + if (where == PANE) + key = KEYC_MOUSEDOWN8_PANE; + if (where == STATUS) + key = KEYC_MOUSEDOWN8_STATUS; + if (where == STATUS_LEFT) + key = KEYC_MOUSEDOWN8_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_MOUSEDOWN8_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_MOUSEDOWN8_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_MOUSEDOWN8_BORDER; + break; + case MOUSE_BUTTON_9: + if (where == PANE) + key = KEYC_MOUSEDOWN9_PANE; + if (where == STATUS) + key = KEYC_MOUSEDOWN9_STATUS; + if (where == STATUS_LEFT) + key = KEYC_MOUSEDOWN9_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_MOUSEDOWN9_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_MOUSEDOWN9_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_MOUSEDOWN9_BORDER; + break; + case MOUSE_BUTTON_10: + if (where == PANE) + key = KEYC_MOUSEDOWN10_PANE; + if (where == STATUS) + key = KEYC_MOUSEDOWN10_STATUS; + if (where == STATUS_LEFT) + key = KEYC_MOUSEDOWN10_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_MOUSEDOWN10_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_MOUSEDOWN10_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_MOUSEDOWN10_BORDER; + break; + case MOUSE_BUTTON_11: + if (where == PANE) + key = KEYC_MOUSEDOWN11_PANE; + if (where == STATUS) + key = KEYC_MOUSEDOWN11_STATUS; + if (where == STATUS_LEFT) + key = KEYC_MOUSEDOWN11_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_MOUSEDOWN11_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_MOUSEDOWN11_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_MOUSEDOWN11_BORDER; + break; } break; case SECOND: switch (MOUSE_BUTTONS(b)) { - case 0: + case MOUSE_BUTTON_1: if (where == PANE) key = KEYC_SECONDCLICK1_PANE; if (where == STATUS) @@ -1027,7 +1363,7 @@ have_event: if (where == BORDER) key = KEYC_SECONDCLICK1_BORDER; break; - case 1: + case MOUSE_BUTTON_2: if (where == PANE) key = KEYC_SECONDCLICK2_PANE; if (where == STATUS) @@ -1041,7 +1377,7 @@ have_event: if (where == BORDER) key = KEYC_SECONDCLICK2_BORDER; break; - case 2: + case MOUSE_BUTTON_3: if (where == PANE) key = KEYC_SECONDCLICK3_PANE; if (where == STATUS) @@ -1055,11 +1391,95 @@ have_event: if (where == BORDER) key = KEYC_SECONDCLICK3_BORDER; break; + case MOUSE_BUTTON_6: + if (where == PANE) + key = KEYC_SECONDCLICK6_PANE; + if (where == STATUS) + key = KEYC_SECONDCLICK6_STATUS; + if (where == STATUS_LEFT) + key = KEYC_SECONDCLICK6_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_SECONDCLICK6_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_SECONDCLICK6_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_SECONDCLICK6_BORDER; + break; + case MOUSE_BUTTON_7: + if (where == PANE) + key = KEYC_SECONDCLICK7_PANE; + if (where == STATUS) + key = KEYC_SECONDCLICK7_STATUS; + if (where == STATUS_LEFT) + key = KEYC_SECONDCLICK7_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_SECONDCLICK7_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_SECONDCLICK7_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_SECONDCLICK7_BORDER; + break; + case MOUSE_BUTTON_8: + if (where == PANE) + key = KEYC_SECONDCLICK8_PANE; + if (where == STATUS) + key = KEYC_SECONDCLICK8_STATUS; + if (where == STATUS_LEFT) + key = KEYC_SECONDCLICK8_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_SECONDCLICK8_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_SECONDCLICK8_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_SECONDCLICK8_BORDER; + break; + case MOUSE_BUTTON_9: + if (where == PANE) + key = KEYC_SECONDCLICK9_PANE; + if (where == STATUS) + key = KEYC_SECONDCLICK9_STATUS; + if (where == STATUS_LEFT) + key = KEYC_SECONDCLICK9_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_SECONDCLICK9_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_SECONDCLICK9_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_SECONDCLICK9_BORDER; + break; + case MOUSE_BUTTON_10: + if (where == PANE) + key = KEYC_SECONDCLICK10_PANE; + if (where == STATUS) + key = KEYC_SECONDCLICK10_STATUS; + if (where == STATUS_LEFT) + key = KEYC_SECONDCLICK10_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_SECONDCLICK10_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_SECONDCLICK10_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_SECONDCLICK10_BORDER; + break; + case MOUSE_BUTTON_11: + if (where == PANE) + key = KEYC_SECONDCLICK11_PANE; + if (where == STATUS) + key = KEYC_SECONDCLICK11_STATUS; + if (where == STATUS_LEFT) + key = KEYC_SECONDCLICK11_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_SECONDCLICK11_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_SECONDCLICK11_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_SECONDCLICK11_BORDER; + break; } break; case DOUBLE: switch (MOUSE_BUTTONS(b)) { - case 0: + case MOUSE_BUTTON_1: if (where == PANE) key = KEYC_DOUBLECLICK1_PANE; if (where == STATUS) @@ -1073,7 +1493,7 @@ have_event: if (where == BORDER) key = KEYC_DOUBLECLICK1_BORDER; break; - case 1: + case MOUSE_BUTTON_2: if (where == PANE) key = KEYC_DOUBLECLICK2_PANE; if (where == STATUS) @@ -1087,7 +1507,7 @@ have_event: if (where == BORDER) key = KEYC_DOUBLECLICK2_BORDER; break; - case 2: + case MOUSE_BUTTON_3: if (where == PANE) key = KEYC_DOUBLECLICK3_PANE; if (where == STATUS) @@ -1101,11 +1521,95 @@ have_event: if (where == BORDER) key = KEYC_DOUBLECLICK3_BORDER; break; + case MOUSE_BUTTON_6: + if (where == PANE) + key = KEYC_DOUBLECLICK6_PANE; + if (where == STATUS) + key = KEYC_DOUBLECLICK6_STATUS; + if (where == STATUS_LEFT) + key = KEYC_DOUBLECLICK6_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_DOUBLECLICK6_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_DOUBLECLICK6_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_DOUBLECLICK6_BORDER; + break; + case MOUSE_BUTTON_7: + if (where == PANE) + key = KEYC_DOUBLECLICK7_PANE; + if (where == STATUS) + key = KEYC_DOUBLECLICK7_STATUS; + if (where == STATUS_LEFT) + key = KEYC_DOUBLECLICK7_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_DOUBLECLICK7_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_DOUBLECLICK7_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_DOUBLECLICK7_BORDER; + break; + case MOUSE_BUTTON_8: + if (where == PANE) + key = KEYC_DOUBLECLICK8_PANE; + if (where == STATUS) + key = KEYC_DOUBLECLICK8_STATUS; + if (where == STATUS_LEFT) + key = KEYC_DOUBLECLICK8_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_DOUBLECLICK8_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_DOUBLECLICK8_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_DOUBLECLICK8_BORDER; + break; + case MOUSE_BUTTON_9: + if (where == PANE) + key = KEYC_DOUBLECLICK9_PANE; + if (where == STATUS) + key = KEYC_DOUBLECLICK9_STATUS; + if (where == STATUS_LEFT) + key = KEYC_DOUBLECLICK9_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_DOUBLECLICK9_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_DOUBLECLICK9_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_DOUBLECLICK9_BORDER; + break; + case MOUSE_BUTTON_10: + if (where == PANE) + key = KEYC_DOUBLECLICK10_PANE; + if (where == STATUS) + key = KEYC_DOUBLECLICK10_STATUS; + if (where == STATUS_LEFT) + key = KEYC_DOUBLECLICK10_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_DOUBLECLICK10_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_DOUBLECLICK10_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_DOUBLECLICK10_BORDER; + break; + case MOUSE_BUTTON_11: + if (where == PANE) + key = KEYC_DOUBLECLICK11_PANE; + if (where == STATUS) + key = KEYC_DOUBLECLICK11_STATUS; + if (where == STATUS_LEFT) + key = KEYC_DOUBLECLICK11_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_DOUBLECLICK11_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_DOUBLECLICK11_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_DOUBLECLICK11_BORDER; + break; } break; case TRIPLE: switch (MOUSE_BUTTONS(b)) { - case 0: + case MOUSE_BUTTON_1: if (where == PANE) key = KEYC_TRIPLECLICK1_PANE; if (where == STATUS) @@ -1119,7 +1623,7 @@ have_event: if (where == BORDER) key = KEYC_TRIPLECLICK1_BORDER; break; - case 1: + case MOUSE_BUTTON_2: if (where == PANE) key = KEYC_TRIPLECLICK2_PANE; if (where == STATUS) @@ -1133,7 +1637,7 @@ have_event: if (where == BORDER) key = KEYC_TRIPLECLICK2_BORDER; break; - case 2: + case MOUSE_BUTTON_3: if (where == PANE) key = KEYC_TRIPLECLICK3_PANE; if (where == STATUS) @@ -1147,6 +1651,90 @@ have_event: if (where == BORDER) key = KEYC_TRIPLECLICK3_BORDER; break; + case MOUSE_BUTTON_6: + if (where == PANE) + key = KEYC_TRIPLECLICK6_PANE; + if (where == STATUS) + key = KEYC_TRIPLECLICK6_STATUS; + if (where == STATUS_LEFT) + key = KEYC_TRIPLECLICK6_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_TRIPLECLICK6_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_TRIPLECLICK6_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_TRIPLECLICK6_BORDER; + break; + case MOUSE_BUTTON_7: + if (where == PANE) + key = KEYC_TRIPLECLICK7_PANE; + if (where == STATUS) + key = KEYC_TRIPLECLICK7_STATUS; + if (where == STATUS_LEFT) + key = KEYC_TRIPLECLICK7_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_TRIPLECLICK7_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_TRIPLECLICK7_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_TRIPLECLICK7_BORDER; + break; + case MOUSE_BUTTON_8: + if (where == PANE) + key = KEYC_TRIPLECLICK8_PANE; + if (where == STATUS) + key = KEYC_TRIPLECLICK8_STATUS; + if (where == STATUS_LEFT) + key = KEYC_TRIPLECLICK8_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_TRIPLECLICK8_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_TRIPLECLICK8_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_TRIPLECLICK8_BORDER; + break; + case MOUSE_BUTTON_9: + if (where == PANE) + key = KEYC_TRIPLECLICK9_PANE; + if (where == STATUS) + key = KEYC_TRIPLECLICK9_STATUS; + if (where == STATUS_LEFT) + key = KEYC_TRIPLECLICK9_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_TRIPLECLICK9_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_TRIPLECLICK9_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_TRIPLECLICK9_BORDER; + break; + case MOUSE_BUTTON_10: + if (where == PANE) + key = KEYC_TRIPLECLICK10_PANE; + if (where == STATUS) + key = KEYC_TRIPLECLICK10_STATUS; + if (where == STATUS_LEFT) + key = KEYC_TRIPLECLICK10_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_TRIPLECLICK10_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_TRIPLECLICK10_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_TRIPLECLICK10_BORDER; + break; + case MOUSE_BUTTON_11: + if (where == PANE) + key = KEYC_TRIPLECLICK11_PANE; + if (where == STATUS) + key = KEYC_TRIPLECLICK11_STATUS; + if (where == STATUS_LEFT) + key = KEYC_TRIPLECLICK11_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_TRIPLECLICK11_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_TRIPLECLICK11_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_TRIPLECLICK11_BORDER; + break; } break; } diff --git a/tmux.h b/tmux.h index bc87d098..fa5b6b50 100644 --- a/tmux.h +++ b/tmux.h @@ -201,26 +201,68 @@ enum { KEYC_MOUSE_KEY(MOUSEDOWN1), KEYC_MOUSE_KEY(MOUSEDOWN2), KEYC_MOUSE_KEY(MOUSEDOWN3), + KEYC_MOUSE_KEY(MOUSEDOWN6), + KEYC_MOUSE_KEY(MOUSEDOWN7), + KEYC_MOUSE_KEY(MOUSEDOWN8), + KEYC_MOUSE_KEY(MOUSEDOWN9), + KEYC_MOUSE_KEY(MOUSEDOWN10), + KEYC_MOUSE_KEY(MOUSEDOWN11), KEYC_MOUSE_KEY(MOUSEUP1), KEYC_MOUSE_KEY(MOUSEUP2), KEYC_MOUSE_KEY(MOUSEUP3), + KEYC_MOUSE_KEY(MOUSEUP6), + KEYC_MOUSE_KEY(MOUSEUP7), + KEYC_MOUSE_KEY(MOUSEUP8), + KEYC_MOUSE_KEY(MOUSEUP9), + KEYC_MOUSE_KEY(MOUSEUP10), + KEYC_MOUSE_KEY(MOUSEUP11), KEYC_MOUSE_KEY(MOUSEDRAG1), KEYC_MOUSE_KEY(MOUSEDRAG2), KEYC_MOUSE_KEY(MOUSEDRAG3), + KEYC_MOUSE_KEY(MOUSEDRAG6), + KEYC_MOUSE_KEY(MOUSEDRAG7), + KEYC_MOUSE_KEY(MOUSEDRAG8), + KEYC_MOUSE_KEY(MOUSEDRAG9), + KEYC_MOUSE_KEY(MOUSEDRAG10), + KEYC_MOUSE_KEY(MOUSEDRAG11), KEYC_MOUSE_KEY(MOUSEDRAGEND1), KEYC_MOUSE_KEY(MOUSEDRAGEND2), KEYC_MOUSE_KEY(MOUSEDRAGEND3), + KEYC_MOUSE_KEY(MOUSEDRAGEND6), + KEYC_MOUSE_KEY(MOUSEDRAGEND7), + KEYC_MOUSE_KEY(MOUSEDRAGEND8), + KEYC_MOUSE_KEY(MOUSEDRAGEND9), + KEYC_MOUSE_KEY(MOUSEDRAGEND10), + KEYC_MOUSE_KEY(MOUSEDRAGEND11), KEYC_MOUSE_KEY(WHEELUP), KEYC_MOUSE_KEY(WHEELDOWN), KEYC_MOUSE_KEY(SECONDCLICK1), KEYC_MOUSE_KEY(SECONDCLICK2), KEYC_MOUSE_KEY(SECONDCLICK3), + KEYC_MOUSE_KEY(SECONDCLICK6), + KEYC_MOUSE_KEY(SECONDCLICK7), + KEYC_MOUSE_KEY(SECONDCLICK8), + KEYC_MOUSE_KEY(SECONDCLICK9), + KEYC_MOUSE_KEY(SECONDCLICK10), + KEYC_MOUSE_KEY(SECONDCLICK11), KEYC_MOUSE_KEY(DOUBLECLICK1), KEYC_MOUSE_KEY(DOUBLECLICK2), KEYC_MOUSE_KEY(DOUBLECLICK3), + KEYC_MOUSE_KEY(DOUBLECLICK6), + KEYC_MOUSE_KEY(DOUBLECLICK7), + KEYC_MOUSE_KEY(DOUBLECLICK8), + KEYC_MOUSE_KEY(DOUBLECLICK9), + KEYC_MOUSE_KEY(DOUBLECLICK10), + KEYC_MOUSE_KEY(DOUBLECLICK11), KEYC_MOUSE_KEY(TRIPLECLICK1), KEYC_MOUSE_KEY(TRIPLECLICK2), KEYC_MOUSE_KEY(TRIPLECLICK3), + KEYC_MOUSE_KEY(TRIPLECLICK6), + KEYC_MOUSE_KEY(TRIPLECLICK7), + KEYC_MOUSE_KEY(TRIPLECLICK8), + KEYC_MOUSE_KEY(TRIPLECLICK9), + KEYC_MOUSE_KEY(TRIPLECLICK10), + KEYC_MOUSE_KEY(TRIPLECLICK11), /* Backspace key. */ KEYC_BSPACE, @@ -1198,21 +1240,33 @@ struct session { RB_HEAD(sessions, session); /* Mouse button masks. */ -#define MOUSE_MASK_BUTTONS 3 +#define MOUSE_MASK_BUTTONS 195 #define MOUSE_MASK_SHIFT 4 #define MOUSE_MASK_META 8 #define MOUSE_MASK_CTRL 16 #define MOUSE_MASK_DRAG 32 -#define MOUSE_MASK_WHEEL 64 #define MOUSE_MASK_MODIFIERS (MOUSE_MASK_SHIFT|MOUSE_MASK_META|MOUSE_MASK_CTRL) -/* Mouse wheel states. */ -#define MOUSE_WHEEL_UP 0 -#define MOUSE_WHEEL_DOWN 1 +/* Mouse wheel type. */ +#define MOUSE_WHEEL_UP 64 +#define MOUSE_WHEEL_DOWN 65 + +/* Mouse button type. */ +#define MOUSE_BUTTON_1 0 +#define MOUSE_BUTTON_2 1 +#define MOUSE_BUTTON_3 2 +#define MOUSE_BUTTON_6 66 +#define MOUSE_BUTTON_7 67 +#define MOUSE_BUTTON_8 128 +#define MOUSE_BUTTON_9 129 +#define MOUSE_BUTTON_10 130 +#define MOUSE_BUTTON_11 131 /* Mouse helpers. */ #define MOUSE_BUTTONS(b) ((b) & MOUSE_MASK_BUTTONS) -#define MOUSE_WHEEL(b) ((b) & MOUSE_MASK_WHEEL) +#define MOUSE_WHEEL(b) \ + (((b) & MOUSE_MASK_BUTTONS) == MOUSE_WHEEL_UP || \ + ((b) & MOUSE_MASK_BUTTONS) == MOUSE_WHEEL_DOWN) #define MOUSE_DRAG(b) ((b) & MOUSE_MASK_DRAG) #define MOUSE_RELEASE(b) (((b) & MOUSE_MASK_BUTTONS) == 3) diff --git a/tty-keys.c b/tty-keys.c index 5d950078..30717af3 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -1118,7 +1118,7 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size, /* Type is M for press, m for release. */ sgr_type = ch; if (sgr_type == 'm') - b |= 3; + b = 3; /* * Some terminals (like PuTTY 0.63) mistakenly send @@ -1126,7 +1126,7 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size, * Discard it before it reaches any program running inside * tmux. */ - if (sgr_type == 'm' && (sgr_b & 64)) + if (sgr_type == 'm' && MOUSE_WHEEL(sgr_b)) return (-2); } else return (-1); -- cgit From 818b2176ef52a13867905d3d3e0269dc1b4d6fb4 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 17 Feb 2022 09:58:47 +0000 Subject: Add a window-resized hook which is fired when the window is actually resized which may be later than the client resize, GitHub issue 2995. --- options-table.c | 1 + resize.c | 1 + 2 files changed, 2 insertions(+) diff --git a/options-table.c b/options-table.c index 62d37c39..6d136f76 100644 --- a/options-table.c +++ b/options-table.c @@ -1246,6 +1246,7 @@ const struct options_table_entry options_table[] = { OPTIONS_TABLE_HOOK("window-linked", ""), OPTIONS_TABLE_WINDOW_HOOK("window-pane-changed", ""), OPTIONS_TABLE_WINDOW_HOOK("window-renamed", ""), + OPTIONS_TABLE_WINDOW_HOOK("window-resized", ""), OPTIONS_TABLE_HOOK("window-unlinked", ""), { .name = NULL } diff --git a/resize.c b/resize.c index 18a02adb..457fee0a 100644 --- a/resize.c +++ b/resize.c @@ -61,6 +61,7 @@ resize_window(struct window *w, u_int sx, u_int sy, int xpixel, int ypixel) tty_update_window_offset(w); server_redraw_window(w); notify_window("window-layout-changed", w); + notify_window("window-resized", w); w->flags &= ~WINDOW_RESIZE; } -- cgit From a9b880921dce2836611873a4ec85e9425dc08b40 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 22 Feb 2022 11:01:57 +0000 Subject: Use correct size for screen when popup is created without borders. --- popup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/popup.c b/popup.c index a3b33dce..12f31c40 100644 --- a/popup.c +++ b/popup.c @@ -690,7 +690,7 @@ popup_display(int flags, enum box_lines lines, struct cmdq_item *item, u_int px, } pd->border_cell.attr = 0; - screen_init(&pd->s, sx - 2, sy - 2, 0); + screen_init(&pd->s, jx, jy, 0); colour_palette_init(&pd->palette); colour_palette_from_option(&pd->palette, global_w_options); -- cgit From 0fd01f8873ccae655b0f997ca0dc46c42ebe399d Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 22 Feb 2022 11:07:25 +0000 Subject: Initialize copy_width before adjusting it, GitHub issue 3079. --- format-draw.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/format-draw.c b/format-draw.c index 1110535f..1a7e60b3 100644 --- a/format-draw.c +++ b/format-draw.c @@ -1154,13 +1154,13 @@ format_trim_right(const char *expanded, u_int limit) while (*cp != '\0') { if (*cp == '#') { end = format_leading_hashes(cp, &n, &leading_width); + copy_width = leading_width; if (width <= skip) { - if (skip - width >= leading_width) + if (skip - width >= copy_width) copy_width = 0; else copy_width -= (skip - width); - } else - copy_width = leading_width; + } if (copy_width != 0) { if (n == 1) *out++ = '#'; -- cgit From fa71e9a07911715da596d618fe045943337c596b Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 22 Feb 2022 11:10:41 +0000 Subject: Add next_session_id format with the next session ID, GitHub issue 3078. --- format.c | 10 ++++++++++ session.c | 2 +- tmux.1 | 6 ++++++ tmux.h | 1 + 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/format.c b/format.c index b34fc341..15f3179c 100644 --- a/format.c +++ b/format.c @@ -1650,6 +1650,13 @@ format_cb_mouse_y(struct format_tree *ft) return (NULL); } +/* Callback for next_session_id. */ +static void * +format_cb_next_session_id(__unused struct format_tree *ft) +{ + return (format_printf("$%u", next_session_id)); +} + /* Callback for origin_flag. */ static void * format_cb_origin_flag(struct format_tree *ft) @@ -2707,6 +2714,9 @@ static const struct format_table_entry format_table[] = { { "mouse_y", FORMAT_TABLE_STRING, format_cb_mouse_y }, + { "next_session_id", FORMAT_TABLE_STRING, + format_cb_next_session_id + }, { "origin_flag", FORMAT_TABLE_STRING, format_cb_origin_flag }, diff --git a/session.c b/session.c index fd567926..9b9b0d9c 100644 --- a/session.c +++ b/session.c @@ -29,7 +29,7 @@ #include "tmux.h" struct sessions sessions; -static u_int next_session_id; +u_int next_session_id; struct session_groups session_groups = RB_INITIALIZER(&session_groups); static void session_free(int, short, void *); diff --git a/tmux.1 b/tmux.1 index c9c9f221..22eb6b1f 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4589,6 +4589,11 @@ Run when a session is renamed. Run when a window is linked into a session. .It window-renamed Run when a window is renamed. +.It window-resized +Run when a window is resized. +This may be after the +.Ar client-resized +hook is run. .It window-unlinked Run when a window is unlinked from a session. .El @@ -5093,6 +5098,7 @@ The following variables are available, where appropriate: .It Li "mouse_word" Ta "" Ta "Word under mouse, if any" .It Li "mouse_x" Ta "" Ta "Mouse X position, if any" .It Li "mouse_y" Ta "" Ta "Mouse Y position, if any" +.It Li "next_session_id" Ta "" Ta "Unique session ID for next new session" .It Li "origin_flag" Ta "" Ta "Pane origin flag" .It Li "pane_active" Ta "" Ta "1 if active pane" .It Li "pane_at_bottom" Ta "" Ta "1 if pane is at the bottom of window" diff --git a/tmux.h b/tmux.h index fa5b6b50..7a83732e 100644 --- a/tmux.h +++ b/tmux.h @@ -3118,6 +3118,7 @@ void control_notify_session_window_changed(struct session *); /* session.c */ extern struct sessions sessions; +extern u_int next_session_id; int session_cmp(struct session *, struct session *); RB_PROTOTYPE(sessions, session, entry, session_cmp); int session_alive(struct session *); -- cgit From d54b18ca2bb1383b120e920412e1a7340c4b1416 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 22 Feb 2022 13:31:18 +0000 Subject: Do not attempt to update focus (and crash) when there is no previous window. --- session.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/session.c b/session.c index 9b9b0d9c..5bc5330e 100644 --- a/session.c +++ b/session.c @@ -503,7 +503,8 @@ session_set_current(struct session *s, struct winlink *wl) winlink_stack_push(&s->lastw, s->curw); s->curw = wl; if (options_get_number(global_options, "focus-events")) { - window_update_focus(old->window); + if (old != NULL) + window_update_focus(old->window); window_update_focus(wl->window); } winlink_clear_flags(wl); -- cgit From e8d6d53a7b4af435806fefa5c9f6cef4387f3451 Mon Sep 17 00:00:00 2001 From: deraadt Date: Tue, 22 Feb 2022 17:35:01 +0000 Subject: MAXCOMLEN is no longer needed in these programs, so remove the annotation from sys/param.h include lines, or remove the include lines entirely if it this was the least requirement. ok millert --- procname.c | 1 - 1 file changed, 1 deletion(-) diff --git a/procname.c b/procname.c index b2cba1c2..7c0872fd 100644 --- a/procname.c +++ b/procname.c @@ -16,7 +16,6 @@ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include /* MAXCOMLEN */ #include #include #include -- cgit From 2e59ff2db95d573f56237776a10314b412721491 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 28 Feb 2022 09:24:22 +0000 Subject: Map control keys back to an ASCII uppercase letter when passing them on as extended keys. --- input-keys.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/input-keys.c b/input-keys.c index 0b618407..ae67b486 100644 --- a/input-keys.c +++ b/input-keys.c @@ -418,7 +418,7 @@ int input_key(struct screen *s, struct bufferevent *bev, key_code key) { struct input_key_entry *ike; - key_code justkey, newkey, outkey; + key_code justkey, newkey, outkey, modifiers; struct utf8_data ud; char tmp[64], modifier; @@ -519,7 +519,12 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key) return (input_key(s, bev, key & ~KEYC_CTRL)); } outkey = (key & KEYC_MASK_KEY); - switch (key & KEYC_MASK_MODIFIERS) { + modifiers = (key & KEYC_MASK_MODIFIERS); + if (outkey < ' ') { + outkey = 64 + outkey; + modifiers |= KEYC_CTRL; + } + switch (modifiers) { case KEYC_SHIFT: modifier = '2'; break; -- cgit From 2da096d828f2b02f9d7fc329474f98aa6740328b Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 28 Feb 2022 09:34:57 +0000 Subject: Exit on SIGHUP before attach also, GitHub issue 3084. --- client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client.c b/client.c index cb64205e..8a0a0673 100644 --- a/client.c +++ b/client.c @@ -530,7 +530,7 @@ client_signal(int sig) if (sig == SIGCHLD) waitpid(WAIT_ANY, &status, WNOHANG); else if (!client_attached) { - if (sig == SIGTERM) + if (sig == SIGTERM || sig == SIGHUP) proc_exit(client_proc); } else { switch (sig) { -- cgit From 047c011a1524a2d699da098ac423ef16f8a11bd6 Mon Sep 17 00:00:00 2001 From: topcat001 Date: Mon, 28 Feb 2022 13:11:28 -0800 Subject: Use PATH_MAX instead of MAXPATHLEN. --- osdep-openbsd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osdep-openbsd.c b/osdep-openbsd.c index 1fc087d3..b4c8fc56 100644 --- a/osdep-openbsd.c +++ b/osdep-openbsd.c @@ -141,7 +141,7 @@ char * osdep_get_cwd(int fd) { int name[] = { CTL_KERN, KERN_PROC_CWD, 0 }; - static char path[MAXPATHLEN]; + static char path[PATH_MAX]; size_t pathlen = sizeof path; if ((name[2] = tcgetpgrp(fd)) == -1) -- cgit From f39a71aaade384d47917349f9d0b8dd709e8262e Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 1 Mar 2022 15:20:22 +0000 Subject: Don't convert codes for special keys (Tab, Enter, Escape). --- input-keys.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/input-keys.c b/input-keys.c index ae67b486..78ae91da 100644 --- a/input-keys.c +++ b/input-keys.c @@ -520,7 +520,7 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key) } outkey = (key & KEYC_MASK_KEY); modifiers = (key & KEYC_MASK_MODIFIERS); - if (outkey < ' ') { + if (outkey < 32 && outkey != 9 && outkey != 13 && outkey != 27) { outkey = 64 + outkey; modifiers |= KEYC_CTRL; } -- cgit From 7d4224f2073ca4111968573e35997b65a0d5787a Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 3 Mar 2022 08:24:12 +0000 Subject: Allow optional arguments. --- arguments.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/arguments.c b/arguments.c index 17bc12ec..37cd0236 100644 --- a/arguments.c +++ b/arguments.c @@ -131,8 +131,9 @@ args_parse(const struct args_parse *parse, struct args_value *values, u_int i; enum args_parse_type type; struct args_value *value, *new; - u_char flag, argument; + u_char flag; const char *found, *string, *s; + int optional_argument; if (count == 0) return (args_create()); @@ -169,18 +170,27 @@ args_parse(const struct args_parse *parse, struct args_value *values, args_free(args); return (NULL); } - argument = *++found; - if (argument != ':') { + if (*++found != ':') { log_debug("%s: -%c", __func__, flag); args_set(args, flag, NULL); continue; } + if (*found == ':') { + optional_argument = 1; + found++; + } new = xcalloc(1, sizeof *new); if (*string != '\0') { new->type = ARGS_STRING; new->string = xstrdup(string); } else { if (i == count) { + if (optional_argument) { + log_debug("%s: -%c", __func__, + flag); + args_set(args, flag, NULL); + continue; + } xasprintf(cause, "-%c expects an argument", flag); -- cgit From a731b1a9162bfa8157c474d411a4d0d6f131439d Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 7 Mar 2022 11:52:09 +0000 Subject: Pass client when adding menu item, GitHub issue 3103. --- status.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/status.c b/status.c index 79033242..8d702de1 100644 --- a/status.c +++ b/status.c @@ -1798,7 +1798,7 @@ status_prompt_complete_window_menu(struct client *c, struct session *s, item.name = tmp; item.key = '0' + size - 1; item.command = NULL; - menu_add_item(menu, &item, NULL, NULL, NULL); + menu_add_item(menu, &item, NULL, c, NULL); free(tmp); if (size == height) -- cgit From 54d22497166de02d50b93e922d762698fb82d25b Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 7 Mar 2022 15:21:39 +0000 Subject: Remove unnecessary declarations. --- osdep-netbsd.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/osdep-netbsd.c b/osdep-netbsd.c index b473e017..6003c035 100644 --- a/osdep-netbsd.c +++ b/osdep-netbsd.c @@ -35,9 +35,6 @@ ((p)->p_stat == SSTOP || (p)->p_stat == SZOMB) struct kinfo_proc2 *cmp_procs(struct kinfo_proc2 *, struct kinfo_proc2 *); -char *osdep_get_name(int, char *); -char *osdep_get_cwd(int); -struct event_base *osdep_event_init(void); struct kinfo_proc2 * cmp_procs(struct kinfo_proc2 *p1, struct kinfo_proc2 *p2) -- cgit From afd9b68d102fc4a7614885267e99299247a031d8 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 8 Mar 2022 11:02:17 +0000 Subject: Add getpeerid compat. --- compat/getpeereid.c | 40 ++++++++++++++++++++++++++++++++++++++++ configure.ac | 11 ++++++++++- 2 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 compat/getpeereid.c diff --git a/compat/getpeereid.c b/compat/getpeereid.c new file mode 100644 index 00000000..722f14a2 --- /dev/null +++ b/compat/getpeereid.c @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2022 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include + +#include "compat.h" + +int +getpeereid(int s, uid_t *uid, gid_t *gid) +{ +#ifdef HAVE_SO_PEERCRED + struct ucred uc; + int len = sizeof uc; + + if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &uc, &len) == -1) + return (-1); + *uid = uc.uid; + *gid = uc.gid; + return (0); +#else + errno = EOPNOTSUPP; + return (-1); +#endif +} diff --git a/configure.ac b/configure.ac index edda0d6b..6ca86ea4 100644 --- a/configure.ac +++ b/configure.ac @@ -160,6 +160,7 @@ AC_REPLACE_FUNCS([ \ freezero \ getdtablecount \ getdtablesize \ + getpeereid \ getline \ getprogname \ memmem \ @@ -169,7 +170,7 @@ AC_REPLACE_FUNCS([ \ strlcat \ strlcpy \ strndup \ - strsep \ + strsep ]) AC_FUNC_STRNLEN @@ -684,6 +685,14 @@ AC_CHECK_DECL( [#include ] ) +# Look for setsockopt(SO_PEERCRED). +AC_CHECK_DECL( + SO_PEERCRED, + AC_DEFINE(HAVE_SO_PEERCRED), + , + [#include ] +) + # Look for fcntl(F_CLOSEM). AC_CHECK_DECL( F_CLOSEM, -- cgit From 9ed1226a66ee7e097ef858f7bc4b992fbcda2dc7 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 8 Mar 2022 11:04:15 +0000 Subject: Need a declaration for getpeereid also. --- compat.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/compat.h b/compat.h index 13334ad7..be726831 100644 --- a/compat.h +++ b/compat.h @@ -334,6 +334,11 @@ char *strndup(const char *, size_t); void *memmem(const void *, size_t, const void *, size_t); #endif +#ifndef HAVE_GETPEEREID +/* getpeereid.c */ +int getpeereid(int, uid_t *, gid_t *); +#endif + #ifndef HAVE_DAEMON /* daemon.c */ int daemon(int, int); -- cgit From 98cd8e4cad3afbc542de665e9d025d12b560bc6b Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 8 Mar 2022 11:28:40 +0000 Subject: Add formats for client and server UID and user (for multiuser setups). --- cmd-list-clients.c | 2 ++ format.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ proc.c | 11 ++++++++++ 3 files changed, 73 insertions(+) diff --git a/cmd-list-clients.c b/cmd-list-clients.c index a5b7d147..53a99178 100644 --- a/cmd-list-clients.c +++ b/cmd-list-clients.c @@ -31,6 +31,8 @@ #define LIST_CLIENTS_TEMPLATE \ "#{client_name}: #{session_name} " \ "[#{client_width}x#{client_height} #{client_termname}] " \ + "#{?#{!=:#{client_uid},#{uid}}," \ + "[user #{?client_user,#{client_user},#{client_uid},}] ,}" \ "#{?client_flags,(,}#{client_flags}#{?client_flags,),}" static enum cmd_retval cmd_list_clients_exec(struct cmd *, struct cmdq_item *); diff --git a/format.c b/format.c index 15f3179c..f381a0ec 100644 --- a/format.c +++ b/format.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -1387,6 +1388,35 @@ format_cb_client_tty(struct format_tree *ft) return (NULL); } +/* Callback for client_uid. */ +static void * +format_cb_client_uid(struct format_tree *ft) +{ + uid_t uid; + + if (ft->c != NULL) { + uid = proc_get_peer_uid(ft->c->peer); + if (uid != (uid_t)-1) + return (format_printf("%ld", (long)uid)); + } + return (NULL); +} + +/* Callback for client_user. */ +static void * +format_cb_client_user(struct format_tree *ft) +{ + uid_t uid; + struct passwd *pw; + + if (ft->c != NULL) { + uid = proc_get_peer_uid(ft->c->peer); + if (uid != (uid_t)-1 && (pw = getpwuid(uid)) != NULL) + return (xstrdup(pw->pw_name)); + } + return (NULL); +} + /* Callback for client_utf8. */ static void * format_cb_client_utf8(struct format_tree *ft) @@ -2521,6 +2551,24 @@ format_cb_tree_mode_format(__unused struct format_tree *ft) return (xstrdup(window_tree_mode.default_format)); } +/* Callback for uid. */ +static void * +format_cb_uid(__unused struct format_tree *ft) +{ + return (format_printf("%ld", (long)getuid())); +} + +/* Callback for user. */ +static void * +format_cb_user(__unused struct format_tree *ft) +{ + struct passwd *pw; + + if ((pw = getpwuid(getuid())) != NULL) + return (xstrdup(pw->pw_name)); + return NULL; +} + /* Format table type. */ enum format_table_type { FORMAT_TABLE_STRING, @@ -2627,6 +2675,12 @@ static const struct format_table_entry format_table[] = { { "client_tty", FORMAT_TABLE_STRING, format_cb_client_tty }, + { "client_uid", FORMAT_TABLE_STRING, + format_cb_client_uid + }, + { "client_user", FORMAT_TABLE_STRING, + format_cb_client_user + }, { "client_utf8", FORMAT_TABLE_STRING, format_cb_client_utf8 }, @@ -2906,6 +2960,12 @@ static const struct format_table_entry format_table[] = { { "tree_mode_format", FORMAT_TABLE_STRING, format_cb_tree_mode_format }, + { "uid", FORMAT_TABLE_STRING, + format_cb_uid + }, + { "user", FORMAT_TABLE_STRING, + format_cb_user + }, { "version", FORMAT_TABLE_STRING, format_cb_version }, diff --git a/proc.c b/proc.c index 9412cec0..9dcb042e 100644 --- a/proc.c +++ b/proc.c @@ -55,6 +55,7 @@ struct tmuxpeer { struct imsgbuf ibuf; struct event event; + uid_t uid; int flags; #define PEER_BAD 0x1 @@ -296,6 +297,7 @@ proc_add_peer(struct tmuxproc *tp, int fd, void (*dispatchcb)(struct imsg *, void *), void *arg) { struct tmuxpeer *peer; + gid_t gid; peer = xcalloc(1, sizeof *peer); peer->parent = tp; @@ -306,6 +308,9 @@ proc_add_peer(struct tmuxproc *tp, int fd, imsg_init(&peer->ibuf, fd); event_set(&peer->event, fd, EV_READ, proc_event_cb, peer); + if (getpeereid(fd, &peer->uid, &gid) != 0) + peer->uid = (uid_t)-1; + log_debug("add peer %p: %d (%p)", peer, fd, arg); TAILQ_INSERT_TAIL(&tp->peers, peer, entry); @@ -361,3 +366,9 @@ proc_fork_and_daemon(int *fd) return (pid); } } + +uid_t +proc_get_peer_uid(struct tmuxpeer *peer) +{ + return (peer->uid); +} -- cgit From 04952f15df1bc81d1878833533d344a6b1d1326c Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 8 Mar 2022 11:35:06 +0000 Subject: Use getpeerucred if available (not tested). --- compat/getpeereid.c | 19 +++++++++++++++++++ configure.ac | 6 ++++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/compat/getpeereid.c b/compat/getpeereid.c index 722f14a2..5a593c07 100644 --- a/compat/getpeereid.c +++ b/compat/getpeereid.c @@ -19,6 +19,10 @@ #include +#ifdef HAVE_UCRED_H +#include +#endif + #include "compat.h" int @@ -33,6 +37,21 @@ getpeereid(int s, uid_t *uid, gid_t *gid) *uid = uc.uid; *gid = uc.gid; return (0); +#elif defined(HAVE_GETPEERUCRED) +int +getpeereid(int s, uid_t *uid, gid_t *gid) +{ + ucred_t *ucred = NULL; + + if (getpeerucred(s, &ucred) == -1) + return (-1); + if ((*uid = ucred_geteuid(ucred)) == -1) + return (-1); + if ((*gid = ucred_getrgid(ucred)) == -1) + return (-1); + ucred_free(ucred); + return (0); +} #else errno = EOPNOTSUPP; return (-1); diff --git a/configure.ac b/configure.ac index 6ca86ea4..e473f141 100644 --- a/configure.ac +++ b/configure.ac @@ -128,6 +128,7 @@ AC_CHECK_HEADERS([ \ sys/dir.h \ sys/ndir.h \ sys/tree.h \ + ucred.h \ util.h \ ]) @@ -146,7 +147,8 @@ AC_CHECK_FUNCS([ \ flock \ prctl \ proc_pidinfo \ - sysconf + getpeerucred \ + sysconf \ ]) # Check for functions with a compatibility implementation. @@ -170,7 +172,7 @@ AC_REPLACE_FUNCS([ \ strlcat \ strlcpy \ strndup \ - strsep + strsep \ ]) AC_FUNC_STRNLEN -- cgit From ad9b8059836d424f70a8579d28e28e0186cdbaa6 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 8 Mar 2022 12:01:19 +0000 Subject: Add argument to refresh-client -l to forward clipboard to a pane. GitHub issue 3068. --- cmd-refresh-client.c | 39 ++++++++++-- input.c | 47 +++++++++------ server-client.c | 1 + tmux.1 | 15 ++++- tmux.h | 166 +++++++++++++++++++++++++++------------------------ tty-keys.c | 28 ++++++--- tty.c | 17 ++++-- 7 files changed, 195 insertions(+), 118 deletions(-) diff --git a/cmd-refresh-client.c b/cmd-refresh-client.c index 2af9cb46..b2665ad9 100644 --- a/cmd-refresh-client.c +++ b/cmd-refresh-client.c @@ -34,7 +34,7 @@ const struct cmd_entry cmd_refresh_client_entry = { .name = "refresh-client", .alias = "refresh", - .args = { "A:B:cC:Df:F:lLRSt:U", 0, 1, NULL }, + .args = { "A:B:cC:Df:F:l::LRSt:U", 0, 1, NULL }, .usage = "[-cDlLRSU] [-A pane:state] [-B name:what:format] " "[-C XxY] [-f flags] " CMD_TARGET_CLIENT_USAGE " [adjustment]", @@ -162,6 +162,37 @@ out: free(copy); } +static enum cmd_retval +cmd_refresh_client_clipboard(struct cmd *self, struct cmdq_item *item) +{ + struct args *args = cmd_get_args(self); + struct client *tc = cmdq_get_target_client(item); + const char *p; + u_int i; + struct cmd_find_state fs; + + p = args_get(args, 'l'); + if (p == NULL) { + if (tc->flags & CLIENT_CLIPBOARDBUFFER) + return (CMD_RETURN_NORMAL); + tc->flags |= CLIENT_CLIPBOARDBUFFER; + } else { + if (cmd_find_target(&fs, item, p, CMD_FIND_PANE, 0) != 0) + return (CMD_RETURN_ERROR); + for (i = 0; i < tc->clipboard_npanes; i++) { + if (tc->clipboard_panes[i] == fs.wp->id) + break; + } + if (i != tc->clipboard_npanes) + return (CMD_RETURN_NORMAL); + tc->clipboard_panes = xreallocarray (tc->clipboard_panes, + tc->clipboard_npanes + 1, sizeof *tc->clipboard_panes); + tc->clipboard_panes[tc->clipboard_npanes++] = fs.wp->id; + } + tty_clipboard_query(&tc->tty); + return (CMD_RETURN_NORMAL); +} + static enum cmd_retval cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item) { @@ -224,10 +255,8 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_NORMAL); } - if (args_has(args, 'l')) { - tty_send_osc52_query(&tc->tty); - return (CMD_RETURN_NORMAL); - } + if (args_has(args, 'l')) + return (cmd_refresh_client_clipboard(self, item)); if (args_has(args, 'F')) /* -F is an alias for -f */ server_client_set_flags(tc, args_get(args, 'F')); diff --git a/input.c b/input.c index 5adc694d..b1856538 100644 --- a/input.c +++ b/input.c @@ -2682,8 +2682,8 @@ input_osc_52(struct input_ctx *ictx, const char *p) { struct window_pane *wp = ictx->wp; char *end; - const char *buf; - size_t len; + const char *buf = NULL; + size_t len = 0; u_char *out; int outlen, state; struct screen_write_ctx ctx; @@ -2703,26 +2703,12 @@ input_osc_52(struct input_ctx *ictx, const char *p) log_debug("%s: %s", __func__, end); if (strcmp(end, "?") == 0) { - if ((pb = paste_get_top(NULL)) != NULL) { + if ((pb = paste_get_top(NULL)) != NULL) buf = paste_buffer_data(pb, &len); - outlen = 4 * ((len + 2) / 3) + 1; - out = xmalloc(outlen); - if ((outlen = b64_ntop(buf, len, out, outlen)) == -1) { - free(out); - return; - } - } else { - outlen = 0; - out = NULL; - } - bufferevent_write(ictx->event, "\033]52;;", 6); - if (outlen != 0) - bufferevent_write(ictx->event, out, outlen); if (ictx->input_end == INPUT_END_BEL) - bufferevent_write(ictx->event, "\007", 1); + input_reply_clipboard(ictx->event, buf, len, "\007"); else - bufferevent_write(ictx->event, "\033\\", 2); - free(out); + input_reply_clipboard(ictx->event, buf, len, "\033\\"); return; } @@ -2780,3 +2766,26 @@ input_osc_104(struct input_ctx *ictx, const char *p) screen_write_fullredraw(&ictx->ctx); free(copy); } + +void +input_reply_clipboard(struct bufferevent *bev, const char *buf, size_t len, + const char *end) +{ + char *out = NULL; + size_t outlen = 0; + + if (buf != NULL && len != 0) { + outlen = 4 * ((len + 2) / 3) + 1; + out = xmalloc(outlen); + if ((outlen = b64_ntop(buf, len, out, outlen)) == -1) { + free(out); + return; + } + } + + bufferevent_write(bev, "\033]52;;", 6); + if (outlen != 0) + bufferevent_write(bev, out, outlen); + bufferevent_write(bev, end, strlen(end)); + free(out); +} diff --git a/server-client.c b/server-client.c index f95088f0..f8236066 100644 --- a/server-client.c +++ b/server-client.c @@ -425,6 +425,7 @@ server_client_lost(struct client *c) if (c->flags & CLIENT_TERMINAL) tty_free(&c->tty); free(c->ttyname); + free(c->clipboard_panes); free(c->term_name); free(c->term_type); diff --git a/tmux.1 b/tmux.1 index 22eb6b1f..215d825c 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1338,11 +1338,12 @@ 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 cDLRSU .Op Fl A Ar pane:state .Op Fl B Ar name:what:format .Op Fl C Ar size .Op Fl f Ar flags +.Op Fl l Op Ar target-pane .Op Fl t Ar target-client .Op Ar adjustment .Xc @@ -1456,7 +1457,11 @@ sets a comma-separated list of client flags, see .Fl l requests the clipboard from the client using the .Xr xterm 1 -escape sequence and stores it in a new paste buffer. +escape sequence. +If +Ar target-pane +is given, the clipboard is sent (in encoded form), otherwise it is stored in a +new paste buffer. .Pp .Fl L , .Fl R , @@ -5054,6 +5059,8 @@ The following variables are available, where appropriate: .It Li "client_termname" Ta "" Ta "Terminal name of client" .It Li "client_termtype" Ta "" Ta "Terminal type of client, if available" .It Li "client_tty" Ta "" Ta "Pseudo terminal of client" +.It Li "client_uid" Ta "" Ta "UID of client process" +.It Li "client_user" Ta "" Ta "User of client process" .It Li "client_utf8" Ta "" Ta "1 if client supports UTF-8" .It Li "client_width" Ta "" Ta "Width of client" .It Li "client_written" Ta "" Ta "Bytes written to client" @@ -5171,6 +5178,8 @@ The following variables are available, where appropriate: .It Li "session_windows" Ta "" Ta "Number of windows in session" .It Li "socket_path" Ta "" Ta "Server socket path" .It Li "start_time" Ta "" Ta "Server start time" +.It Li "uid" Ta "" Ta "Server UID" +.It Li "user" Ta "" Ta "Server user" .It Li "version" Ta "" Ta "Server version" .It Li "window_active" Ta "" Ta "1 if window active" .It Li "window_active_clients" Ta "" Ta "Number of clients viewing this window" @@ -5185,7 +5194,6 @@ The following variables are available, where appropriate: .It Li "window_cell_width" Ta "" Ta "Width of each cell in pixels" .It Li "window_end_flag" Ta "" Ta "1 if window has the highest index" .It Li "window_flags" Ta "#F" Ta "Window flags with # escaped as ##" -.It Li "window_raw_flags" Ta "" Ta "Window flags with nothing escaped" .It Li "window_format" Ta "" Ta "1 if format is for a window" .It Li "window_height" Ta "" Ta "Height of window" .It Li "window_id" Ta "" Ta "Unique window ID" @@ -5200,6 +5208,7 @@ The following variables are available, where appropriate: .It Li "window_offset_x" Ta "" Ta "X offset into window if larger than client" .It Li "window_offset_y" Ta "" Ta "Y offset into window if larger than client" .It Li "window_panes" Ta "" Ta "Number of panes in window" +.It Li "window_raw_flags" Ta "" Ta "Window flags with nothing escaped" .It Li "window_silence_flag" Ta "" Ta "1 if window has silence alert" .It Li "window_stack_index" Ta "" Ta "Index in session most recent stack" .It Li "window_start_flag" Ta "" Ta "1 if window has the lowest index" diff --git a/tmux.h b/tmux.h index 7a83732e..0700f1b9 100644 --- a/tmux.h +++ b/tmux.h @@ -1331,7 +1331,7 @@ LIST_HEAD(tty_terms, tty_term); struct tty { struct client *client; struct event start_timer; - struct event query_timer; + struct event clipboard_timer; u_int sx; u_int sy; @@ -1684,50 +1684,50 @@ typedef int (*overlay_key_cb)(struct client *, void *, struct key_event *); typedef void (*overlay_free_cb)(struct client *, void *); typedef void (*overlay_resize_cb)(struct client *, void *); struct client { - const char *name; - struct tmuxpeer *peer; - struct cmdq_list *queue; + const char *name; + struct tmuxpeer *peer; + struct cmdq_list *queue; - struct client_windows windows; + struct client_windows windows; - struct control_state *control_state; - u_int pause_age; + struct control_state *control_state; + u_int pause_age; - pid_t pid; - int fd; - int out_fd; - struct event event; - int retval; + pid_t pid; + int fd; + int out_fd; + struct event event; + int retval; - struct timeval creation_time; - struct timeval activity_time; + struct timeval creation_time; + struct timeval activity_time; - struct environ *environ; + struct environ *environ; struct format_job_tree *jobs; - char *title; - const char *cwd; + char *title; + const char *cwd; - char *term_name; - int term_features; - char *term_type; - char **term_caps; - u_int term_ncaps; + char *term_name; + int term_features; + char *term_type; + char **term_caps; + u_int term_ncaps; - char *ttyname; - struct tty tty; + char *ttyname; + struct tty tty; - size_t written; - size_t discarded; - size_t redraw; + size_t written; + size_t discarded; + size_t redraw; - struct event repeat_timer; + struct event repeat_timer; - struct event click_timer; - u_int click_button; - struct mouse_event click_event; + struct event click_timer; + u_int click_button; + struct mouse_event click_event; - struct status_line status; + struct status_line status; #define CLIENT_TERMINAL 0x1 #define CLIENT_LOGIN 0x2 @@ -1764,6 +1764,7 @@ struct client { #define CLIENT_CONTROL_PAUSEAFTER 0x100000000ULL #define CLIENT_CONTROL_WAITEXIT 0x200000000ULL #define CLIENT_WINDOWSIZECHANGED 0x400000000ULL +#define CLIENT_CLIPBOARDBUFFER 0x800000000ULL #define CLIENT_ALLREDRAWFLAGS \ (CLIENT_REDRAWWINDOW| \ CLIENT_REDRAWSTATUS| \ @@ -1775,73 +1776,79 @@ struct client { (CLIENT_DEAD| \ CLIENT_SUSPENDED| \ CLIENT_EXIT) -#define CLIENT_NODETACHFLAGS \ +#define CLIENT_NODETACHFLAGS \ (CLIENT_DEAD| \ CLIENT_EXIT) #define CLIENT_NOSIZEFLAGS \ (CLIENT_DEAD| \ CLIENT_SUSPENDED| \ CLIENT_EXIT) - uint64_t flags; + uint64_t flags; enum { CLIENT_EXIT_RETURN, CLIENT_EXIT_SHUTDOWN, CLIENT_EXIT_DETACH - } exit_type; - enum msgtype exit_msgtype; - char *exit_session; - char *exit_message; - - struct key_table *keytable; - - uint64_t redraw_panes; - - int message_ignore_keys; - int message_ignore_styles; - char *message_string; - struct event message_timer; - - char *prompt_string; - struct utf8_data *prompt_buffer; - char *prompt_last; - size_t prompt_index; - prompt_input_cb prompt_inputcb; - prompt_free_cb prompt_freecb; - void *prompt_data; - u_int prompt_hindex[PROMPT_NTYPES]; - enum { PROMPT_ENTRY, PROMPT_COMMAND } prompt_mode; - struct utf8_data *prompt_saved; + } exit_type; + enum msgtype exit_msgtype; + char *exit_session; + char *exit_message; + + struct key_table *keytable; + + uint64_t redraw_panes; + + int message_ignore_keys; + int message_ignore_styles; + char *message_string; + struct event message_timer; + + char *prompt_string; + struct utf8_data *prompt_buffer; + char *prompt_last; + size_t prompt_index; + prompt_input_cb prompt_inputcb; + prompt_free_cb prompt_freecb; + void *prompt_data; + u_int prompt_hindex[PROMPT_NTYPES]; + enum { + PROMPT_ENTRY, + PROMPT_COMMAND + } prompt_mode; + struct utf8_data *prompt_saved; #define PROMPT_SINGLE 0x1 #define PROMPT_NUMERIC 0x2 #define PROMPT_INCREMENTAL 0x4 #define PROMPT_NOFORMAT 0x8 #define PROMPT_KEY 0x10 - int prompt_flags; - enum prompt_type prompt_type; - int prompt_cursor; + int prompt_flags; + enum prompt_type prompt_type; + int prompt_cursor; - struct session *session; - struct session *last_session; + struct session *session; + struct session *last_session; - int references; + int references; + + void *pan_window; + u_int pan_ox; + u_int pan_oy; - void *pan_window; - u_int pan_ox; - u_int pan_oy; + overlay_check_cb overlay_check; + overlay_mode_cb overlay_mode; + overlay_draw_cb overlay_draw; + overlay_key_cb overlay_key; + overlay_free_cb overlay_free; + overlay_resize_cb overlay_resize; + void *overlay_data; + struct event overlay_timer; - overlay_check_cb overlay_check; - overlay_mode_cb overlay_mode; - overlay_draw_cb overlay_draw; - overlay_key_cb overlay_key; - overlay_free_cb overlay_free; - overlay_resize_cb overlay_resize; - void *overlay_data; - struct event overlay_timer; + struct client_files files; - struct client_files files; + u_int *clipboard_panes; + u_int clipboard_npanes; - TAILQ_ENTRY(client) entry; + TAILQ_ENTRY(client) entry; }; TAILQ_HEAD(clients, client); @@ -2015,6 +2022,7 @@ void proc_remove_peer(struct tmuxpeer *); void proc_kill_peer(struct tmuxpeer *); void proc_toggle_log(struct tmuxproc *); pid_t proc_fork_and_daemon(int *); +uid_t proc_get_peer_uid(struct tmuxpeer *); /* cfg.c */ extern int cfg_finished; @@ -2228,7 +2236,7 @@ void tty_reset(struct tty *); void tty_region_off(struct tty *); void tty_margin_off(struct tty *); void tty_cursor(struct tty *, u_int, u_int); -void tty_send_osc52_query(struct tty *); +void tty_clipboard_query(struct tty *); void tty_putcode(struct tty *, enum tty_code_code); void tty_putcode1(struct tty *, enum tty_code_code, int); void tty_putcode2(struct tty *, enum tty_code_code, int, int); @@ -2674,6 +2682,8 @@ void input_parse_pane(struct window_pane *); void input_parse_buffer(struct window_pane *, u_char *, size_t); void input_parse_screen(struct input_ctx *, struct screen *, screen_write_init_ctx_cb, void *, u_char *, size_t); +void input_reply_clipboard(struct bufferevent *, const char *, size_t, + const char *); /* input-key.c */ void input_key_build(void); diff --git a/tty-keys.c b/tty-keys.c index 30717af3..8538e74b 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -1154,12 +1154,14 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size, * partial. */ static int -tty_keys_clipboard(__unused struct tty *tty, const char *buf, size_t len, - size_t *size) +tty_keys_clipboard(struct tty *tty, const char *buf, size_t len, size_t *size) { - size_t end, terminator, needed; - char *copy, *out; - int outlen; + struct client *c = tty->client; + struct window_pane *wp; + size_t end, terminator, needed; + char *copy, *out; + int outlen; + u_int i; *size = 0; @@ -1221,6 +1223,7 @@ tty_keys_clipboard(__unused struct tty *tty, const char *buf, size_t len, if (~tty->flags & TTY_OSC52QUERY) return (0); tty->flags &= ~TTY_OSC52QUERY; + evtimer_del(&tty->clipboard_timer); /* It has to be a string so copy it. */ copy = xmalloc(end + 1); @@ -1237,9 +1240,20 @@ tty_keys_clipboard(__unused struct tty *tty, const char *buf, size_t len, } free(copy); - /* Create a new paste buffer. */ + /* Create a new paste buffer and forward to panes. */ log_debug("%s: %.*s", __func__, outlen, out); - paste_add(NULL, out, outlen); + if (c->flags & CLIENT_CLIPBOARDBUFFER) { + paste_add(NULL, out, outlen); + c->flags &= ~CLIENT_CLIPBOARDBUFFER; + } + for (i = 0; i < c->clipboard_npanes; i++) { + wp = window_pane_find_by_id(c->clipboard_panes[i]); + if (wp != NULL) + input_reply_clipboard(wp->event, out, outlen, "\033\\"); + } + free(c->clipboard_panes); + c->clipboard_panes = NULL; + c->clipboard_npanes = 0; return (0); } diff --git a/tty.c b/tty.c index 06b1bfb2..40735ceb 100644 --- a/tty.c +++ b/tty.c @@ -2921,24 +2921,29 @@ tty_default_attributes(struct tty *tty, const struct grid_cell *defaults, } static void -tty_query_timer_callback(__unused int fd, __unused short events, void *data) +tty_clipboard_query_callback(__unused int fd, __unused short events, void *data) { struct tty *tty = data; + struct client *c = tty->client; + + c->flags &= ~CLIENT_CLIPBOARDBUFFER; + free(c->clipboard_panes); + c->clipboard_panes = NULL; + c->clipboard_npanes = 0; tty->flags &= ~TTY_OSC52QUERY; } void -tty_send_osc52_query(struct tty *tty) +tty_clipboard_query(struct tty *tty) { struct timeval tv = { .tv_sec = TTY_QUERY_TIMEOUT }; if ((~tty->flags & TTY_STARTED) || (tty->flags & TTY_OSC52QUERY)) return; tty_putcode_ptr2(tty, TTYC_MS, "", "?"); - tty->flags |= TTY_OSC52QUERY; - evtimer_set(&tty->query_timer, tty_query_timer_callback, tty); - evtimer_add(&tty->query_timer, &tv); + tty->flags |= TTY_OSC52QUERY; + evtimer_set(&tty->clipboard_timer, tty_clipboard_query_callback, tty); + evtimer_add(&tty->clipboard_timer, &tv); } - -- cgit From a3d920930bf77cafa5260e25584dd3ba1d26f9cb Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 8 Mar 2022 18:31:46 +0000 Subject: Add remain-on-exit-format to set text shown when pane is dead. --- format.c | 37 +++++++++++++++++++++++++++++++++++++ options-table.c | 13 +++++++++++++ server-fn.c | 42 +++++++++++++++++++----------------------- tmux.1 | 7 +++++++ tmux.h | 1 + 5 files changed, 77 insertions(+), 23 deletions(-) diff --git a/format.c b/format.c index f381a0ec..981161b3 100644 --- a/format.c +++ b/format.c @@ -1756,6 +1756,23 @@ format_cb_pane_dead(struct format_tree *ft) return (NULL); } +/* Callback for pane_dead_signal. */ +static void * +format_cb_pane_dead_signal(struct format_tree *ft) +{ + struct window_pane *wp = ft->wp; + const char *name; + + if (wp != NULL) { + if ((wp->flags & PANE_STATUSREADY) && WIFSIGNALED(wp->status)) { + name = sig2name(WTERMSIG(wp->status)); + return (format_printf("%s", name)); + } + return (NULL); + } + return (NULL); +} + /* Callback for pane_dead_status. */ static void * format_cb_pane_dead_status(struct format_tree *ft) @@ -1770,6 +1787,20 @@ format_cb_pane_dead_status(struct format_tree *ft) return (NULL); } +/* Callback for pane_dead_time. */ +static void * +format_cb_pane_dead_time(struct format_tree *ft) +{ + struct window_pane *wp = ft->wp; + + if (wp != NULL) { + if (wp->flags & PANE_STATUSDRAWN) + return (&wp->dead_time); + return (NULL); + } + return (NULL); +} + /* Callback for pane_format. */ static void * format_cb_pane_format(struct format_tree *ft) @@ -2804,9 +2835,15 @@ static const struct format_table_entry format_table[] = { { "pane_dead", FORMAT_TABLE_STRING, format_cb_pane_dead }, + { "pane_dead_signal", FORMAT_TABLE_STRING, + format_cb_pane_dead_signal + }, { "pane_dead_status", FORMAT_TABLE_STRING, format_cb_pane_dead_status }, + { "pane_dead_time", FORMAT_TABLE_TIME, + format_cb_pane_dead_time + }, { "pane_fg", FORMAT_TABLE_STRING, format_cb_pane_fg }, diff --git a/options-table.c b/options-table.c index 6d136f76..d1bc3577 100644 --- a/options-table.c +++ b/options-table.c @@ -1061,6 +1061,19 @@ const struct options_table_entry options_table[] = { "killed ('off' or 'failed') when the program inside exits." }, + { .name = "remain-on-exit-format", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, + .default_str = "Pane is dead (" + "#{?#{!=:#{pane_dead_status},}," + "status #{pane_dead_status},}" + "#{?#{!=:#{pane_dead_signal},}," + "signal #{pane_dead_signal},}, " + "#{t:pane_dead_time})", + .text = "Message shown after the program in a pane has exited, if " + "remain-on-exit is enabled." + }, + { .name = "synchronize-panes", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, diff --git a/server-fn.c b/server-fn.c index b29bc15c..2e9665f5 100644 --- a/server-fn.c +++ b/server-fn.c @@ -312,9 +312,11 @@ server_destroy_pane(struct window_pane *wp, int notify) struct window *w = wp->window; struct screen_write_ctx ctx; struct grid_cell gc; - time_t t; - char tim[26]; int remain_on_exit; + const char *s; + char *expanded; + u_int sx = screen_size_x(&wp->base); + u_int sy = screen_size_y(&wp->base); if (wp->fd != -1) { bufferevent_free(wp->event); @@ -338,32 +340,26 @@ server_destroy_pane(struct window_pane *wp, int notify) return; wp->flags |= PANE_STATUSDRAWN; + gettimeofday(&wp->dead_time, NULL); if (notify) notify_pane("pane-died", wp); - screen_write_start_pane(&ctx, wp, &wp->base); - screen_write_scrollregion(&ctx, 0, screen_size_y(ctx.s) - 1); - screen_write_cursormove(&ctx, 0, screen_size_y(ctx.s) - 1, 0); - screen_write_linefeed(&ctx, 1, 8); - memcpy(&gc, &grid_default_cell, sizeof gc); - - time(&t); - ctime_r(&t, tim); - tim[strcspn(tim, "\n")] = '\0'; - - if (WIFEXITED(wp->status)) { - screen_write_nputs(&ctx, -1, &gc, - "Pane is dead (status %d, %s)", - WEXITSTATUS(wp->status), - tim); - } else if (WIFSIGNALED(wp->status)) { - screen_write_nputs(&ctx, -1, &gc, - "Pane is dead (signal %s, %s)", - sig2name(WTERMSIG(wp->status)), - tim); + s = options_get_string(wp->options, "remain-on-exit-format"); + if (*s != '\0') { + screen_write_start_pane(&ctx, wp, &wp->base); + screen_write_scrollregion(&ctx, 0, sy - 1); + screen_write_cursormove(&ctx, 0, sy - 1, 0); + screen_write_linefeed(&ctx, 1, 8); + memcpy(&gc, &grid_default_cell, sizeof gc); + + expanded = format_single(NULL, s, NULL, NULL, NULL, wp); + format_draw(&ctx, &gc, sx, expanded, NULL, 0); + free(expanded); + + screen_write_stop(&ctx); } + wp->base.mode &= ~MODE_CURSOR; - screen_write_stop(&ctx); wp->flags |= PANE_REDRAW; return; } diff --git a/tmux.1 b/tmux.1 index 215d825c..d204e454 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4468,6 +4468,11 @@ The pane may be reactivated with the .Ic respawn-pane command. .Pp +.It Ic remain-on-exit-format Ar string +Set the text shown at the bottom of exited panes when +.Ic remain-on-exit +is enabled. +.Pp .It Xo Ic synchronize-panes .Op Ic on | off .Xc @@ -5117,7 +5122,9 @@ The following variables are available, where appropriate: .It Li "pane_current_command" Ta "" Ta "Current command if available" .It Li "pane_current_path" Ta "" Ta "Current path if available" .It Li "pane_dead" Ta "" Ta "1 if pane is dead" +.It Li "pane_dead_signal" Ta "" Ta "Exit signal of process in dead pane" .It Li "pane_dead_status" Ta "" Ta "Exit status of process in dead pane" +.It Li "pane_dead_time" Ta "" Ta "Exit time of process in dead pane" .It Li "pane_fg" Ta "" Ta "Pane foreground colour" .It Li "pane_format" Ta "" Ta "1 if format is for a pane" .It Li "pane_height" Ta "" Ta "Height of pane" diff --git a/tmux.h b/tmux.h index 0700f1b9..77c3b176 100644 --- a/tmux.h +++ b/tmux.h @@ -1026,6 +1026,7 @@ struct window_pane { pid_t pid; char tty[TTY_NAME_MAX]; int status; + struct timeval dead_time; int fd; struct bufferevent *event; -- cgit From cf217fa6181013bb70f77d6a50565e086205c5f4 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 8 Mar 2022 21:58:37 +0000 Subject: Fix user hooks (which are strings not arrays). --- notify.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 48 insertions(+), 15 deletions(-) diff --git a/notify.c b/notify.c index 2510a394..f5342710 100644 --- a/notify.c +++ b/notify.c @@ -35,18 +35,37 @@ struct notify_entry { int pane; }; +static struct cmdq_item * +notify_insert_one_hook(struct cmdq_item *item, struct notify_entry *ne, + struct cmd_list *cmdlist, struct cmdq_state *state) +{ + struct cmdq_item *new_item; + char *s; + + if (cmdlist == NULL) + return (item); + if (log_get_level() != 0) { + s = cmd_list_print(cmdlist, 0); + log_debug("%s: hook %s is: %s", __func__, ne->name, s); + free (s); + } + new_item = cmdq_get_command(cmdlist, state); + return (cmdq_insert_after(item, new_item)); +} + static void notify_insert_hook(struct cmdq_item *item, struct notify_entry *ne) { struct cmd_find_state fs; struct options *oo; - struct cmdq_item *new_item; - struct cmdq_state *new_state; + struct cmdq_state *state; struct options_entry *o; struct options_array_item *a; struct cmd_list *cmdlist; + const char *value; + struct cmd_parse_result *pr; - log_debug("%s: %s", __func__, ne->name); + log_debug("%s: inserting hook %s", __func__, ne->name); cmd_find_clear_state(&fs, 0); if (cmd_find_empty_state(&ne->fs) || !cmd_find_valid_state(&ne->fs)) @@ -67,23 +86,37 @@ notify_insert_hook(struct cmdq_item *item, struct notify_entry *ne) oo = fs.wl->window->options; o = options_get(oo, ne->name); } - if (o == NULL) + if (o == NULL) { + log_debug("%s: hook %s not found", __func__, ne->name); return; + } - new_state = cmdq_new_state(&fs, NULL, CMDQ_STATE_NOHOOKS); - cmdq_add_formats(new_state, ne->formats); - - a = options_array_first(o); - while (a != NULL) { - cmdlist = options_array_item_value(a)->cmdlist; - if (cmdlist != NULL) { - new_item = cmdq_get_command(cmdlist, new_state); - item = cmdq_insert_after(item, new_item); + state = cmdq_new_state(&fs, NULL, CMDQ_STATE_NOHOOKS); + cmdq_add_formats(state, ne->formats); + + if (*ne->name == '@') { + value = options_get_string(oo, ne->name); + pr = cmd_parse_from_string(value, NULL); + switch (pr->status) { + case CMD_PARSE_ERROR: + log_debug("%s: can't parse hook %s: %s", __func__, + ne->name, pr->error); + free(pr->error); + break; + case CMD_PARSE_SUCCESS: + notify_insert_one_hook(item, ne, pr->cmdlist, state); + break; + } + } else { + a = options_array_first(o); + while (a != NULL) { + cmdlist = options_array_item_value(a)->cmdlist; + item = notify_insert_one_hook(item, ne, cmdlist, state); + a = options_array_next(a); } - a = options_array_next(a); } - cmdq_free_state(new_state); + cmdq_free_state(state); } static enum cmd_retval -- cgit From bfbe972225d10be7f6eaca22d88d90df4fae09c9 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 8 Mar 2022 22:14:25 +0000 Subject: With -f use percentages of window size not pane size, GitHub issue 2866. --- cmd-split-window.c | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/cmd-split-window.c b/cmd-split-window.c index 0f82e648..4c8054ec 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -61,6 +61,7 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) struct client *tc = cmdq_get_target_client(item); struct session *s = target->s; struct winlink *wl = target->wl; + struct window *w = wl->window; struct window_pane *wp = target->wp, *new_wp; enum layout_type type; struct layout_cell *lc; @@ -87,10 +88,17 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) cmdq_error(item, "percentage %s", errstr); return (CMD_RETURN_ERROR); } - if (type == LAYOUT_TOPBOTTOM) - size = (wp->sy * percentage) / 100; - else - size = (wp->sx * percentage) / 100; + if (args_has(args, 'f')) { + if (type == LAYOUT_TOPBOTTOM) + size = (w->sy * percentage) / 100; + else + size = (w->sx * percentage) / 100; + } else { + if (type == LAYOUT_TOPBOTTOM) + size = (wp->sy * percentage) / 100; + else + size = (wp->sx * percentage) / 100; + } } else { size = args_strtonum(args, 'l', 0, INT_MAX, &cause); if (cause != NULL) { @@ -106,10 +114,17 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) free(cause); return (CMD_RETURN_ERROR); } - if (type == LAYOUT_TOPBOTTOM) - size = (wp->sy * percentage) / 100; - else - size = (wp->sx * percentage) / 100; + if (args_has(args, 'f')) { + if (type == LAYOUT_TOPBOTTOM) + size = (w->sy * percentage) / 100; + else + size = (w->sx * percentage) / 100; + } else { + if (type == LAYOUT_TOPBOTTOM) + size = (wp->sy * percentage) / 100; + else + size = (wp->sx * percentage) / 100; + } } else size = -1; -- cgit From e6e737ac0bf9a5be729b5c71f3a582355432d041 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 16 Mar 2022 17:00:17 +0000 Subject: Add an option to set the character used for unused areas of the terminal, GitHub issue 3110. --- options-table.c | 7 ++++++ options.c | 6 ++++++ screen-redraw.c | 13 ++++++++---- tmux.1 | 3 +++ tmux.h | 66 +++++++++++++++++++++++++++++---------------------------- window.c | 19 +++++++++++++++++ 6 files changed, 78 insertions(+), 36 deletions(-) diff --git a/options-table.c b/options-table.c index d1bc3577..46b96ec4 100644 --- a/options-table.c +++ b/options-table.c @@ -882,6 +882,13 @@ const struct options_table_entry options_table[] = { .text = "Style of the marked line in copy mode." }, + { .name = "fill-character", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_WINDOW, + .default_str = "", + .text = "Character used to fill unused parts of window." + }, + { .name = "main-pane-height", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, diff --git a/options.c b/options.c index 865ab01f..f9cf2afd 100644 --- a/options.c +++ b/options.c @@ -1108,6 +1108,8 @@ options_push_changes(const char *name) struct window_pane *wp; int c; + log_debug("%s: %s", __func__, name); + if (strcmp(name, "automatic-rename") == 0) { RB_FOREACH(w, windows, &windows) { if (w->active == NULL) @@ -1130,6 +1132,10 @@ options_push_changes(const char *name) &wp->screen->default_mode); } } + if (strcmp(name, "fill-character") == 0) { + RB_FOREACH(w, windows, &windows) + window_set_fill_character(w); + } if (strcmp(name, "key-table") == 0) { TAILQ_FOREACH(loop, &clients, entry) server_client_set_key_table(loop, NULL); diff --git a/screen-redraw.c b/screen-redraw.c index ef79d9aa..c4906ab8 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -47,11 +47,16 @@ enum screen_redraw_border_type { /* Get cell border character. */ static void -screen_redraw_border_set(struct window_pane *wp, enum pane_lines pane_lines, - int cell_type, struct grid_cell *gc) +screen_redraw_border_set(struct window *w, struct window_pane *wp, + enum pane_lines pane_lines, int cell_type, struct grid_cell *gc) { u_int idx; + if (cell_type == CELL_OUTSIDE && w->fill_character != NULL) { + utf8_copy(&gc->data, &w->fill_character[0]); + return; + } + switch (pane_lines) { case PANE_LINES_NUMBER: if (cell_type == CELL_OUTSIDE) { @@ -409,7 +414,7 @@ screen_redraw_make_pane_status(struct client *c, struct window_pane *wp, else py = wp->yoff + wp->sy; cell_type = screen_redraw_type_of_cell(c, px, py, pane_status); - screen_redraw_border_set(wp, pane_lines, cell_type, &gc); + screen_redraw_border_set(w, wp, pane_lines, cell_type, &gc); screen_write_cell(&ctx, &gc); } gc.attr &= ~GRID_ATTR_CHARSET; @@ -690,7 +695,7 @@ screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j) screen_redraw_check_is(x, y, pane_status, marked_pane.wp)) gc.attr ^= GRID_ATTR_REVERSE; } - screen_redraw_border_set(wp, ctx->pane_lines, cell_type, &gc); + screen_redraw_border_set(w, wp, ctx->pane_lines, cell_type, &gc); if (cell_type == CELL_TOPBOTTOM && (c->flags & CLIENT_UTF8) && diff --git a/tmux.1 b/tmux.1 index d204e454..6c9ff820 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4114,6 +4114,9 @@ Set clock colour. .Xc Set clock hour format. .Pp +.It Ic fill-character Ar character +Set the character used to fill areas of the terminal unused by a window. +.Pp .It Ic main-pane-height Ar height .It Ic main-pane-width Ar width Set the width or height of the main (left or top) pane in the diff --git a/tmux.h b/tmux.h index 77c3b176..66a264f1 100644 --- a/tmux.h +++ b/tmux.h @@ -1069,40 +1069,41 @@ RB_HEAD(window_pane_tree, window_pane); /* Window structure. */ struct window { - u_int id; - void *latest; + u_int id; + void *latest; - char *name; - struct event name_event; - struct timeval name_time; + char *name; + struct event name_event; + struct timeval name_time; - struct event alerts_timer; - struct event offset_timer; + struct event alerts_timer; + struct event offset_timer; - struct timeval activity_time; + struct timeval activity_time; - struct window_pane *active; - struct window_pane *last; - struct window_panes panes; + struct window_pane *active; + struct window_pane *last; + struct window_panes panes; - int lastlayout; - struct layout_cell *layout_root; - struct layout_cell *saved_layout_root; - char *old_layout; + int lastlayout; + struct layout_cell *layout_root; + struct layout_cell *saved_layout_root; + char *old_layout; - u_int sx; - u_int sy; - u_int manual_sx; - u_int manual_sy; - u_int xpixel; - u_int ypixel; + u_int sx; + u_int sy; + u_int manual_sx; + u_int manual_sy; + u_int xpixel; + u_int ypixel; - u_int new_sx; - u_int new_sy; - u_int new_xpixel; - u_int new_ypixel; + u_int new_sx; + u_int new_sy; + u_int new_xpixel; + u_int new_ypixel; - int flags; + struct utf8_data *fill_character; + int flags; #define WINDOW_BELL 0x1 #define WINDOW_ACTIVITY 0x2 #define WINDOW_SILENCE 0x4 @@ -1111,15 +1112,15 @@ struct window { #define WINDOW_RESIZE 0x20 #define WINDOW_ALERTFLAGS (WINDOW_BELL|WINDOW_ACTIVITY|WINDOW_SILENCE) - int alerts_queued; - TAILQ_ENTRY(window) alerts_entry; + int alerts_queued; + TAILQ_ENTRY(window) alerts_entry; - struct options *options; + struct options *options; - u_int references; - TAILQ_HEAD(, winlink) winlinks; + u_int references; + TAILQ_HEAD(, winlink) winlinks; - RB_ENTRY(window) entry; + RB_ENTRY(window) entry; }; RB_HEAD(windows, window); @@ -2976,6 +2977,7 @@ void *window_pane_get_new_data(struct window_pane *, struct window_pane_offset *, size_t *); void window_pane_update_used_data(struct window_pane *, struct window_pane_offset *, size_t); +void window_set_fill_character(struct window *); /* layout.c */ u_int layout_count_cells(struct layout_cell *); diff --git a/window.c b/window.c index 2ca3833c..b14a9c60 100644 --- a/window.c +++ b/window.c @@ -331,6 +331,7 @@ window_create(u_int sx, u_int sy, u_int xpixel, u_int ypixel) w->id = next_window_id++; RB_INSERT(windows, &windows, w); + window_set_fill_character(w); window_update_activity(w); log_debug("%s: @%u create %ux%u (%ux%u)", __func__, w->id, sx, sy, @@ -362,6 +363,7 @@ window_destroy(struct window *w) event_del(&w->offset_timer); options_free(w->options); + free(w->fill_character); free(w->name); free(w); @@ -1589,3 +1591,20 @@ window_pane_update_used_data(struct window_pane *wp, size = EVBUFFER_LENGTH(wp->event->input) - used; wpo->used += size; } + +void +window_set_fill_character(struct window *w) +{ + const char *value; + struct utf8_data *ud; + + free(w->fill_character); + w->fill_character = NULL; + + value = options_get_string(w->options, "fill-character"); + if (*value != '\0' && utf8_isvalid(value)) { + ud = utf8_fromcstr(value); + if (ud != NULL && ud[0].width == 1) + w->fill_character = ud; + } +} -- cgit From 10d689e7354f789f951016f7f4d57a0c2d14e124 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 17 Mar 2022 11:35:37 +0000 Subject: Add an option (scroll-on-clear) to control if tmux scrolls into history on clear, from Robert Lange in GitHub issue 3121. --- options-table.c | 8 ++++++++ screen-write.c | 6 +++++- tmux.1 | 6 ++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/options-table.c b/options-table.c index 46b96ec4..b0870118 100644 --- a/options-table.c +++ b/options-table.c @@ -1081,6 +1081,14 @@ const struct options_table_entry options_table[] = { "remain-on-exit is enabled." }, + { .name = "scroll-on-clear", + .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, + .default_num = 1, + .text = "Whether the contents of the screen should be scrolled into" + "history when clearing the whole screen." + }, + { .name = "synchronize-panes", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, diff --git a/screen-write.c b/screen-write.c index 0d70f668..aa898f78 100644 --- a/screen-write.c +++ b/screen-write.c @@ -1427,7 +1427,11 @@ screen_write_clearendofscreen(struct screen_write_ctx *ctx, u_int bg) ttyctx.bg = bg; /* Scroll into history if it is enabled and clearing entire screen. */ - if (s->cx == 0 && s->cy == 0 && (gd->flags & GRID_HISTORY)) + if (s->cx == 0 && + s->cy == 0 && + (gd->flags & GRID_HISTORY) && + ctx->wp != NULL && + options_get_number(ctx->wp->options, "scroll-on-clear")) grid_view_clear_history(gd, bg); else { if (s->cx <= sx - 1) diff --git a/tmux.1 b/tmux.1 index 6c9ff820..bc5c1a55 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4476,6 +4476,12 @@ Set the text shown at the bottom of exited panes when .Ic remain-on-exit is enabled. .Pp +.It Xo Ic scroll-on-clear +.Op Ic on | off +.Xc +When the entire screen is cleared and this option is on, scroll the contents of +the screen into history before clearing it. +.Pp .It Xo Ic synchronize-panes .Op Ic on | off .Xc -- cgit From 6a1706a62fb2442326cd37d84ff8b0d39bf0b7a6 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 17 Mar 2022 13:39:13 +0000 Subject: Check scroll-on-clear for ED also. --- screen-write.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/screen-write.c b/screen-write.c index aa898f78..6b6a750e 100644 --- a/screen-write.c +++ b/screen-write.c @@ -1479,7 +1479,9 @@ screen_write_clearscreen(struct screen_write_ctx *ctx, u_int bg) ttyctx.bg = bg; /* Scroll into history if it is enabled. */ - if (s->grid->flags & GRID_HISTORY) + if ((s->grid->flags & GRID_HISTORY) && + ctx->wp != NULL && + options_get_number(ctx->wp->options, "scroll-on-clear")) grid_view_clear_history(s->grid, bg); else grid_view_clear(s->grid, 0, 0, sx, sy, bg); -- cgit From 792d13af49f2550a9a8d11b0099528628957a1a0 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 24 Mar 2022 09:05:57 +0000 Subject: Add a capability for OSC 7 and use it similarly to how the title is set (and controlled by the same set-titles option). GitHub issue 3127. --- server-client.c | 25 ++++++++++++++++++++++++- tmux.1 | 7 +++++++ tmux.h | 3 +++ tty-features.c | 13 +++++++++++++ tty-term.c | 1 + tty.c | 12 ++++++++++++ 6 files changed, 60 insertions(+), 1 deletion(-) diff --git a/server-client.c b/server-client.c index f8236066..a2a367be 100644 --- a/server-client.c +++ b/server-client.c @@ -43,6 +43,7 @@ static void server_client_check_exit(struct client *); static void server_client_check_redraw(struct client *); static void server_client_check_modes(struct client *); static void server_client_set_title(struct client *); +static void server_client_set_path(struct client *); static void server_client_reset_state(struct client *); static int server_client_assume_paste(struct session *); static void server_client_update_latest(struct client *); @@ -2603,8 +2604,10 @@ server_client_check_redraw(struct client *c) } if (c->flags & CLIENT_ALLREDRAWFLAGS) { - if (options_get_number(s->options, "set-titles")) + if (options_get_number(s->options, "set-titles")) { server_client_set_title(c); + server_client_set_path(c); + } screen_redraw_screen(c); } @@ -2650,6 +2653,26 @@ server_client_set_title(struct client *c) format_free(ft); } +/* Set client path. */ +static void +server_client_set_path(struct client *c) +{ + struct session *s = c->session; + const char *path; + + if (s->curw == NULL) + return; + if (s->curw->window->active->base.path == NULL) + path = ""; + else + path = s->curw->window->active->base.path; + if (c->path == NULL || strcmp(path, c->path) != 0) { + free(c->path); + c->path = xstrdup(path); + tty_set_path(&c->tty, c->path); + } +} + /* Dispatch message from client. */ static void server_client_dispatch(struct imsg *imsg, void *arg) diff --git a/tmux.1 b/tmux.1 index bc5c1a55..5c8a3bd9 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3624,6 +3624,8 @@ Supports DECSLRM margins. Supports .Xr xterm 1 mouse sequences. +.It osc7 +Supports the OSC 7 working directory extension. .It overline Supports the overline SGR attribute. .It rectfill @@ -6391,6 +6393,11 @@ $ printf '\e033[4 q' If .Em Se is not set, \&Ss with argument 0 will be used to reset the cursor style instead. +.It Em \&Swd +Set the opening sequence for the working directory notification. +The sequence is terminated using the standard +.Em fsl +capability. .It Em \&Sync Start (parameter is 1) or end (parameter is 2) a synchronized update. .It Em \&Tc diff --git a/tmux.h b/tmux.h index 66a264f1..3bde14a2 100644 --- a/tmux.h +++ b/tmux.h @@ -539,6 +539,7 @@ enum tty_code_code { TTYC_SMULX, TTYC_SMXX, TTYC_SS, + TTYC_SWD, TTYC_SYNC, TTYC_TC, TTYC_TSL, @@ -1708,6 +1709,7 @@ struct client { struct format_job_tree *jobs; char *title; + char *path; const char *cwd; char *term_name; @@ -2258,6 +2260,7 @@ void tty_start_tty(struct tty *); void tty_send_requests(struct tty *); void tty_stop_tty(struct tty *); void tty_set_title(struct tty *, const char *); +void tty_set_path(struct tty *, const char *); void tty_update_mode(struct tty *, int, struct screen *); void tty_draw_line(struct tty *, struct screen *, u_int, u_int, u_int, u_int, u_int, const struct grid_cell *, struct colour_palette *); diff --git a/tty-features.c b/tty-features.c index 3aca2520..4d83a465 100644 --- a/tty-features.c +++ b/tty-features.c @@ -53,6 +53,18 @@ static const struct tty_feature tty_feature_title = { 0 }; +/* Terminal has OSC 7 working directory. */ +static const char *tty_feature_osc7_capabilities[] = { + "Swd=\\E]7;", + "fsl=\\a", + NULL +}; +static const struct tty_feature tty_feature_osc7 = { + "osc7", + tty_feature_osc7_capabilities, + 0 +}; + /* Terminal has mouse support. */ static const char *tty_feature_mouse_capabilities[] = { "kmous=\\E[M", @@ -249,6 +261,7 @@ static const struct tty_feature *tty_features[] = { &tty_feature_focus, &tty_feature_margins, &tty_feature_mouse, + &tty_feature_osc7, &tty_feature_overline, &tty_feature_rectfill, &tty_feature_rgb, diff --git a/tty-term.c b/tty-term.c index b3d01850..61eda56d 100644 --- a/tty-term.c +++ b/tty-term.c @@ -274,6 +274,7 @@ static const struct tty_term_code_entry tty_term_codes[] = { [TTYC_SMUL] = { TTYCODE_STRING, "smul" }, [TTYC_SMXX] = { TTYCODE_STRING, "smxx" }, [TTYC_SS] = { TTYCODE_STRING, "Ss" }, + [TTYC_SWD] = { TTYCODE_STRING, "Swd" }, [TTYC_SYNC] = { TTYCODE_STRING, "Sync" }, [TTYC_TC] = { TTYCODE_FLAG, "Tc" }, [TTYC_TSL] = { TTYCODE_STRING, "tsl" }, diff --git a/tty.c b/tty.c index 40735ceb..ea10e61e 100644 --- a/tty.c +++ b/tty.c @@ -655,6 +655,18 @@ tty_set_title(struct tty *tty, const char *title) tty_putcode(tty, TTYC_FSL); } +void +tty_set_path(struct tty *tty, const char *title) +{ + if (!tty_term_has(tty->term, TTYC_SWD) || + !tty_term_has(tty->term, TTYC_FSL)) + return; + + tty_putcode(tty, TTYC_SWD); + tty_puts(tty, title); + tty_putcode(tty, TTYC_FSL); +} + static void tty_force_cursor_colour(struct tty *tty, int c) { -- cgit From 938130bc6925808681cbc003a392ccce2a4455c1 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 24 Mar 2022 12:07:25 +0000 Subject: Add unit (milliseconds) to escape-time, show unset colours as "none" rather than "invalid" and don't show the same text twice for user options in customize mode. --- colour.c | 2 +- options-table.c | 1 + window-customize.c | 4 +--- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/colour.c b/colour.c index 6ede25da..a282d182 100644 --- a/colour.c +++ b/colour.c @@ -128,7 +128,7 @@ colour_tostring(int c) u_char r, g, b; if (c == -1) - return ("invalid"); + return ("none"); if (c & COLOUR_FLAG_RGB) { colour_split_rgb(c, &r, &g, &b); diff --git a/options-table.c b/options-table.c index b0870118..4e68af00 100644 --- a/options-table.c +++ b/options-table.c @@ -278,6 +278,7 @@ const struct options_table_entry options_table[] = { .minimum = 0, .maximum = INT_MAX, .default_num = 500, + .unit = "milliseconds", .text = "Time to wait before assuming a key is Escape." }, diff --git a/window-customize.c b/window-customize.c index 98387e50..4a16e90c 100644 --- a/window-customize.c +++ b/window-customize.c @@ -680,9 +680,7 @@ window_customize_draw_option(struct window_customize_modedata *data, } ft = format_create_from_state(NULL, NULL, &fs); - if (oe == NULL) - text = "This is a user option."; - else if (oe->text == NULL) + if (oe == NULL || oe->text == NULL) text = "This option doesn't have a description."; else text = oe->text; -- cgit From 422fcd294a6de4ba940144ede9ebb86a9ab6e41f Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 25 Mar 2022 06:14:42 +0000 Subject: Fix exit message if creating socket fails. --- server.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server.c b/server.c index eb6e9610..f99dfea3 100644 --- a/server.c +++ b/server.c @@ -228,10 +228,10 @@ server_start(struct tmuxproc *client, int flags, struct event_base *base, if (cause != NULL) { if (c != NULL) { - cmdq_append(c, cmdq_get_error(cause)); + c->exit_message = cause; c->flags |= CLIENT_EXIT; - } - free(cause); + } else + free(cause); } evtimer_set(&server_ev_tidy, server_tidy_event, NULL); -- cgit From 207b1bc385480cf453f2994a12f65706977b6c0f Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 28 Mar 2022 07:40:57 +0000 Subject: Report error if creating socket fails with -D. --- server.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/server.c b/server.c index f99dfea3..f46dd056 100644 --- a/server.c +++ b/server.c @@ -230,8 +230,10 @@ server_start(struct tmuxproc *client, int flags, struct event_base *base, if (c != NULL) { c->exit_message = cause; c->flags |= CLIENT_EXIT; - } else - free(cause); + } else { + fprintf(stderr, "%s\n", cause); + exit(1); + } } evtimer_set(&server_ev_tidy, server_tidy_event, NULL); -- cgit From fc7f1e7acb3539a43df46136e971f770515e0b0d Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 28 Mar 2022 08:42:13 +0100 Subject: Add support for systemd socket activation (where systemd creates the Unix domain socket for tmux rather than tmux creating it). Build with --enable-systemd. From Julien Moutinho in GitHub issue 3119. --- Makefile.am | 5 +++++ compat.h | 5 +++++ compat/systemd.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ configure.ac | 25 ++++++++++++++++++++++++ server.c | 6 +++++- tmux.h | 1 + 6 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 compat/systemd.c diff --git a/Makefile.am b/Makefile.am index 68494932..5bdd9d5f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -204,6 +204,11 @@ if NEED_FORKPTY nodist_tmux_SOURCES += compat/forkpty-@PLATFORM@.c endif +# Add compat file for systemd. +if HAVE_SYSTEMD +nodist_tmux_SOURCES += compat/systemd.c +endif + # Add compat file for utf8proc. if HAVE_UTF8PROC nodist_tmux_SOURCES += compat/utf8proc.c diff --git a/compat.h b/compat.h index be726831..6eb97619 100644 --- a/compat.h +++ b/compat.h @@ -421,6 +421,11 @@ void *reallocarray(void *, size_t, size_t); void *recallocarray(void *, size_t, size_t, size_t); #endif +#ifdef HAVE_SYSTEMD +/* systemd.c */ +int systemd_create_socket(int, char **); +#endif + #ifdef HAVE_UTF8PROC /* utf8proc.c */ int utf8proc_wcwidth(wchar_t); diff --git a/compat/systemd.c b/compat/systemd.c new file mode 100644 index 00000000..7317e43a --- /dev/null +++ b/compat/systemd.c @@ -0,0 +1,58 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2022 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include + +#include "tmux.h" + +int +systemd_create_socket(int flags, char **cause) +{ + int fds; + int fd; + struct sockaddr_un sa; + int addrlen = sizeof sa; + + fds = sd_listen_fds(0); + if (fds > 1) { /* too many file descriptors */ + errno = E2BIG; + goto fail; + } + + if (fds == 1) { /* socket-activated */ + fd = SD_LISTEN_FDS_START; + if (!sd_is_socket_unix(fd, SOCK_STREAM, 1, NULL, 0)) { + errno = EPFNOSUPPORT; + goto fail; + } + if (getsockname(fd, (struct sockaddr *)&sa, &addrlen) == -1) + goto fail; + socket_path = xstrdup(sa.sun_path); + return (fd); + } + + return (server_create_socket(flags, cause)); + +fail: + if (cause != NULL) + xasprintf(cause, "systemd socket error (%s)", strerror(errno)); + return (-1); +} diff --git a/configure.ac b/configure.ac index e473f141..276f71c8 100644 --- a/configure.ac +++ b/configure.ac @@ -390,6 +390,31 @@ if test "x$enable_utf8proc" = xyes; then fi AM_CONDITIONAL(HAVE_UTF8PROC, [test "x$enable_utf8proc" = xyes]) +# Check for systemd support. +AC_ARG_ENABLE( + systemd, + AS_HELP_STRING(--enable-systemd, enable systemd integration) +) +if test x"$enable_systemd" = xyes; then + PKG_CHECK_MODULES( + SYSTEMD, + libsystemd, + [ + AM_CPPFLAGS="$SYSTEMD_CFLAGS $AM_CPPFLAGS" + CPPFLAGS="$AM_CPPFLAGS $SAVED_CPPFLAGS" + LIBS="$SYSTEMD_LIBS $LIBS" + found_systemd=yes + ], + found_systemd=no + ) + if test "x$found_systemd" = xyes; then + AC_DEFINE(HAVE_SYSTEMD) + else + AC_MSG_ERROR("systemd not found") + fi +fi +AM_CONDITIONAL(HAVE_SYSTEMD, [test "x$found_systemd" = xyes]) + # Check for b64_ntop. If we have b64_ntop, we assume b64_pton as well. AC_MSG_CHECKING(for b64_ntop) AC_LINK_IFELSE([AC_LANG_PROGRAM( diff --git a/server.c b/server.c index 2db5a8d8..bf3a8361 100644 --- a/server.c +++ b/server.c @@ -100,7 +100,7 @@ server_check_marked(void) } /* Create server socket. */ -static int +int server_create_socket(int flags, char **cause) { struct sockaddr_un sa; @@ -214,7 +214,11 @@ server_start(struct tmuxproc *client, int flags, struct event_base *base, gettimeofday(&start_time, NULL); +#ifdef HAVE_SYSTEMD + server_fd = systemd_create_socket(flags, &cause); +#else server_fd = server_create_socket(flags, &cause); +#endif if (server_fd != -1) server_update_socket(); if (~flags & CLIENT_NOFORK) diff --git a/tmux.h b/tmux.h index 370c7773..f16b5250 100644 --- a/tmux.h +++ b/tmux.h @@ -2583,6 +2583,7 @@ int server_start(struct tmuxproc *, int, struct event_base *, int, char *); void server_update_socket(void); void server_add_accept(int); void printflike(1, 2) server_add_message(const char *, ...); +int server_create_socket(int, char **); /* server-client.c */ RB_PROTOTYPE(client_windows, client_window, entry, server_client_window_cmp); -- cgit From ded695504faad8f5d6430731f0bcb9beb0e1430a Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 30 Mar 2022 07:05:26 +0000 Subject: Capture up to used size not available size for each line. --- grid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grid.c b/grid.c index 91dc8f42..1109ac58 100644 --- a/grid.c +++ b/grid.c @@ -1003,7 +1003,7 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx, gl = grid_peek_line(gd, py); for (xx = px; xx < px + nx; xx++) { - if (gl == NULL || xx >= gl->cellsize) + if (gl == NULL || xx >= gl->cellused) break; grid_get_cell(gd, xx, py, &gc); if (gc.flags & GRID_FLAG_PADDING) -- cgit From 2d9f4ca9a1368f47ddd6e121c9b1a7822884a31a Mon Sep 17 00:00:00 2001 From: naddy Date: Thu, 31 Mar 2022 17:27:27 +0000 Subject: man pages: add missing commas between subordinate and main clauses jmc@ dislikes a comma before "then" in a conditional, so leave those untouched. ok jmc@ --- tmux.1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tmux.1 b/tmux.1 index 5c8a3bd9..9342004f 100644 --- a/tmux.1 +++ b/tmux.1 @@ -43,7 +43,7 @@ then later reattached. .Pp When .Nm -is started it creates a new +is started, it creates a new .Em session with a single .Em window @@ -5915,7 +5915,7 @@ does not surround the popup by a border. sets the type of border line for the popup. When .Fl B -is specified the +is specified, the .Fl b option is ignored. See @@ -6636,7 +6636,7 @@ are replaced with underscores For input, .Nm always runs with a UTF-8 locale. -If en_US.UTF-8 is provided by the operating system it is used and +If en_US.UTF-8 is provided by the operating system, it is used and .Ev LC_CTYPE is ignored for input. Otherwise, -- cgit From 65c0af76fbe06bdf89431575a88ba22ea5b88d90 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 1 Apr 2022 10:11:59 +0000 Subject: Preserve CRLF flag when respawning. --- screen.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/screen.c b/screen.c index da108866..912ab126 100644 --- a/screen.c +++ b/screen.c @@ -104,7 +104,7 @@ screen_reinit(struct screen *s) s->rupper = 0; s->rlower = screen_size_y(s) - 1; - s->mode = MODE_CURSOR|MODE_WRAP; + s->mode = MODE_CURSOR|MODE_WRAP|(s->mode & MODE_CRLF); if (options_get_number(global_options, "extended-keys") == 2) s->mode |= MODE_KEXTENDED; -- cgit From 921be619303fdad6db3c46662fc0cde2903c9535 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 3 Feb 2022 11:06:11 +0000 Subject: Adjust size given to resize-pane for pane status line, GitHub issue 3050. --- cmd-resize-pane.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/cmd-resize-pane.c b/cmd-resize-pane.c index 81744f72..c9439441 100644 --- a/cmd-resize-pane.c +++ b/cmd-resize-pane.c @@ -60,7 +60,7 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item) const char *errstr; char *cause; u_int adjust; - int x, y; + int x, y, status; struct grid *gd = wp->base.grid; if (args_has(args, 'T')) { @@ -121,6 +121,17 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item) free(cause); return (CMD_RETURN_ERROR); } + status = options_get_number(w->options, "pane-border-status"); + switch (status) { + case PANE_STATUS_TOP: + if (y != INT_MAX && wp->yoff == 1) + y++; + break; + case PANE_STATUS_BOTTOM: + if (y != INT_MAX && wp->yoff + wp->sy == w->sy - 1) + y++; + break; + } layout_resize_pane_to(wp, LAYOUT_TOPBOTTOM, y); } -- cgit From 9efa4199554b4b5613a19bf5b4d9036869978b4c Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 4 Feb 2022 11:57:22 +0000 Subject: Use ACS for pane border indicators so they work with different line types, from Thomas Adam. --- screen-redraw.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/screen-redraw.c b/screen-redraw.c index 8dd75f40..ef79d9aa 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -727,8 +727,10 @@ screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j) border == SCREEN_REDRAW_BORDER_RIGHT) || (cell_type == CELL_RIGHTJOIN && border == SCREEN_REDRAW_BORDER_LEFT)))) && - screen_redraw_check_is(x, y, pane_status, active)) + screen_redraw_check_is(x, y, pane_status, active)) { + gc.attr |= GRID_ATTR_CHARSET; utf8_set(&gc.data, BORDER_MARKERS[border]); + } } tty_cell(tty, &gc, &grid_default_cell, NULL); -- cgit From 2adbe3ec1606806e31c74507cfa0225327e27225 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 14 Feb 2022 09:10:48 +0000 Subject: Do not return error with -q, GitHub issue 3065. --- cmd-show-options.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cmd-show-options.c b/cmd-show-options.c index 90226ad3..252a33c6 100644 --- a/cmd-show-options.c +++ b/cmd-show-options.c @@ -102,7 +102,7 @@ cmd_show_options_exec(struct cmd *self, struct cmdq_item *item) name = options_match(argument, &idx, &ambiguous); if (name == NULL) { if (args_has(args, 'q')) - goto fail; + goto out; if (ambiguous) cmdq_error(item, "ambiguous option: %s", argument); else @@ -113,7 +113,7 @@ cmd_show_options_exec(struct cmd *self, struct cmdq_item *item) &cause); if (scope == OPTIONS_TABLE_NONE) { if (args_has(args, 'q')) - goto fail; + goto out; cmdq_error(item, "%s", cause); free(cause); goto fail; @@ -128,11 +128,12 @@ cmd_show_options_exec(struct cmd *self, struct cmdq_item *item) cmd_show_options_print(self, item, o, idx, parent); else if (*name == '@') { if (args_has(args, 'q')) - goto fail; + goto out; cmdq_error(item, "invalid option: %s", argument); goto fail; } +out: free(name); free(argument); return (CMD_RETURN_NORMAL); -- cgit From 190b88fcabc413485584bffed6d8ed8b61db1b1f Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 15 Feb 2022 13:03:02 +0000 Subject: Do not create a buffer from an OSC 52 response if we have not sent a query. --- cmd-refresh-client.c | 2 +- tmux.h | 4 +++- tty-keys.c | 5 +++++ tty.c | 27 ++++++++++++++++++++++++++- 4 files changed, 35 insertions(+), 3 deletions(-) diff --git a/cmd-refresh-client.c b/cmd-refresh-client.c index 821558ae..2af9cb46 100644 --- a/cmd-refresh-client.c +++ b/cmd-refresh-client.c @@ -225,7 +225,7 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item) } if (args_has(args, 'l')) { - tty_putcode_ptr2(&tc->tty, TTYC_MS, "", "?"); + tty_send_osc52_query(&tc->tty); return (CMD_RETURN_NORMAL); } diff --git a/tmux.h b/tmux.h index a4f484eb..313432e0 100644 --- a/tmux.h +++ b/tmux.h @@ -1278,6 +1278,7 @@ LIST_HEAD(tty_terms, tty_term); struct tty { struct client *client; struct event start_timer; + struct event query_timer; u_int sx; u_int sy; @@ -1321,7 +1322,7 @@ struct tty { #define TTY_NOBLOCK 0x8 #define TTY_STARTED 0x10 #define TTY_OPENED 0x20 -/* 0x40 unused */ +#define TTY_OSC52QUERY 0x40 #define TTY_BLOCK 0x80 #define TTY_HAVEDA 0x100 #define TTY_HAVEXDA 0x200 @@ -2174,6 +2175,7 @@ void tty_reset(struct tty *); void tty_region_off(struct tty *); void tty_margin_off(struct tty *); void tty_cursor(struct tty *, u_int, u_int); +void tty_send_osc52_query(struct tty *); void tty_putcode(struct tty *, enum tty_code_code); void tty_putcode1(struct tty *, enum tty_code_code, int); void tty_putcode2(struct tty *, enum tty_code_code, int, int); diff --git a/tty-keys.c b/tty-keys.c index 65b600c0..5d950078 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -1217,6 +1217,11 @@ tty_keys_clipboard(__unused struct tty *tty, const char *buf, size_t len, buf++; end--; + /* If we did not request this, ignore it. */ + if (~tty->flags & TTY_OSC52QUERY) + return (0); + tty->flags &= ~TTY_OSC52QUERY; + /* It has to be a string so copy it. */ copy = xmalloc(end + 1); memcpy(copy, buf, end); diff --git a/tty.c b/tty.c index 8410a64c..06b1bfb2 100644 --- a/tty.c +++ b/tty.c @@ -83,6 +83,8 @@ static void tty_check_overlay_range(struct tty *, u_int, u_int, u_int, #define TTY_BLOCK_START(tty) (1 + ((tty)->sx * (tty)->sy) * 8) #define TTY_BLOCK_STOP(tty) (1 + ((tty)->sx * (tty)->sy) / 8) +#define TTY_QUERY_TIMEOUT 5 + void tty_create_log(void) { @@ -307,7 +309,7 @@ tty_start_tty(struct tty *tty) { struct client *c = tty->client; struct termios tio; - struct timeval tv = { .tv_sec = 3 }; + struct timeval tv = { .tv_sec = TTY_QUERY_TIMEOUT }; setblocking(c->fd, 0); event_add(&tty->event_in, NULL); @@ -2917,3 +2919,26 @@ tty_default_attributes(struct tty *tty, const struct grid_cell *defaults, gc.bg = bg; tty_attributes(tty, &gc, defaults, palette); } + +static void +tty_query_timer_callback(__unused int fd, __unused short events, void *data) +{ + struct tty *tty = data; + + tty->flags &= ~TTY_OSC52QUERY; +} + +void +tty_send_osc52_query(struct tty *tty) +{ + struct timeval tv = { .tv_sec = TTY_QUERY_TIMEOUT }; + + if ((~tty->flags & TTY_STARTED) || (tty->flags & TTY_OSC52QUERY)) + return; + tty_putcode_ptr2(tty, TTYC_MS, "", "?"); + tty->flags |= TTY_OSC52QUERY; + + evtimer_set(&tty->query_timer, tty_query_timer_callback, tty); + evtimer_add(&tty->query_timer, &tv); +} + -- cgit From eabbc80b75ef8e02653c2728d7f646fc6a8f558f Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 15 Feb 2022 13:11:29 +0000 Subject: Add an option (default off) to control the passthrough escape sequence. Like set-clipboard and allow-rename it is safer to forbid this by default. --- input.c | 6 +++++- options-table.c | 20 ++++++++++++++------ tmux.1 | 7 +++++++ 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/input.c b/input.c index 16f31ad7..5adc694d 100644 --- a/input.c +++ b/input.c @@ -2235,15 +2235,19 @@ input_enter_dcs(struct input_ctx *ictx) static int input_dcs_dispatch(struct input_ctx *ictx) { + struct window_pane *wp = ictx->wp; struct screen_write_ctx *sctx = &ictx->ctx; u_char *buf = ictx->input_buf; size_t len = ictx->input_len; const char prefix[] = "tmux;"; const u_int prefixlen = (sizeof prefix) - 1; + if (wp == NULL) + return (0); if (ictx->flags & INPUT_DISCARD) return (0); - + if (!options_get_number(ictx->wp->options, "allow-passthrough")) + return (0); log_debug("%s: \"%s\"", __func__, buf); if (len >= prefixlen && strncmp(buf, prefix, prefixlen) == 0) diff --git a/options-table.c b/options-table.c index 94f8701e..c8ae046e 100644 --- a/options-table.c +++ b/options-table.c @@ -301,7 +301,7 @@ const struct options_table_entry options_table[] = { .choices = options_table_extended_keys_list, .default_num = 0, .text = "Whether to request extended key sequences from terminals " - "that support it." + "that support it." }, { .name = "focus-events", @@ -800,6 +800,14 @@ const struct options_table_entry options_table[] = { "linked to ('off')." }, + { .name = "allow-passthrough", + .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, + .default_num = 0, + .text = "Whether applications are allowed to use the escape sequence " + "to bypass tmux." + }, + { .name = "allow-rename", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, @@ -988,7 +996,7 @@ const struct options_table_entry options_table[] = { .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." + "these are only supported on terminals with UTF-8 support." }, { .name = "pane-border-status", @@ -1040,7 +1048,7 @@ const struct options_table_entry options_table[] = { .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." + "these are only supported on terminals with UTF-8 support." }, { .name = "remain-on-exit", @@ -1170,7 +1178,7 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_WINDOW, .default_num = 1, .text = "Whether xterm-style function key sequences should be sent. " - "This option is no longer used." + "This option is no longer used." }, /* Hook options. */ @@ -1218,8 +1226,8 @@ const struct options_table_entry options_table[] = { OPTIONS_TABLE_HOOK("client-active", ""), OPTIONS_TABLE_HOOK("client-attached", ""), OPTIONS_TABLE_HOOK("client-detached", ""), - OPTIONS_TABLE_HOOK("client-focus-in", ""), - OPTIONS_TABLE_HOOK("client-focus-out", ""), + OPTIONS_TABLE_HOOK("client-focus-in", ""), + OPTIONS_TABLE_HOOK("client-focus-out", ""), OPTIONS_TABLE_HOOK("client-resized", ""), OPTIONS_TABLE_HOOK("client-session-changed", ""), OPTIONS_TABLE_PANE_HOOK("pane-died", ""), diff --git a/tmux.1 b/tmux.1 index 638c3232..2128f81a 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4406,6 +4406,13 @@ The default is on. Available pane options are: .Pp .Bl -tag -width Ds -compact +.It Xo Ic allow-passthrough +.Op Ic on | off +.Xc +Allow programs in the pane to bypass +.Nm +using a terminal escape sequence (\eePtmux;...\ee\e\e). +.Pp .It Xo Ic allow-rename .Op Ic on | off .Xc -- cgit From 7f40c5b647241e0ac3c71c3c95a8cc33790e707e Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 16 Feb 2022 12:26:23 +0000 Subject: No not allow static linking on macOS. --- configure.ac | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/configure.ac b/configure.ac index c71a54b0..fb0ee239 100644 --- a/configure.ac +++ b/configure.ac @@ -69,6 +69,11 @@ AC_ARG_ENABLE( AS_HELP_STRING(--enable-static, create a static build) ) if test "x$enable_static" = xyes; then + case "$host_os" in + *darwin*) + AC_MSG_ERROR([static linking is not supported on macOS]) + ;; + esac test "x$PKG_CONFIG" != x && PKG_CONFIG="$PKG_CONFIG --static" AM_LDFLAGS="-static $AM_LDFLAGS" LDFLAGS="$AM_LDFLAGS $SAVED_LDFLAGS" -- cgit From 6a0a783c268b00df165b115d915b628e36a0f69b Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 16 Feb 2022 18:55:05 +0000 Subject: Support more mouse buttons when the terminal sends them, GitHub issue 3055. --- input-keys.c | 6 +- key-string.c | 42 ++++ menu.c | 6 +- popup.c | 8 +- server-client.c | 640 +++++++++++++++++++++++++++++++++++++++++++++++++++++--- tmux.h | 66 +++++- tty-keys.c | 4 +- 7 files changed, 728 insertions(+), 44 deletions(-) diff --git a/input-keys.c b/input-keys.c index b4770808..47614aa0 100644 --- a/input-keys.c +++ b/input-keys.c @@ -577,13 +577,13 @@ input_key_get_mouse(struct screen *s, struct mouse_event *m, u_int x, u_int y, */ if (m->sgr_type != ' ') { if (MOUSE_DRAG(m->sgr_b) && - MOUSE_BUTTONS(m->sgr_b) == 3 && + MOUSE_RELEASE(m->sgr_b) && (~s->mode & MODE_MOUSE_ALL)) return (0); } else { if (MOUSE_DRAG(m->b) && - MOUSE_BUTTONS(m->b) == 3 && - MOUSE_BUTTONS(m->lb) == 3 && + MOUSE_RELEASE(m->b) && + MOUSE_RELEASE(m->lb) && (~s->mode & MODE_MOUSE_ALL)) return (0); } diff --git a/key-string.c b/key-string.c index 4f7be858..0ca91306 100644 --- a/key-string.c +++ b/key-string.c @@ -91,26 +91,68 @@ static const struct { KEYC_MOUSE_STRING(MOUSEDOWN1, MouseDown1), KEYC_MOUSE_STRING(MOUSEDOWN2, MouseDown2), KEYC_MOUSE_STRING(MOUSEDOWN3, MouseDown3), + KEYC_MOUSE_STRING(MOUSEDOWN6, MouseDown6), + KEYC_MOUSE_STRING(MOUSEDOWN7, MouseDown7), + KEYC_MOUSE_STRING(MOUSEDOWN8, MouseDown8), + KEYC_MOUSE_STRING(MOUSEDOWN9, MouseDown9), + KEYC_MOUSE_STRING(MOUSEDOWN10, MouseDown10), + KEYC_MOUSE_STRING(MOUSEDOWN11, MouseDown11), KEYC_MOUSE_STRING(MOUSEUP1, MouseUp1), KEYC_MOUSE_STRING(MOUSEUP2, MouseUp2), KEYC_MOUSE_STRING(MOUSEUP3, MouseUp3), + KEYC_MOUSE_STRING(MOUSEUP6, MouseUp6), + KEYC_MOUSE_STRING(MOUSEUP7, MouseUp7), + KEYC_MOUSE_STRING(MOUSEUP8, MouseUp8), + KEYC_MOUSE_STRING(MOUSEUP9, MouseUp9), + KEYC_MOUSE_STRING(MOUSEUP10, MouseUp10), + KEYC_MOUSE_STRING(MOUSEUP11, MouseUp11), KEYC_MOUSE_STRING(MOUSEDRAG1, MouseDrag1), KEYC_MOUSE_STRING(MOUSEDRAG2, MouseDrag2), KEYC_MOUSE_STRING(MOUSEDRAG3, MouseDrag3), + KEYC_MOUSE_STRING(MOUSEDRAG6, MouseDrag6), + KEYC_MOUSE_STRING(MOUSEDRAG7, MouseDrag7), + KEYC_MOUSE_STRING(MOUSEDRAG8, MouseDrag8), + KEYC_MOUSE_STRING(MOUSEDRAG9, MouseDrag9), + KEYC_MOUSE_STRING(MOUSEDRAG10, MouseDrag10), + KEYC_MOUSE_STRING(MOUSEDRAG11, MouseDrag11), KEYC_MOUSE_STRING(MOUSEDRAGEND1, MouseDragEnd1), KEYC_MOUSE_STRING(MOUSEDRAGEND2, MouseDragEnd2), KEYC_MOUSE_STRING(MOUSEDRAGEND3, MouseDragEnd3), + KEYC_MOUSE_STRING(MOUSEDRAGEND6, MouseDragEnd6), + KEYC_MOUSE_STRING(MOUSEDRAGEND7, MouseDragEnd7), + KEYC_MOUSE_STRING(MOUSEDRAGEND8, MouseDragEnd8), + KEYC_MOUSE_STRING(MOUSEDRAGEND9, MouseDragEnd9), + KEYC_MOUSE_STRING(MOUSEDRAGEND10, MouseDragEnd10), + KEYC_MOUSE_STRING(MOUSEDRAGEND11, MouseDragEnd11), KEYC_MOUSE_STRING(WHEELUP, WheelUp), KEYC_MOUSE_STRING(WHEELDOWN, WheelDown), KEYC_MOUSE_STRING(SECONDCLICK1, SecondClick1), KEYC_MOUSE_STRING(SECONDCLICK2, SecondClick2), KEYC_MOUSE_STRING(SECONDCLICK3, SecondClick3), + KEYC_MOUSE_STRING(SECONDCLICK6, SecondClick6), + KEYC_MOUSE_STRING(SECONDCLICK7, SecondClick7), + KEYC_MOUSE_STRING(SECONDCLICK8, SecondClick8), + KEYC_MOUSE_STRING(SECONDCLICK9, SecondClick9), + KEYC_MOUSE_STRING(SECONDCLICK10, SecondClick10), + KEYC_MOUSE_STRING(SECONDCLICK11, SecondClick11), KEYC_MOUSE_STRING(DOUBLECLICK1, DoubleClick1), KEYC_MOUSE_STRING(DOUBLECLICK2, DoubleClick2), KEYC_MOUSE_STRING(DOUBLECLICK3, DoubleClick3), + KEYC_MOUSE_STRING(DOUBLECLICK6, DoubleClick6), + KEYC_MOUSE_STRING(DOUBLECLICK7, DoubleClick7), + KEYC_MOUSE_STRING(DOUBLECLICK8, DoubleClick8), + KEYC_MOUSE_STRING(DOUBLECLICK9, DoubleClick9), + KEYC_MOUSE_STRING(DOUBLECLICK10, DoubleClick10), + KEYC_MOUSE_STRING(DOUBLECLICK11, DoubleClick11), KEYC_MOUSE_STRING(TRIPLECLICK1, TripleClick1), KEYC_MOUSE_STRING(TRIPLECLICK2, TripleClick2), KEYC_MOUSE_STRING(TRIPLECLICK3, TripleClick3), + KEYC_MOUSE_STRING(TRIPLECLICK6, TripleClick6), + KEYC_MOUSE_STRING(TRIPLECLICK7, TripleClick7), + KEYC_MOUSE_STRING(TRIPLECLICK8, TripleClick8), + KEYC_MOUSE_STRING(TRIPLECLICK9, TripleClick9), + KEYC_MOUSE_STRING(TRIPLECLICK10, TripleClick10), + KEYC_MOUSE_STRING(TRIPLECLICK11, TripleClick11) }; /* Find key string in table. */ diff --git a/menu.c b/menu.c index aaa1287e..c17d10b1 100644 --- a/menu.c +++ b/menu.c @@ -235,7 +235,7 @@ menu_key_cb(struct client *c, void *data, struct key_event *event) if (KEYC_IS_MOUSE(event->key)) { if (md->flags & MENU_NOMOUSE) { - if (MOUSE_BUTTONS(m->b) != 0) + if (MOUSE_BUTTONS(m->b) != MOUSE_BUTTON_1) return (1); return (0); } @@ -248,7 +248,7 @@ menu_key_cb(struct client *c, void *data, struct key_event *event) return (1); } else { if (!MOUSE_RELEASE(m->b) && - MOUSE_WHEEL(m->b) == 0 && + !MOUSE_WHEEL(m->b) && !MOUSE_DRAG(m->b)) return (1); } @@ -262,7 +262,7 @@ menu_key_cb(struct client *c, void *data, struct key_event *event) if (MOUSE_RELEASE(m->b)) goto chosen; } else { - if (MOUSE_WHEEL(m->b) == 0 && !MOUSE_DRAG(m->b)) + if (!MOUSE_WHEEL(m->b) && !MOUSE_DRAG(m->b)) goto chosen; } md->choice = m->y - (md->py + 1); diff --git a/popup.c b/popup.c index 1048af60..5522df3e 100644 --- a/popup.c +++ b/popup.c @@ -508,7 +508,7 @@ popup_key_cb(struct client *c, void *data, struct key_event *event) m->x > pd->px + pd->sx - 1 || m->y < pd->py || m->y > pd->py + pd->sy - 1) { - if (MOUSE_BUTTONS(m->b) == 2) + if (MOUSE_BUTTONS(m->b) == MOUSE_BUTTON_3) goto menu; return (0); } @@ -523,16 +523,16 @@ popup_key_cb(struct client *c, void *data, struct key_event *event) border = BOTTOM; } if ((m->b & MOUSE_MASK_MODIFIERS) == 0 && - MOUSE_BUTTONS(m->b) == 2 && + MOUSE_BUTTONS(m->b) == MOUSE_BUTTON_3 && (border == LEFT || border == TOP)) goto menu; if (((m->b & MOUSE_MASK_MODIFIERS) == MOUSE_MASK_META) || border != NONE) { if (!MOUSE_DRAG(m->b)) goto out; - if (MOUSE_BUTTONS(m->lb) == 0) + if (MOUSE_BUTTONS(m->lb) == MOUSE_BUTTON_1) pd->dragging = MOVE; - else if (MOUSE_BUTTONS(m->lb) == 2) + else if (MOUSE_BUTTONS(m->lb) == MOUSE_BUTTON_3) pd->dragging = SIZE; pd->dx = m->lx - pd->px; pd->dy = m->ly - pd->py; diff --git a/server-client.c b/server-client.c index 06154be7..0f043796 100644 --- a/server-client.c +++ b/server-client.c @@ -593,11 +593,11 @@ server_client_check_mouse(struct client *c, struct key_event *event) log_debug("double-click at %u,%u", x, y); } else if ((m->sgr_type != ' ' && MOUSE_DRAG(m->sgr_b) && - MOUSE_BUTTONS(m->sgr_b) == 3) || + MOUSE_RELEASE(m->sgr_b)) || (m->sgr_type == ' ' && MOUSE_DRAG(m->b) && - MOUSE_BUTTONS(m->b) == 3 && - MOUSE_BUTTONS(m->lb) == 3)) { + MOUSE_RELEASE(m->b) && + MOUSE_RELEASE(m->lb))) { type = MOVE; x = m->x, y = m->y, b = 0; log_debug("move at %u,%u", x, y); @@ -750,7 +750,7 @@ have_event: m->wp = -1; /* Stop dragging if needed. */ - if (type != DRAG && type != WHEEL && c->tty.mouse_drag_flag) { + if (type != DRAG && type != WHEEL && c->tty.mouse_drag_flag != 0) { if (c->tty.mouse_drag_release != NULL) c->tty.mouse_drag_release(c, m); @@ -761,8 +761,8 @@ have_event: * End a mouse drag by passing a MouseDragEnd key corresponding * to the button that started the drag. */ - switch (c->tty.mouse_drag_flag) { - case 1: + switch (c->tty.mouse_drag_flag - 1) { + case MOUSE_BUTTON_1: if (where == PANE) key = KEYC_MOUSEDRAGEND1_PANE; if (where == STATUS) @@ -776,7 +776,7 @@ have_event: if (where == BORDER) key = KEYC_MOUSEDRAGEND1_BORDER; break; - case 2: + case MOUSE_BUTTON_2: if (where == PANE) key = KEYC_MOUSEDRAGEND2_PANE; if (where == STATUS) @@ -790,7 +790,7 @@ have_event: if (where == BORDER) key = KEYC_MOUSEDRAGEND2_BORDER; break; - case 3: + case MOUSE_BUTTON_3: if (where == PANE) key = KEYC_MOUSEDRAGEND3_PANE; if (where == STATUS) @@ -804,6 +804,90 @@ have_event: if (where == BORDER) key = KEYC_MOUSEDRAGEND3_BORDER; break; + case MOUSE_BUTTON_6: + if (where == PANE) + key = KEYC_MOUSEDRAGEND6_PANE; + if (where == STATUS) + key = KEYC_MOUSEDRAGEND6_STATUS; + if (where == STATUS_LEFT) + key = KEYC_MOUSEDRAGEND6_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_MOUSEDRAGEND6_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_MOUSEDRAGEND6_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_MOUSEDRAGEND6_BORDER; + break; + case MOUSE_BUTTON_7: + if (where == PANE) + key = KEYC_MOUSEDRAGEND7_PANE; + if (where == STATUS) + key = KEYC_MOUSEDRAGEND7_STATUS; + if (where == STATUS_LEFT) + key = KEYC_MOUSEDRAGEND7_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_MOUSEDRAGEND7_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_MOUSEDRAGEND7_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_MOUSEDRAGEND7_BORDER; + break; + case MOUSE_BUTTON_8: + if (where == PANE) + key = KEYC_MOUSEDRAGEND8_PANE; + if (where == STATUS) + key = KEYC_MOUSEDRAGEND8_STATUS; + if (where == STATUS_LEFT) + key = KEYC_MOUSEDRAGEND8_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_MOUSEDRAGEND8_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_MOUSEDRAGEND8_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_MOUSEDRAGEND8_BORDER; + break; + case MOUSE_BUTTON_9: + if (where == PANE) + key = KEYC_MOUSEDRAGEND9_PANE; + if (where == STATUS) + key = KEYC_MOUSEDRAGEND9_STATUS; + if (where == STATUS_LEFT) + key = KEYC_MOUSEDRAGEND9_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_MOUSEDRAGEND9_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_MOUSEDRAGEND9_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_MOUSEDRAGEND9_BORDER; + break; + case MOUSE_BUTTON_10: + if (where == PANE) + key = KEYC_MOUSEDRAGEND10_PANE; + if (where == STATUS) + key = KEYC_MOUSEDRAGEND10_STATUS; + if (where == STATUS_LEFT) + key = KEYC_MOUSEDRAGEND10_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_MOUSEDRAGEND10_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_MOUSEDRAGEND10_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_MOUSEDRAGEND10_BORDER; + break; + case MOUSE_BUTTON_11: + if (where == PANE) + key = KEYC_MOUSEDRAGEND11_PANE; + if (where == STATUS) + key = KEYC_MOUSEDRAGEND11_STATUS; + if (where == STATUS_LEFT) + key = KEYC_MOUSEDRAGEND11_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_MOUSEDRAGEND11_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_MOUSEDRAGEND11_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_MOUSEDRAGEND11_BORDER; + break; default: key = KEYC_MOUSE; break; @@ -836,7 +920,7 @@ have_event: key = KEYC_DRAGGING; else { switch (MOUSE_BUTTONS(b)) { - case 0: + case MOUSE_BUTTON_1: if (where == PANE) key = KEYC_MOUSEDRAG1_PANE; if (where == STATUS) @@ -850,7 +934,7 @@ have_event: if (where == BORDER) key = KEYC_MOUSEDRAG1_BORDER; break; - case 1: + case MOUSE_BUTTON_2: if (where == PANE) key = KEYC_MOUSEDRAG2_PANE; if (where == STATUS) @@ -864,7 +948,7 @@ have_event: if (where == BORDER) key = KEYC_MOUSEDRAG2_BORDER; break; - case 2: + case MOUSE_BUTTON_3: if (where == PANE) key = KEYC_MOUSEDRAG3_PANE; if (where == STATUS) @@ -878,6 +962,90 @@ have_event: if (where == BORDER) key = KEYC_MOUSEDRAG3_BORDER; break; + case MOUSE_BUTTON_6: + if (where == PANE) + key = KEYC_MOUSEDRAG6_PANE; + if (where == STATUS) + key = KEYC_MOUSEDRAG6_STATUS; + if (where == STATUS_LEFT) + key = KEYC_MOUSEDRAG6_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_MOUSEDRAG6_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_MOUSEDRAG6_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_MOUSEDRAG6_BORDER; + break; + case MOUSE_BUTTON_7: + if (where == PANE) + key = KEYC_MOUSEDRAG7_PANE; + if (where == STATUS) + key = KEYC_MOUSEDRAG7_STATUS; + if (where == STATUS_LEFT) + key = KEYC_MOUSEDRAG7_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_MOUSEDRAG7_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_MOUSEDRAG7_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_MOUSEDRAG7_BORDER; + break; + case MOUSE_BUTTON_8: + if (where == PANE) + key = KEYC_MOUSEDRAG8_PANE; + if (where == STATUS) + key = KEYC_MOUSEDRAG8_STATUS; + if (where == STATUS_LEFT) + key = KEYC_MOUSEDRAG8_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_MOUSEDRAG8_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_MOUSEDRAG8_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_MOUSEDRAG8_BORDER; + break; + case MOUSE_BUTTON_9: + if (where == PANE) + key = KEYC_MOUSEDRAG9_PANE; + if (where == STATUS) + key = KEYC_MOUSEDRAG9_STATUS; + if (where == STATUS_LEFT) + key = KEYC_MOUSEDRAG9_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_MOUSEDRAG9_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_MOUSEDRAG9_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_MOUSEDRAG9_BORDER; + break; + case MOUSE_BUTTON_10: + if (where == PANE) + key = KEYC_MOUSEDRAG10_PANE; + if (where == STATUS) + key = KEYC_MOUSEDRAG10_STATUS; + if (where == STATUS_LEFT) + key = KEYC_MOUSEDRAG10_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_MOUSEDRAG10_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_MOUSEDRAG10_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_MOUSEDRAG10_BORDER; + break; + case MOUSE_BUTTON_11: + if (where == PANE) + key = KEYC_MOUSEDRAG11_PANE; + if (where == STATUS) + key = KEYC_MOUSEDRAG11_STATUS; + if (where == STATUS_LEFT) + key = KEYC_MOUSEDRAG11_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_MOUSEDRAG11_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_MOUSEDRAG11_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_MOUSEDRAG11_BORDER; + break; } } @@ -918,7 +1086,7 @@ have_event: break; case UP: switch (MOUSE_BUTTONS(b)) { - case 0: + case MOUSE_BUTTON_1: if (where == PANE) key = KEYC_MOUSEUP1_PANE; if (where == STATUS) @@ -932,7 +1100,7 @@ have_event: if (where == BORDER) key = KEYC_MOUSEUP1_BORDER; break; - case 1: + case MOUSE_BUTTON_2: if (where == PANE) key = KEYC_MOUSEUP2_PANE; if (where == STATUS) @@ -946,7 +1114,7 @@ have_event: if (where == BORDER) key = KEYC_MOUSEUP2_BORDER; break; - case 2: + case MOUSE_BUTTON_3: if (where == PANE) key = KEYC_MOUSEUP3_PANE; if (where == STATUS) @@ -960,11 +1128,95 @@ have_event: if (where == BORDER) key = KEYC_MOUSEUP3_BORDER; break; + case MOUSE_BUTTON_6: + if (where == PANE) + key = KEYC_MOUSEUP6_PANE; + if (where == STATUS) + key = KEYC_MOUSEUP6_STATUS; + if (where == STATUS_LEFT) + key = KEYC_MOUSEUP6_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_MOUSEUP6_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_MOUSEUP6_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_MOUSEUP6_BORDER; + break; + case MOUSE_BUTTON_7: + if (where == PANE) + key = KEYC_MOUSEUP7_PANE; + if (where == STATUS) + key = KEYC_MOUSEUP7_STATUS; + if (where == STATUS_LEFT) + key = KEYC_MOUSEUP7_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_MOUSEUP7_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_MOUSEUP7_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_MOUSEUP7_BORDER; + break; + case MOUSE_BUTTON_8: + if (where == PANE) + key = KEYC_MOUSEUP8_PANE; + if (where == STATUS) + key = KEYC_MOUSEUP8_STATUS; + if (where == STATUS_LEFT) + key = KEYC_MOUSEUP8_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_MOUSEUP8_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_MOUSEUP8_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_MOUSEUP8_BORDER; + break; + case MOUSE_BUTTON_9: + if (where == PANE) + key = KEYC_MOUSEUP9_PANE; + if (where == STATUS) + key = KEYC_MOUSEUP9_STATUS; + if (where == STATUS_LEFT) + key = KEYC_MOUSEUP9_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_MOUSEUP9_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_MOUSEUP9_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_MOUSEUP9_BORDER; + break; + case MOUSE_BUTTON_10: + if (where == PANE) + key = KEYC_MOUSEUP1_PANE; + if (where == STATUS) + key = KEYC_MOUSEUP1_STATUS; + if (where == STATUS_LEFT) + key = KEYC_MOUSEUP1_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_MOUSEUP1_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_MOUSEUP1_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_MOUSEUP1_BORDER; + break; + case MOUSE_BUTTON_11: + if (where == PANE) + key = KEYC_MOUSEUP11_PANE; + if (where == STATUS) + key = KEYC_MOUSEUP11_STATUS; + if (where == STATUS_LEFT) + key = KEYC_MOUSEUP11_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_MOUSEUP11_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_MOUSEUP11_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_MOUSEUP11_BORDER; + break; } break; case DOWN: switch (MOUSE_BUTTONS(b)) { - case 0: + case MOUSE_BUTTON_1: if (where == PANE) key = KEYC_MOUSEDOWN1_PANE; if (where == STATUS) @@ -978,7 +1230,7 @@ have_event: if (where == BORDER) key = KEYC_MOUSEDOWN1_BORDER; break; - case 1: + case MOUSE_BUTTON_2: if (where == PANE) key = KEYC_MOUSEDOWN2_PANE; if (where == STATUS) @@ -992,7 +1244,7 @@ have_event: if (where == BORDER) key = KEYC_MOUSEDOWN2_BORDER; break; - case 2: + case MOUSE_BUTTON_3: if (where == PANE) key = KEYC_MOUSEDOWN3_PANE; if (where == STATUS) @@ -1006,11 +1258,95 @@ have_event: if (where == BORDER) key = KEYC_MOUSEDOWN3_BORDER; break; + case MOUSE_BUTTON_6: + if (where == PANE) + key = KEYC_MOUSEDOWN6_PANE; + if (where == STATUS) + key = KEYC_MOUSEDOWN6_STATUS; + if (where == STATUS_LEFT) + key = KEYC_MOUSEDOWN6_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_MOUSEDOWN6_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_MOUSEDOWN6_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_MOUSEDOWN6_BORDER; + break; + case MOUSE_BUTTON_7: + if (where == PANE) + key = KEYC_MOUSEDOWN7_PANE; + if (where == STATUS) + key = KEYC_MOUSEDOWN7_STATUS; + if (where == STATUS_LEFT) + key = KEYC_MOUSEDOWN7_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_MOUSEDOWN7_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_MOUSEDOWN7_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_MOUSEDOWN7_BORDER; + break; + case MOUSE_BUTTON_8: + if (where == PANE) + key = KEYC_MOUSEDOWN8_PANE; + if (where == STATUS) + key = KEYC_MOUSEDOWN8_STATUS; + if (where == STATUS_LEFT) + key = KEYC_MOUSEDOWN8_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_MOUSEDOWN8_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_MOUSEDOWN8_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_MOUSEDOWN8_BORDER; + break; + case MOUSE_BUTTON_9: + if (where == PANE) + key = KEYC_MOUSEDOWN9_PANE; + if (where == STATUS) + key = KEYC_MOUSEDOWN9_STATUS; + if (where == STATUS_LEFT) + key = KEYC_MOUSEDOWN9_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_MOUSEDOWN9_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_MOUSEDOWN9_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_MOUSEDOWN9_BORDER; + break; + case MOUSE_BUTTON_10: + if (where == PANE) + key = KEYC_MOUSEDOWN10_PANE; + if (where == STATUS) + key = KEYC_MOUSEDOWN10_STATUS; + if (where == STATUS_LEFT) + key = KEYC_MOUSEDOWN10_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_MOUSEDOWN10_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_MOUSEDOWN10_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_MOUSEDOWN10_BORDER; + break; + case MOUSE_BUTTON_11: + if (where == PANE) + key = KEYC_MOUSEDOWN11_PANE; + if (where == STATUS) + key = KEYC_MOUSEDOWN11_STATUS; + if (where == STATUS_LEFT) + key = KEYC_MOUSEDOWN11_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_MOUSEDOWN11_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_MOUSEDOWN11_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_MOUSEDOWN11_BORDER; + break; } break; case SECOND: switch (MOUSE_BUTTONS(b)) { - case 0: + case MOUSE_BUTTON_1: if (where == PANE) key = KEYC_SECONDCLICK1_PANE; if (where == STATUS) @@ -1024,7 +1360,7 @@ have_event: if (where == BORDER) key = KEYC_SECONDCLICK1_BORDER; break; - case 1: + case MOUSE_BUTTON_2: if (where == PANE) key = KEYC_SECONDCLICK2_PANE; if (where == STATUS) @@ -1038,7 +1374,7 @@ have_event: if (where == BORDER) key = KEYC_SECONDCLICK2_BORDER; break; - case 2: + case MOUSE_BUTTON_3: if (where == PANE) key = KEYC_SECONDCLICK3_PANE; if (where == STATUS) @@ -1052,11 +1388,95 @@ have_event: if (where == BORDER) key = KEYC_SECONDCLICK3_BORDER; break; + case MOUSE_BUTTON_6: + if (where == PANE) + key = KEYC_SECONDCLICK6_PANE; + if (where == STATUS) + key = KEYC_SECONDCLICK6_STATUS; + if (where == STATUS_LEFT) + key = KEYC_SECONDCLICK6_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_SECONDCLICK6_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_SECONDCLICK6_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_SECONDCLICK6_BORDER; + break; + case MOUSE_BUTTON_7: + if (where == PANE) + key = KEYC_SECONDCLICK7_PANE; + if (where == STATUS) + key = KEYC_SECONDCLICK7_STATUS; + if (where == STATUS_LEFT) + key = KEYC_SECONDCLICK7_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_SECONDCLICK7_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_SECONDCLICK7_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_SECONDCLICK7_BORDER; + break; + case MOUSE_BUTTON_8: + if (where == PANE) + key = KEYC_SECONDCLICK8_PANE; + if (where == STATUS) + key = KEYC_SECONDCLICK8_STATUS; + if (where == STATUS_LEFT) + key = KEYC_SECONDCLICK8_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_SECONDCLICK8_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_SECONDCLICK8_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_SECONDCLICK8_BORDER; + break; + case MOUSE_BUTTON_9: + if (where == PANE) + key = KEYC_SECONDCLICK9_PANE; + if (where == STATUS) + key = KEYC_SECONDCLICK9_STATUS; + if (where == STATUS_LEFT) + key = KEYC_SECONDCLICK9_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_SECONDCLICK9_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_SECONDCLICK9_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_SECONDCLICK9_BORDER; + break; + case MOUSE_BUTTON_10: + if (where == PANE) + key = KEYC_SECONDCLICK10_PANE; + if (where == STATUS) + key = KEYC_SECONDCLICK10_STATUS; + if (where == STATUS_LEFT) + key = KEYC_SECONDCLICK10_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_SECONDCLICK10_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_SECONDCLICK10_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_SECONDCLICK10_BORDER; + break; + case MOUSE_BUTTON_11: + if (where == PANE) + key = KEYC_SECONDCLICK11_PANE; + if (where == STATUS) + key = KEYC_SECONDCLICK11_STATUS; + if (where == STATUS_LEFT) + key = KEYC_SECONDCLICK11_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_SECONDCLICK11_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_SECONDCLICK11_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_SECONDCLICK11_BORDER; + break; } break; case DOUBLE: switch (MOUSE_BUTTONS(b)) { - case 0: + case MOUSE_BUTTON_1: if (where == PANE) key = KEYC_DOUBLECLICK1_PANE; if (where == STATUS) @@ -1070,7 +1490,7 @@ have_event: if (where == BORDER) key = KEYC_DOUBLECLICK1_BORDER; break; - case 1: + case MOUSE_BUTTON_2: if (where == PANE) key = KEYC_DOUBLECLICK2_PANE; if (where == STATUS) @@ -1084,7 +1504,7 @@ have_event: if (where == BORDER) key = KEYC_DOUBLECLICK2_BORDER; break; - case 2: + case MOUSE_BUTTON_3: if (where == PANE) key = KEYC_DOUBLECLICK3_PANE; if (where == STATUS) @@ -1098,11 +1518,95 @@ have_event: if (where == BORDER) key = KEYC_DOUBLECLICK3_BORDER; break; + case MOUSE_BUTTON_6: + if (where == PANE) + key = KEYC_DOUBLECLICK6_PANE; + if (where == STATUS) + key = KEYC_DOUBLECLICK6_STATUS; + if (where == STATUS_LEFT) + key = KEYC_DOUBLECLICK6_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_DOUBLECLICK6_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_DOUBLECLICK6_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_DOUBLECLICK6_BORDER; + break; + case MOUSE_BUTTON_7: + if (where == PANE) + key = KEYC_DOUBLECLICK7_PANE; + if (where == STATUS) + key = KEYC_DOUBLECLICK7_STATUS; + if (where == STATUS_LEFT) + key = KEYC_DOUBLECLICK7_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_DOUBLECLICK7_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_DOUBLECLICK7_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_DOUBLECLICK7_BORDER; + break; + case MOUSE_BUTTON_8: + if (where == PANE) + key = KEYC_DOUBLECLICK8_PANE; + if (where == STATUS) + key = KEYC_DOUBLECLICK8_STATUS; + if (where == STATUS_LEFT) + key = KEYC_DOUBLECLICK8_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_DOUBLECLICK8_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_DOUBLECLICK8_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_DOUBLECLICK8_BORDER; + break; + case MOUSE_BUTTON_9: + if (where == PANE) + key = KEYC_DOUBLECLICK9_PANE; + if (where == STATUS) + key = KEYC_DOUBLECLICK9_STATUS; + if (where == STATUS_LEFT) + key = KEYC_DOUBLECLICK9_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_DOUBLECLICK9_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_DOUBLECLICK9_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_DOUBLECLICK9_BORDER; + break; + case MOUSE_BUTTON_10: + if (where == PANE) + key = KEYC_DOUBLECLICK10_PANE; + if (where == STATUS) + key = KEYC_DOUBLECLICK10_STATUS; + if (where == STATUS_LEFT) + key = KEYC_DOUBLECLICK10_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_DOUBLECLICK10_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_DOUBLECLICK10_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_DOUBLECLICK10_BORDER; + break; + case MOUSE_BUTTON_11: + if (where == PANE) + key = KEYC_DOUBLECLICK11_PANE; + if (where == STATUS) + key = KEYC_DOUBLECLICK11_STATUS; + if (where == STATUS_LEFT) + key = KEYC_DOUBLECLICK11_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_DOUBLECLICK11_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_DOUBLECLICK11_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_DOUBLECLICK11_BORDER; + break; } break; case TRIPLE: switch (MOUSE_BUTTONS(b)) { - case 0: + case MOUSE_BUTTON_1: if (where == PANE) key = KEYC_TRIPLECLICK1_PANE; if (where == STATUS) @@ -1116,7 +1620,7 @@ have_event: if (where == BORDER) key = KEYC_TRIPLECLICK1_BORDER; break; - case 1: + case MOUSE_BUTTON_2: if (where == PANE) key = KEYC_TRIPLECLICK2_PANE; if (where == STATUS) @@ -1130,7 +1634,7 @@ have_event: if (where == BORDER) key = KEYC_TRIPLECLICK2_BORDER; break; - case 2: + case MOUSE_BUTTON_3: if (where == PANE) key = KEYC_TRIPLECLICK3_PANE; if (where == STATUS) @@ -1144,6 +1648,90 @@ have_event: if (where == BORDER) key = KEYC_TRIPLECLICK3_BORDER; break; + case MOUSE_BUTTON_6: + if (where == PANE) + key = KEYC_TRIPLECLICK6_PANE; + if (where == STATUS) + key = KEYC_TRIPLECLICK6_STATUS; + if (where == STATUS_LEFT) + key = KEYC_TRIPLECLICK6_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_TRIPLECLICK6_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_TRIPLECLICK6_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_TRIPLECLICK6_BORDER; + break; + case MOUSE_BUTTON_7: + if (where == PANE) + key = KEYC_TRIPLECLICK7_PANE; + if (where == STATUS) + key = KEYC_TRIPLECLICK7_STATUS; + if (where == STATUS_LEFT) + key = KEYC_TRIPLECLICK7_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_TRIPLECLICK7_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_TRIPLECLICK7_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_TRIPLECLICK7_BORDER; + break; + case MOUSE_BUTTON_8: + if (where == PANE) + key = KEYC_TRIPLECLICK8_PANE; + if (where == STATUS) + key = KEYC_TRIPLECLICK8_STATUS; + if (where == STATUS_LEFT) + key = KEYC_TRIPLECLICK8_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_TRIPLECLICK8_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_TRIPLECLICK8_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_TRIPLECLICK8_BORDER; + break; + case MOUSE_BUTTON_9: + if (where == PANE) + key = KEYC_TRIPLECLICK9_PANE; + if (where == STATUS) + key = KEYC_TRIPLECLICK9_STATUS; + if (where == STATUS_LEFT) + key = KEYC_TRIPLECLICK9_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_TRIPLECLICK9_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_TRIPLECLICK9_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_TRIPLECLICK9_BORDER; + break; + case MOUSE_BUTTON_10: + if (where == PANE) + key = KEYC_TRIPLECLICK10_PANE; + if (where == STATUS) + key = KEYC_TRIPLECLICK10_STATUS; + if (where == STATUS_LEFT) + key = KEYC_TRIPLECLICK10_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_TRIPLECLICK10_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_TRIPLECLICK10_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_TRIPLECLICK10_BORDER; + break; + case MOUSE_BUTTON_11: + if (where == PANE) + key = KEYC_TRIPLECLICK11_PANE; + if (where == STATUS) + key = KEYC_TRIPLECLICK11_STATUS; + if (where == STATUS_LEFT) + key = KEYC_TRIPLECLICK11_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_TRIPLECLICK11_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_TRIPLECLICK11_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_TRIPLECLICK11_BORDER; + break; } break; } diff --git a/tmux.h b/tmux.h index 313432e0..f172ba1a 100644 --- a/tmux.h +++ b/tmux.h @@ -202,26 +202,68 @@ enum { KEYC_MOUSE_KEY(MOUSEDOWN1), KEYC_MOUSE_KEY(MOUSEDOWN2), KEYC_MOUSE_KEY(MOUSEDOWN3), + KEYC_MOUSE_KEY(MOUSEDOWN6), + KEYC_MOUSE_KEY(MOUSEDOWN7), + KEYC_MOUSE_KEY(MOUSEDOWN8), + KEYC_MOUSE_KEY(MOUSEDOWN9), + KEYC_MOUSE_KEY(MOUSEDOWN10), + KEYC_MOUSE_KEY(MOUSEDOWN11), KEYC_MOUSE_KEY(MOUSEUP1), KEYC_MOUSE_KEY(MOUSEUP2), KEYC_MOUSE_KEY(MOUSEUP3), + KEYC_MOUSE_KEY(MOUSEUP6), + KEYC_MOUSE_KEY(MOUSEUP7), + KEYC_MOUSE_KEY(MOUSEUP8), + KEYC_MOUSE_KEY(MOUSEUP9), + KEYC_MOUSE_KEY(MOUSEUP10), + KEYC_MOUSE_KEY(MOUSEUP11), KEYC_MOUSE_KEY(MOUSEDRAG1), KEYC_MOUSE_KEY(MOUSEDRAG2), KEYC_MOUSE_KEY(MOUSEDRAG3), + KEYC_MOUSE_KEY(MOUSEDRAG6), + KEYC_MOUSE_KEY(MOUSEDRAG7), + KEYC_MOUSE_KEY(MOUSEDRAG8), + KEYC_MOUSE_KEY(MOUSEDRAG9), + KEYC_MOUSE_KEY(MOUSEDRAG10), + KEYC_MOUSE_KEY(MOUSEDRAG11), KEYC_MOUSE_KEY(MOUSEDRAGEND1), KEYC_MOUSE_KEY(MOUSEDRAGEND2), KEYC_MOUSE_KEY(MOUSEDRAGEND3), + KEYC_MOUSE_KEY(MOUSEDRAGEND6), + KEYC_MOUSE_KEY(MOUSEDRAGEND7), + KEYC_MOUSE_KEY(MOUSEDRAGEND8), + KEYC_MOUSE_KEY(MOUSEDRAGEND9), + KEYC_MOUSE_KEY(MOUSEDRAGEND10), + KEYC_MOUSE_KEY(MOUSEDRAGEND11), KEYC_MOUSE_KEY(WHEELUP), KEYC_MOUSE_KEY(WHEELDOWN), KEYC_MOUSE_KEY(SECONDCLICK1), KEYC_MOUSE_KEY(SECONDCLICK2), KEYC_MOUSE_KEY(SECONDCLICK3), + KEYC_MOUSE_KEY(SECONDCLICK6), + KEYC_MOUSE_KEY(SECONDCLICK7), + KEYC_MOUSE_KEY(SECONDCLICK8), + KEYC_MOUSE_KEY(SECONDCLICK9), + KEYC_MOUSE_KEY(SECONDCLICK10), + KEYC_MOUSE_KEY(SECONDCLICK11), KEYC_MOUSE_KEY(DOUBLECLICK1), KEYC_MOUSE_KEY(DOUBLECLICK2), KEYC_MOUSE_KEY(DOUBLECLICK3), + KEYC_MOUSE_KEY(DOUBLECLICK6), + KEYC_MOUSE_KEY(DOUBLECLICK7), + KEYC_MOUSE_KEY(DOUBLECLICK8), + KEYC_MOUSE_KEY(DOUBLECLICK9), + KEYC_MOUSE_KEY(DOUBLECLICK10), + KEYC_MOUSE_KEY(DOUBLECLICK11), KEYC_MOUSE_KEY(TRIPLECLICK1), KEYC_MOUSE_KEY(TRIPLECLICK2), KEYC_MOUSE_KEY(TRIPLECLICK3), + KEYC_MOUSE_KEY(TRIPLECLICK6), + KEYC_MOUSE_KEY(TRIPLECLICK7), + KEYC_MOUSE_KEY(TRIPLECLICK8), + KEYC_MOUSE_KEY(TRIPLECLICK9), + KEYC_MOUSE_KEY(TRIPLECLICK10), + KEYC_MOUSE_KEY(TRIPLECLICK11), /* Backspace key. */ KEYC_BSPACE, @@ -1199,21 +1241,33 @@ struct session { RB_HEAD(sessions, session); /* Mouse button masks. */ -#define MOUSE_MASK_BUTTONS 3 +#define MOUSE_MASK_BUTTONS 195 #define MOUSE_MASK_SHIFT 4 #define MOUSE_MASK_META 8 #define MOUSE_MASK_CTRL 16 #define MOUSE_MASK_DRAG 32 -#define MOUSE_MASK_WHEEL 64 #define MOUSE_MASK_MODIFIERS (MOUSE_MASK_SHIFT|MOUSE_MASK_META|MOUSE_MASK_CTRL) -/* Mouse wheel states. */ -#define MOUSE_WHEEL_UP 0 -#define MOUSE_WHEEL_DOWN 1 +/* Mouse wheel type. */ +#define MOUSE_WHEEL_UP 64 +#define MOUSE_WHEEL_DOWN 65 + +/* Mouse button type. */ +#define MOUSE_BUTTON_1 0 +#define MOUSE_BUTTON_2 1 +#define MOUSE_BUTTON_3 2 +#define MOUSE_BUTTON_6 66 +#define MOUSE_BUTTON_7 67 +#define MOUSE_BUTTON_8 128 +#define MOUSE_BUTTON_9 129 +#define MOUSE_BUTTON_10 130 +#define MOUSE_BUTTON_11 131 /* Mouse helpers. */ #define MOUSE_BUTTONS(b) ((b) & MOUSE_MASK_BUTTONS) -#define MOUSE_WHEEL(b) ((b) & MOUSE_MASK_WHEEL) +#define MOUSE_WHEEL(b) \ + (((b) & MOUSE_MASK_BUTTONS) == MOUSE_WHEEL_UP || \ + ((b) & MOUSE_MASK_BUTTONS) == MOUSE_WHEEL_DOWN) #define MOUSE_DRAG(b) ((b) & MOUSE_MASK_DRAG) #define MOUSE_RELEASE(b) (((b) & MOUSE_MASK_BUTTONS) == 3) diff --git a/tty-keys.c b/tty-keys.c index 5d950078..30717af3 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -1118,7 +1118,7 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size, /* Type is M for press, m for release. */ sgr_type = ch; if (sgr_type == 'm') - b |= 3; + b = 3; /* * Some terminals (like PuTTY 0.63) mistakenly send @@ -1126,7 +1126,7 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size, * Discard it before it reaches any program running inside * tmux. */ - if (sgr_type == 'm' && (sgr_b & 64)) + if (sgr_type == 'm' && MOUSE_WHEEL(sgr_b)) return (-2); } else return (-1); -- cgit From 4893edd5d67e80e8d4323f6cae02495a7164e3f0 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 17 Feb 2022 09:58:47 +0000 Subject: Add a window-resized hook which is fired when the window is actually resized which may be later than the client resize, GitHub issue 2995. --- options-table.c | 1 + resize.c | 1 + 2 files changed, 2 insertions(+) diff --git a/options-table.c b/options-table.c index c8ae046e..b8a5ccbb 100644 --- a/options-table.c +++ b/options-table.c @@ -1245,6 +1245,7 @@ const struct options_table_entry options_table[] = { OPTIONS_TABLE_HOOK("window-linked", ""), OPTIONS_TABLE_WINDOW_HOOK("window-pane-changed", ""), OPTIONS_TABLE_WINDOW_HOOK("window-renamed", ""), + OPTIONS_TABLE_WINDOW_HOOK("window-resized", ""), OPTIONS_TABLE_HOOK("window-unlinked", ""), { .name = NULL } diff --git a/resize.c b/resize.c index 18a02adb..457fee0a 100644 --- a/resize.c +++ b/resize.c @@ -61,6 +61,7 @@ resize_window(struct window *w, u_int sx, u_int sy, int xpixel, int ypixel) tty_update_window_offset(w); server_redraw_window(w); notify_window("window-layout-changed", w); + notify_window("window-resized", w); w->flags &= ~WINDOW_RESIZE; } -- cgit From f74a98cd077c301b27c18a0a98620f7eda003489 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 22 Feb 2022 11:01:57 +0000 Subject: Use correct size for screen when popup is created without borders. --- popup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/popup.c b/popup.c index 5522df3e..2e57153d 100644 --- a/popup.c +++ b/popup.c @@ -689,7 +689,7 @@ popup_display(int flags, enum box_lines lines, struct cmdq_item *item, u_int px, } pd->border_cell.attr = 0; - screen_init(&pd->s, sx - 2, sy - 2, 0); + screen_init(&pd->s, jx, jy, 0); colour_palette_init(&pd->palette); colour_palette_from_option(&pd->palette, global_w_options); -- cgit From 92a26a8b8c4b32223fb81d7bde93c986ff4f7f6b Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 22 Feb 2022 11:07:25 +0000 Subject: Initialize copy_width before adjusting it, GitHub issue 3079. --- format-draw.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/format-draw.c b/format-draw.c index 1110535f..1a7e60b3 100644 --- a/format-draw.c +++ b/format-draw.c @@ -1154,13 +1154,13 @@ format_trim_right(const char *expanded, u_int limit) while (*cp != '\0') { if (*cp == '#') { end = format_leading_hashes(cp, &n, &leading_width); + copy_width = leading_width; if (width <= skip) { - if (skip - width >= leading_width) + if (skip - width >= copy_width) copy_width = 0; else copy_width -= (skip - width); - } else - copy_width = leading_width; + } if (copy_width != 0) { if (n == 1) *out++ = '#'; -- cgit From a26ebccd421e53ce1e3fcad6f1db9759cb58b043 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 22 Feb 2022 11:10:41 +0000 Subject: Add next_session_id format with the next session ID, GitHub issue 3078. --- format.c | 10 ++++++++++ session.c | 2 +- tmux.1 | 6 ++++++ tmux.h | 1 + 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/format.c b/format.c index 2fc0961e..5c6eed36 100644 --- a/format.c +++ b/format.c @@ -1650,6 +1650,13 @@ format_cb_mouse_y(struct format_tree *ft) return (NULL); } +/* Callback for next_session_id. */ +static void * +format_cb_next_session_id(__unused struct format_tree *ft) +{ + return (format_printf("$%u", next_session_id)); +} + /* Callback for origin_flag. */ static void * format_cb_origin_flag(struct format_tree *ft) @@ -2707,6 +2714,9 @@ static const struct format_table_entry format_table[] = { { "mouse_y", FORMAT_TABLE_STRING, format_cb_mouse_y }, + { "next_session_id", FORMAT_TABLE_STRING, + format_cb_next_session_id + }, { "origin_flag", FORMAT_TABLE_STRING, format_cb_origin_flag }, diff --git a/session.c b/session.c index f30f6a61..d9d9c486 100644 --- a/session.c +++ b/session.c @@ -27,7 +27,7 @@ #include "tmux.h" struct sessions sessions; -static u_int next_session_id; +u_int next_session_id; struct session_groups session_groups = RB_INITIALIZER(&session_groups); static void session_free(int, short, void *); diff --git a/tmux.1 b/tmux.1 index 2128f81a..1837e248 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4592,6 +4592,11 @@ Run when a session is renamed. Run when a window is linked into a session. .It window-renamed Run when a window is renamed. +.It window-resized +Run when a window is resized. +This may be after the +.Ar client-resized +hook is run. .It window-unlinked Run when a window is unlinked from a session. .El @@ -5096,6 +5101,7 @@ The following variables are available, where appropriate: .It Li "mouse_word" Ta "" Ta "Word under mouse, if any" .It Li "mouse_x" Ta "" Ta "Mouse X position, if any" .It Li "mouse_y" Ta "" Ta "Mouse Y position, if any" +.It Li "next_session_id" Ta "" Ta "Unique session ID for next new session" .It Li "origin_flag" Ta "" Ta "Pane origin flag" .It Li "pane_active" Ta "" Ta "1 if active pane" .It Li "pane_at_bottom" Ta "" Ta "1 if pane is at the bottom of window" diff --git a/tmux.h b/tmux.h index f172ba1a..7eccaa55 100644 --- a/tmux.h +++ b/tmux.h @@ -3119,6 +3119,7 @@ void control_notify_session_window_changed(struct session *); /* session.c */ extern struct sessions sessions; +extern u_int next_session_id; int session_cmp(struct session *, struct session *); RB_PROTOTYPE(sessions, session, entry, session_cmp); int session_alive(struct session *); -- cgit From 046530878b351dc5d99347179f9598d0b40fa268 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 22 Feb 2022 13:31:18 +0000 Subject: Do not attempt to update focus (and crash) when there is no previous window. --- session.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/session.c b/session.c index d9d9c486..75028882 100644 --- a/session.c +++ b/session.c @@ -501,7 +501,8 @@ session_set_current(struct session *s, struct winlink *wl) winlink_stack_push(&s->lastw, s->curw); s->curw = wl; if (options_get_number(global_options, "focus-events")) { - window_update_focus(old->window); + if (old != NULL) + window_update_focus(old->window); window_update_focus(wl->window); } winlink_clear_flags(wl); -- cgit From c030d6fe362eec41ccd5a5cb21321ef8cf07314b Mon Sep 17 00:00:00 2001 From: deraadt Date: Tue, 22 Feb 2022 17:35:01 +0000 Subject: MAXCOMLEN is no longer needed in these programs, so remove the annotation from sys/param.h include lines, or remove the include lines entirely if it this was the least requirement. ok millert --- osdep-openbsd.c | 1 - 1 file changed, 1 deletion(-) diff --git a/osdep-openbsd.c b/osdep-openbsd.c index 54464753..1fc087d3 100644 --- a/osdep-openbsd.c +++ b/osdep-openbsd.c @@ -16,7 +16,6 @@ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include /* MAXCOMLEN */ #include #include #include -- cgit From 9947f7416a3e43c30abcfbc5092c7e3e7b122dcb Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 28 Feb 2022 09:24:22 +0000 Subject: Map control keys back to an ASCII uppercase letter when passing them on as extended keys. --- input-keys.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/input-keys.c b/input-keys.c index 47614aa0..f478cb95 100644 --- a/input-keys.c +++ b/input-keys.c @@ -417,7 +417,7 @@ int input_key(struct screen *s, struct bufferevent *bev, key_code key) { struct input_key_entry *ike; - key_code justkey, newkey, outkey; + key_code justkey, newkey, outkey, modifiers; struct utf8_data ud; char tmp[64], modifier; @@ -518,7 +518,12 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key) return (input_key(s, bev, key & ~KEYC_CTRL)); } outkey = (key & KEYC_MASK_KEY); - switch (key & KEYC_MASK_MODIFIERS) { + modifiers = (key & KEYC_MASK_MODIFIERS); + if (outkey < ' ') { + outkey = 64 + outkey; + modifiers |= KEYC_CTRL; + } + switch (modifiers) { case KEYC_SHIFT: modifier = '2'; break; -- cgit From 42e795933625dbda27e3c0a453fc7d0fbf4bb758 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 28 Feb 2022 09:34:57 +0000 Subject: Exit on SIGHUP before attach also, GitHub issue 3084. --- client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client.c b/client.c index 8ca08524..08708c21 100644 --- a/client.c +++ b/client.c @@ -531,7 +531,7 @@ client_signal(int sig) if (sig == SIGCHLD) waitpid(WAIT_ANY, &status, WNOHANG); else if (!client_attached) { - if (sig == SIGTERM) + if (sig == SIGTERM || sig == SIGHUP) proc_exit(client_proc); } else { switch (sig) { -- cgit From 141a823ea421171d10466eccf0b2d03c0417667e Mon Sep 17 00:00:00 2001 From: topcat001 Date: Mon, 28 Feb 2022 13:11:28 -0800 Subject: Use PATH_MAX instead of MAXPATHLEN. --- osdep-openbsd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osdep-openbsd.c b/osdep-openbsd.c index 1fc087d3..b4c8fc56 100644 --- a/osdep-openbsd.c +++ b/osdep-openbsd.c @@ -141,7 +141,7 @@ char * osdep_get_cwd(int fd) { int name[] = { CTL_KERN, KERN_PROC_CWD, 0 }; - static char path[MAXPATHLEN]; + static char path[PATH_MAX]; size_t pathlen = sizeof path; if ((name[2] = tcgetpgrp(fd)) == -1) -- cgit From bc0bd8213d26d7b85cdf2c26fbd68942291b5994 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 1 Mar 2022 15:20:22 +0000 Subject: Don't convert codes for special keys (Tab, Enter, Escape). --- input-keys.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/input-keys.c b/input-keys.c index f478cb95..feb62f6d 100644 --- a/input-keys.c +++ b/input-keys.c @@ -519,7 +519,7 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key) } outkey = (key & KEYC_MASK_KEY); modifiers = (key & KEYC_MASK_MODIFIERS); - if (outkey < ' ') { + if (outkey < 32 && outkey != 9 && outkey != 13 && outkey != 27) { outkey = 64 + outkey; modifiers |= KEYC_CTRL; } -- cgit From 355ced93cc219897bf3c660e481808c006ffab6a Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 3 Mar 2022 08:24:12 +0000 Subject: Allow optional arguments. --- arguments.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/arguments.c b/arguments.c index a27d8e0a..91c470e2 100644 --- a/arguments.c +++ b/arguments.c @@ -131,8 +131,9 @@ args_parse(const struct args_parse *parse, struct args_value *values, u_int i; enum args_parse_type type; struct args_value *value, *new; - u_char flag, argument; + u_char flag; const char *found, *string, *s; + int optional_argument; if (count == 0) return (args_create()); @@ -169,18 +170,27 @@ args_parse(const struct args_parse *parse, struct args_value *values, args_free(args); return (NULL); } - argument = *++found; - if (argument != ':') { + if (*++found != ':') { log_debug("%s: -%c", __func__, flag); args_set(args, flag, NULL); continue; } + if (*found == ':') { + optional_argument = 1; + found++; + } new = xcalloc(1, sizeof *new); if (*string != '\0') { new->type = ARGS_STRING; new->string = xstrdup(string); } else { if (i == count) { + if (optional_argument) { + log_debug("%s: -%c", __func__, + flag); + args_set(args, flag, NULL); + continue; + } xasprintf(cause, "-%c expects an argument", flag); -- cgit From d5a84de84293798438989d7894e8001bec8c19f1 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 7 Mar 2022 11:52:09 +0000 Subject: Pass client when adding menu item, GitHub issue 3103. --- status.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/status.c b/status.c index 79033242..8d702de1 100644 --- a/status.c +++ b/status.c @@ -1798,7 +1798,7 @@ status_prompt_complete_window_menu(struct client *c, struct session *s, item.name = tmp; item.key = '0' + size - 1; item.command = NULL; - menu_add_item(menu, &item, NULL, NULL, NULL); + menu_add_item(menu, &item, NULL, c, NULL); free(tmp); if (size == height) -- cgit From 367ee79df08f80f0ae28a967ed3d9922e4ccfb22 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 7 Mar 2022 15:21:39 +0000 Subject: Remove unnecessary declarations. --- osdep-netbsd.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/osdep-netbsd.c b/osdep-netbsd.c index b473e017..6003c035 100644 --- a/osdep-netbsd.c +++ b/osdep-netbsd.c @@ -35,9 +35,6 @@ ((p)->p_stat == SSTOP || (p)->p_stat == SZOMB) struct kinfo_proc2 *cmp_procs(struct kinfo_proc2 *, struct kinfo_proc2 *); -char *osdep_get_name(int, char *); -char *osdep_get_cwd(int); -struct event_base *osdep_event_init(void); struct kinfo_proc2 * cmp_procs(struct kinfo_proc2 *p1, struct kinfo_proc2 *p2) -- cgit From f1d87241987e4b46ae839395c06d9eb48e630fa7 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 8 Mar 2022 11:02:17 +0000 Subject: Add getpeerid compat. --- compat/getpeereid.c | 40 ++++++++++++++++++++++++++++++++++++++++ configure.ac | 11 ++++++++++- 2 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 compat/getpeereid.c diff --git a/compat/getpeereid.c b/compat/getpeereid.c new file mode 100644 index 00000000..722f14a2 --- /dev/null +++ b/compat/getpeereid.c @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2022 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include + +#include "compat.h" + +int +getpeereid(int s, uid_t *uid, gid_t *gid) +{ +#ifdef HAVE_SO_PEERCRED + struct ucred uc; + int len = sizeof uc; + + if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &uc, &len) == -1) + return (-1); + *uid = uc.uid; + *gid = uc.gid; + return (0); +#else + errno = EOPNOTSUPP; + return (-1); +#endif +} diff --git a/configure.ac b/configure.ac index fb0ee239..6f607b0b 100644 --- a/configure.ac +++ b/configure.ac @@ -160,6 +160,7 @@ AC_REPLACE_FUNCS([ \ freezero \ getdtablecount \ getdtablesize \ + getpeereid \ getline \ getprogname \ memmem \ @@ -169,7 +170,7 @@ AC_REPLACE_FUNCS([ \ strlcat \ strlcpy \ strndup \ - strsep \ + strsep ]) AC_FUNC_STRNLEN @@ -684,6 +685,14 @@ AC_CHECK_DECL( [#include ] ) +# Look for setsockopt(SO_PEERCRED). +AC_CHECK_DECL( + SO_PEERCRED, + AC_DEFINE(HAVE_SO_PEERCRED), + , + [#include ] +) + # Look for fcntl(F_CLOSEM). AC_CHECK_DECL( F_CLOSEM, -- cgit From 759f94965454a043285813cf4fd8b83944587973 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 8 Mar 2022 11:04:15 +0000 Subject: Need a declaration for getpeereid also. --- compat.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/compat.h b/compat.h index 13334ad7..be726831 100644 --- a/compat.h +++ b/compat.h @@ -334,6 +334,11 @@ char *strndup(const char *, size_t); void *memmem(const void *, size_t, const void *, size_t); #endif +#ifndef HAVE_GETPEEREID +/* getpeereid.c */ +int getpeereid(int, uid_t *, gid_t *); +#endif + #ifndef HAVE_DAEMON /* daemon.c */ int daemon(int, int); -- cgit From f97d784f172a10e0444ad93bb536ee0428613aa7 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 8 Mar 2022 11:35:06 +0000 Subject: Use getpeerucred if available (not tested). --- compat/getpeereid.c | 19 +++++++++++++++++++ configure.ac | 6 ++++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/compat/getpeereid.c b/compat/getpeereid.c index 722f14a2..5a593c07 100644 --- a/compat/getpeereid.c +++ b/compat/getpeereid.c @@ -19,6 +19,10 @@ #include +#ifdef HAVE_UCRED_H +#include +#endif + #include "compat.h" int @@ -33,6 +37,21 @@ getpeereid(int s, uid_t *uid, gid_t *gid) *uid = uc.uid; *gid = uc.gid; return (0); +#elif defined(HAVE_GETPEERUCRED) +int +getpeereid(int s, uid_t *uid, gid_t *gid) +{ + ucred_t *ucred = NULL; + + if (getpeerucred(s, &ucred) == -1) + return (-1); + if ((*uid = ucred_geteuid(ucred)) == -1) + return (-1); + if ((*gid = ucred_getrgid(ucred)) == -1) + return (-1); + ucred_free(ucred); + return (0); +} #else errno = EOPNOTSUPP; return (-1); diff --git a/configure.ac b/configure.ac index 6f607b0b..a9c035d7 100644 --- a/configure.ac +++ b/configure.ac @@ -128,6 +128,7 @@ AC_CHECK_HEADERS([ \ sys/dir.h \ sys/ndir.h \ sys/tree.h \ + ucred.h \ util.h \ ]) @@ -146,7 +147,8 @@ AC_CHECK_FUNCS([ \ flock \ prctl \ proc_pidinfo \ - sysconf + getpeerucred \ + sysconf \ ]) # Check for functions with a compatibility implementation. @@ -170,7 +172,7 @@ AC_REPLACE_FUNCS([ \ strlcat \ strlcpy \ strndup \ - strsep + strsep \ ]) AC_FUNC_STRNLEN -- cgit From 8aed44420152b87e08f8f120ffbe4a888ee5d951 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 8 Mar 2022 11:28:40 +0000 Subject: Add formats for client and server UID and user (for multiuser setups). --- cmd-list-clients.c | 2 ++ format.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ proc.c | 11 ++++++++++ 3 files changed, 73 insertions(+) diff --git a/cmd-list-clients.c b/cmd-list-clients.c index a5b7d147..53a99178 100644 --- a/cmd-list-clients.c +++ b/cmd-list-clients.c @@ -31,6 +31,8 @@ #define LIST_CLIENTS_TEMPLATE \ "#{client_name}: #{session_name} " \ "[#{client_width}x#{client_height} #{client_termname}] " \ + "#{?#{!=:#{client_uid},#{uid}}," \ + "[user #{?client_user,#{client_user},#{client_uid},}] ,}" \ "#{?client_flags,(,}#{client_flags}#{?client_flags,),}" static enum cmd_retval cmd_list_clients_exec(struct cmd *, struct cmdq_item *); diff --git a/format.c b/format.c index 5c6eed36..52eb4642 100644 --- a/format.c +++ b/format.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -1387,6 +1388,35 @@ format_cb_client_tty(struct format_tree *ft) return (NULL); } +/* Callback for client_uid. */ +static void * +format_cb_client_uid(struct format_tree *ft) +{ + uid_t uid; + + if (ft->c != NULL) { + uid = proc_get_peer_uid(ft->c->peer); + if (uid != (uid_t)-1) + return (format_printf("%ld", (long)uid)); + } + return (NULL); +} + +/* Callback for client_user. */ +static void * +format_cb_client_user(struct format_tree *ft) +{ + uid_t uid; + struct passwd *pw; + + if (ft->c != NULL) { + uid = proc_get_peer_uid(ft->c->peer); + if (uid != (uid_t)-1 && (pw = getpwuid(uid)) != NULL) + return (xstrdup(pw->pw_name)); + } + return (NULL); +} + /* Callback for client_utf8. */ static void * format_cb_client_utf8(struct format_tree *ft) @@ -2521,6 +2551,24 @@ format_cb_tree_mode_format(__unused struct format_tree *ft) return (xstrdup(window_tree_mode.default_format)); } +/* Callback for uid. */ +static void * +format_cb_uid(__unused struct format_tree *ft) +{ + return (format_printf("%ld", (long)getuid())); +} + +/* Callback for user. */ +static void * +format_cb_user(__unused struct format_tree *ft) +{ + struct passwd *pw; + + if ((pw = getpwuid(getuid())) != NULL) + return (xstrdup(pw->pw_name)); + return NULL; +} + /* Format table type. */ enum format_table_type { FORMAT_TABLE_STRING, @@ -2627,6 +2675,12 @@ static const struct format_table_entry format_table[] = { { "client_tty", FORMAT_TABLE_STRING, format_cb_client_tty }, + { "client_uid", FORMAT_TABLE_STRING, + format_cb_client_uid + }, + { "client_user", FORMAT_TABLE_STRING, + format_cb_client_user + }, { "client_utf8", FORMAT_TABLE_STRING, format_cb_client_utf8 }, @@ -2906,6 +2960,12 @@ static const struct format_table_entry format_table[] = { { "tree_mode_format", FORMAT_TABLE_STRING, format_cb_tree_mode_format }, + { "uid", FORMAT_TABLE_STRING, + format_cb_uid + }, + { "user", FORMAT_TABLE_STRING, + format_cb_user + }, { "version", FORMAT_TABLE_STRING, format_cb_version }, diff --git a/proc.c b/proc.c index 958a9483..a9b1473e 100644 --- a/proc.c +++ b/proc.c @@ -56,6 +56,7 @@ struct tmuxpeer { struct imsgbuf ibuf; struct event event; + uid_t uid; int flags; #define PEER_BAD 0x1 @@ -308,6 +309,7 @@ proc_add_peer(struct tmuxproc *tp, int fd, void (*dispatchcb)(struct imsg *, void *), void *arg) { struct tmuxpeer *peer; + gid_t gid; peer = xcalloc(1, sizeof *peer); peer->parent = tp; @@ -318,6 +320,9 @@ proc_add_peer(struct tmuxproc *tp, int fd, imsg_init(&peer->ibuf, fd); event_set(&peer->event, fd, EV_READ, proc_event_cb, peer); + if (getpeereid(fd, &peer->uid, &gid) != 0) + peer->uid = (uid_t)-1; + log_debug("add peer %p: %d (%p)", peer, fd, arg); TAILQ_INSERT_TAIL(&tp->peers, peer, entry); @@ -373,3 +378,9 @@ proc_fork_and_daemon(int *fd) return (pid); } } + +uid_t +proc_get_peer_uid(struct tmuxpeer *peer) +{ + return (peer->uid); +} -- cgit From 57f331438a1eb67d6f1d3380278c8df7c9003dc1 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 8 Mar 2022 12:01:19 +0000 Subject: Add argument to refresh-client -l to forward clipboard to a pane. GitHub issue 3068. --- cmd-refresh-client.c | 39 ++++++++++-- input.c | 47 +++++++++------ server-client.c | 1 + tmux.1 | 15 ++++- tmux.h | 166 +++++++++++++++++++++++++++------------------------ tty-keys.c | 28 ++++++--- tty.c | 17 ++++-- 7 files changed, 195 insertions(+), 118 deletions(-) diff --git a/cmd-refresh-client.c b/cmd-refresh-client.c index 2af9cb46..b2665ad9 100644 --- a/cmd-refresh-client.c +++ b/cmd-refresh-client.c @@ -34,7 +34,7 @@ const struct cmd_entry cmd_refresh_client_entry = { .name = "refresh-client", .alias = "refresh", - .args = { "A:B:cC:Df:F:lLRSt:U", 0, 1, NULL }, + .args = { "A:B:cC:Df:F:l::LRSt:U", 0, 1, NULL }, .usage = "[-cDlLRSU] [-A pane:state] [-B name:what:format] " "[-C XxY] [-f flags] " CMD_TARGET_CLIENT_USAGE " [adjustment]", @@ -162,6 +162,37 @@ out: free(copy); } +static enum cmd_retval +cmd_refresh_client_clipboard(struct cmd *self, struct cmdq_item *item) +{ + struct args *args = cmd_get_args(self); + struct client *tc = cmdq_get_target_client(item); + const char *p; + u_int i; + struct cmd_find_state fs; + + p = args_get(args, 'l'); + if (p == NULL) { + if (tc->flags & CLIENT_CLIPBOARDBUFFER) + return (CMD_RETURN_NORMAL); + tc->flags |= CLIENT_CLIPBOARDBUFFER; + } else { + if (cmd_find_target(&fs, item, p, CMD_FIND_PANE, 0) != 0) + return (CMD_RETURN_ERROR); + for (i = 0; i < tc->clipboard_npanes; i++) { + if (tc->clipboard_panes[i] == fs.wp->id) + break; + } + if (i != tc->clipboard_npanes) + return (CMD_RETURN_NORMAL); + tc->clipboard_panes = xreallocarray (tc->clipboard_panes, + tc->clipboard_npanes + 1, sizeof *tc->clipboard_panes); + tc->clipboard_panes[tc->clipboard_npanes++] = fs.wp->id; + } + tty_clipboard_query(&tc->tty); + return (CMD_RETURN_NORMAL); +} + static enum cmd_retval cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item) { @@ -224,10 +255,8 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_NORMAL); } - if (args_has(args, 'l')) { - tty_send_osc52_query(&tc->tty); - return (CMD_RETURN_NORMAL); - } + if (args_has(args, 'l')) + return (cmd_refresh_client_clipboard(self, item)); if (args_has(args, 'F')) /* -F is an alias for -f */ server_client_set_flags(tc, args_get(args, 'F')); diff --git a/input.c b/input.c index 5adc694d..b1856538 100644 --- a/input.c +++ b/input.c @@ -2682,8 +2682,8 @@ input_osc_52(struct input_ctx *ictx, const char *p) { struct window_pane *wp = ictx->wp; char *end; - const char *buf; - size_t len; + const char *buf = NULL; + size_t len = 0; u_char *out; int outlen, state; struct screen_write_ctx ctx; @@ -2703,26 +2703,12 @@ input_osc_52(struct input_ctx *ictx, const char *p) log_debug("%s: %s", __func__, end); if (strcmp(end, "?") == 0) { - if ((pb = paste_get_top(NULL)) != NULL) { + if ((pb = paste_get_top(NULL)) != NULL) buf = paste_buffer_data(pb, &len); - outlen = 4 * ((len + 2) / 3) + 1; - out = xmalloc(outlen); - if ((outlen = b64_ntop(buf, len, out, outlen)) == -1) { - free(out); - return; - } - } else { - outlen = 0; - out = NULL; - } - bufferevent_write(ictx->event, "\033]52;;", 6); - if (outlen != 0) - bufferevent_write(ictx->event, out, outlen); if (ictx->input_end == INPUT_END_BEL) - bufferevent_write(ictx->event, "\007", 1); + input_reply_clipboard(ictx->event, buf, len, "\007"); else - bufferevent_write(ictx->event, "\033\\", 2); - free(out); + input_reply_clipboard(ictx->event, buf, len, "\033\\"); return; } @@ -2780,3 +2766,26 @@ input_osc_104(struct input_ctx *ictx, const char *p) screen_write_fullredraw(&ictx->ctx); free(copy); } + +void +input_reply_clipboard(struct bufferevent *bev, const char *buf, size_t len, + const char *end) +{ + char *out = NULL; + size_t outlen = 0; + + if (buf != NULL && len != 0) { + outlen = 4 * ((len + 2) / 3) + 1; + out = xmalloc(outlen); + if ((outlen = b64_ntop(buf, len, out, outlen)) == -1) { + free(out); + return; + } + } + + bufferevent_write(bev, "\033]52;;", 6); + if (outlen != 0) + bufferevent_write(bev, out, outlen); + bufferevent_write(bev, end, strlen(end)); + free(out); +} diff --git a/server-client.c b/server-client.c index 0f043796..3b888d01 100644 --- a/server-client.c +++ b/server-client.c @@ -422,6 +422,7 @@ server_client_lost(struct client *c) if (c->flags & CLIENT_TERMINAL) tty_free(&c->tty); free(c->ttyname); + free(c->clipboard_panes); free(c->term_name); free(c->term_type); diff --git a/tmux.1 b/tmux.1 index 1837e248..475ddfbb 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1341,11 +1341,12 @@ 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 cDLRSU .Op Fl A Ar pane:state .Op Fl B Ar name:what:format .Op Fl C Ar size .Op Fl f Ar flags +.Op Fl l Op Ar target-pane .Op Fl t Ar target-client .Op Ar adjustment .Xc @@ -1459,7 +1460,11 @@ sets a comma-separated list of client flags, see .Fl l requests the clipboard from the client using the .Xr xterm 1 -escape sequence and stores it in a new paste buffer. +escape sequence. +If +Ar target-pane +is given, the clipboard is sent (in encoded form), otherwise it is stored in a +new paste buffer. .Pp .Fl L , .Fl R , @@ -5057,6 +5062,8 @@ The following variables are available, where appropriate: .It Li "client_termname" Ta "" Ta "Terminal name of client" .It Li "client_termtype" Ta "" Ta "Terminal type of client, if available" .It Li "client_tty" Ta "" Ta "Pseudo terminal of client" +.It Li "client_uid" Ta "" Ta "UID of client process" +.It Li "client_user" Ta "" Ta "User of client process" .It Li "client_utf8" Ta "" Ta "1 if client supports UTF-8" .It Li "client_width" Ta "" Ta "Width of client" .It Li "client_written" Ta "" Ta "Bytes written to client" @@ -5174,6 +5181,8 @@ The following variables are available, where appropriate: .It Li "session_windows" Ta "" Ta "Number of windows in session" .It Li "socket_path" Ta "" Ta "Server socket path" .It Li "start_time" Ta "" Ta "Server start time" +.It Li "uid" Ta "" Ta "Server UID" +.It Li "user" Ta "" Ta "Server user" .It Li "version" Ta "" Ta "Server version" .It Li "window_active" Ta "" Ta "1 if window active" .It Li "window_active_clients" Ta "" Ta "Number of clients viewing this window" @@ -5188,7 +5197,6 @@ The following variables are available, where appropriate: .It Li "window_cell_width" Ta "" Ta "Width of each cell in pixels" .It Li "window_end_flag" Ta "" Ta "1 if window has the highest index" .It Li "window_flags" Ta "#F" Ta "Window flags with # escaped as ##" -.It Li "window_raw_flags" Ta "" Ta "Window flags with nothing escaped" .It Li "window_format" Ta "" Ta "1 if format is for a window" .It Li "window_height" Ta "" Ta "Height of window" .It Li "window_id" Ta "" Ta "Unique window ID" @@ -5203,6 +5211,7 @@ The following variables are available, where appropriate: .It Li "window_offset_x" Ta "" Ta "X offset into window if larger than client" .It Li "window_offset_y" Ta "" Ta "Y offset into window if larger than client" .It Li "window_panes" Ta "" Ta "Number of panes in window" +.It Li "window_raw_flags" Ta "" Ta "Window flags with nothing escaped" .It Li "window_silence_flag" Ta "" Ta "1 if window has silence alert" .It Li "window_stack_index" Ta "" Ta "Index in session most recent stack" .It Li "window_start_flag" Ta "" Ta "1 if window has the lowest index" diff --git a/tmux.h b/tmux.h index 7eccaa55..78283aa2 100644 --- a/tmux.h +++ b/tmux.h @@ -1332,7 +1332,7 @@ LIST_HEAD(tty_terms, tty_term); struct tty { struct client *client; struct event start_timer; - struct event query_timer; + struct event clipboard_timer; u_int sx; u_int sy; @@ -1685,50 +1685,50 @@ typedef int (*overlay_key_cb)(struct client *, void *, struct key_event *); typedef void (*overlay_free_cb)(struct client *, void *); typedef void (*overlay_resize_cb)(struct client *, void *); struct client { - const char *name; - struct tmuxpeer *peer; - struct cmdq_list *queue; + const char *name; + struct tmuxpeer *peer; + struct cmdq_list *queue; - struct client_windows windows; + struct client_windows windows; - struct control_state *control_state; - u_int pause_age; + struct control_state *control_state; + u_int pause_age; - pid_t pid; - int fd; - int out_fd; - struct event event; - int retval; + pid_t pid; + int fd; + int out_fd; + struct event event; + int retval; - struct timeval creation_time; - struct timeval activity_time; + struct timeval creation_time; + struct timeval activity_time; - struct environ *environ; + struct environ *environ; struct format_job_tree *jobs; - char *title; - const char *cwd; + char *title; + const char *cwd; - char *term_name; - int term_features; - char *term_type; - char **term_caps; - u_int term_ncaps; + char *term_name; + int term_features; + char *term_type; + char **term_caps; + u_int term_ncaps; - char *ttyname; - struct tty tty; + char *ttyname; + struct tty tty; - size_t written; - size_t discarded; - size_t redraw; + size_t written; + size_t discarded; + size_t redraw; - struct event repeat_timer; + struct event repeat_timer; - struct event click_timer; - u_int click_button; - struct mouse_event click_event; + struct event click_timer; + u_int click_button; + struct mouse_event click_event; - struct status_line status; + struct status_line status; #define CLIENT_TERMINAL 0x1 #define CLIENT_LOGIN 0x2 @@ -1765,6 +1765,7 @@ struct client { #define CLIENT_CONTROL_PAUSEAFTER 0x100000000ULL #define CLIENT_CONTROL_WAITEXIT 0x200000000ULL #define CLIENT_WINDOWSIZECHANGED 0x400000000ULL +#define CLIENT_CLIPBOARDBUFFER 0x800000000ULL #define CLIENT_ALLREDRAWFLAGS \ (CLIENT_REDRAWWINDOW| \ CLIENT_REDRAWSTATUS| \ @@ -1776,73 +1777,79 @@ struct client { (CLIENT_DEAD| \ CLIENT_SUSPENDED| \ CLIENT_EXIT) -#define CLIENT_NODETACHFLAGS \ +#define CLIENT_NODETACHFLAGS \ (CLIENT_DEAD| \ CLIENT_EXIT) #define CLIENT_NOSIZEFLAGS \ (CLIENT_DEAD| \ CLIENT_SUSPENDED| \ CLIENT_EXIT) - uint64_t flags; + uint64_t flags; enum { CLIENT_EXIT_RETURN, CLIENT_EXIT_SHUTDOWN, CLIENT_EXIT_DETACH - } exit_type; - enum msgtype exit_msgtype; - char *exit_session; - char *exit_message; - - struct key_table *keytable; - - uint64_t redraw_panes; - - int message_ignore_keys; - int message_ignore_styles; - char *message_string; - struct event message_timer; - - char *prompt_string; - struct utf8_data *prompt_buffer; - char *prompt_last; - size_t prompt_index; - prompt_input_cb prompt_inputcb; - prompt_free_cb prompt_freecb; - void *prompt_data; - u_int prompt_hindex[PROMPT_NTYPES]; - enum { PROMPT_ENTRY, PROMPT_COMMAND } prompt_mode; - struct utf8_data *prompt_saved; + } exit_type; + enum msgtype exit_msgtype; + char *exit_session; + char *exit_message; + + struct key_table *keytable; + + uint64_t redraw_panes; + + int message_ignore_keys; + int message_ignore_styles; + char *message_string; + struct event message_timer; + + char *prompt_string; + struct utf8_data *prompt_buffer; + char *prompt_last; + size_t prompt_index; + prompt_input_cb prompt_inputcb; + prompt_free_cb prompt_freecb; + void *prompt_data; + u_int prompt_hindex[PROMPT_NTYPES]; + enum { + PROMPT_ENTRY, + PROMPT_COMMAND + } prompt_mode; + struct utf8_data *prompt_saved; #define PROMPT_SINGLE 0x1 #define PROMPT_NUMERIC 0x2 #define PROMPT_INCREMENTAL 0x4 #define PROMPT_NOFORMAT 0x8 #define PROMPT_KEY 0x10 - int prompt_flags; - enum prompt_type prompt_type; - int prompt_cursor; + int prompt_flags; + enum prompt_type prompt_type; + int prompt_cursor; - struct session *session; - struct session *last_session; + struct session *session; + struct session *last_session; - int references; + int references; + + void *pan_window; + u_int pan_ox; + u_int pan_oy; - void *pan_window; - u_int pan_ox; - u_int pan_oy; + overlay_check_cb overlay_check; + overlay_mode_cb overlay_mode; + overlay_draw_cb overlay_draw; + overlay_key_cb overlay_key; + overlay_free_cb overlay_free; + overlay_resize_cb overlay_resize; + void *overlay_data; + struct event overlay_timer; - overlay_check_cb overlay_check; - overlay_mode_cb overlay_mode; - overlay_draw_cb overlay_draw; - overlay_key_cb overlay_key; - overlay_free_cb overlay_free; - overlay_resize_cb overlay_resize; - void *overlay_data; - struct event overlay_timer; + struct client_files files; - struct client_files files; + u_int *clipboard_panes; + u_int clipboard_npanes; - TAILQ_ENTRY(client) entry; + TAILQ_ENTRY(client) entry; }; TAILQ_HEAD(clients, client); @@ -2016,6 +2023,7 @@ void proc_remove_peer(struct tmuxpeer *); void proc_kill_peer(struct tmuxpeer *); void proc_toggle_log(struct tmuxproc *); pid_t proc_fork_and_daemon(int *); +uid_t proc_get_peer_uid(struct tmuxpeer *); /* cfg.c */ extern int cfg_finished; @@ -2229,7 +2237,7 @@ void tty_reset(struct tty *); void tty_region_off(struct tty *); void tty_margin_off(struct tty *); void tty_cursor(struct tty *, u_int, u_int); -void tty_send_osc52_query(struct tty *); +void tty_clipboard_query(struct tty *); void tty_putcode(struct tty *, enum tty_code_code); void tty_putcode1(struct tty *, enum tty_code_code, int); void tty_putcode2(struct tty *, enum tty_code_code, int, int); @@ -2675,6 +2683,8 @@ void input_parse_pane(struct window_pane *); void input_parse_buffer(struct window_pane *, u_char *, size_t); void input_parse_screen(struct input_ctx *, struct screen *, screen_write_init_ctx_cb, void *, u_char *, size_t); +void input_reply_clipboard(struct bufferevent *, const char *, size_t, + const char *); /* input-key.c */ void input_key_build(void); diff --git a/tty-keys.c b/tty-keys.c index 30717af3..8538e74b 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -1154,12 +1154,14 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size, * partial. */ static int -tty_keys_clipboard(__unused struct tty *tty, const char *buf, size_t len, - size_t *size) +tty_keys_clipboard(struct tty *tty, const char *buf, size_t len, size_t *size) { - size_t end, terminator, needed; - char *copy, *out; - int outlen; + struct client *c = tty->client; + struct window_pane *wp; + size_t end, terminator, needed; + char *copy, *out; + int outlen; + u_int i; *size = 0; @@ -1221,6 +1223,7 @@ tty_keys_clipboard(__unused struct tty *tty, const char *buf, size_t len, if (~tty->flags & TTY_OSC52QUERY) return (0); tty->flags &= ~TTY_OSC52QUERY; + evtimer_del(&tty->clipboard_timer); /* It has to be a string so copy it. */ copy = xmalloc(end + 1); @@ -1237,9 +1240,20 @@ tty_keys_clipboard(__unused struct tty *tty, const char *buf, size_t len, } free(copy); - /* Create a new paste buffer. */ + /* Create a new paste buffer and forward to panes. */ log_debug("%s: %.*s", __func__, outlen, out); - paste_add(NULL, out, outlen); + if (c->flags & CLIENT_CLIPBOARDBUFFER) { + paste_add(NULL, out, outlen); + c->flags &= ~CLIENT_CLIPBOARDBUFFER; + } + for (i = 0; i < c->clipboard_npanes; i++) { + wp = window_pane_find_by_id(c->clipboard_panes[i]); + if (wp != NULL) + input_reply_clipboard(wp->event, out, outlen, "\033\\"); + } + free(c->clipboard_panes); + c->clipboard_panes = NULL; + c->clipboard_npanes = 0; return (0); } diff --git a/tty.c b/tty.c index 06b1bfb2..40735ceb 100644 --- a/tty.c +++ b/tty.c @@ -2921,24 +2921,29 @@ tty_default_attributes(struct tty *tty, const struct grid_cell *defaults, } static void -tty_query_timer_callback(__unused int fd, __unused short events, void *data) +tty_clipboard_query_callback(__unused int fd, __unused short events, void *data) { struct tty *tty = data; + struct client *c = tty->client; + + c->flags &= ~CLIENT_CLIPBOARDBUFFER; + free(c->clipboard_panes); + c->clipboard_panes = NULL; + c->clipboard_npanes = 0; tty->flags &= ~TTY_OSC52QUERY; } void -tty_send_osc52_query(struct tty *tty) +tty_clipboard_query(struct tty *tty) { struct timeval tv = { .tv_sec = TTY_QUERY_TIMEOUT }; if ((~tty->flags & TTY_STARTED) || (tty->flags & TTY_OSC52QUERY)) return; tty_putcode_ptr2(tty, TTYC_MS, "", "?"); - tty->flags |= TTY_OSC52QUERY; - evtimer_set(&tty->query_timer, tty_query_timer_callback, tty); - evtimer_add(&tty->query_timer, &tv); + tty->flags |= TTY_OSC52QUERY; + evtimer_set(&tty->clipboard_timer, tty_clipboard_query_callback, tty); + evtimer_add(&tty->clipboard_timer, &tv); } - -- cgit From 98b92c0525f7cd5878122815cbe9adb4e292ce2c Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 8 Mar 2022 18:31:46 +0000 Subject: Add remain-on-exit-format to set text shown when pane is dead. --- format.c | 37 +++++++++++++++++++++++++++++++++++++ options-table.c | 13 +++++++++++++ server-fn.c | 42 +++++++++++++++++++----------------------- tmux.1 | 7 +++++++ tmux.h | 1 + 5 files changed, 77 insertions(+), 23 deletions(-) diff --git a/format.c b/format.c index 52eb4642..2e4bceb5 100644 --- a/format.c +++ b/format.c @@ -1756,6 +1756,23 @@ format_cb_pane_dead(struct format_tree *ft) return (NULL); } +/* Callback for pane_dead_signal. */ +static void * +format_cb_pane_dead_signal(struct format_tree *ft) +{ + struct window_pane *wp = ft->wp; + const char *name; + + if (wp != NULL) { + if ((wp->flags & PANE_STATUSREADY) && WIFSIGNALED(wp->status)) { + name = sig2name(WTERMSIG(wp->status)); + return (format_printf("%s", name)); + } + return (NULL); + } + return (NULL); +} + /* Callback for pane_dead_status. */ static void * format_cb_pane_dead_status(struct format_tree *ft) @@ -1770,6 +1787,20 @@ format_cb_pane_dead_status(struct format_tree *ft) return (NULL); } +/* Callback for pane_dead_time. */ +static void * +format_cb_pane_dead_time(struct format_tree *ft) +{ + struct window_pane *wp = ft->wp; + + if (wp != NULL) { + if (wp->flags & PANE_STATUSDRAWN) + return (&wp->dead_time); + return (NULL); + } + return (NULL); +} + /* Callback for pane_format. */ static void * format_cb_pane_format(struct format_tree *ft) @@ -2804,9 +2835,15 @@ static const struct format_table_entry format_table[] = { { "pane_dead", FORMAT_TABLE_STRING, format_cb_pane_dead }, + { "pane_dead_signal", FORMAT_TABLE_STRING, + format_cb_pane_dead_signal + }, { "pane_dead_status", FORMAT_TABLE_STRING, format_cb_pane_dead_status }, + { "pane_dead_time", FORMAT_TABLE_TIME, + format_cb_pane_dead_time + }, { "pane_fg", FORMAT_TABLE_STRING, format_cb_pane_fg }, diff --git a/options-table.c b/options-table.c index b8a5ccbb..31b0e372 100644 --- a/options-table.c +++ b/options-table.c @@ -1060,6 +1060,19 @@ const struct options_table_entry options_table[] = { "killed ('off' or 'failed') when the program inside exits." }, + { .name = "remain-on-exit-format", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, + .default_str = "Pane is dead (" + "#{?#{!=:#{pane_dead_status},}," + "status #{pane_dead_status},}" + "#{?#{!=:#{pane_dead_signal},}," + "signal #{pane_dead_signal},}, " + "#{t:pane_dead_time})", + .text = "Message shown after the program in a pane has exited, if " + "remain-on-exit is enabled." + }, + { .name = "synchronize-panes", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, diff --git a/server-fn.c b/server-fn.c index 92793093..2a79f3e3 100644 --- a/server-fn.c +++ b/server-fn.c @@ -310,9 +310,11 @@ server_destroy_pane(struct window_pane *wp, int notify) struct window *w = wp->window; struct screen_write_ctx ctx; struct grid_cell gc; - time_t t; - char tim[26]; int remain_on_exit; + const char *s; + char *expanded; + u_int sx = screen_size_x(&wp->base); + u_int sy = screen_size_y(&wp->base); if (wp->fd != -1) { #ifdef HAVE_UTEMPTER @@ -339,32 +341,26 @@ server_destroy_pane(struct window_pane *wp, int notify) return; wp->flags |= PANE_STATUSDRAWN; + gettimeofday(&wp->dead_time, NULL); if (notify) notify_pane("pane-died", wp); - screen_write_start_pane(&ctx, wp, &wp->base); - screen_write_scrollregion(&ctx, 0, screen_size_y(ctx.s) - 1); - screen_write_cursormove(&ctx, 0, screen_size_y(ctx.s) - 1, 0); - screen_write_linefeed(&ctx, 1, 8); - memcpy(&gc, &grid_default_cell, sizeof gc); - - time(&t); - ctime_r(&t, tim); - tim[strcspn(tim, "\n")] = '\0'; - - if (WIFEXITED(wp->status)) { - screen_write_nputs(&ctx, -1, &gc, - "Pane is dead (status %d, %s)", - WEXITSTATUS(wp->status), - tim); - } else if (WIFSIGNALED(wp->status)) { - screen_write_nputs(&ctx, -1, &gc, - "Pane is dead (signal %s, %s)", - sig2name(WTERMSIG(wp->status)), - tim); + s = options_get_string(wp->options, "remain-on-exit-format"); + if (*s != '\0') { + screen_write_start_pane(&ctx, wp, &wp->base); + screen_write_scrollregion(&ctx, 0, sy - 1); + screen_write_cursormove(&ctx, 0, sy - 1, 0); + screen_write_linefeed(&ctx, 1, 8); + memcpy(&gc, &grid_default_cell, sizeof gc); + + expanded = format_single(NULL, s, NULL, NULL, NULL, wp); + format_draw(&ctx, &gc, sx, expanded, NULL, 0); + free(expanded); + + screen_write_stop(&ctx); } + wp->base.mode &= ~MODE_CURSOR; - screen_write_stop(&ctx); wp->flags |= PANE_REDRAW; return; } diff --git a/tmux.1 b/tmux.1 index 475ddfbb..a7a6846f 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4471,6 +4471,11 @@ The pane may be reactivated with the .Ic respawn-pane command. .Pp +.It Ic remain-on-exit-format Ar string +Set the text shown at the bottom of exited panes when +.Ic remain-on-exit +is enabled. +.Pp .It Xo Ic synchronize-panes .Op Ic on | off .Xc @@ -5120,7 +5125,9 @@ The following variables are available, where appropriate: .It Li "pane_current_command" Ta "" Ta "Current command if available" .It Li "pane_current_path" Ta "" Ta "Current path if available" .It Li "pane_dead" Ta "" Ta "1 if pane is dead" +.It Li "pane_dead_signal" Ta "" Ta "Exit signal of process in dead pane" .It Li "pane_dead_status" Ta "" Ta "Exit status of process in dead pane" +.It Li "pane_dead_time" Ta "" Ta "Exit time of process in dead pane" .It Li "pane_fg" Ta "" Ta "Pane foreground colour" .It Li "pane_format" Ta "" Ta "1 if format is for a pane" .It Li "pane_height" Ta "" Ta "Height of pane" diff --git a/tmux.h b/tmux.h index 78283aa2..8a8535dd 100644 --- a/tmux.h +++ b/tmux.h @@ -1027,6 +1027,7 @@ struct window_pane { pid_t pid; char tty[TTY_NAME_MAX]; int status; + struct timeval dead_time; int fd; struct bufferevent *event; -- cgit From 23e613fcf5a1f3dc6f27f52d523bff17528f4b52 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 8 Mar 2022 21:58:37 +0000 Subject: Fix user hooks (which are strings not arrays). --- notify.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 48 insertions(+), 15 deletions(-) diff --git a/notify.c b/notify.c index 9c55d0b8..baeb0600 100644 --- a/notify.c +++ b/notify.c @@ -34,18 +34,37 @@ struct notify_entry { int pane; }; +static struct cmdq_item * +notify_insert_one_hook(struct cmdq_item *item, struct notify_entry *ne, + struct cmd_list *cmdlist, struct cmdq_state *state) +{ + struct cmdq_item *new_item; + char *s; + + if (cmdlist == NULL) + return (item); + if (log_get_level() != 0) { + s = cmd_list_print(cmdlist, 0); + log_debug("%s: hook %s is: %s", __func__, ne->name, s); + free (s); + } + new_item = cmdq_get_command(cmdlist, state); + return (cmdq_insert_after(item, new_item)); +} + static void notify_insert_hook(struct cmdq_item *item, struct notify_entry *ne) { struct cmd_find_state fs; struct options *oo; - struct cmdq_item *new_item; - struct cmdq_state *new_state; + struct cmdq_state *state; struct options_entry *o; struct options_array_item *a; struct cmd_list *cmdlist; + const char *value; + struct cmd_parse_result *pr; - log_debug("%s: %s", __func__, ne->name); + log_debug("%s: inserting hook %s", __func__, ne->name); cmd_find_clear_state(&fs, 0); if (cmd_find_empty_state(&ne->fs) || !cmd_find_valid_state(&ne->fs)) @@ -66,23 +85,37 @@ notify_insert_hook(struct cmdq_item *item, struct notify_entry *ne) oo = fs.wl->window->options; o = options_get(oo, ne->name); } - if (o == NULL) + if (o == NULL) { + log_debug("%s: hook %s not found", __func__, ne->name); return; + } - new_state = cmdq_new_state(&fs, NULL, CMDQ_STATE_NOHOOKS); - cmdq_add_formats(new_state, ne->formats); - - a = options_array_first(o); - while (a != NULL) { - cmdlist = options_array_item_value(a)->cmdlist; - if (cmdlist != NULL) { - new_item = cmdq_get_command(cmdlist, new_state); - item = cmdq_insert_after(item, new_item); + state = cmdq_new_state(&fs, NULL, CMDQ_STATE_NOHOOKS); + cmdq_add_formats(state, ne->formats); + + if (*ne->name == '@') { + value = options_get_string(oo, ne->name); + pr = cmd_parse_from_string(value, NULL); + switch (pr->status) { + case CMD_PARSE_ERROR: + log_debug("%s: can't parse hook %s: %s", __func__, + ne->name, pr->error); + free(pr->error); + break; + case CMD_PARSE_SUCCESS: + notify_insert_one_hook(item, ne, pr->cmdlist, state); + break; + } + } else { + a = options_array_first(o); + while (a != NULL) { + cmdlist = options_array_item_value(a)->cmdlist; + item = notify_insert_one_hook(item, ne, cmdlist, state); + a = options_array_next(a); } - a = options_array_next(a); } - cmdq_free_state(new_state); + cmdq_free_state(state); } static enum cmd_retval -- cgit From ad7113e0dbf2e5f5b6b33fd34824c5cf16e9053c Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 8 Mar 2022 22:14:25 +0000 Subject: With -f use percentages of window size not pane size, GitHub issue 2866. --- cmd-split-window.c | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/cmd-split-window.c b/cmd-split-window.c index 888d4106..9947dfd3 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -60,6 +60,7 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) struct client *tc = cmdq_get_target_client(item); struct session *s = target->s; struct winlink *wl = target->wl; + struct window *w = wl->window; struct window_pane *wp = target->wp, *new_wp; enum layout_type type; struct layout_cell *lc; @@ -86,10 +87,17 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) cmdq_error(item, "percentage %s", errstr); return (CMD_RETURN_ERROR); } - if (type == LAYOUT_TOPBOTTOM) - size = (wp->sy * percentage) / 100; - else - size = (wp->sx * percentage) / 100; + if (args_has(args, 'f')) { + if (type == LAYOUT_TOPBOTTOM) + size = (w->sy * percentage) / 100; + else + size = (w->sx * percentage) / 100; + } else { + if (type == LAYOUT_TOPBOTTOM) + size = (wp->sy * percentage) / 100; + else + size = (wp->sx * percentage) / 100; + } } else { size = args_strtonum(args, 'l', 0, INT_MAX, &cause); if (cause != NULL) { @@ -105,10 +113,17 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) free(cause); return (CMD_RETURN_ERROR); } - if (type == LAYOUT_TOPBOTTOM) - size = (wp->sy * percentage) / 100; - else - size = (wp->sx * percentage) / 100; + if (args_has(args, 'f')) { + if (type == LAYOUT_TOPBOTTOM) + size = (w->sy * percentage) / 100; + else + size = (w->sx * percentage) / 100; + } else { + if (type == LAYOUT_TOPBOTTOM) + size = (wp->sy * percentage) / 100; + else + size = (wp->sx * percentage) / 100; + } } else size = -1; -- cgit From fe44b105e4a2d1d7baa12b37d0b84d8c6be9addc Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 16 Mar 2022 17:00:17 +0000 Subject: Add an option to set the character used for unused areas of the terminal, GitHub issue 3110. --- options-table.c | 7 ++++++ options.c | 6 ++++++ screen-redraw.c | 13 ++++++++---- tmux.1 | 3 +++ tmux.h | 66 +++++++++++++++++++++++++++++---------------------------- window.c | 19 +++++++++++++++++ 6 files changed, 78 insertions(+), 36 deletions(-) diff --git a/options-table.c b/options-table.c index 31b0e372..3aa72e78 100644 --- a/options-table.c +++ b/options-table.c @@ -881,6 +881,13 @@ const struct options_table_entry options_table[] = { .text = "Style of the marked line in copy mode." }, + { .name = "fill-character", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_WINDOW, + .default_str = "", + .text = "Character used to fill unused parts of window." + }, + { .name = "main-pane-height", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, diff --git a/options.c b/options.c index 865ab01f..f9cf2afd 100644 --- a/options.c +++ b/options.c @@ -1108,6 +1108,8 @@ options_push_changes(const char *name) struct window_pane *wp; int c; + log_debug("%s: %s", __func__, name); + if (strcmp(name, "automatic-rename") == 0) { RB_FOREACH(w, windows, &windows) { if (w->active == NULL) @@ -1130,6 +1132,10 @@ options_push_changes(const char *name) &wp->screen->default_mode); } } + if (strcmp(name, "fill-character") == 0) { + RB_FOREACH(w, windows, &windows) + window_set_fill_character(w); + } if (strcmp(name, "key-table") == 0) { TAILQ_FOREACH(loop, &clients, entry) server_client_set_key_table(loop, NULL); diff --git a/screen-redraw.c b/screen-redraw.c index ef79d9aa..c4906ab8 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -47,11 +47,16 @@ enum screen_redraw_border_type { /* Get cell border character. */ static void -screen_redraw_border_set(struct window_pane *wp, enum pane_lines pane_lines, - int cell_type, struct grid_cell *gc) +screen_redraw_border_set(struct window *w, struct window_pane *wp, + enum pane_lines pane_lines, int cell_type, struct grid_cell *gc) { u_int idx; + if (cell_type == CELL_OUTSIDE && w->fill_character != NULL) { + utf8_copy(&gc->data, &w->fill_character[0]); + return; + } + switch (pane_lines) { case PANE_LINES_NUMBER: if (cell_type == CELL_OUTSIDE) { @@ -409,7 +414,7 @@ screen_redraw_make_pane_status(struct client *c, struct window_pane *wp, else py = wp->yoff + wp->sy; cell_type = screen_redraw_type_of_cell(c, px, py, pane_status); - screen_redraw_border_set(wp, pane_lines, cell_type, &gc); + screen_redraw_border_set(w, wp, pane_lines, cell_type, &gc); screen_write_cell(&ctx, &gc); } gc.attr &= ~GRID_ATTR_CHARSET; @@ -690,7 +695,7 @@ screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j) screen_redraw_check_is(x, y, pane_status, marked_pane.wp)) gc.attr ^= GRID_ATTR_REVERSE; } - screen_redraw_border_set(wp, ctx->pane_lines, cell_type, &gc); + screen_redraw_border_set(w, wp, ctx->pane_lines, cell_type, &gc); if (cell_type == CELL_TOPBOTTOM && (c->flags & CLIENT_UTF8) && diff --git a/tmux.1 b/tmux.1 index a7a6846f..a3a37e1e 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4117,6 +4117,9 @@ Set clock colour. .Xc Set clock hour format. .Pp +.It Ic fill-character Ar character +Set the character used to fill areas of the terminal unused by a window. +.Pp .It Ic main-pane-height Ar height .It Ic main-pane-width Ar width Set the width or height of the main (left or top) pane in the diff --git a/tmux.h b/tmux.h index 8a8535dd..e23ad3a1 100644 --- a/tmux.h +++ b/tmux.h @@ -1070,40 +1070,41 @@ RB_HEAD(window_pane_tree, window_pane); /* Window structure. */ struct window { - u_int id; - void *latest; + u_int id; + void *latest; - char *name; - struct event name_event; - struct timeval name_time; + char *name; + struct event name_event; + struct timeval name_time; - struct event alerts_timer; - struct event offset_timer; + struct event alerts_timer; + struct event offset_timer; - struct timeval activity_time; + struct timeval activity_time; - struct window_pane *active; - struct window_pane *last; - struct window_panes panes; + struct window_pane *active; + struct window_pane *last; + struct window_panes panes; - int lastlayout; - struct layout_cell *layout_root; - struct layout_cell *saved_layout_root; - char *old_layout; + int lastlayout; + struct layout_cell *layout_root; + struct layout_cell *saved_layout_root; + char *old_layout; - u_int sx; - u_int sy; - u_int manual_sx; - u_int manual_sy; - u_int xpixel; - u_int ypixel; + u_int sx; + u_int sy; + u_int manual_sx; + u_int manual_sy; + u_int xpixel; + u_int ypixel; - u_int new_sx; - u_int new_sy; - u_int new_xpixel; - u_int new_ypixel; + u_int new_sx; + u_int new_sy; + u_int new_xpixel; + u_int new_ypixel; - int flags; + struct utf8_data *fill_character; + int flags; #define WINDOW_BELL 0x1 #define WINDOW_ACTIVITY 0x2 #define WINDOW_SILENCE 0x4 @@ -1112,15 +1113,15 @@ struct window { #define WINDOW_RESIZE 0x20 #define WINDOW_ALERTFLAGS (WINDOW_BELL|WINDOW_ACTIVITY|WINDOW_SILENCE) - int alerts_queued; - TAILQ_ENTRY(window) alerts_entry; + int alerts_queued; + TAILQ_ENTRY(window) alerts_entry; - struct options *options; + struct options *options; - u_int references; - TAILQ_HEAD(, winlink) winlinks; + u_int references; + TAILQ_HEAD(, winlink) winlinks; - RB_ENTRY(window) entry; + RB_ENTRY(window) entry; }; RB_HEAD(windows, window); @@ -2977,6 +2978,7 @@ void *window_pane_get_new_data(struct window_pane *, struct window_pane_offset *, size_t *); void window_pane_update_used_data(struct window_pane *, struct window_pane_offset *, size_t); +void window_set_fill_character(struct window *); /* layout.c */ u_int layout_count_cells(struct layout_cell *); diff --git a/window.c b/window.c index fcc61c2a..bce8913a 100644 --- a/window.c +++ b/window.c @@ -329,6 +329,7 @@ window_create(u_int sx, u_int sy, u_int xpixel, u_int ypixel) w->id = next_window_id++; RB_INSERT(windows, &windows, w); + window_set_fill_character(w); window_update_activity(w); log_debug("%s: @%u create %ux%u (%ux%u)", __func__, w->id, sx, sy, @@ -360,6 +361,7 @@ window_destroy(struct window *w) event_del(&w->offset_timer); options_free(w->options); + free(w->fill_character); free(w->name); free(w); @@ -1599,3 +1601,20 @@ window_pane_update_used_data(struct window_pane *wp, size = EVBUFFER_LENGTH(wp->event->input) - used; wpo->used += size; } + +void +window_set_fill_character(struct window *w) +{ + const char *value; + struct utf8_data *ud; + + free(w->fill_character); + w->fill_character = NULL; + + value = options_get_string(w->options, "fill-character"); + if (*value != '\0' && utf8_isvalid(value)) { + ud = utf8_fromcstr(value); + if (ud != NULL && ud[0].width == 1) + w->fill_character = ud; + } +} -- cgit From c0508c9321d2bd3b94a850d953db9532854b45cd Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 17 Mar 2022 11:35:37 +0000 Subject: Add an option (scroll-on-clear) to control if tmux scrolls into history on clear, from Robert Lange in GitHub issue 3121. --- options-table.c | 8 ++++++++ screen-write.c | 6 +++++- tmux.1 | 6 ++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/options-table.c b/options-table.c index 3aa72e78..a816f6ac 100644 --- a/options-table.c +++ b/options-table.c @@ -1080,6 +1080,14 @@ const struct options_table_entry options_table[] = { "remain-on-exit is enabled." }, + { .name = "scroll-on-clear", + .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, + .default_num = 1, + .text = "Whether the contents of the screen should be scrolled into" + "history when clearing the whole screen." + }, + { .name = "synchronize-panes", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, diff --git a/screen-write.c b/screen-write.c index 0d70f668..aa898f78 100644 --- a/screen-write.c +++ b/screen-write.c @@ -1427,7 +1427,11 @@ screen_write_clearendofscreen(struct screen_write_ctx *ctx, u_int bg) ttyctx.bg = bg; /* Scroll into history if it is enabled and clearing entire screen. */ - if (s->cx == 0 && s->cy == 0 && (gd->flags & GRID_HISTORY)) + if (s->cx == 0 && + s->cy == 0 && + (gd->flags & GRID_HISTORY) && + ctx->wp != NULL && + options_get_number(ctx->wp->options, "scroll-on-clear")) grid_view_clear_history(gd, bg); else { if (s->cx <= sx - 1) diff --git a/tmux.1 b/tmux.1 index a3a37e1e..abaeb49c 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4479,6 +4479,12 @@ Set the text shown at the bottom of exited panes when .Ic remain-on-exit is enabled. .Pp +.It Xo Ic scroll-on-clear +.Op Ic on | off +.Xc +When the entire screen is cleared and this option is on, scroll the contents of +the screen into history before clearing it. +.Pp .It Xo Ic synchronize-panes .Op Ic on | off .Xc -- cgit From 60a0a904e007390b8fba484eea9461bf095bff7a Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 17 Mar 2022 13:39:13 +0000 Subject: Check scroll-on-clear for ED also. --- screen-write.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/screen-write.c b/screen-write.c index aa898f78..6b6a750e 100644 --- a/screen-write.c +++ b/screen-write.c @@ -1479,7 +1479,9 @@ screen_write_clearscreen(struct screen_write_ctx *ctx, u_int bg) ttyctx.bg = bg; /* Scroll into history if it is enabled. */ - if (s->grid->flags & GRID_HISTORY) + if ((s->grid->flags & GRID_HISTORY) && + ctx->wp != NULL && + options_get_number(ctx->wp->options, "scroll-on-clear")) grid_view_clear_history(s->grid, bg); else grid_view_clear(s->grid, 0, 0, sx, sy, bg); -- cgit From 89a0046ad360d7a499732e327bae69b7e4b2536e Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 24 Mar 2022 09:05:57 +0000 Subject: Add a capability for OSC 7 and use it similarly to how the title is set (and controlled by the same set-titles option). GitHub issue 3127. --- server-client.c | 25 ++++++++++++++++++++++++- tmux.1 | 7 +++++++ tmux.h | 3 +++ tty-features.c | 13 +++++++++++++ tty-term.c | 1 + tty.c | 12 ++++++++++++ 6 files changed, 60 insertions(+), 1 deletion(-) diff --git a/server-client.c b/server-client.c index 3b888d01..2350a982 100644 --- a/server-client.c +++ b/server-client.c @@ -40,6 +40,7 @@ static void server_client_check_exit(struct client *); static void server_client_check_redraw(struct client *); static void server_client_check_modes(struct client *); static void server_client_set_title(struct client *); +static void server_client_set_path(struct client *); static void server_client_reset_state(struct client *); static int server_client_assume_paste(struct session *); static void server_client_update_latest(struct client *); @@ -2600,8 +2601,10 @@ server_client_check_redraw(struct client *c) } if (c->flags & CLIENT_ALLREDRAWFLAGS) { - if (options_get_number(s->options, "set-titles")) + if (options_get_number(s->options, "set-titles")) { server_client_set_title(c); + server_client_set_path(c); + } screen_redraw_screen(c); } @@ -2647,6 +2650,26 @@ server_client_set_title(struct client *c) format_free(ft); } +/* Set client path. */ +static void +server_client_set_path(struct client *c) +{ + struct session *s = c->session; + const char *path; + + if (s->curw == NULL) + return; + if (s->curw->window->active->base.path == NULL) + path = ""; + else + path = s->curw->window->active->base.path; + if (c->path == NULL || strcmp(path, c->path) != 0) { + free(c->path); + c->path = xstrdup(path); + tty_set_path(&c->tty, c->path); + } +} + /* Dispatch message from client. */ static void server_client_dispatch(struct imsg *imsg, void *arg) diff --git a/tmux.1 b/tmux.1 index abaeb49c..3503242c 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3627,6 +3627,8 @@ Supports DECSLRM margins. Supports .Xr xterm 1 mouse sequences. +.It osc7 +Supports the OSC 7 working directory extension. .It overline Supports the overline SGR attribute. .It rectfill @@ -6394,6 +6396,11 @@ $ printf '\e033[4 q' If .Em Se is not set, \&Ss with argument 0 will be used to reset the cursor style instead. +.It Em \&Swd +Set the opening sequence for the working directory notification. +The sequence is terminated using the standard +.Em fsl +capability. .It Em \&Sync Start (parameter is 1) or end (parameter is 2) a synchronized update. .It Em \&Tc diff --git a/tmux.h b/tmux.h index e23ad3a1..370c7773 100644 --- a/tmux.h +++ b/tmux.h @@ -540,6 +540,7 @@ enum tty_code_code { TTYC_SMULX, TTYC_SMXX, TTYC_SS, + TTYC_SWD, TTYC_SYNC, TTYC_TC, TTYC_TSL, @@ -1709,6 +1710,7 @@ struct client { struct format_job_tree *jobs; char *title; + char *path; const char *cwd; char *term_name; @@ -2259,6 +2261,7 @@ void tty_start_tty(struct tty *); void tty_send_requests(struct tty *); void tty_stop_tty(struct tty *); void tty_set_title(struct tty *, const char *); +void tty_set_path(struct tty *, const char *); void tty_update_mode(struct tty *, int, struct screen *); void tty_draw_line(struct tty *, struct screen *, u_int, u_int, u_int, u_int, u_int, const struct grid_cell *, struct colour_palette *); diff --git a/tty-features.c b/tty-features.c index 3aca2520..4d83a465 100644 --- a/tty-features.c +++ b/tty-features.c @@ -53,6 +53,18 @@ static const struct tty_feature tty_feature_title = { 0 }; +/* Terminal has OSC 7 working directory. */ +static const char *tty_feature_osc7_capabilities[] = { + "Swd=\\E]7;", + "fsl=\\a", + NULL +}; +static const struct tty_feature tty_feature_osc7 = { + "osc7", + tty_feature_osc7_capabilities, + 0 +}; + /* Terminal has mouse support. */ static const char *tty_feature_mouse_capabilities[] = { "kmous=\\E[M", @@ -249,6 +261,7 @@ static const struct tty_feature *tty_features[] = { &tty_feature_focus, &tty_feature_margins, &tty_feature_mouse, + &tty_feature_osc7, &tty_feature_overline, &tty_feature_rectfill, &tty_feature_rgb, diff --git a/tty-term.c b/tty-term.c index 8e07da05..fdf0c4fa 100644 --- a/tty-term.c +++ b/tty-term.c @@ -277,6 +277,7 @@ static const struct tty_term_code_entry tty_term_codes[] = { [TTYC_SMUL] = { TTYCODE_STRING, "smul" }, [TTYC_SMXX] = { TTYCODE_STRING, "smxx" }, [TTYC_SS] = { TTYCODE_STRING, "Ss" }, + [TTYC_SWD] = { TTYCODE_STRING, "Swd" }, [TTYC_SYNC] = { TTYCODE_STRING, "Sync" }, [TTYC_TC] = { TTYCODE_FLAG, "Tc" }, [TTYC_TSL] = { TTYCODE_STRING, "tsl" }, diff --git a/tty.c b/tty.c index 40735ceb..ea10e61e 100644 --- a/tty.c +++ b/tty.c @@ -655,6 +655,18 @@ tty_set_title(struct tty *tty, const char *title) tty_putcode(tty, TTYC_FSL); } +void +tty_set_path(struct tty *tty, const char *title) +{ + if (!tty_term_has(tty->term, TTYC_SWD) || + !tty_term_has(tty->term, TTYC_FSL)) + return; + + tty_putcode(tty, TTYC_SWD); + tty_puts(tty, title); + tty_putcode(tty, TTYC_FSL); +} + static void tty_force_cursor_colour(struct tty *tty, int c) { -- cgit From d4eda7f9e5d33c613bb22770a677779ffbe9b363 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 24 Mar 2022 12:07:25 +0000 Subject: Add unit (milliseconds) to escape-time, show unset colours as "none" rather than "invalid" and don't show the same text twice for user options in customize mode. --- colour.c | 2 +- options-table.c | 1 + window-customize.c | 4 +--- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/colour.c b/colour.c index 6ede25da..a282d182 100644 --- a/colour.c +++ b/colour.c @@ -128,7 +128,7 @@ colour_tostring(int c) u_char r, g, b; if (c == -1) - return ("invalid"); + return ("none"); if (c & COLOUR_FLAG_RGB) { colour_split_rgb(c, &r, &g, &b); diff --git a/options-table.c b/options-table.c index a816f6ac..17be7ec4 100644 --- a/options-table.c +++ b/options-table.c @@ -277,6 +277,7 @@ const struct options_table_entry options_table[] = { .minimum = 0, .maximum = INT_MAX, .default_num = 500, + .unit = "milliseconds", .text = "Time to wait before assuming a key is Escape." }, diff --git a/window-customize.c b/window-customize.c index 98387e50..4a16e90c 100644 --- a/window-customize.c +++ b/window-customize.c @@ -680,9 +680,7 @@ window_customize_draw_option(struct window_customize_modedata *data, } ft = format_create_from_state(NULL, NULL, &fs); - if (oe == NULL) - text = "This is a user option."; - else if (oe->text == NULL) + if (oe == NULL || oe->text == NULL) text = "This option doesn't have a description."; else text = oe->text; -- cgit From 98de5784a0a35681b736dc11bb6758a08d428562 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 25 Mar 2022 06:14:42 +0000 Subject: Fix exit message if creating socket fails. --- server.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server.c b/server.c index 29bcf88a..2db5a8d8 100644 --- a/server.c +++ b/server.c @@ -230,10 +230,10 @@ server_start(struct tmuxproc *client, int flags, struct event_base *base, if (cause != NULL) { if (c != NULL) { - cmdq_append(c, cmdq_get_error(cause)); + c->exit_message = cause; c->flags |= CLIENT_EXIT; - } - free(cause); + } else + free(cause); } evtimer_set(&server_ev_tidy, server_tidy_event, NULL); -- cgit From 1c69a91c25654339a0e670ed5a1495d52b37eb8e Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 28 Mar 2022 08:42:13 +0100 Subject: Add support for systemd socket activation (where systemd creates the Unix domain socket for tmux rather than tmux creating it). Build with --enable-systemd. From Julien Moutinho in GitHub issue 3119. --- Makefile.am | 5 +++++ compat.h | 5 +++++ compat/systemd.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ configure.ac | 25 ++++++++++++++++++++++++ server.c | 6 +++++- tmux.h | 1 + 6 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 compat/systemd.c diff --git a/Makefile.am b/Makefile.am index 68494932..5bdd9d5f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -204,6 +204,11 @@ if NEED_FORKPTY nodist_tmux_SOURCES += compat/forkpty-@PLATFORM@.c endif +# Add compat file for systemd. +if HAVE_SYSTEMD +nodist_tmux_SOURCES += compat/systemd.c +endif + # Add compat file for utf8proc. if HAVE_UTF8PROC nodist_tmux_SOURCES += compat/utf8proc.c diff --git a/compat.h b/compat.h index be726831..6eb97619 100644 --- a/compat.h +++ b/compat.h @@ -421,6 +421,11 @@ void *reallocarray(void *, size_t, size_t); void *recallocarray(void *, size_t, size_t, size_t); #endif +#ifdef HAVE_SYSTEMD +/* systemd.c */ +int systemd_create_socket(int, char **); +#endif + #ifdef HAVE_UTF8PROC /* utf8proc.c */ int utf8proc_wcwidth(wchar_t); diff --git a/compat/systemd.c b/compat/systemd.c new file mode 100644 index 00000000..7317e43a --- /dev/null +++ b/compat/systemd.c @@ -0,0 +1,58 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2022 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include + +#include "tmux.h" + +int +systemd_create_socket(int flags, char **cause) +{ + int fds; + int fd; + struct sockaddr_un sa; + int addrlen = sizeof sa; + + fds = sd_listen_fds(0); + if (fds > 1) { /* too many file descriptors */ + errno = E2BIG; + goto fail; + } + + if (fds == 1) { /* socket-activated */ + fd = SD_LISTEN_FDS_START; + if (!sd_is_socket_unix(fd, SOCK_STREAM, 1, NULL, 0)) { + errno = EPFNOSUPPORT; + goto fail; + } + if (getsockname(fd, (struct sockaddr *)&sa, &addrlen) == -1) + goto fail; + socket_path = xstrdup(sa.sun_path); + return (fd); + } + + return (server_create_socket(flags, cause)); + +fail: + if (cause != NULL) + xasprintf(cause, "systemd socket error (%s)", strerror(errno)); + return (-1); +} diff --git a/configure.ac b/configure.ac index a9c035d7..20199515 100644 --- a/configure.ac +++ b/configure.ac @@ -390,6 +390,31 @@ if test "x$enable_utf8proc" = xyes; then fi AM_CONDITIONAL(HAVE_UTF8PROC, [test "x$enable_utf8proc" = xyes]) +# Check for systemd support. +AC_ARG_ENABLE( + systemd, + AS_HELP_STRING(--enable-systemd, enable systemd integration) +) +if test x"$enable_systemd" = xyes; then + PKG_CHECK_MODULES( + SYSTEMD, + libsystemd, + [ + AM_CPPFLAGS="$SYSTEMD_CFLAGS $AM_CPPFLAGS" + CPPFLAGS="$AM_CPPFLAGS $SAVED_CPPFLAGS" + LIBS="$SYSTEMD_LIBS $LIBS" + found_systemd=yes + ], + found_systemd=no + ) + if test "x$found_systemd" = xyes; then + AC_DEFINE(HAVE_SYSTEMD) + else + AC_MSG_ERROR("systemd not found") + fi +fi +AM_CONDITIONAL(HAVE_SYSTEMD, [test "x$found_systemd" = xyes]) + # Check for b64_ntop. If we have b64_ntop, we assume b64_pton as well. AC_MSG_CHECKING(for b64_ntop) AC_LINK_IFELSE([AC_LANG_PROGRAM( diff --git a/server.c b/server.c index 2db5a8d8..bf3a8361 100644 --- a/server.c +++ b/server.c @@ -100,7 +100,7 @@ server_check_marked(void) } /* Create server socket. */ -static int +int server_create_socket(int flags, char **cause) { struct sockaddr_un sa; @@ -214,7 +214,11 @@ server_start(struct tmuxproc *client, int flags, struct event_base *base, gettimeofday(&start_time, NULL); +#ifdef HAVE_SYSTEMD + server_fd = systemd_create_socket(flags, &cause); +#else server_fd = server_create_socket(flags, &cause); +#endif if (server_fd != -1) server_update_socket(); if (~flags & CLIENT_NOFORK) diff --git a/tmux.h b/tmux.h index 370c7773..f16b5250 100644 --- a/tmux.h +++ b/tmux.h @@ -2583,6 +2583,7 @@ int server_start(struct tmuxproc *, int, struct event_base *, int, char *); void server_update_socket(void); void server_add_accept(int); void printflike(1, 2) server_add_message(const char *, ...); +int server_create_socket(int, char **); /* server-client.c */ RB_PROTOTYPE(client_windows, client_window, entry, server_client_window_cmp); -- cgit From 880abd0ec2d4ce5c79645d59a6ba0fe08c0734aa Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 28 Mar 2022 07:40:57 +0000 Subject: Report error if creating socket fails with -D. --- server.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/server.c b/server.c index bf3a8361..5b70dcd0 100644 --- a/server.c +++ b/server.c @@ -236,8 +236,10 @@ server_start(struct tmuxproc *client, int flags, struct event_base *base, if (c != NULL) { c->exit_message = cause; c->flags |= CLIENT_EXIT; - } else - free(cause); + } else { + fprintf(stderr, "%s\n", cause); + exit(1); + } } evtimer_set(&server_ev_tidy, server_tidy_event, NULL); -- cgit From 2df7bc14fa43def1e4bb334f25bdb2191a4af799 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 30 Mar 2022 07:05:26 +0000 Subject: Capture up to used size not available size for each line. --- grid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grid.c b/grid.c index 91dc8f42..1109ac58 100644 --- a/grid.c +++ b/grid.c @@ -1003,7 +1003,7 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx, gl = grid_peek_line(gd, py); for (xx = px; xx < px + nx; xx++) { - if (gl == NULL || xx >= gl->cellsize) + if (gl == NULL || xx >= gl->cellused) break; grid_get_cell(gd, xx, py, &gc); if (gc.flags & GRID_FLAG_PADDING) -- cgit From fc6580574e88d27e481b9fa70eadc443f59ef1b5 Mon Sep 17 00:00:00 2001 From: naddy Date: Thu, 31 Mar 2022 17:27:27 +0000 Subject: man pages: add missing commas between subordinate and main clauses jmc@ dislikes a comma before "then" in a conditional, so leave those untouched. ok jmc@ --- tmux.1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tmux.1 b/tmux.1 index 3503242c..ae6b6fb8 100644 --- a/tmux.1 +++ b/tmux.1 @@ -43,7 +43,7 @@ then later reattached. .Pp When .Nm -is started it creates a new +is started, it creates a new .Em session with a single .Em window @@ -5918,7 +5918,7 @@ does not surround the popup by a border. sets the type of border line for the popup. When .Fl B -is specified the +is specified, the .Fl b option is ignored. See @@ -6639,7 +6639,7 @@ are replaced with underscores For input, .Nm always runs with a UTF-8 locale. -If en_US.UTF-8 is provided by the operating system it is used and +If en_US.UTF-8 is provided by the operating system, it is used and .Ev LC_CTYPE is ignored for input. Otherwise, -- cgit From 1e9c3b3c63b9fb98bcf02eb8b09d1cefb0b72d26 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 1 Apr 2022 10:11:59 +0000 Subject: Preserve CRLF flag when respawning. --- screen.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/screen.c b/screen.c index 7046e098..62cf4765 100644 --- a/screen.c +++ b/screen.c @@ -103,7 +103,7 @@ screen_reinit(struct screen *s) s->rupper = 0; s->rlower = screen_size_y(s) - 1; - s->mode = MODE_CURSOR|MODE_WRAP; + s->mode = MODE_CURSOR|MODE_WRAP|(s->mode & MODE_CRLF); if (options_get_number(global_options, "extended-keys") == 2) s->mode |= MODE_KEXTENDED; -- cgit From 39b1e96b453a535a8f7b5542dbb49f82c729f24c Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 6 Apr 2022 14:24:17 +0100 Subject: Add to CHANGES. --- CHANGES | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/CHANGES b/CHANGES index 159ab905..3c682e5f 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,35 @@ CHANGES FROM 3.2a TO 3.3 +* Add an option (default off) to control the passthrough escape sequence. + +* Support more mouse buttons when the terminal sends them. + +* Add a window-resized hook which is fired when the window is actually resized + which may be later than the client resize. + +* Add next_session_id format with the next session ID. + +* Add formats for client and server UID and user. + +* Add argument to refresh-client -l to forward clipboard to a pane. + +* Add remain-on-exit-format to set text shown when pane is dead. + +* With split-window -f use percentages of window size not pane size. + +* Add an option (fill-character) to set the character used for unused areas of + a client. + +* Add an option (scroll-on-clear) to control if tmux scrolls into history on + clear. + +* Add a capability for OSC 7 and use it similarly to how the title is set (and + controlled by the same set-titles option). + +* Add support for systemd socket activation (where systemd creates the Unix + domain socket for tmux rather than tmux creating it). Build with + --enable-systemd. + * Add an option (pane-border-indicators) to select how the active pane is shown on the pane border (colour, arrows or both). -- cgit From 6e9a9d265e2c5199566e3890e6763a74b558bf80 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 6 Apr 2022 14:26:19 +0100 Subject: Fix version. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 20199515..17a1935d 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ # configure.ac -AC_INIT([tmux], 3.3-rc) +AC_INIT([tmux], next-3.4) AC_PREREQ([2.60]) AC_CONFIG_AUX_DIR(etc) -- cgit From d6306b634e4a044e3380ed984dc7f5e5d67e69ac Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 6 Apr 2022 14:28:50 +0100 Subject: Add an ACL list for users connecting to the tmux socket. Users may be forbidden from attaching, forced to attach read-only, or allowed to attach read-write. A new command, server-access, configures the list. tmux gets the user using getpeereid(3) of the client socket. Users must still configure file system permissions manually. --- Makefile.am | 2 + client.c | 1 + cmd-attach-session.c | 3 +- cmd-queue.c | 26 ++++++-- cmd-server-access.c | 147 +++++++++++++++++++++++++++++++++++++++++ cmd.c | 2 + proc.c | 6 ++ server-acl.c | 182 +++++++++++++++++++++++++++++++++++++++++++++++++++ server-client.c | 22 ++++++- server.c | 16 +++-- tmux.1 | 40 ++++++++++- tmux.h | 12 ++++ 12 files changed, 445 insertions(+), 14 deletions(-) create mode 100644 cmd-server-access.c create mode 100644 server-acl.c diff --git a/Makefile.am b/Makefile.am index 5bdd9d5f..fb94e820 100644 --- a/Makefile.am +++ b/Makefile.am @@ -123,6 +123,7 @@ dist_tmux_SOURCES = \ cmd-select-pane.c \ cmd-select-window.c \ cmd-send-keys.c \ + cmd-server-access.c \ cmd-set-buffer.c \ cmd-set-environment.c \ cmd-set-option.c \ @@ -172,6 +173,7 @@ dist_tmux_SOURCES = \ screen-redraw.c \ screen-write.c \ screen.c \ + server-acl.c \ server-client.c \ server-fn.c \ server.c \ diff --git a/client.c b/client.c index 08708c21..df6cee94 100644 --- a/client.c +++ b/client.c @@ -361,6 +361,7 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags, /* Send identify messages. */ client_send_identify(ttynam, termname, caps, ncaps, cwd, feat); tty_term_free_list(caps, ncaps); + proc_flush_peer(client_peer); /* Send first command. */ if (msg == MSG_COMMAND) { diff --git a/cmd-attach-session.c b/cmd-attach-session.c index cc795b22..b92a7f2b 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -43,7 +43,7 @@ const struct cmd_entry cmd_attach_session_entry = { /* -t is special */ - .flags = CMD_STARTSERVER, + .flags = CMD_STARTSERVER|CMD_READONLY, .exec = cmd_attach_session_exec }; @@ -69,6 +69,7 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag, if (c == NULL) return (CMD_RETURN_NORMAL); + if (server_client_check_nested(c)) { cmdq_error(item, "sessions should be nested with care, " "unset $TMUX to force"); diff --git a/cmd-queue.c b/cmd-queue.c index 4fbdc4e7..a12aaf10 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -19,9 +19,11 @@ #include #include +#include #include #include #include +#include #include "tmux.h" @@ -558,17 +560,31 @@ cmdq_add_message(struct cmdq_item *item) { struct client *c = item->client; struct cmdq_state *state = item->state; - const char *name, *key; + const char *key; char *tmp; + uid_t uid; + struct passwd *pw; + char *user = NULL; tmp = cmd_print(item->cmd); if (c != NULL) { - name = c->name; + uid = proc_get_peer_uid(c->peer); + if (uid != getuid()) { + if ((pw = getpwuid(uid)) != NULL) + xasprintf(&user, "[%s]", pw->pw_name); + else + user = xstrdup("[unknown]"); + } else + user = xstrdup(""); if (c->session != NULL && state->event.key != KEYC_NONE) { key = key_string_lookup_key(state->event.key, 0); - server_add_message("%s key %s: %s", name, key, tmp); - } else - server_add_message("%s command: %s", name, tmp); + server_add_message("%s%s key %s: %s", c->name, user, + key, tmp); + } else { + server_add_message("%s%s command: %s", c->name, user, + tmp); + } + free(user); } else server_add_message("command: %s", tmp); free(tmp); diff --git a/cmd-server-access.c b/cmd-server-access.c new file mode 100644 index 00000000..4fd1dfbf --- /dev/null +++ b/cmd-server-access.c @@ -0,0 +1,147 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2021 Dallas Lyons + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include "tmux.h" + +/* + * Controls access to session. + */ + +static enum cmd_retval cmd_server_access_exec(struct cmd *, struct cmdq_item *); + +const struct cmd_entry cmd_server_access_entry = { + .name = "server-access", + .alias = NULL, + + .args = { "adlrw", 0, 1, NULL }, + .usage = "[-adlrw]" CMD_TARGET_PANE_USAGE " [user]", + + .flags = CMD_CLIENT_CANFAIL, + .exec = cmd_server_access_exec +}; + +static enum cmd_retval +cmd_server_access_deny(struct cmdq_item *item, struct passwd *pw) +{ + struct client *loop; + struct server_acl_user *user; + uid_t uid; + + if ((user = server_acl_user_find(pw->pw_uid)) == NULL) { + cmdq_error(item, "user %s not found", pw->pw_name); + return (CMD_RETURN_ERROR); + } + TAILQ_FOREACH(loop, &clients, entry) { + uid = proc_get_peer_uid(loop->peer); + if (uid == server_acl_get_uid(user)) { + loop->exit_message = xstrdup("access not allowed"); + loop->flags |= CLIENT_EXIT; + } + } + server_acl_user_deny(pw->pw_uid); + + return (CMD_RETURN_NORMAL); +} + +static enum cmd_retval +cmd_server_access_exec(struct cmd *self, struct cmdq_item *item) +{ + + struct args *args = cmd_get_args(self); + struct client *c = cmdq_get_target_client(item); + char *name; + struct passwd *pw = NULL; + + if (args_has(args, 'l')) { + server_acl_display(item); + return (CMD_RETURN_NORMAL); + } + if (args_count(args) == 0) { + cmdq_error(item, "missing user arguement"); + return (CMD_RETURN_ERROR); + } + + name = format_single(item, args_string(args, 0), c, NULL, NULL, NULL); + if (*name != '\0') + pw = getpwnam(name); + if (pw == NULL) { + cmdq_error(item, "unknown user: %s", name); + return (CMD_RETURN_ERROR); + } + free(name); + + if (pw->pw_uid == 0 || pw->pw_uid == getuid()) { + cmdq_error(item, "%s owns the server, can't change access", + pw->pw_name); + return (CMD_RETURN_ERROR); + } + + if (args_has(args, 'a') && args_has(args, 'd')) { + cmdq_error(item, "-a and -d cannot be used together"); + return (CMD_RETURN_ERROR); + } + if (args_has(args, 'w') && args_has(args, 'r')) { + cmdq_error(item, "-r and -w cannot be used together"); + return (CMD_RETURN_ERROR); + } + + if (args_has(args, 'd')) + return (cmd_server_access_deny(item, pw)); + if (args_has(args, 'a')) { + if (server_acl_user_find(pw->pw_uid) != NULL) { + cmdq_error(item, "user %s is already added", + pw->pw_name); + return (CMD_RETURN_ERROR); + } + server_acl_user_allow(pw->pw_uid); + /* Do not return - allow -r or -w with -a. */ + } else if (args_has(args, 'r') || args_has(args, 'w')) { + /* -r or -w implies -a if user does not exist. */ + if (server_acl_user_find(pw->pw_uid) == NULL) + server_acl_user_allow(pw->pw_uid); + } + + if (args_has(args, 'w')) { + if (server_acl_user_find(pw->pw_uid) == NULL) { + cmdq_error(item, "user %s not found", pw->pw_name); + return (CMD_RETURN_ERROR); + } + server_acl_user_allow_write(pw->pw_uid); + return (CMD_RETURN_NORMAL); + } + + if (args_has(args, 'r')) { + if (server_acl_user_find(pw->pw_uid) == NULL) { + cmdq_error(item, "user %s not found", pw->pw_name); + return (CMD_RETURN_ERROR); + } + server_acl_user_deny_write(pw->pw_uid); + return (CMD_RETURN_NORMAL); + } + + return (CMD_RETURN_NORMAL); +} diff --git a/cmd.c b/cmd.c index f8027dfe..ed4f9940 100644 --- a/cmd.c +++ b/cmd.c @@ -95,6 +95,7 @@ extern const struct cmd_entry cmd_select_pane_entry; extern const struct cmd_entry cmd_select_window_entry; extern const struct cmd_entry cmd_send_keys_entry; extern const struct cmd_entry cmd_send_prefix_entry; +extern const struct cmd_entry cmd_server_access_entry; extern const struct cmd_entry cmd_set_buffer_entry; extern const struct cmd_entry cmd_set_environment_entry; extern const struct cmd_entry cmd_set_hook_entry; @@ -187,6 +188,7 @@ const struct cmd_entry *cmd_table[] = { &cmd_select_window_entry, &cmd_send_keys_entry, &cmd_send_prefix_entry, + &cmd_server_access_entry, &cmd_set_buffer_entry, &cmd_set_environment_entry, &cmd_set_hook_entry, diff --git a/proc.c b/proc.c index a9b1473e..f0eded99 100644 --- a/proc.c +++ b/proc.c @@ -349,6 +349,12 @@ proc_kill_peer(struct tmuxpeer *peer) peer->flags |= PEER_BAD; } +void +proc_flush_peer(struct tmuxpeer *peer) +{ + imsg_flush(&peer->ibuf); +} + void proc_toggle_log(struct tmuxproc *tp) { diff --git a/server-acl.c b/server-acl.c new file mode 100644 index 00000000..5d9f6223 --- /dev/null +++ b/server-acl.c @@ -0,0 +1,182 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2021 Holland Schutte, Jayson Morberg + * Copyright (c) 2021 Dallas Lyons + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "tmux.h" + +struct server_acl_user { + uid_t uid; + + int flags; +#define SERVER_ACL_READONLY 0x1 + + RB_ENTRY(server_acl_user) entry; +}; + +static int +server_acl_cmp(struct server_acl_user *user1, struct server_acl_user *user2) +{ + if (user1->uid < user2->uid) + return -1; + return user1->uid > user2->uid; +} + +RB_HEAD(server_acl_entries, server_acl_user) server_acl_entries; +RB_GENERATE_STATIC(server_acl_entries, server_acl_user, entry, server_acl_cmp); + +/* Initialize server_acl tree. */ +void +server_acl_init(void) +{ + RB_INIT(&server_acl_entries); + + if (getuid() != 0) + server_acl_user_allow(0); + server_acl_user_allow(getuid()); +} + +/* Find user entry. */ +struct server_acl_user* +server_acl_user_find(uid_t uid) +{ + struct server_acl_user find = { .uid = uid }; + + return RB_FIND(server_acl_entries, &server_acl_entries, &find); +} + +/* Display the tree. */ +void +server_acl_display(struct cmdq_item *item) +{ + struct server_acl_user *loop; + struct passwd *pw; + const char *name; + + RB_FOREACH(loop, server_acl_entries, &server_acl_entries) { + if (loop->uid == 0) + continue; + if ((pw = getpwuid(loop->uid)) != NULL) + name = pw->pw_name; + else + name = "unknown"; + if (loop->flags == SERVER_ACL_READONLY) + cmdq_print(item, "%s (R)", name); + else + cmdq_print(item, "%s (W)", name); + } +} + +/* Allow a user. */ +void +server_acl_user_allow(uid_t uid) +{ + struct server_acl_user *user; + + user = server_acl_user_find(uid); + if (user == NULL) { + user = xcalloc(1, sizeof *user); + user->uid = uid; + RB_INSERT(server_acl_entries, &server_acl_entries, user); + } +} + +/* Deny a user (remove from the tree). */ +void +server_acl_user_deny(uid_t uid) +{ + struct server_acl_user *user; + + user = server_acl_user_find(uid); + if (user != NULL) { + RB_REMOVE(server_acl_entries, &server_acl_entries, user); + free(user); + } +} + +/* Allow this user write access. */ +void +server_acl_user_allow_write(uid_t uid) +{ + struct server_acl_user *user; + struct client *c; + + user = server_acl_user_find(uid); + if (user == NULL) + return; + user->flags &= ~SERVER_ACL_READONLY; + + TAILQ_FOREACH(c, &clients, entry) { + uid = proc_get_peer_uid(c->peer); + if (uid != (uid_t)-1 && uid == user->uid) + c->flags &= ~CLIENT_READONLY; + } +} + +/* Deny this user write access. */ +void +server_acl_user_deny_write(uid_t uid) +{ + struct server_acl_user *user; + struct client *c; + + user = server_acl_user_find(uid); + if (user == NULL) + return; + user->flags |= SERVER_ACL_READONLY; + + TAILQ_FOREACH(c, &clients, entry) { + uid = proc_get_peer_uid(c->peer); + if (uid == user->uid && uid == user->uid) + c->flags |= CLIENT_READONLY; + } +} + +/* + * Check if the client's UID exists in the ACL list and if so, set as read only + * if needed. Return false if the user does not exist. + */ +int +server_acl_join(struct client *c) +{ + struct server_acl_user *user; + uid_t uid = proc_get_peer_uid(c->peer); + + user = server_acl_user_find(uid); + if (user == NULL) + return (0); + if (user->flags & SERVER_ACL_READONLY) + c->flags |= CLIENT_READONLY; + return (1); +} + +/* Get UID for user entry. */ +uid_t +server_acl_get_uid(struct server_acl_user *user) +{ + return (user->uid); +} diff --git a/server-client.c b/server-client.c index 2350a982..22c8fa80 100644 --- a/server-client.c +++ b/server-client.c @@ -2772,6 +2772,14 @@ server_client_dispatch(struct imsg *imsg, void *arg) } } +/* Callback when command is not allowed. */ +static enum cmd_retval +server_client_read_only(struct cmdq_item *item, __unused void *data) +{ + cmdq_error(item, "client is read-only"); + return (CMD_RETURN_ERROR); +} + /* Callback when command is done. */ static enum cmd_retval server_client_command_done(struct cmdq_item *item, __unused void *data) @@ -2796,6 +2804,7 @@ server_client_dispatch_command(struct client *c, struct imsg *imsg) char **argv, *cause; struct cmd_parse_result *pr; struct args_value *values; + struct cmdq_item *new_item; if (c->flags & CLIENT_EXIT) return; @@ -2834,7 +2843,12 @@ server_client_dispatch_command(struct client *c, struct imsg *imsg) free(values); cmd_free_argv(argc, argv); - cmdq_append(c, cmdq_get_command(pr->cmdlist, NULL)); + if ((c->flags & CLIENT_READONLY) && + !cmd_list_all_have(pr->cmdlist, CMD_READONLY)) + new_item = cmdq_get_callback(server_client_read_only, NULL); + else + new_item = cmdq_get_command(pr->cmdlist, NULL); + cmdq_append(c, new_item); cmdq_append(c, cmdq_get_callback(server_client_command_done, NULL)); cmd_list_free(pr->cmdlist); @@ -3072,9 +3086,11 @@ server_client_set_flags(struct client *c, const char *flags) continue; log_debug("client %s set flag %s", c->name, next); - if (not) + if (not) { + if (c->flags & CLIENT_READONLY) + flag &= ~CLIENT_READONLY; c->flags &= ~flag; - else + } else c->flags |= flag; if (flag == CLIENT_CONTROL_NOOUTPUT) control_reset_offsets(c); diff --git a/server.c b/server.c index 5b70dcd0..05bc50f4 100644 --- a/server.c +++ b/server.c @@ -245,6 +245,8 @@ server_start(struct tmuxproc *client, int flags, struct event_base *base, evtimer_set(&server_ev_tidy, server_tidy_event, NULL); evtimer_add(&server_ev_tidy, &tv); + server_acl_init(); + server_add_accept(0); proc_loop(server_proc, server_loop); @@ -361,9 +363,10 @@ server_update_socket(void) static void server_accept(int fd, short events, __unused void *data) { - struct sockaddr_storage sa; - socklen_t slen = sizeof sa; - int newfd; + struct sockaddr_storage sa; + socklen_t slen = sizeof sa; + int newfd; + struct client *c; server_add_accept(0); if (!(events & EV_READ)) @@ -380,11 +383,16 @@ server_accept(int fd, short events, __unused void *data) } fatal("accept failed"); } + if (server_exit) { close(newfd); return; } - server_client_create(newfd); + c = server_client_create(newfd); + if (!server_acl_join(c)) { + c->exit_message = xstrdup("access not allowed"); + c->flags |= CLIENT_EXIT; + } } /* diff --git a/tmux.1 b/tmux.1 index ae6b6fb8..d21e1e42 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1488,6 +1488,44 @@ option. .D1 Pq alias: Ic rename Rename the session to .Ar new-name . +.It Xo Ic server-access +.Op Fl adlrw +.Op Ar user +.Xc +Change the access or read/write permission of +.Ar user . +The user running the +.Nm +server (its owner) and the root user cannot be changed and are always +permitted access. +.Pp +.Fl a +and +.Fl d +are used to give or revoke access for the specified user. +If the user is already attached, the +.Fl d +flag causes their clients to be detached. +.Pp +.Fl r +and +.Fl w +change the permissions for +.Ar user : +.Fl r +makes their clients read-only and +.Fl w +writable. +.Fl l +lists current access permissions. +.Pp +By default, the access list is empty and +.Nm +creates sockets with file system permissions preventing access by any user +other than the owner (and root). +These permissions must be changed manually. +Great care should be taken not to allow access to untrusted users even +read-only. .Tg showmsgs .It Xo Ic show-messages .Op Fl JT @@ -5072,7 +5110,7 @@ The following variables are available, where appropriate: .It Li "client_name" Ta "" Ta "Name of client" .It Li "client_pid" Ta "" Ta "PID of client process" .It Li "client_prefix" Ta "" Ta "1 if prefix key has been pressed" -.It Li "client_readonly" Ta "" Ta "1 if client is readonly" +.It Li "client_readonly" Ta "" Ta "1 if client is read-only" .It Li "client_session" Ta "" Ta "Name of the client's session" .It Li "client_termfeatures" Ta "" Ta "Terminal features of client, if any" .It Li "client_termname" Ta "" Ta "Terminal name of client" diff --git a/tmux.h b/tmux.h index f16b5250..90a3f93c 100644 --- a/tmux.h +++ b/tmux.h @@ -2025,6 +2025,7 @@ struct tmuxpeer *proc_add_peer(struct tmuxproc *, int, void (*)(struct imsg *, void *), void *); void proc_remove_peer(struct tmuxpeer *); void proc_kill_peer(struct tmuxpeer *); +void proc_flush_peer(struct tmuxpeer *); void proc_toggle_log(struct tmuxproc *); pid_t proc_fork_and_daemon(int *); uid_t proc_get_peer_uid(struct tmuxpeer *); @@ -3269,4 +3270,15 @@ struct window_pane *spawn_pane(struct spawn_context *, char **); /* regsub.c */ char *regsub(const char *, const char *, const char *, int); +/* server-acl.c */ +void server_acl_init(void); +struct server_acl_user *server_acl_user_find(uid_t); +void server_acl_display(struct cmdq_item *); +void server_acl_user_allow(uid_t); +void server_acl_user_deny(uid_t); +void server_acl_user_allow_write(uid_t); +void server_acl_user_deny_write(uid_t); +int server_acl_join(struct client *); +uid_t server_acl_get_uid(struct server_acl_user *); + #endif /* TMUX_H */ -- cgit From 18105c8ecb901c72040db09f58585503a8409fd0 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 6 Apr 2022 14:30:37 +0100 Subject: Do not send focus sequences when focus is enabled or disabled by the application if it is turned off. GitHub issue 3142. --- input.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/input.c b/input.c index b1856538..45ccefd6 100644 --- a/input.c +++ b/input.c @@ -1798,6 +1798,8 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx) screen_write_mode_set(sctx, MODE_FOCUSON); if (wp == NULL) break; + if (!options_get_number(global_options, "focus-events")) + break; if (wp->flags & PANE_FOCUSED) bufferevent_write(wp->event, "\033[I", 3); else -- cgit From 996e54763c04749860532a0d6b7df0d75671a405 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 6 Apr 2022 14:41:44 +0100 Subject: Better error reporting when applying custom layouts. --- cmd-select-layout.c | 7 ++++--- layout-custom.c | 23 +++++++++++++++++------ tmux.h | 2 +- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/cmd-select-layout.c b/cmd-select-layout.c index c857a0e1..6dfe2b6a 100644 --- a/cmd-select-layout.c +++ b/cmd-select-layout.c @@ -77,7 +77,7 @@ cmd_select_layout_exec(struct cmd *self, struct cmdq_item *item) struct window *w = wl->window; struct window_pane *wp = target->wp; const char *layoutname; - char *oldlayout; + char *oldlayout, *cause; int next, previous, layout; server_unzoom_window(w); @@ -124,8 +124,9 @@ cmd_select_layout_exec(struct cmd *self, struct cmdq_item *item) } if (layoutname != NULL) { - if (layout_parse(w, layoutname) == -1) { - cmdq_error(item, "can't set layout: %s", layoutname); + if (layout_parse(w, layoutname, &cause) == -1) { + cmdq_error(item, "%s: %s", cause, layoutname); + free(cause); goto error; } goto changed; diff --git a/layout-custom.c b/layout-custom.c index e7fb4253..932b30e7 100644 --- a/layout-custom.c +++ b/layout-custom.c @@ -154,7 +154,7 @@ layout_check(struct layout_cell *lc) /* Parse a layout string and arrange window as layout. */ int -layout_parse(struct window *w, const char *layout) +layout_parse(struct window *w, const char *layout, char **cause) { struct layout_cell *lc, *lcchild; struct window_pane *wp; @@ -165,22 +165,31 @@ layout_parse(struct window *w, const char *layout) if (sscanf(layout, "%hx,", &csum) != 1) return (-1); layout += 5; - if (csum != layout_checksum(layout)) + if (csum != layout_checksum(layout)) { + *cause = xstrdup("invalid layout"); return (-1); + } /* Build the layout. */ lc = layout_construct(NULL, &layout); - if (lc == NULL) + if (lc == NULL) { + *cause = xstrdup("invalid layout"); return (-1); - if (*layout != '\0') + } + if (*layout != '\0') { + *cause = xstrdup("invalid layout"); goto fail; + } /* Check this window will fit into the layout. */ for (;;) { npanes = window_count_panes(w); ncells = layout_count_cells(lc); - if (npanes > ncells) + if (npanes > ncells) { + xasprintf(cause, "have %u panes but need %u", npanes, + ncells); goto fail; + } if (npanes == ncells) break; @@ -217,8 +226,10 @@ layout_parse(struct window *w, const char *layout) } /* Check the new layout. */ - if (!layout_check(lc)) + if (!layout_check(lc)) { + *cause = xstrdup("size mismatch after applying layout"); return (-1); + } /* Resize to the layout size. */ window_resize(w, lc->sx, lc->sy, -1, -1); diff --git a/tmux.h b/tmux.h index 90a3f93c..2ec09692 100644 --- a/tmux.h +++ b/tmux.h @@ -3020,7 +3020,7 @@ void layout_spread_out(struct window_pane *); /* layout-custom.c */ char *layout_dump(struct layout_cell *); -int layout_parse(struct window *, const char *); +int layout_parse(struct window *, const char *, char **); /* layout-set.c */ int layout_set_lookup(const char *); -- cgit From 0c84a20d2f44db64057f806c270ab1d751030fc1 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 6 Apr 2022 14:43:12 +0100 Subject: Emit window-layout-changed on swap-pane, from George Nachman. --- cmd-swap-pane.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cmd-swap-pane.c b/cmd-swap-pane.c index 7d477739..4191b894 100644 --- a/cmd-swap-pane.c +++ b/cmd-swap-pane.c @@ -135,6 +135,9 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item) } server_redraw_window(src_w); server_redraw_window(dst_w); + notify_window("window-layout-changed", src_w); + if (src_w != dst_w) + notify_window("window-layout-changed", dst_w); out: if (window_pop_zoom(src_w)) -- cgit From 3a6d82b7c8d4254fa87959d8cf19b313f5e05480 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 6 Apr 2022 16:39:46 +0100 Subject: Some style nits. --- cmd-command-prompt.c | 2 +- cmd-queue.c | 2 +- cmd-refresh-client.c | 2 +- configure.ac | 2 +- format.c | 2 +- log.c | 2 +- notify.c | 2 +- proc.c | 2 +- screen.c | 4 ++-- server-acl.c | 6 +++--- server-client.c | 2 +- tmux.c | 2 +- window-buffer.c | 4 ++-- window-client.c | 2 +- window-tree.c | 2 +- window.c | 2 +- 16 files changed, 20 insertions(+), 20 deletions(-) diff --git a/cmd-command-prompt.c b/cmd-command-prompt.c index a7a02702..4455856b 100644 --- a/cmd-command-prompt.c +++ b/cmd-command-prompt.c @@ -112,7 +112,7 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item) } next_prompt = prompts; } else - next_prompt = prompts = xstrdup (s); + next_prompt = prompts = xstrdup(s); if ((s = args_get(args, 'I')) != NULL) next_input = inputs = xstrdup(s); else diff --git a/cmd-queue.c b/cmd-queue.c index a12aaf10..633af06b 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -126,7 +126,7 @@ cmdq_new(void) { struct cmdq_list *queue; - queue = xcalloc (1, sizeof *queue); + queue = xcalloc(1, sizeof *queue); TAILQ_INIT (&queue->list); return (queue); } diff --git a/cmd-refresh-client.c b/cmd-refresh-client.c index b2665ad9..6b947280 100644 --- a/cmd-refresh-client.c +++ b/cmd-refresh-client.c @@ -185,7 +185,7 @@ cmd_refresh_client_clipboard(struct cmd *self, struct cmdq_item *item) } if (i != tc->clipboard_npanes) return (CMD_RETURN_NORMAL); - tc->clipboard_panes = xreallocarray (tc->clipboard_panes, + tc->clipboard_panes = xreallocarray(tc->clipboard_panes, tc->clipboard_npanes + 1, sizeof *tc->clipboard_panes); tc->clipboard_panes[tc->clipboard_npanes++] = fs.wp->id; } diff --git a/configure.ac b/configure.ac index 17a1935d..276f71c8 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ # configure.ac -AC_INIT([tmux], next-3.4) +AC_INIT([tmux], next-3.4) AC_PREREQ([2.60]) AC_CONFIG_AUX_DIR(etc) diff --git a/format.c b/format.c index 2e4bceb5..5e04a6a2 100644 --- a/format.c +++ b/format.c @@ -2597,7 +2597,7 @@ format_cb_user(__unused struct format_tree *ft) if ((pw = getpwuid(getuid())) != NULL) return (xstrdup(pw->pw_name)); - return NULL; + return (NULL); } /* Format table type. */ diff --git a/log.c b/log.c index d4436672..0e0d1d1a 100644 --- a/log.c +++ b/log.c @@ -143,7 +143,7 @@ fatal(const char *msg, ...) va_list ap; if (snprintf(tmp, sizeof tmp, "fatal: %s: ", strerror(errno)) < 0) - exit (1); + exit(1); va_start(ap, msg); log_vwrite(msg, ap, tmp); diff --git a/notify.c b/notify.c index baeb0600..85255857 100644 --- a/notify.c +++ b/notify.c @@ -46,7 +46,7 @@ notify_insert_one_hook(struct cmdq_item *item, struct notify_entry *ne, if (log_get_level() != 0) { s = cmd_list_print(cmdlist, 0); log_debug("%s: hook %s is: %s", __func__, ne->name, s); - free (s); + free(s); } new_item = cmdq_get_command(cmdlist, state); return (cmdq_insert_after(item, new_item)); diff --git a/proc.c b/proc.c index f0eded99..67ec214a 100644 --- a/proc.c +++ b/proc.c @@ -202,7 +202,7 @@ proc_start(const char *name) #endif , event_get_version(), event_get_method() #ifdef HAVE_UTF8PROC - , utf8proc_version () + , utf8proc_version() #endif ); diff --git a/screen.c b/screen.c index 62cf4765..eceef642 100644 --- a/screen.c +++ b/screen.c @@ -661,9 +661,9 @@ screen_mode_to_string(int mode) static char tmp[1024]; if (mode == 0) - return "NONE"; + return ("NONE"); if (mode == ALL_MODES) - return "ALL"; + return ("ALL"); *tmp = '\0'; if (mode & MODE_CURSOR) diff --git a/server-acl.c b/server-acl.c index 5d9f6223..344f795c 100644 --- a/server-acl.c +++ b/server-acl.c @@ -42,8 +42,8 @@ static int server_acl_cmp(struct server_acl_user *user1, struct server_acl_user *user2) { if (user1->uid < user2->uid) - return -1; - return user1->uid > user2->uid; + return (-1); + return (user1->uid > user2->uid); } RB_HEAD(server_acl_entries, server_acl_user) server_acl_entries; @@ -66,7 +66,7 @@ server_acl_user_find(uid_t uid) { struct server_acl_user find = { .uid = uid }; - return RB_FIND(server_acl_entries, &server_acl_entries, &find); + return (RB_FIND(server_acl_entries, &server_acl_entries, &find)); } /* Display the tree. */ diff --git a/server-client.c b/server-client.c index 22c8fa80..8144bcd5 100644 --- a/server-client.c +++ b/server-client.c @@ -3158,7 +3158,7 @@ server_client_add_client_window(struct client *c, u_int id) cw->window = id; RB_INSERT(client_windows, &c->windows, cw); } - return cw; + return (cw); } /* Get client active pane. */ diff --git a/tmux.c b/tmux.c index 11c368ff..b9f2be30 100644 --- a/tmux.c +++ b/tmux.c @@ -325,7 +325,7 @@ find_home(void) const char * getversion(void) { - return TMUX_VERSION; + return (TMUX_VERSION); } int diff --git a/window-buffer.c b/window-buffer.c index 07851e75..2f52ee3f 100644 --- a/window-buffer.c +++ b/window-buffer.c @@ -308,7 +308,7 @@ window_buffer_get_key(void *modedata, void *itemdata, u_int line) } pb = paste_get_name(item->name); if (pb == NULL) - return KEYC_NONE; + return (KEYC_NONE); ft = format_create(NULL, NULL, FORMAT_NONE, 0); format_defaults(ft, NULL, NULL, 0, NULL); @@ -320,7 +320,7 @@ window_buffer_get_key(void *modedata, void *itemdata, u_int line) key = key_string_lookup_string(expanded); free(expanded); format_free(ft); - return key; + return (key); } static struct screen * diff --git a/window-client.c b/window-client.c index 00f36c7c..8d501b0d 100644 --- a/window-client.c +++ b/window-client.c @@ -281,7 +281,7 @@ window_client_get_key(void *modedata, void *itemdata, u_int line) key = key_string_lookup_string(expanded); free(expanded); format_free(ft); - return key; + return (key); } static struct screen * diff --git a/window-tree.c b/window-tree.c index 77011ea2..d90bf817 100644 --- a/window-tree.c +++ b/window-tree.c @@ -895,7 +895,7 @@ window_tree_get_key(void *modedata, void *itemdata, u_int line) key = key_string_lookup_string(expanded); free(expanded); format_free(ft); - return key; + return (key); } static struct screen * diff --git a/window.c b/window.c index bce8913a..c0cd9bdc 100644 --- a/window.c +++ b/window.c @@ -1056,7 +1056,7 @@ window_pane_resize(struct window_pane *wp, u_int sx, u_int sy) if (sx == wp->sx && sy == wp->sy) return; - r = xmalloc (sizeof *r); + r = xmalloc(sizeof *r); r->sx = sx; r->sy = sy; r->osx = wp->sx; -- cgit From 8bcd392ee79996f828fd40c52198071ec0f273dd Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 6 Apr 2022 16:47:59 +0100 Subject: On platforms with no way to get peer UID, use getuid(), also fix some failure checks. --- cmd-queue.c | 2 +- compat/getpeereid.c | 3 +-- server-acl.c | 8 ++++++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/cmd-queue.c b/cmd-queue.c index 633af06b..6c7c3675 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -569,7 +569,7 @@ cmdq_add_message(struct cmdq_item *item) tmp = cmd_print(item->cmd); if (c != NULL) { uid = proc_get_peer_uid(c->peer); - if (uid != getuid()) { + if (uid != (uid_t)-1 && uid != getuid()) { if ((pw = getpwuid(uid)) != NULL) xasprintf(&user, "[%s]", pw->pw_name); else diff --git a/compat/getpeereid.c b/compat/getpeereid.c index 5a593c07..c4988438 100644 --- a/compat/getpeereid.c +++ b/compat/getpeereid.c @@ -53,7 +53,6 @@ getpeereid(int s, uid_t *uid, gid_t *gid) return (0); } #else - errno = EOPNOTSUPP; - return (-1); + return (getuid()); #endif } diff --git a/server-acl.c b/server-acl.c index 344f795c..26f2490d 100644 --- a/server-acl.c +++ b/server-acl.c @@ -151,7 +151,7 @@ server_acl_user_deny_write(uid_t uid) TAILQ_FOREACH(c, &clients, entry) { uid = proc_get_peer_uid(c->peer); - if (uid == user->uid && uid == user->uid) + if (uid != (uid_t)-1 && uid == user->uid) c->flags |= CLIENT_READONLY; } } @@ -164,7 +164,11 @@ int server_acl_join(struct client *c) { struct server_acl_user *user; - uid_t uid = proc_get_peer_uid(c->peer); + uid_t uid; + + uid = proc_get_peer_uid(c->peer); + if (uid == (uid_t)-1) + return (0); user = server_acl_user_find(uid); if (user == NULL) -- cgit From d4423dca19d6d5cbaa97336a744ec760841c3816 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 14 Apr 2022 06:59:29 +0000 Subject: Fix clearphist alias, from Jacqueline Jolicoeur via jmc@. --- tmux.1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tmux.1 b/tmux.1 index 9342004f..f6b498e9 100644 --- a/tmux.1 +++ b/tmux.1 @@ -5580,11 +5580,11 @@ session option. .Pp Commands related to the status line are as follows: .Bl -tag -width Ds -.Tg clrphist +.Tg clearphist .It Xo Ic clear-prompt-history .Op Fl T Ar prompt-type .Xc -.D1 Pq alias: Ic clrphist +.D1 Pq alias: Ic clearphist Clear status prompt history for prompt type .Ar prompt-type . If -- cgit From e0c982c5adf54fcfc7c6e588b1350fd8ae474310 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 15 Apr 2022 12:45:43 +0100 Subject: iTerm2 has OSC 7, from Gregory Anders. --- tty-features.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tty-features.c b/tty-features.c index 4d83a465..2848b4d6 100644 --- a/tty-features.c +++ b/tty-features.c @@ -376,7 +376,7 @@ tty_default_features(int *feat, const char *name, u_int version) }, { .name = "iTerm2", .features = TTY_FEATURES_BASE_MODERN_XTERM - ",cstyle,extkeys,margins,usstyle,sync" + ",cstyle,extkeys,margins,usstyle,sync,osc7" }, { .name = "XTerm", /* -- cgit From 58d1a206c6ae6b33059ea6b469c21dad92ea0841 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 18 Apr 2022 11:47:14 +0100 Subject: Add a way for lines added to copy mode to be passed through the parser to handle escape sequences and use it for run-shell, GitHub issue 3156. --- cfg.c | 2 +- cmd-queue.c | 2 +- cmd-run-shell.c | 20 ++++++++---------- input.c | 3 +++ tmux.h | 5 +++-- window-copy.c | 63 +++++++++++++++++++++++++++++++++++++++++++++------------ 6 files changed, 66 insertions(+), 29 deletions(-) diff --git a/cfg.c b/cfg.c index 5b64c2aa..e92faa7c 100644 --- a/cfg.c +++ b/cfg.c @@ -250,7 +250,7 @@ cfg_show_causes(struct session *s) if (wme == NULL || wme->mode != &window_view_mode) window_pane_set_mode(wp, NULL, &window_view_mode, NULL, NULL); for (i = 0; i < cfg_ncauses; i++) { - window_copy_add(wp, "%s", cfg_causes[i]); + window_copy_add(wp, 0, "%s", cfg_causes[i]); free(cfg_causes[i]); } diff --git a/cmd-queue.c b/cmd-queue.c index 6c7c3675..8325e2e8 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -856,7 +856,7 @@ cmdq_print(struct cmdq_item *item, const char *fmt, ...) window_pane_set_mode(wp, NULL, &window_view_mode, NULL, NULL); } - window_copy_add(wp, "%s", msg); + window_copy_add(wp, 0, "%s", msg); } free(msg); diff --git a/cmd-run-shell.c b/cmd-run-shell.c index 5e914e65..db5774d2 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -84,22 +84,17 @@ cmd_run_shell_print(struct job *job, const char *msg) if (cdata->wp_id != -1) wp = window_pane_find_by_id(cdata->wp_id); - if (wp == NULL) { - if (cdata->item != NULL) { - cmdq_print(cdata->item, "%s", msg); - return; - } - if (cmd_find_from_nothing(&fs, 0) != 0) - return; + if (wp == NULL && cdata->item != NULL) + wp = server_client_get_pane(cdata->client); + if (wp == NULL && cmd_find_from_nothing(&fs, 0) == 0) wp = fs.wp; - if (wp == NULL) - return; - } + if (wp == NULL) + return; wme = TAILQ_FIRST(&wp->modes); if (wme == NULL || wme->mode != &window_view_mode) window_pane_set_mode(wp, NULL, &window_view_mode, NULL, NULL); - window_copy_add(wp, "%s", msg); + window_copy_add(wp, 1, "%s", msg); } static enum cmd_retval @@ -227,7 +222,8 @@ cmd_run_shell_callback(struct job *job) int retcode, status; do { - if ((line = evbuffer_readline(event->input)) != NULL) { + line = evbuffer_readln(event->input, NULL, EVBUFFER_EOL_LF); + if (line != NULL) { cmd_run_shell_print(job, line); free(line); } diff --git a/input.c b/input.c index 45ccefd6..693b6f32 100644 --- a/input.c +++ b/input.c @@ -1078,6 +1078,9 @@ input_reply(struct input_ctx *ictx, const char *fmt, ...) va_list ap; char *reply; + if (bev == NULL) + return; + va_start(ap, fmt); xvasprintf(&reply, fmt, ap); va_end(ap); diff --git a/tmux.h b/tmux.h index 2ec09692..a893bdb2 100644 --- a/tmux.h +++ b/tmux.h @@ -3085,8 +3085,9 @@ extern const struct window_mode window_client_mode; /* window-copy.c */ extern const struct window_mode window_copy_mode; extern const struct window_mode window_view_mode; -void printflike(2, 3) window_copy_add(struct window_pane *, const char *, ...); -void printflike(2, 0) window_copy_vadd(struct window_pane *, const char *, +void printflike(3, 4) window_copy_add(struct window_pane *, int, const char *, + ...); +void printflike(3, 0) window_copy_vadd(struct window_pane *, int, const char *, va_list); void window_copy_pageup(struct window_pane *, int); void window_copy_start_drag(struct client *, struct mouse_event *); diff --git a/window-copy.c b/window-copy.c index 7c55b0f2..03070556 100644 --- a/window-copy.c +++ b/window-copy.c @@ -222,6 +222,8 @@ struct window_copy_mode_data { struct screen *backing; int backing_written; /* backing display started */ + struct screen *writing; + struct input_ctx *ictx; int viewmode; /* view mode entered */ @@ -467,13 +469,16 @@ window_copy_view_init(struct window_mode_entry *wme, struct window_pane *wp = wme->wp; struct window_copy_mode_data *data; struct screen *base = &wp->base; - struct screen *s; + u_int sx = screen_size_x(base); data = window_copy_common_init(wme); data->viewmode = 1; - data->backing = s = xmalloc(sizeof *data->backing); - screen_init(s, screen_size_x(base), screen_size_y(base), UINT_MAX); + data->backing = xmalloc(sizeof *data->backing); + screen_init(data->backing, sx, screen_size_y(base), UINT_MAX); + data->writing = xmalloc(sizeof *data->writing); + screen_init(data->writing, sx, screen_size_y(base), 0); + data->ictx = input_init(NULL, NULL, NULL); data->mx = data->cx; data->my = screen_hsize(data->backing) + data->cy - data->oy; data->showmark = 0; @@ -492,6 +497,12 @@ window_copy_free(struct window_mode_entry *wme) free(data->searchstr); free(data->jumpchar); + if (data->writing != NULL) { + screen_free(data->writing); + free(data->writing); + } + if (data->ictx != NULL) + input_free(data->ictx); screen_free(data->backing); free(data->backing); @@ -500,41 +511,67 @@ window_copy_free(struct window_mode_entry *wme) } void -window_copy_add(struct window_pane *wp, const char *fmt, ...) +window_copy_add(struct window_pane *wp, int parse, const char *fmt, ...) { va_list ap; va_start(ap, fmt); - window_copy_vadd(wp, fmt, ap); + window_copy_vadd(wp, parse, fmt, ap); va_end(ap); } +static void +window_copy_init_ctx_cb(__unused struct screen_write_ctx *ctx, + struct tty_ctx *ttyctx) +{ + memcpy(&ttyctx->defaults, &grid_default_cell, sizeof ttyctx->defaults); + ttyctx->palette = NULL; + ttyctx->redraw_cb = NULL; + ttyctx->set_client_cb = NULL; + ttyctx->arg = NULL; +} + void -window_copy_vadd(struct window_pane *wp, const char *fmt, va_list ap) +window_copy_vadd(struct window_pane *wp, int parse, const char *fmt, va_list ap) { struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes); struct window_copy_mode_data *data = wme->data; struct screen *backing = data->backing; - struct screen_write_ctx back_ctx, ctx; + struct screen *writing = data->writing; + struct screen_write_ctx writing_ctx, backing_ctx, ctx; struct grid_cell gc; u_int old_hsize, old_cy; + u_int sx = screen_size_x(backing); + char *text; - memcpy(&gc, &grid_default_cell, sizeof gc); + if (parse) { + vasprintf(&text, fmt, ap); + screen_write_start(&writing_ctx, writing); + screen_write_reset(&writing_ctx); + input_parse_screen(data->ictx, writing, window_copy_init_ctx_cb, + data, text, strlen(text)); + free(text); + } old_hsize = screen_hsize(data->backing); - screen_write_start(&back_ctx, backing); + screen_write_start(&backing_ctx, backing); if (data->backing_written) { /* * On the second or later line, do a CRLF before writing * (so it's on a new line). */ - screen_write_carriagereturn(&back_ctx); - screen_write_linefeed(&back_ctx, 0, 8); + screen_write_carriagereturn(&backing_ctx); + screen_write_linefeed(&backing_ctx, 0, 8); } else data->backing_written = 1; old_cy = backing->cy; - screen_write_vnputs(&back_ctx, 0, &gc, fmt, ap); - screen_write_stop(&back_ctx); + if (parse) + screen_write_fast_copy(&backing_ctx, writing, 0, 0, sx, 1); + else { + memcpy(&gc, &grid_default_cell, sizeof gc); + screen_write_vnputs(&backing_ctx, 0, &gc, fmt, ap); + } + screen_write_stop(&backing_ctx); data->oy += screen_hsize(data->backing) - old_hsize; -- cgit From c6b51cea923e0c4e92636998a776ada42511b6e5 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 27 Apr 2022 11:34:08 +0100 Subject: If a mouse position was above the maximum supported by the normal mouse protocol (223), tmux was allowing it to wrap around. However, since tmux was not correctly handling this on input, other programs also do not handle it correctly, and the alternative SGR mouse mode is now widespread, this seems unnecessary, so remove this feature. Also define some constants to make it clearer what the numbers mean. Mostly from Leonid S Usov in GitHub issue 3165. --- input-keys.c | 31 +++++++++++++++++++++++-------- tmux.h | 6 ++++++ tty-keys.c | 16 ++++++---------- 3 files changed, 35 insertions(+), 18 deletions(-) diff --git a/input-keys.c b/input-keys.c index feb62f6d..ebf61333 100644 --- a/input-keys.c +++ b/input-keys.c @@ -606,19 +606,34 @@ input_key_get_mouse(struct screen *s, struct mouse_event *m, u_int x, u_int y, len = xsnprintf(buf, sizeof buf, "\033[<%u;%u;%u%c", m->sgr_b, x + 1, y + 1, m->sgr_type); } else if (s->mode & MODE_MOUSE_UTF8) { - if (m->b > 0x7ff - 32 || x > 0x7ff - 33 || y > 0x7ff - 33) + if (m->b > MOUSE_PARAM_UTF8_MAX - MOUSE_PARAM_BTN_OFF || + x > MOUSE_PARAM_UTF8_MAX - MOUSE_PARAM_POS_OFF || + y > MOUSE_PARAM_UTF8_MAX - MOUSE_PARAM_POS_OFF) return (0); len = xsnprintf(buf, sizeof buf, "\033[M"); - len += input_key_split2(m->b + 32, &buf[len]); - len += input_key_split2(x + 33, &buf[len]); - len += input_key_split2(y + 33, &buf[len]); + len += input_key_split2(m->b + MOUSE_PARAM_BTN_OFF, &buf[len]); + len += input_key_split2(x + MOUSE_PARAM_POS_OFF, &buf[len]); + len += input_key_split2(y + MOUSE_PARAM_POS_OFF, &buf[len]); } else { - if (m->b > 223) + if (m->b + MOUSE_PARAM_BTN_OFF > MOUSE_PARAM_MAX) return (0); + len = xsnprintf(buf, sizeof buf, "\033[M"); - buf[len++] = m->b + 32; - buf[len++] = x + 33; - buf[len++] = y + 33; + buf[len++] = m->b + MOUSE_PARAM_BTN_OFF; + + /* + * The incoming x and y may be out of the range which can be + * supported by the "normal" mouse protocol. Clamp the + * coordinates to the supported range. + */ + if (x + MOUSE_PARAM_POS_OFF > MOUSE_PARAM_MAX) + buf[len++] = MOUSE_PARAM_MAX; + else + buf[len++] = x + MOUSE_PARAM_POS_OFF; + if (y + MOUSE_PARAM_POS_OFF > MOUSE_PARAM_MAX) + buf[len++] = MOUSE_PARAM_MAX; + else + buf[len++] = y + MOUSE_PARAM_POS_OFF; } *rbuf = buf; diff --git a/tmux.h b/tmux.h index a893bdb2..53084b8b 100644 --- a/tmux.h +++ b/tmux.h @@ -581,6 +581,12 @@ enum tty_code_code { #define MOTION_MOUSE_MODES (MODE_MOUSE_BUTTON|MODE_MOUSE_ALL) #define CURSOR_MODES (MODE_CURSOR|MODE_CURSOR_BLINKING|MODE_CURSOR_VERY_VISIBLE) +/* Mouse protocol constants. */ +#define MOUSE_PARAM_MAX 0xff +#define MOUSE_PARAM_UTF8_MAX 0x7ff +#define MOUSE_PARAM_BTN_OFF 0x20 +#define MOUSE_PARAM_POS_OFF 0x21 + /* A single UTF-8 character. */ typedef u_int utf8_char; diff --git a/tty-keys.c b/tty-keys.c index 8538e74b..64dd91bb 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -1061,17 +1061,13 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size, log_debug("%s: mouse input: %.*s", c->name, (int)*size, buf); /* Check and return the mouse input. */ - if (b < 32) + if (b < MOUSE_PARAM_BTN_OFF || + x < MOUSE_PARAM_POS_OFF || + y < MOUSE_PARAM_POS_OFF) return (-1); - b -= 32; - if (x >= 33) - x -= 33; - else - x = 256 - x; - if (y >= 33) - y -= 33; - else - y = 256 - y; + b -= MOUSE_PARAM_BTN_OFF; + x -= MOUSE_PARAM_POS_OFF; + y -= MOUSE_PARAM_POS_OFF; } else if (buf[2] == '<') { /* Read the three inputs. */ *size = 3; -- cgit From 3b7dae9a5339c4495d30a4c1bb28ffe8aead0b27 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 2 May 2022 10:46:11 +0100 Subject: Do not allow pipe-pane on dead panes, from Anindya Mukherjee, GitHub issue 3174. --- cmd-pipe-pane.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cmd-pipe-pane.c b/cmd-pipe-pane.c index aac23a0f..0fa656ce 100644 --- a/cmd-pipe-pane.c +++ b/cmd-pipe-pane.c @@ -67,6 +67,12 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmdq_item *item) struct format_tree *ft; sigset_t set, oldset; + /* Do nothing if pane is dead. */ + if (wp->fd == -1 || (wp->flags & PANE_EXITED)) { + cmdq_error(item, "target pane has exited"); + return (CMD_RETURN_ERROR); + } + /* Destroy the old pipe. */ old_fd = wp->pipe_fd; if (wp->pipe_fd != -1) { -- cgit From 2cad9a6af89d745bb1816d69c21789a7ce052417 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sat, 14 May 2022 20:13:52 +0100 Subject: Check if args_strtonum argument is NULL or not a string, from Anindya Mukherjee. --- arguments.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arguments.c b/arguments.c index 91c470e2..d0dc2d4d 100644 --- a/arguments.c +++ b/arguments.c @@ -831,6 +831,12 @@ args_strtonum(struct args *args, u_char flag, long long minval, return (0); } value = TAILQ_LAST(&entry->values, args_values); + if (value == NULL || + value->type != ARGS_STRING || + value->string == NULL) { + *cause = xstrdup("missing"); + return (0); + } ll = strtonum(value->string, minval, maxval, &errstr); if (errstr != NULL) { -- cgit From ead75c2d512cb1c47b96cf62ddebea1a415119f3 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sat, 14 May 2022 20:16:46 +0100 Subject: Typos, from imcusg at gmail dot com. --- CHANGES | 2 +- cmd-server-access.c | 2 +- tools/ansicode.txt | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGES b/CHANGES index 3c682e5f..997aad1d 100644 --- a/CHANGES +++ b/CHANGES @@ -3251,7 +3251,7 @@ The list of older changes is below. * (nicm) -n on new-session is now -s, and -n is now the initial window name. This was documented but not implemented :-/. * (nicm) kill-window command, bound to & by default (because it should be hard - to hit accidently). + to hit accidentally). * (nicm) bell-style option with three choices: "none" completely ignore bell; "any" pass through a bell in any window to current; "current" ignore bells except in current window. This applies only to the bell terminal signal, diff --git a/cmd-server-access.c b/cmd-server-access.c index 4fd1dfbf..45442a62 100644 --- a/cmd-server-access.c +++ b/cmd-server-access.c @@ -81,7 +81,7 @@ cmd_server_access_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_NORMAL); } if (args_count(args) == 0) { - cmdq_error(item, "missing user arguement"); + cmdq_error(item, "missing user argument"); return (CMD_RETURN_ERROR); } diff --git a/tools/ansicode.txt b/tools/ansicode.txt index 5a9c79e0..e77d31ae 100644 --- a/tools/ansicode.txt +++ b/tools/ansicode.txt @@ -19,7 +19,7 @@ Standardized Video Terminals" in the April-1984 issue of BYTE magazine. ANSI X3.4-1977 defines the 7-bit ASCII character set (C0 and G0). It was written in 1968, revised in 1977, and explains the decisions made in laying out the ASCII code. In particular, it explains why ANSI chose to make ASCII -incompatible with EBCDIC in order to make it self-consistant. +incompatible with EBCDIC in order to make it self-consistent. ANSI X3.41-1974 introduces the idea of an 8-bit ASCII character set (C1 and G1 in addition to the existing C0 and G0). It describes how to use the 8-bit @@ -294,7 +294,7 @@ Pt = Start VT105 graphics on a VT125 ============================================================================== - Indepenent control functions (from Appendix E of X3.64-1977). + Independent control functions (from Appendix E of X3.64-1977). These four controls have the same meaning regardless of the current definition of the C0 and C1 control sets. Each control is a two-character ESCape sequence, the 2nd character is lowercase. -- cgit From 95baa32383bf095daf1a1e707de6b540ddb05d37 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 20 May 2022 08:33:57 +0100 Subject: Set PWD so shells have a hint about the real path (this was done before but lost in a merge). GitHub issue 3186. --- spawn.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/spawn.c b/spawn.c index c8ac1bd8..2cb2b65e 100644 --- a/spawn.c +++ b/spawn.c @@ -209,7 +209,7 @@ spawn_pane(struct spawn_context *sc, char **cause) struct window_pane *new_wp; struct environ *child; struct environ_entry *ee; - char **argv, *cp, **argvp, *argv0, *cwd; + char **argv, *cp, **argvp, *argv0, *cwd, *new_cwd; const char *cmd, *tmp; int argc; u_int idx; @@ -225,9 +225,15 @@ spawn_pane(struct spawn_context *sc, char **cause) * Work out the current working directory. If respawning, use * the pane's stored one unless specified. */ - if (sc->cwd != NULL) + if (sc->cwd != NULL) { cwd = format_single(item, sc->cwd, c, target->s, NULL, NULL); - else if (~sc->flags & SPAWN_RESPAWN) + if (*cwd != '/') { + xasprintf(&new_cwd, "%s/%s", server_client_get_cwd(c, + target->s), cwd); + free(cwd); + cwd = new_cwd; + } + } else if (~sc->flags & SPAWN_RESPAWN) cwd = xstrdup(server_client_get_cwd(c, target->s)); else cwd = NULL; @@ -335,8 +341,7 @@ spawn_pane(struct spawn_context *sc, char **cause) log_debug("%s: cmd=%s", __func__, cp); free(cp); } - if (cwd != NULL) - log_debug("%s: cwd=%s", __func__, cwd); + log_debug("%s: cwd=%s", __func__, new_wp->cwd); cmd_log_argv(new_wp->argc, new_wp->argv, "%s", __func__); environ_log(child, "%s: environment ", __func__); @@ -382,9 +387,13 @@ spawn_pane(struct spawn_context *sc, char **cause) * Child process. Change to the working directory or home if that * fails. */ - if (chdir(new_wp->cwd) != 0 && - ((tmp = find_home()) == NULL || chdir(tmp) != 0) && - chdir("/") != 0) + if (chdir(new_wp->cwd) == 0) + environ_set(child, "PWD", 0, "%s", new_wp->cwd); + else if ((tmp = find_home()) != NULL || chdir(tmp) == 0) + environ_set(child, "PWD", 0, "%s", tmp); + else if (chdir("/") == 0) + environ_set(child, "PWD", 0, "/"); + else fatal("chdir failed"); /* -- cgit From 1b28b2b51d264544a7c9490087561c9192459ba5 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 20 May 2022 08:49:05 +0100 Subject: Add pane_start_path to match start_command. --- format.c | 17 +++++++++++++++++ tmux.1 | 1 + 2 files changed, 18 insertions(+) diff --git a/format.c b/format.c index 5e04a6a2..ccd259ec 100644 --- a/format.c +++ b/format.c @@ -801,6 +801,20 @@ format_cb_start_command(struct format_tree *ft) return (cmd_stringify_argv(wp->argc, wp->argv)); } +/* Callback for pane_start_path. */ +static void * +format_cb_start_path(struct format_tree *ft) +{ + struct window_pane *wp = ft->wp; + + if (wp == NULL) + return (NULL); + + if (wp->cwd == NULL) + return (xstrdup("")); + return (xstrdup(wp->cwd)); +} + /* Callback for pane_current_command. */ static void * format_cb_current_command(struct format_tree *ft) @@ -2898,6 +2912,9 @@ static const struct format_table_entry format_table[] = { { "pane_start_command", FORMAT_TABLE_STRING, format_cb_start_command }, + { "pane_start_path", FORMAT_TABLE_STRING, + format_cb_start_path + }, { "pane_synchronized", FORMAT_TABLE_STRING, format_cb_pane_synchronized }, diff --git a/tmux.1 b/tmux.1 index e1115fe2..61834a94 100644 --- a/tmux.1 +++ b/tmux.1 @@ -5195,6 +5195,7 @@ The following variables are available, where appropriate: .It Li "pane_right" Ta "" Ta "Right of pane" .It Li "pane_search_string" Ta "" Ta "Last search string in copy mode" .It Li "pane_start_command" Ta "" Ta "Command pane started with" +.It Li "pane_start_path" Ta "" Ta "Path pane started with" .It Li "pane_synchronized" Ta "" Ta "1 if pane is synchronized" .It Li "pane_tabs" Ta "" Ta "Pane tab positions" .It Li "pane_title" Ta "#T" Ta "Title of pane (can be set by application)" -- cgit From cf7e1c94dfa472f698cc5902d901ef1cef938f64 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 20 May 2022 09:00:37 +0100 Subject: Remove duplicates from completion list, GitHub issue 3178. --- status.c | 46 ++++++++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/status.c b/status.c index 8d702de1..929276d2 100644 --- a/status.c +++ b/status.c @@ -1576,11 +1576,25 @@ status_prompt_add_history(const char *line, u_int type) status_prompt_hsize[type] = newsize; } +/* Add to completion list. */ +static void +status_prompt_add_list(char ***list, u_int *size, const char *s) +{ + u_int i; + + for (i = 0; i < *size; i++) { + if (strcmp((*list)[i], s) == 0) + return; + } + *list = xreallocarray(*list, (*size) + 1, sizeof **list); + (*list)[(*size)++] = xstrdup(s); +} + /* Build completion list. */ static char ** status_prompt_complete_list(u_int *size, const char *s, int at_start) { - char **list = NULL; + char **list = NULL, *tmp; const char **layout, *value, *cp; const struct cmd_entry **cmdent; const struct options_table_entry *oe; @@ -1594,15 +1608,11 @@ status_prompt_complete_list(u_int *size, const char *s, int at_start) *size = 0; for (cmdent = cmd_table; *cmdent != NULL; cmdent++) { - if (strncmp((*cmdent)->name, s, slen) == 0) { - list = xreallocarray(list, (*size) + 1, sizeof *list); - list[(*size)++] = xstrdup((*cmdent)->name); - } + if (strncmp((*cmdent)->name, s, slen) == 0) + status_prompt_add_list(&list, size, (*cmdent)->name); if ((*cmdent)->alias != NULL && - strncmp((*cmdent)->alias, s, slen) == 0) { - list = xreallocarray(list, (*size) + 1, sizeof *list); - list[(*size)++] = xstrdup((*cmdent)->alias); - } + strncmp((*cmdent)->alias, s, slen) == 0) + status_prompt_add_list(&list, size, (*cmdent)->alias); } o = options_get_only(global_options, "command-alias"); if (o != NULL) { @@ -1615,8 +1625,9 @@ status_prompt_complete_list(u_int *size, const char *s, int at_start) if (slen > valuelen || strncmp(value, s, slen) != 0) goto next; - list = xreallocarray(list, (*size) + 1, sizeof *list); - list[(*size)++] = xstrndup(value, valuelen); + xasprintf(&tmp, "%.*s", (int)valuelen, value); + status_prompt_add_list(&list, size, tmp); + free(tmp); next: a = options_array_next(a); @@ -1624,18 +1635,13 @@ status_prompt_complete_list(u_int *size, const char *s, int at_start) } if (at_start) return (list); - for (oe = options_table; oe->name != NULL; oe++) { - if (strncmp(oe->name, s, slen) == 0) { - list = xreallocarray(list, (*size) + 1, sizeof *list); - list[(*size)++] = xstrdup(oe->name); - } + if (strncmp(oe->name, s, slen) == 0) + status_prompt_add_list(&list, size, oe->name); } for (layout = layouts; *layout != NULL; layout++) { - if (strncmp(*layout, s, slen) == 0) { - list = xreallocarray(list, (*size) + 1, sizeof *list); - list[(*size)++] = xstrdup(*layout); - } + if (strncmp(*layout, s, slen) == 0) + status_prompt_add_list(&list, size, *layout); } return (list); } -- cgit From bf33e807b60c34792d60a9304cec782323da8826 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 30 May 2022 08:43:06 +0100 Subject: Fix property name, from Sergei Dyshel. --- tmux.1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tmux.1 b/tmux.1 index 61834a94..d89ee840 100644 --- a/tmux.1 +++ b/tmux.1 @@ -5880,7 +5880,7 @@ milliseconds. If .Ar delay is not given, the -.Ic message-time +.Ic display-time option is used; a delay of zero waits for a key press. .Ql N ignores key presses and closes only after the delay expires. -- cgit From cd692b5a68be0eb95252380db97fbbec587d6350 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 30 May 2022 12:48:57 +0000 Subject: Add an ACL list for users connecting to the tmux socket. Users may be forbidden from attaching, forced to attach read-only, or allowed to attach read-write. A new command, server-access, configures the list. tmux gets the user using getpeereid(3) of the client socket. Users must still configure file system permissions manually. From Dallas Lyons and others. --- Makefile | 2 + client.c | 1 + cmd-attach-session.c | 3 +- cmd-server-access.c | 147 +++++++++++++++++++++++++++++++++++++++++++++++++++ cmd.c | 2 + proc.c | 6 +++ server.c | 16 ++++-- tmux.1 | 40 +++++++++++++- 8 files changed, 211 insertions(+), 6 deletions(-) create mode 100644 cmd-server-access.c diff --git a/Makefile b/Makefile index 4cbbd34a..fc7ed262 100644 --- a/Makefile +++ b/Makefile @@ -55,6 +55,7 @@ SRCS= alerts.c \ cmd-select-pane.c \ cmd-select-window.c \ cmd-send-keys.c \ + cmd-server-access.c \ cmd-set-buffer.c \ cmd-set-environment.c \ cmd-set-option.c \ @@ -104,6 +105,7 @@ SRCS= alerts.c \ screen-redraw.c \ screen-write.c \ screen.c \ + server-acl.c \ server-client.c \ server-fn.c \ server.c \ diff --git a/client.c b/client.c index 8a0a0673..ef7dea69 100644 --- a/client.c +++ b/client.c @@ -360,6 +360,7 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags, /* Send identify messages. */ client_send_identify(ttynam, termname, caps, ncaps, cwd, feat); tty_term_free_list(caps, ncaps); + proc_flush_peer(client_peer); /* Send first command. */ if (msg == MSG_COMMAND) { diff --git a/cmd-attach-session.c b/cmd-attach-session.c index cc795b22..b92a7f2b 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -43,7 +43,7 @@ const struct cmd_entry cmd_attach_session_entry = { /* -t is special */ - .flags = CMD_STARTSERVER, + .flags = CMD_STARTSERVER|CMD_READONLY, .exec = cmd_attach_session_exec }; @@ -69,6 +69,7 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag, if (c == NULL) return (CMD_RETURN_NORMAL); + if (server_client_check_nested(c)) { cmdq_error(item, "sessions should be nested with care, " "unset $TMUX to force"); diff --git a/cmd-server-access.c b/cmd-server-access.c new file mode 100644 index 00000000..4fd1dfbf --- /dev/null +++ b/cmd-server-access.c @@ -0,0 +1,147 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2021 Dallas Lyons + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include "tmux.h" + +/* + * Controls access to session. + */ + +static enum cmd_retval cmd_server_access_exec(struct cmd *, struct cmdq_item *); + +const struct cmd_entry cmd_server_access_entry = { + .name = "server-access", + .alias = NULL, + + .args = { "adlrw", 0, 1, NULL }, + .usage = "[-adlrw]" CMD_TARGET_PANE_USAGE " [user]", + + .flags = CMD_CLIENT_CANFAIL, + .exec = cmd_server_access_exec +}; + +static enum cmd_retval +cmd_server_access_deny(struct cmdq_item *item, struct passwd *pw) +{ + struct client *loop; + struct server_acl_user *user; + uid_t uid; + + if ((user = server_acl_user_find(pw->pw_uid)) == NULL) { + cmdq_error(item, "user %s not found", pw->pw_name); + return (CMD_RETURN_ERROR); + } + TAILQ_FOREACH(loop, &clients, entry) { + uid = proc_get_peer_uid(loop->peer); + if (uid == server_acl_get_uid(user)) { + loop->exit_message = xstrdup("access not allowed"); + loop->flags |= CLIENT_EXIT; + } + } + server_acl_user_deny(pw->pw_uid); + + return (CMD_RETURN_NORMAL); +} + +static enum cmd_retval +cmd_server_access_exec(struct cmd *self, struct cmdq_item *item) +{ + + struct args *args = cmd_get_args(self); + struct client *c = cmdq_get_target_client(item); + char *name; + struct passwd *pw = NULL; + + if (args_has(args, 'l')) { + server_acl_display(item); + return (CMD_RETURN_NORMAL); + } + if (args_count(args) == 0) { + cmdq_error(item, "missing user arguement"); + return (CMD_RETURN_ERROR); + } + + name = format_single(item, args_string(args, 0), c, NULL, NULL, NULL); + if (*name != '\0') + pw = getpwnam(name); + if (pw == NULL) { + cmdq_error(item, "unknown user: %s", name); + return (CMD_RETURN_ERROR); + } + free(name); + + if (pw->pw_uid == 0 || pw->pw_uid == getuid()) { + cmdq_error(item, "%s owns the server, can't change access", + pw->pw_name); + return (CMD_RETURN_ERROR); + } + + if (args_has(args, 'a') && args_has(args, 'd')) { + cmdq_error(item, "-a and -d cannot be used together"); + return (CMD_RETURN_ERROR); + } + if (args_has(args, 'w') && args_has(args, 'r')) { + cmdq_error(item, "-r and -w cannot be used together"); + return (CMD_RETURN_ERROR); + } + + if (args_has(args, 'd')) + return (cmd_server_access_deny(item, pw)); + if (args_has(args, 'a')) { + if (server_acl_user_find(pw->pw_uid) != NULL) { + cmdq_error(item, "user %s is already added", + pw->pw_name); + return (CMD_RETURN_ERROR); + } + server_acl_user_allow(pw->pw_uid); + /* Do not return - allow -r or -w with -a. */ + } else if (args_has(args, 'r') || args_has(args, 'w')) { + /* -r or -w implies -a if user does not exist. */ + if (server_acl_user_find(pw->pw_uid) == NULL) + server_acl_user_allow(pw->pw_uid); + } + + if (args_has(args, 'w')) { + if (server_acl_user_find(pw->pw_uid) == NULL) { + cmdq_error(item, "user %s not found", pw->pw_name); + return (CMD_RETURN_ERROR); + } + server_acl_user_allow_write(pw->pw_uid); + return (CMD_RETURN_NORMAL); + } + + if (args_has(args, 'r')) { + if (server_acl_user_find(pw->pw_uid) == NULL) { + cmdq_error(item, "user %s not found", pw->pw_name); + return (CMD_RETURN_ERROR); + } + server_acl_user_deny_write(pw->pw_uid); + return (CMD_RETURN_NORMAL); + } + + return (CMD_RETURN_NORMAL); +} diff --git a/cmd.c b/cmd.c index 123fd366..cbdb7c15 100644 --- a/cmd.c +++ b/cmd.c @@ -96,6 +96,7 @@ extern const struct cmd_entry cmd_select_pane_entry; extern const struct cmd_entry cmd_select_window_entry; extern const struct cmd_entry cmd_send_keys_entry; extern const struct cmd_entry cmd_send_prefix_entry; +extern const struct cmd_entry cmd_server_access_entry; extern const struct cmd_entry cmd_set_buffer_entry; extern const struct cmd_entry cmd_set_environment_entry; extern const struct cmd_entry cmd_set_hook_entry; @@ -188,6 +189,7 @@ const struct cmd_entry *cmd_table[] = { &cmd_select_window_entry, &cmd_send_keys_entry, &cmd_send_prefix_entry, + &cmd_server_access_entry, &cmd_set_buffer_entry, &cmd_set_environment_entry, &cmd_set_hook_entry, diff --git a/proc.c b/proc.c index 9dcb042e..330d73f3 100644 --- a/proc.c +++ b/proc.c @@ -337,6 +337,12 @@ proc_kill_peer(struct tmuxpeer *peer) peer->flags |= PEER_BAD; } +void +proc_flush_peer(struct tmuxpeer *peer) +{ + imsg_flush(&peer->ibuf); +} + void proc_toggle_log(struct tmuxproc *tp) { diff --git a/server.c b/server.c index f46dd056..3a2580a9 100644 --- a/server.c +++ b/server.c @@ -239,6 +239,8 @@ server_start(struct tmuxproc *client, int flags, struct event_base *base, evtimer_set(&server_ev_tidy, server_tidy_event, NULL); evtimer_add(&server_ev_tidy, &tv); + server_acl_init(); + server_add_accept(0); proc_loop(server_proc, server_loop); @@ -355,9 +357,10 @@ server_update_socket(void) static void server_accept(int fd, short events, __unused void *data) { - struct sockaddr_storage sa; - socklen_t slen = sizeof sa; - int newfd; + struct sockaddr_storage sa; + socklen_t slen = sizeof sa; + int newfd; + struct client *c; server_add_accept(0); if (!(events & EV_READ)) @@ -374,11 +377,16 @@ server_accept(int fd, short events, __unused void *data) } fatal("accept failed"); } + if (server_exit) { close(newfd); return; } - server_client_create(newfd); + c = server_client_create(newfd); + if (!server_acl_join(c)) { + c->exit_message = xstrdup("access not allowed"); + c->flags |= CLIENT_EXIT; + } } /* diff --git a/tmux.1 b/tmux.1 index f6b498e9..3f7ed889 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1485,6 +1485,44 @@ option. .D1 Pq alias: Ic rename Rename the session to .Ar new-name . +.It Xo Ic server-access +.Op Fl adlrw +.Op Ar user +.Xc +Change the access or read/write permission of +.Ar user . +The user running the +.Nm +server (its owner) and the root user cannot be changed and are always +permitted access. +.Pp +.Fl a +and +.Fl d +are used to give or revoke access for the specified user. +If the user is already attached, the +.Fl d +flag causes their clients to be detached. +.Pp +.Fl r +and +.Fl w +change the permissions for +.Ar user : +.Fl r +makes their clients read-only and +.Fl w +writable. +.Fl l +lists current access permissions. +.Pp +By default, the access list is empty and +.Nm +creates sockets with file system permissions preventing access by any user +other than the owner (and root). +These permissions must be changed manually. +Great care should be taken not to allow access to untrusted users even +read-only. .Tg showmsgs .It Xo Ic show-messages .Op Fl JT @@ -5069,7 +5107,7 @@ The following variables are available, where appropriate: .It Li "client_name" Ta "" Ta "Name of client" .It Li "client_pid" Ta "" Ta "PID of client process" .It Li "client_prefix" Ta "" Ta "1 if prefix key has been pressed" -.It Li "client_readonly" Ta "" Ta "1 if client is readonly" +.It Li "client_readonly" Ta "" Ta "1 if client is read-only" .It Li "client_session" Ta "" Ta "Name of the client's session" .It Li "client_termfeatures" Ta "" Ta "Terminal features of client, if any" .It Li "client_termname" Ta "" Ta "Terminal name of client" -- cgit From 4ae2c646576cc4e9f91c730c80bf75fd5122e4b3 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 30 May 2022 12:52:02 +0000 Subject: Better error reporting when applying custom layouts. --- cmd-select-layout.c | 7 ++++--- layout-custom.c | 23 +++++++++++++++++------ 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/cmd-select-layout.c b/cmd-select-layout.c index c857a0e1..6dfe2b6a 100644 --- a/cmd-select-layout.c +++ b/cmd-select-layout.c @@ -77,7 +77,7 @@ cmd_select_layout_exec(struct cmd *self, struct cmdq_item *item) struct window *w = wl->window; struct window_pane *wp = target->wp; const char *layoutname; - char *oldlayout; + char *oldlayout, *cause; int next, previous, layout; server_unzoom_window(w); @@ -124,8 +124,9 @@ cmd_select_layout_exec(struct cmd *self, struct cmdq_item *item) } if (layoutname != NULL) { - if (layout_parse(w, layoutname) == -1) { - cmdq_error(item, "can't set layout: %s", layoutname); + if (layout_parse(w, layoutname, &cause) == -1) { + cmdq_error(item, "%s: %s", cause, layoutname); + free(cause); goto error; } goto changed; diff --git a/layout-custom.c b/layout-custom.c index e7fb4253..932b30e7 100644 --- a/layout-custom.c +++ b/layout-custom.c @@ -154,7 +154,7 @@ layout_check(struct layout_cell *lc) /* Parse a layout string and arrange window as layout. */ int -layout_parse(struct window *w, const char *layout) +layout_parse(struct window *w, const char *layout, char **cause) { struct layout_cell *lc, *lcchild; struct window_pane *wp; @@ -165,22 +165,31 @@ layout_parse(struct window *w, const char *layout) if (sscanf(layout, "%hx,", &csum) != 1) return (-1); layout += 5; - if (csum != layout_checksum(layout)) + if (csum != layout_checksum(layout)) { + *cause = xstrdup("invalid layout"); return (-1); + } /* Build the layout. */ lc = layout_construct(NULL, &layout); - if (lc == NULL) + if (lc == NULL) { + *cause = xstrdup("invalid layout"); return (-1); - if (*layout != '\0') + } + if (*layout != '\0') { + *cause = xstrdup("invalid layout"); goto fail; + } /* Check this window will fit into the layout. */ for (;;) { npanes = window_count_panes(w); ncells = layout_count_cells(lc); - if (npanes > ncells) + if (npanes > ncells) { + xasprintf(cause, "have %u panes but need %u", npanes, + ncells); goto fail; + } if (npanes == ncells) break; @@ -217,8 +226,10 @@ layout_parse(struct window *w, const char *layout) } /* Check the new layout. */ - if (!layout_check(lc)) + if (!layout_check(lc)) { + *cause = xstrdup("size mismatch after applying layout"); return (-1); + } /* Resize to the layout size. */ window_resize(w, lc->sx, lc->sy, -1, -1); -- cgit From af611815ea70d687a15e20426ed1b88017d3d248 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 30 May 2022 12:54:09 +0000 Subject: Emit window-layout-changed on swap-pane, from George Nachman. --- cmd-swap-pane.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cmd-swap-pane.c b/cmd-swap-pane.c index 7d477739..4191b894 100644 --- a/cmd-swap-pane.c +++ b/cmd-swap-pane.c @@ -135,6 +135,9 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item) } server_redraw_window(src_w); server_redraw_window(dst_w); + notify_window("window-layout-changed", src_w); + if (src_w != dst_w) + notify_window("window-layout-changed", dst_w); out: if (window_pop_zoom(src_w)) -- cgit From 0a8f356c7278dba34a526adea03561f2063df359 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 30 May 2022 12:55:25 +0000 Subject: Spacing/style nits. --- cmd-command-prompt.c | 2 +- cmd-refresh-client.c | 2 +- format.c | 2 +- log.c | 2 +- notify.c | 2 +- screen.c | 4 +- server-acl.c | 186 +++++++++++++++++++++++++++++++++++++++++++++++++++ server-client.c | 24 +++++-- window-buffer.c | 4 +- window-client.c | 2 +- window-tree.c | 2 +- window.c | 2 +- 12 files changed, 218 insertions(+), 16 deletions(-) create mode 100644 server-acl.c diff --git a/cmd-command-prompt.c b/cmd-command-prompt.c index a7a02702..4455856b 100644 --- a/cmd-command-prompt.c +++ b/cmd-command-prompt.c @@ -112,7 +112,7 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item) } next_prompt = prompts; } else - next_prompt = prompts = xstrdup (s); + next_prompt = prompts = xstrdup(s); if ((s = args_get(args, 'I')) != NULL) next_input = inputs = xstrdup(s); else diff --git a/cmd-refresh-client.c b/cmd-refresh-client.c index b2665ad9..6b947280 100644 --- a/cmd-refresh-client.c +++ b/cmd-refresh-client.c @@ -185,7 +185,7 @@ cmd_refresh_client_clipboard(struct cmd *self, struct cmdq_item *item) } if (i != tc->clipboard_npanes) return (CMD_RETURN_NORMAL); - tc->clipboard_panes = xreallocarray (tc->clipboard_panes, + tc->clipboard_panes = xreallocarray(tc->clipboard_panes, tc->clipboard_npanes + 1, sizeof *tc->clipboard_panes); tc->clipboard_panes[tc->clipboard_npanes++] = fs.wp->id; } diff --git a/format.c b/format.c index 981161b3..24d03af0 100644 --- a/format.c +++ b/format.c @@ -2597,7 +2597,7 @@ format_cb_user(__unused struct format_tree *ft) if ((pw = getpwuid(getuid())) != NULL) return (xstrdup(pw->pw_name)); - return NULL; + return (NULL); } /* Format table type. */ diff --git a/log.c b/log.c index abc097dc..ede6e257 100644 --- a/log.c +++ b/log.c @@ -144,7 +144,7 @@ fatal(const char *msg, ...) va_list ap; if (snprintf(tmp, sizeof tmp, "fatal: %s: ", strerror(errno)) < 0) - exit (1); + exit(1); va_start(ap, msg); log_vwrite(msg, ap, tmp); diff --git a/notify.c b/notify.c index f5342710..619bd933 100644 --- a/notify.c +++ b/notify.c @@ -47,7 +47,7 @@ notify_insert_one_hook(struct cmdq_item *item, struct notify_entry *ne, if (log_get_level() != 0) { s = cmd_list_print(cmdlist, 0); log_debug("%s: hook %s is: %s", __func__, ne->name, s); - free (s); + free(s); } new_item = cmdq_get_command(cmdlist, state); return (cmdq_insert_after(item, new_item)); diff --git a/screen.c b/screen.c index 912ab126..87b3330a 100644 --- a/screen.c +++ b/screen.c @@ -662,9 +662,9 @@ screen_mode_to_string(int mode) static char tmp[1024]; if (mode == 0) - return "NONE"; + return ("NONE"); if (mode == ALL_MODES) - return "ALL"; + return ("ALL"); *tmp = '\0'; if (mode & MODE_CURSOR) diff --git a/server-acl.c b/server-acl.c new file mode 100644 index 00000000..26f2490d --- /dev/null +++ b/server-acl.c @@ -0,0 +1,186 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2021 Holland Schutte, Jayson Morberg + * Copyright (c) 2021 Dallas Lyons + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "tmux.h" + +struct server_acl_user { + uid_t uid; + + int flags; +#define SERVER_ACL_READONLY 0x1 + + RB_ENTRY(server_acl_user) entry; +}; + +static int +server_acl_cmp(struct server_acl_user *user1, struct server_acl_user *user2) +{ + if (user1->uid < user2->uid) + return (-1); + return (user1->uid > user2->uid); +} + +RB_HEAD(server_acl_entries, server_acl_user) server_acl_entries; +RB_GENERATE_STATIC(server_acl_entries, server_acl_user, entry, server_acl_cmp); + +/* Initialize server_acl tree. */ +void +server_acl_init(void) +{ + RB_INIT(&server_acl_entries); + + if (getuid() != 0) + server_acl_user_allow(0); + server_acl_user_allow(getuid()); +} + +/* Find user entry. */ +struct server_acl_user* +server_acl_user_find(uid_t uid) +{ + struct server_acl_user find = { .uid = uid }; + + return (RB_FIND(server_acl_entries, &server_acl_entries, &find)); +} + +/* Display the tree. */ +void +server_acl_display(struct cmdq_item *item) +{ + struct server_acl_user *loop; + struct passwd *pw; + const char *name; + + RB_FOREACH(loop, server_acl_entries, &server_acl_entries) { + if (loop->uid == 0) + continue; + if ((pw = getpwuid(loop->uid)) != NULL) + name = pw->pw_name; + else + name = "unknown"; + if (loop->flags == SERVER_ACL_READONLY) + cmdq_print(item, "%s (R)", name); + else + cmdq_print(item, "%s (W)", name); + } +} + +/* Allow a user. */ +void +server_acl_user_allow(uid_t uid) +{ + struct server_acl_user *user; + + user = server_acl_user_find(uid); + if (user == NULL) { + user = xcalloc(1, sizeof *user); + user->uid = uid; + RB_INSERT(server_acl_entries, &server_acl_entries, user); + } +} + +/* Deny a user (remove from the tree). */ +void +server_acl_user_deny(uid_t uid) +{ + struct server_acl_user *user; + + user = server_acl_user_find(uid); + if (user != NULL) { + RB_REMOVE(server_acl_entries, &server_acl_entries, user); + free(user); + } +} + +/* Allow this user write access. */ +void +server_acl_user_allow_write(uid_t uid) +{ + struct server_acl_user *user; + struct client *c; + + user = server_acl_user_find(uid); + if (user == NULL) + return; + user->flags &= ~SERVER_ACL_READONLY; + + TAILQ_FOREACH(c, &clients, entry) { + uid = proc_get_peer_uid(c->peer); + if (uid != (uid_t)-1 && uid == user->uid) + c->flags &= ~CLIENT_READONLY; + } +} + +/* Deny this user write access. */ +void +server_acl_user_deny_write(uid_t uid) +{ + struct server_acl_user *user; + struct client *c; + + user = server_acl_user_find(uid); + if (user == NULL) + return; + user->flags |= SERVER_ACL_READONLY; + + TAILQ_FOREACH(c, &clients, entry) { + uid = proc_get_peer_uid(c->peer); + if (uid != (uid_t)-1 && uid == user->uid) + c->flags |= CLIENT_READONLY; + } +} + +/* + * Check if the client's UID exists in the ACL list and if so, set as read only + * if needed. Return false if the user does not exist. + */ +int +server_acl_join(struct client *c) +{ + struct server_acl_user *user; + uid_t uid; + + uid = proc_get_peer_uid(c->peer); + if (uid == (uid_t)-1) + return (0); + + user = server_acl_user_find(uid); + if (user == NULL) + return (0); + if (user->flags & SERVER_ACL_READONLY) + c->flags |= CLIENT_READONLY; + return (1); +} + +/* Get UID for user entry. */ +uid_t +server_acl_get_uid(struct server_acl_user *user) +{ + return (user->uid); +} diff --git a/server-client.c b/server-client.c index a2a367be..7c4c2fdd 100644 --- a/server-client.c +++ b/server-client.c @@ -2775,6 +2775,14 @@ server_client_dispatch(struct imsg *imsg, void *arg) } } +/* Callback when command is not allowed. */ +static enum cmd_retval +server_client_read_only(struct cmdq_item *item, __unused void *data) +{ + cmdq_error(item, "client is read-only"); + return (CMD_RETURN_ERROR); +} + /* Callback when command is done. */ static enum cmd_retval server_client_command_done(struct cmdq_item *item, __unused void *data) @@ -2799,6 +2807,7 @@ server_client_dispatch_command(struct client *c, struct imsg *imsg) char **argv, *cause; struct cmd_parse_result *pr; struct args_value *values; + struct cmdq_item *new_item; if (c->flags & CLIENT_EXIT) return; @@ -2837,7 +2846,12 @@ server_client_dispatch_command(struct client *c, struct imsg *imsg) free(values); cmd_free_argv(argc, argv); - cmdq_append(c, cmdq_get_command(pr->cmdlist, NULL)); + if ((c->flags & CLIENT_READONLY) && + !cmd_list_all_have(pr->cmdlist, CMD_READONLY)) + new_item = cmdq_get_callback(server_client_read_only, NULL); + else + new_item = cmdq_get_command(pr->cmdlist, NULL); + cmdq_append(c, new_item); cmdq_append(c, cmdq_get_callback(server_client_command_done, NULL)); cmd_list_free(pr->cmdlist); @@ -3071,9 +3085,11 @@ server_client_set_flags(struct client *c, const char *flags) continue; log_debug("client %s set flag %s", c->name, next); - if (not) + if (not) { + if (c->flags & CLIENT_READONLY) + flag &= ~CLIENT_READONLY; c->flags &= ~flag; - else + } else c->flags |= flag; if (flag == CLIENT_CONTROL_NOOUTPUT) control_reset_offsets(c); @@ -3141,7 +3157,7 @@ server_client_add_client_window(struct client *c, u_int id) cw->window = id; RB_INSERT(client_windows, &c->windows, cw); } - return cw; + return (cw); } /* Get client active pane. */ diff --git a/window-buffer.c b/window-buffer.c index a2fa08ad..544a1155 100644 --- a/window-buffer.c +++ b/window-buffer.c @@ -308,7 +308,7 @@ window_buffer_get_key(void *modedata, void *itemdata, u_int line) } pb = paste_get_name(item->name); if (pb == NULL) - return KEYC_NONE; + return (KEYC_NONE); ft = format_create(NULL, NULL, FORMAT_NONE, 0); format_defaults(ft, NULL, NULL, 0, NULL); @@ -320,7 +320,7 @@ window_buffer_get_key(void *modedata, void *itemdata, u_int line) key = key_string_lookup_string(expanded); free(expanded); format_free(ft); - return key; + return (key); } static struct screen * diff --git a/window-client.c b/window-client.c index 00f36c7c..8d501b0d 100644 --- a/window-client.c +++ b/window-client.c @@ -281,7 +281,7 @@ window_client_get_key(void *modedata, void *itemdata, u_int line) key = key_string_lookup_string(expanded); free(expanded); format_free(ft); - return key; + return (key); } static struct screen * diff --git a/window-tree.c b/window-tree.c index b594edd9..fc21af43 100644 --- a/window-tree.c +++ b/window-tree.c @@ -895,7 +895,7 @@ window_tree_get_key(void *modedata, void *itemdata, u_int line) key = key_string_lookup_string(expanded); free(expanded); format_free(ft); - return key; + return (key); } static struct screen * diff --git a/window.c b/window.c index b14a9c60..f14c3bc7 100644 --- a/window.c +++ b/window.c @@ -1046,7 +1046,7 @@ window_pane_resize(struct window_pane *wp, u_int sx, u_int sy) if (sx == wp->sx && sy == wp->sy) return; - r = xmalloc (sizeof *r); + r = xmalloc(sizeof *r); r->sx = sx; r->sy = sy; r->osx = wp->sx; -- cgit From 20b0b38cf47112c0219b5bd041d61c5a28fae0fd Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 30 May 2022 12:57:31 +0000 Subject: iTerm2 has OSC 7, from Gregory Anders. --- tty-features.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tty-features.c b/tty-features.c index 4d83a465..2848b4d6 100644 --- a/tty-features.c +++ b/tty-features.c @@ -376,7 +376,7 @@ tty_default_features(int *feat, const char *name, u_int version) }, { .name = "iTerm2", .features = TTY_FEATURES_BASE_MODERN_XTERM - ",cstyle,extkeys,margins,usstyle,sync" + ",cstyle,extkeys,margins,usstyle,sync,osc7" }, { .name = "XTerm", /* -- cgit From cd89000c1d75d0cfec28cf7e81b06f80a43ea093 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 30 May 2022 13:00:18 +0000 Subject: Add a way for lines added to copy mode to be passed through the parser to handle escape sequences and use it for run-shell, GitHub issue 3156. --- cfg.c | 2 +- cmd-queue.c | 30 ++++++++++++++++++++------- cmd-run-shell.c | 20 ++++++++---------- input.c | 5 +++++ tmux.h | 19 ++++++++++++++--- window-copy.c | 63 +++++++++++++++++++++++++++++++++++++++++++++------------ 6 files changed, 103 insertions(+), 36 deletions(-) diff --git a/cfg.c b/cfg.c index 3130e323..d32e5ff1 100644 --- a/cfg.c +++ b/cfg.c @@ -251,7 +251,7 @@ cfg_show_causes(struct session *s) if (wme == NULL || wme->mode != &window_view_mode) window_pane_set_mode(wp, NULL, &window_view_mode, NULL, NULL); for (i = 0; i < cfg_ncauses; i++) { - window_copy_add(wp, "%s", cfg_causes[i]); + window_copy_add(wp, 0, "%s", cfg_causes[i]); free(cfg_causes[i]); } diff --git a/cmd-queue.c b/cmd-queue.c index 4fbdc4e7..8325e2e8 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -19,9 +19,11 @@ #include #include +#include #include #include #include +#include #include "tmux.h" @@ -124,7 +126,7 @@ cmdq_new(void) { struct cmdq_list *queue; - queue = xcalloc (1, sizeof *queue); + queue = xcalloc(1, sizeof *queue); TAILQ_INIT (&queue->list); return (queue); } @@ -558,17 +560,31 @@ cmdq_add_message(struct cmdq_item *item) { struct client *c = item->client; struct cmdq_state *state = item->state; - const char *name, *key; + const char *key; char *tmp; + uid_t uid; + struct passwd *pw; + char *user = NULL; tmp = cmd_print(item->cmd); if (c != NULL) { - name = c->name; + uid = proc_get_peer_uid(c->peer); + if (uid != (uid_t)-1 && uid != getuid()) { + if ((pw = getpwuid(uid)) != NULL) + xasprintf(&user, "[%s]", pw->pw_name); + else + user = xstrdup("[unknown]"); + } else + user = xstrdup(""); if (c->session != NULL && state->event.key != KEYC_NONE) { key = key_string_lookup_key(state->event.key, 0); - server_add_message("%s key %s: %s", name, key, tmp); - } else - server_add_message("%s command: %s", name, tmp); + server_add_message("%s%s key %s: %s", c->name, user, + key, tmp); + } else { + server_add_message("%s%s command: %s", c->name, user, + tmp); + } + free(user); } else server_add_message("command: %s", tmp); free(tmp); @@ -840,7 +856,7 @@ cmdq_print(struct cmdq_item *item, const char *fmt, ...) window_pane_set_mode(wp, NULL, &window_view_mode, NULL, NULL); } - window_copy_add(wp, "%s", msg); + window_copy_add(wp, 0, "%s", msg); } free(msg); diff --git a/cmd-run-shell.c b/cmd-run-shell.c index 5e914e65..db5774d2 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -84,22 +84,17 @@ cmd_run_shell_print(struct job *job, const char *msg) if (cdata->wp_id != -1) wp = window_pane_find_by_id(cdata->wp_id); - if (wp == NULL) { - if (cdata->item != NULL) { - cmdq_print(cdata->item, "%s", msg); - return; - } - if (cmd_find_from_nothing(&fs, 0) != 0) - return; + if (wp == NULL && cdata->item != NULL) + wp = server_client_get_pane(cdata->client); + if (wp == NULL && cmd_find_from_nothing(&fs, 0) == 0) wp = fs.wp; - if (wp == NULL) - return; - } + if (wp == NULL) + return; wme = TAILQ_FIRST(&wp->modes); if (wme == NULL || wme->mode != &window_view_mode) window_pane_set_mode(wp, NULL, &window_view_mode, NULL, NULL); - window_copy_add(wp, "%s", msg); + window_copy_add(wp, 1, "%s", msg); } static enum cmd_retval @@ -227,7 +222,8 @@ cmd_run_shell_callback(struct job *job) int retcode, status; do { - if ((line = evbuffer_readline(event->input)) != NULL) { + line = evbuffer_readln(event->input, NULL, EVBUFFER_EOL_LF); + if (line != NULL) { cmd_run_shell_print(job, line); free(line); } diff --git a/input.c b/input.c index b1856538..693b6f32 100644 --- a/input.c +++ b/input.c @@ -1078,6 +1078,9 @@ input_reply(struct input_ctx *ictx, const char *fmt, ...) va_list ap; char *reply; + if (bev == NULL) + return; + va_start(ap, fmt); xvasprintf(&reply, fmt, ap); va_end(ap); @@ -1798,6 +1801,8 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx) screen_write_mode_set(sctx, MODE_FOCUSON); if (wp == NULL) break; + if (!options_get_number(global_options, "focus-events")) + break; if (wp->flags & PANE_FOCUSED) bufferevent_write(wp->event, "\033[I", 3); else diff --git a/tmux.h b/tmux.h index 3bde14a2..089b45fb 100644 --- a/tmux.h +++ b/tmux.h @@ -2024,6 +2024,7 @@ struct tmuxpeer *proc_add_peer(struct tmuxproc *, int, void (*)(struct imsg *, void *), void *); void proc_remove_peer(struct tmuxpeer *); void proc_kill_peer(struct tmuxpeer *); +void proc_flush_peer(struct tmuxpeer *); void proc_toggle_log(struct tmuxproc *); pid_t proc_fork_and_daemon(int *); uid_t proc_get_peer_uid(struct tmuxpeer *); @@ -3017,7 +3018,7 @@ void layout_spread_out(struct window_pane *); /* layout-custom.c */ char *layout_dump(struct layout_cell *); -int layout_parse(struct window *, const char *); +int layout_parse(struct window *, const char *, char **); /* layout-set.c */ int layout_set_lookup(const char *); @@ -3082,8 +3083,9 @@ extern const struct window_mode window_client_mode; /* window-copy.c */ extern const struct window_mode window_copy_mode; extern const struct window_mode window_view_mode; -void printflike(2, 3) window_copy_add(struct window_pane *, const char *, ...); -void printflike(2, 0) window_copy_vadd(struct window_pane *, const char *, +void printflike(3, 4) window_copy_add(struct window_pane *, int, const char *, + ...); +void printflike(3, 0) window_copy_vadd(struct window_pane *, int, const char *, va_list); void window_copy_pageup(struct window_pane *, int); void window_copy_start_drag(struct client *, struct mouse_event *); @@ -3266,4 +3268,15 @@ struct window_pane *spawn_pane(struct spawn_context *, char **); /* regsub.c */ char *regsub(const char *, const char *, const char *, int); +/* server-acl.c */ +void server_acl_init(void); +struct server_acl_user *server_acl_user_find(uid_t); +void server_acl_display(struct cmdq_item *); +void server_acl_user_allow(uid_t); +void server_acl_user_deny(uid_t); +void server_acl_user_allow_write(uid_t); +void server_acl_user_deny_write(uid_t); +int server_acl_join(struct client *); +uid_t server_acl_get_uid(struct server_acl_user *); + #endif /* TMUX_H */ diff --git a/window-copy.c b/window-copy.c index 7c55b0f2..03070556 100644 --- a/window-copy.c +++ b/window-copy.c @@ -222,6 +222,8 @@ struct window_copy_mode_data { struct screen *backing; int backing_written; /* backing display started */ + struct screen *writing; + struct input_ctx *ictx; int viewmode; /* view mode entered */ @@ -467,13 +469,16 @@ window_copy_view_init(struct window_mode_entry *wme, struct window_pane *wp = wme->wp; struct window_copy_mode_data *data; struct screen *base = &wp->base; - struct screen *s; + u_int sx = screen_size_x(base); data = window_copy_common_init(wme); data->viewmode = 1; - data->backing = s = xmalloc(sizeof *data->backing); - screen_init(s, screen_size_x(base), screen_size_y(base), UINT_MAX); + data->backing = xmalloc(sizeof *data->backing); + screen_init(data->backing, sx, screen_size_y(base), UINT_MAX); + data->writing = xmalloc(sizeof *data->writing); + screen_init(data->writing, sx, screen_size_y(base), 0); + data->ictx = input_init(NULL, NULL, NULL); data->mx = data->cx; data->my = screen_hsize(data->backing) + data->cy - data->oy; data->showmark = 0; @@ -492,6 +497,12 @@ window_copy_free(struct window_mode_entry *wme) free(data->searchstr); free(data->jumpchar); + if (data->writing != NULL) { + screen_free(data->writing); + free(data->writing); + } + if (data->ictx != NULL) + input_free(data->ictx); screen_free(data->backing); free(data->backing); @@ -500,41 +511,67 @@ window_copy_free(struct window_mode_entry *wme) } void -window_copy_add(struct window_pane *wp, const char *fmt, ...) +window_copy_add(struct window_pane *wp, int parse, const char *fmt, ...) { va_list ap; va_start(ap, fmt); - window_copy_vadd(wp, fmt, ap); + window_copy_vadd(wp, parse, fmt, ap); va_end(ap); } +static void +window_copy_init_ctx_cb(__unused struct screen_write_ctx *ctx, + struct tty_ctx *ttyctx) +{ + memcpy(&ttyctx->defaults, &grid_default_cell, sizeof ttyctx->defaults); + ttyctx->palette = NULL; + ttyctx->redraw_cb = NULL; + ttyctx->set_client_cb = NULL; + ttyctx->arg = NULL; +} + void -window_copy_vadd(struct window_pane *wp, const char *fmt, va_list ap) +window_copy_vadd(struct window_pane *wp, int parse, const char *fmt, va_list ap) { struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes); struct window_copy_mode_data *data = wme->data; struct screen *backing = data->backing; - struct screen_write_ctx back_ctx, ctx; + struct screen *writing = data->writing; + struct screen_write_ctx writing_ctx, backing_ctx, ctx; struct grid_cell gc; u_int old_hsize, old_cy; + u_int sx = screen_size_x(backing); + char *text; - memcpy(&gc, &grid_default_cell, sizeof gc); + if (parse) { + vasprintf(&text, fmt, ap); + screen_write_start(&writing_ctx, writing); + screen_write_reset(&writing_ctx); + input_parse_screen(data->ictx, writing, window_copy_init_ctx_cb, + data, text, strlen(text)); + free(text); + } old_hsize = screen_hsize(data->backing); - screen_write_start(&back_ctx, backing); + screen_write_start(&backing_ctx, backing); if (data->backing_written) { /* * On the second or later line, do a CRLF before writing * (so it's on a new line). */ - screen_write_carriagereturn(&back_ctx); - screen_write_linefeed(&back_ctx, 0, 8); + screen_write_carriagereturn(&backing_ctx); + screen_write_linefeed(&backing_ctx, 0, 8); } else data->backing_written = 1; old_cy = backing->cy; - screen_write_vnputs(&back_ctx, 0, &gc, fmt, ap); - screen_write_stop(&back_ctx); + if (parse) + screen_write_fast_copy(&backing_ctx, writing, 0, 0, sx, 1); + else { + memcpy(&gc, &grid_default_cell, sizeof gc); + screen_write_vnputs(&backing_ctx, 0, &gc, fmt, ap); + } + screen_write_stop(&backing_ctx); data->oy += screen_hsize(data->backing) - old_hsize; -- cgit From 2b60ff588ebc26258848fa9d89a6e32e46eeba58 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 30 May 2022 13:02:55 +0000 Subject: If a mouse position was above the maximum supported by the normal mouse protocol (223), tmux was allowing it to wrap around. However, since tmux was not correctly handling this on input, other programs also do not handle it correctly, and the alternative SGR mouse mode is now widespread, this seems unnecessary, so remove this feature. Also define some constants to make it clearer what the numbers mean. Mostly from Leonid S Usov in GitHub issue 3165. --- input-keys.c | 31 +++++++++++++++++++++++-------- tmux.h | 6 ++++++ tty-keys.c | 16 ++++++---------- 3 files changed, 35 insertions(+), 18 deletions(-) diff --git a/input-keys.c b/input-keys.c index 78ae91da..038003df 100644 --- a/input-keys.c +++ b/input-keys.c @@ -607,19 +607,34 @@ input_key_get_mouse(struct screen *s, struct mouse_event *m, u_int x, u_int y, len = xsnprintf(buf, sizeof buf, "\033[<%u;%u;%u%c", m->sgr_b, x + 1, y + 1, m->sgr_type); } else if (s->mode & MODE_MOUSE_UTF8) { - if (m->b > 0x7ff - 32 || x > 0x7ff - 33 || y > 0x7ff - 33) + if (m->b > MOUSE_PARAM_UTF8_MAX - MOUSE_PARAM_BTN_OFF || + x > MOUSE_PARAM_UTF8_MAX - MOUSE_PARAM_POS_OFF || + y > MOUSE_PARAM_UTF8_MAX - MOUSE_PARAM_POS_OFF) return (0); len = xsnprintf(buf, sizeof buf, "\033[M"); - len += input_key_split2(m->b + 32, &buf[len]); - len += input_key_split2(x + 33, &buf[len]); - len += input_key_split2(y + 33, &buf[len]); + len += input_key_split2(m->b + MOUSE_PARAM_BTN_OFF, &buf[len]); + len += input_key_split2(x + MOUSE_PARAM_POS_OFF, &buf[len]); + len += input_key_split2(y + MOUSE_PARAM_POS_OFF, &buf[len]); } else { - if (m->b > 223) + if (m->b + MOUSE_PARAM_BTN_OFF > MOUSE_PARAM_MAX) return (0); + len = xsnprintf(buf, sizeof buf, "\033[M"); - buf[len++] = m->b + 32; - buf[len++] = x + 33; - buf[len++] = y + 33; + buf[len++] = m->b + MOUSE_PARAM_BTN_OFF; + + /* + * The incoming x and y may be out of the range which can be + * supported by the "normal" mouse protocol. Clamp the + * coordinates to the supported range. + */ + if (x + MOUSE_PARAM_POS_OFF > MOUSE_PARAM_MAX) + buf[len++] = MOUSE_PARAM_MAX; + else + buf[len++] = x + MOUSE_PARAM_POS_OFF; + if (y + MOUSE_PARAM_POS_OFF > MOUSE_PARAM_MAX) + buf[len++] = MOUSE_PARAM_MAX; + else + buf[len++] = y + MOUSE_PARAM_POS_OFF; } *rbuf = buf; diff --git a/tmux.h b/tmux.h index 089b45fb..dd177550 100644 --- a/tmux.h +++ b/tmux.h @@ -580,6 +580,12 @@ enum tty_code_code { #define MOTION_MOUSE_MODES (MODE_MOUSE_BUTTON|MODE_MOUSE_ALL) #define CURSOR_MODES (MODE_CURSOR|MODE_CURSOR_BLINKING|MODE_CURSOR_VERY_VISIBLE) +/* Mouse protocol constants. */ +#define MOUSE_PARAM_MAX 0xff +#define MOUSE_PARAM_UTF8_MAX 0x7ff +#define MOUSE_PARAM_BTN_OFF 0x20 +#define MOUSE_PARAM_POS_OFF 0x21 + /* A single UTF-8 character. */ typedef u_int utf8_char; diff --git a/tty-keys.c b/tty-keys.c index 8538e74b..64dd91bb 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -1061,17 +1061,13 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size, log_debug("%s: mouse input: %.*s", c->name, (int)*size, buf); /* Check and return the mouse input. */ - if (b < 32) + if (b < MOUSE_PARAM_BTN_OFF || + x < MOUSE_PARAM_POS_OFF || + y < MOUSE_PARAM_POS_OFF) return (-1); - b -= 32; - if (x >= 33) - x -= 33; - else - x = 256 - x; - if (y >= 33) - y -= 33; - else - y = 256 - y; + b -= MOUSE_PARAM_BTN_OFF; + x -= MOUSE_PARAM_POS_OFF; + y -= MOUSE_PARAM_POS_OFF; } else if (buf[2] == '<') { /* Read the three inputs. */ *size = 3; -- cgit From af1496b300cd755dcffd514ed0a329943f633cd4 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 30 May 2022 13:03:46 +0000 Subject: Do not allow pipe-pane on dead panes, from Anindya Mukherjee, GitHub issue 3174. --- cmd-pipe-pane.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cmd-pipe-pane.c b/cmd-pipe-pane.c index 1a8c851b..bedc8f24 100644 --- a/cmd-pipe-pane.c +++ b/cmd-pipe-pane.c @@ -68,6 +68,12 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmdq_item *item) struct format_tree *ft; sigset_t set, oldset; + /* Do nothing if pane is dead. */ + if (wp->fd == -1 || (wp->flags & PANE_EXITED)) { + cmdq_error(item, "target pane has exited"); + return (CMD_RETURN_ERROR); + } + /* Destroy the old pipe. */ old_fd = wp->pipe_fd; if (wp->pipe_fd != -1) { -- cgit From 006a529db11bb7050e0c925f7c76b2ff5023655a Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 30 May 2022 13:04:24 +0000 Subject: Check if args_strtonum argument is NULL or not a string, from Anindya Mukherjee. --- arguments.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arguments.c b/arguments.c index 37cd0236..c94a3a29 100644 --- a/arguments.c +++ b/arguments.c @@ -831,6 +831,12 @@ args_strtonum(struct args *args, u_char flag, long long minval, return (0); } value = TAILQ_LAST(&entry->values, args_values); + if (value == NULL || + value->type != ARGS_STRING || + value->string == NULL) { + *cause = xstrdup("missing"); + return (0); + } ll = strtonum(value->string, minval, maxval, &errstr); if (errstr != NULL) { -- cgit From 384f0ee269a49b8a139f8090264420df3a93ff78 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 30 May 2022 13:06:10 +0000 Subject: Fix property name from Sergei Dyshel, and a typo from imcusg at gmail dot com. --- cmd-server-access.c | 2 +- tmux.1 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd-server-access.c b/cmd-server-access.c index 4fd1dfbf..45442a62 100644 --- a/cmd-server-access.c +++ b/cmd-server-access.c @@ -81,7 +81,7 @@ cmd_server_access_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_NORMAL); } if (args_count(args) == 0) { - cmdq_error(item, "missing user arguement"); + cmdq_error(item, "missing user argument"); return (CMD_RETURN_ERROR); } diff --git a/tmux.1 b/tmux.1 index 3f7ed889..da13b1e9 100644 --- a/tmux.1 +++ b/tmux.1 @@ -5876,7 +5876,7 @@ milliseconds. If .Ar delay is not given, the -.Ic message-time +.Ic display-time option is used; a delay of zero waits for a key press. .Ql N ignores key presses and closes only after the delay expires. -- cgit From 6a5d210e558ffe9d8ec9b84f06da07e69629db7b Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 30 May 2022 13:06:41 +0000 Subject: Set PWD so shells have a hint about the real path (this was done before but lost in a merge). GitHub issue 3186. --- spawn.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/spawn.c b/spawn.c index 294eb50f..2349f1b3 100644 --- a/spawn.c +++ b/spawn.c @@ -211,7 +211,7 @@ spawn_pane(struct spawn_context *sc, char **cause) struct window_pane *new_wp; struct environ *child; struct environ_entry *ee; - char **argv, *cp, **argvp, *argv0, *cwd; + char **argv, *cp, **argvp, *argv0, *cwd, *new_cwd; const char *cmd, *tmp; int argc; u_int idx; @@ -227,9 +227,15 @@ spawn_pane(struct spawn_context *sc, char **cause) * Work out the current working directory. If respawning, use * the pane's stored one unless specified. */ - if (sc->cwd != NULL) + if (sc->cwd != NULL) { cwd = format_single(item, sc->cwd, c, target->s, NULL, NULL); - else if (~sc->flags & SPAWN_RESPAWN) + if (*cwd != '/') { + xasprintf(&new_cwd, "%s/%s", server_client_get_cwd(c, + target->s), cwd); + free(cwd); + cwd = new_cwd; + } + } else if (~sc->flags & SPAWN_RESPAWN) cwd = xstrdup(server_client_get_cwd(c, target->s)); else cwd = NULL; @@ -337,8 +343,7 @@ spawn_pane(struct spawn_context *sc, char **cause) log_debug("%s: cmd=%s", __func__, cp); free(cp); } - if (cwd != NULL) - log_debug("%s: cwd=%s", __func__, cwd); + log_debug("%s: cwd=%s", __func__, new_wp->cwd); cmd_log_argv(new_wp->argc, new_wp->argv, "%s", __func__); environ_log(child, "%s: environment ", __func__); @@ -384,9 +389,13 @@ spawn_pane(struct spawn_context *sc, char **cause) * Child process. Change to the working directory or home if that * fails. */ - if (chdir(new_wp->cwd) != 0 && - ((tmp = find_home()) == NULL || chdir(tmp) != 0) && - chdir("/") != 0) + if (chdir(new_wp->cwd) == 0) + environ_set(child, "PWD", 0, "%s", new_wp->cwd); + else if ((tmp = find_home()) != NULL || chdir(tmp) == 0) + environ_set(child, "PWD", 0, "%s", tmp); + else if (chdir("/") == 0) + environ_set(child, "PWD", 0, "/"); + else fatal("chdir failed"); /* -- cgit From 2f2bb82f5f9c7ba995e8c21a217926efbbb4c5e5 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 30 May 2022 13:07:06 +0000 Subject: Add pane_start_path to match start_command. --- format.c | 17 +++++++++++++++++ tmux.1 | 1 + 2 files changed, 18 insertions(+) diff --git a/format.c b/format.c index 24d03af0..d085e348 100644 --- a/format.c +++ b/format.c @@ -801,6 +801,20 @@ format_cb_start_command(struct format_tree *ft) return (cmd_stringify_argv(wp->argc, wp->argv)); } +/* Callback for pane_start_path. */ +static void * +format_cb_start_path(struct format_tree *ft) +{ + struct window_pane *wp = ft->wp; + + if (wp == NULL) + return (NULL); + + if (wp->cwd == NULL) + return (xstrdup("")); + return (xstrdup(wp->cwd)); +} + /* Callback for pane_current_command. */ static void * format_cb_current_command(struct format_tree *ft) @@ -2898,6 +2912,9 @@ static const struct format_table_entry format_table[] = { { "pane_start_command", FORMAT_TABLE_STRING, format_cb_start_command }, + { "pane_start_path", FORMAT_TABLE_STRING, + format_cb_start_path + }, { "pane_synchronized", FORMAT_TABLE_STRING, format_cb_pane_synchronized }, diff --git a/tmux.1 b/tmux.1 index da13b1e9..c58597c6 100644 --- a/tmux.1 +++ b/tmux.1 @@ -5192,6 +5192,7 @@ The following variables are available, where appropriate: .It Li "pane_right" Ta "" Ta "Right of pane" .It Li "pane_search_string" Ta "" Ta "Last search string in copy mode" .It Li "pane_start_command" Ta "" Ta "Command pane started with" +.It Li "pane_start_path" Ta "" Ta "Path pane started with" .It Li "pane_synchronized" Ta "" Ta "1 if pane is synchronized" .It Li "pane_tabs" Ta "" Ta "Pane tab positions" .It Li "pane_title" Ta "#T" Ta "Title of pane (can be set by application)" -- cgit From 58c8ea120943232887b2d4be53b77009c504af06 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 30 May 2022 13:07:46 +0000 Subject: Remove duplicates from completion list, GitHub issue 3178. --- status.c | 46 ++++++++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/status.c b/status.c index 8d702de1..929276d2 100644 --- a/status.c +++ b/status.c @@ -1576,11 +1576,25 @@ status_prompt_add_history(const char *line, u_int type) status_prompt_hsize[type] = newsize; } +/* Add to completion list. */ +static void +status_prompt_add_list(char ***list, u_int *size, const char *s) +{ + u_int i; + + for (i = 0; i < *size; i++) { + if (strcmp((*list)[i], s) == 0) + return; + } + *list = xreallocarray(*list, (*size) + 1, sizeof **list); + (*list)[(*size)++] = xstrdup(s); +} + /* Build completion list. */ static char ** status_prompt_complete_list(u_int *size, const char *s, int at_start) { - char **list = NULL; + char **list = NULL, *tmp; const char **layout, *value, *cp; const struct cmd_entry **cmdent; const struct options_table_entry *oe; @@ -1594,15 +1608,11 @@ status_prompt_complete_list(u_int *size, const char *s, int at_start) *size = 0; for (cmdent = cmd_table; *cmdent != NULL; cmdent++) { - if (strncmp((*cmdent)->name, s, slen) == 0) { - list = xreallocarray(list, (*size) + 1, sizeof *list); - list[(*size)++] = xstrdup((*cmdent)->name); - } + if (strncmp((*cmdent)->name, s, slen) == 0) + status_prompt_add_list(&list, size, (*cmdent)->name); if ((*cmdent)->alias != NULL && - strncmp((*cmdent)->alias, s, slen) == 0) { - list = xreallocarray(list, (*size) + 1, sizeof *list); - list[(*size)++] = xstrdup((*cmdent)->alias); - } + strncmp((*cmdent)->alias, s, slen) == 0) + status_prompt_add_list(&list, size, (*cmdent)->alias); } o = options_get_only(global_options, "command-alias"); if (o != NULL) { @@ -1615,8 +1625,9 @@ status_prompt_complete_list(u_int *size, const char *s, int at_start) if (slen > valuelen || strncmp(value, s, slen) != 0) goto next; - list = xreallocarray(list, (*size) + 1, sizeof *list); - list[(*size)++] = xstrndup(value, valuelen); + xasprintf(&tmp, "%.*s", (int)valuelen, value); + status_prompt_add_list(&list, size, tmp); + free(tmp); next: a = options_array_next(a); @@ -1624,18 +1635,13 @@ status_prompt_complete_list(u_int *size, const char *s, int at_start) } if (at_start) return (list); - for (oe = options_table; oe->name != NULL; oe++) { - if (strncmp(oe->name, s, slen) == 0) { - list = xreallocarray(list, (*size) + 1, sizeof *list); - list[(*size)++] = xstrdup(oe->name); - } + if (strncmp(oe->name, s, slen) == 0) + status_prompt_add_list(&list, size, oe->name); } for (layout = layouts; *layout != NULL; layout++) { - if (strncmp(*layout, s, slen) == 0) { - list = xreallocarray(list, (*size) + 1, sizeof *list); - list[(*size)++] = xstrdup(*layout); - } + if (strncmp(*layout, s, slen) == 0) + status_prompt_add_list(&list, size, *layout); } return (list); } -- cgit From 86a184b288bca66ec62706f413b7c151cb0850a4 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 31 May 2022 10:22:42 +0000 Subject: Trim menu item text correctly, GitHub issue 3197. --- menu.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/menu.c b/menu.c index c17d10b1..16120bed 100644 --- a/menu.c +++ b/menu.c @@ -56,7 +56,7 @@ menu_add_item(struct menu *menu, const struct menu_item *item, { struct menu_item *new_item; const char *key = NULL, *cmd, *suffix = ""; - char *s, *name; + char *s, *trimmed, *name; u_int width, max_width; int line; size_t keylen, slen; @@ -103,11 +103,13 @@ menu_add_item(struct menu *menu, const struct menu_item *item, 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); + trimmed = format_trim_right(s, max_width); + if (key != NULL) { + xasprintf(&name, "%s%s#[default] #[align=right](%s)", + trimmed, suffix, key); + } else + xasprintf(&name, "%s%s", trimmed, suffix); + free(trimmed); new_item->name = name; free(s); -- cgit From 74fb959f6d09f60dea363cf9671c61eb3c7bf15d Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 31 May 2022 16:13:43 +0000 Subject: Add a missing space. --- cmd-server-access.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd-server-access.c b/cmd-server-access.c index 45442a62..b2b718b8 100644 --- a/cmd-server-access.c +++ b/cmd-server-access.c @@ -38,7 +38,7 @@ const struct cmd_entry cmd_server_access_entry = { .alias = NULL, .args = { "adlrw", 0, 1, NULL }, - .usage = "[-adlrw]" CMD_TARGET_PANE_USAGE " [user]", + .usage = "[-adlrw] " CMD_TARGET_PANE_USAGE " [user]", .flags = CMD_CLIENT_CANFAIL, .exec = cmd_server_access_exec -- cgit From ced83792b1b2c982f967ae7f6938fa0bbda12353 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 1 Jun 2022 08:28:32 +0100 Subject: Set version to 3.3. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 276f71c8..542ae67f 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ # configure.ac -AC_INIT([tmux], next-3.4) +AC_INIT([tmux], 3.3) AC_PREREQ([2.60]) AC_CONFIG_AUX_DIR(etc) -- cgit From 87fe00e8b44901240fc22d7120c1b31e4331f6f5 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 1 Jun 2022 08:23:40 +0100 Subject: Update CHANGES. --- CHANGES | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/CHANGES b/CHANGES index 997aad1d..73848e7d 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,25 @@ CHANGES FROM 3.2a TO 3.3 +* Add an ACL list for users connecting to the tmux socket. Users may be + forbidden from attaching, forced to attach read-only, or allowed to attach + read-write. A new command, server-access, configures the list. File system + permissions must still be configured manually. + +* Emit window-layout-changed on swap-pane. + +* Better error reporting when applying custom layouts. + +* Handle ANSI escape sequences in run-shell output. + +* Add pane_start_path to match start_command. + +* Set PWD so shells have a hint about the real path. + +* Do not allow pipe-pane on dead panes. + +* Do not report mouse positions (incorrectly) above the maximum of 223 in + normal mouse mode. + * Add an option (default off) to control the passthrough escape sequence. * Support more mouse buttons when the terminal sends them. -- cgit From b566cd57bfa0d28fcc2e44e5a7b2e88433a5e016 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 1 Jun 2022 08:50:54 +0100 Subject: Now back to 3.4. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 542ae67f..276f71c8 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ # configure.ac -AC_INIT([tmux], 3.3) +AC_INIT([tmux], next-3.4) AC_PREREQ([2.60]) AC_CONFIG_AUX_DIR(etc) -- cgit From 201a8d8e7eb0bf208918c698e64aa120864c6dfc Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 1 Jun 2022 15:43:22 +0000 Subject: If escape-time is 0, force to 1 instead - not waiting at all is asking for problems on some platforms. --- tty-keys.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tty-keys.c b/tty-keys.c index 64dd91bb..d26dbf1e 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -798,6 +798,8 @@ partial_key: /* Get the time period. */ delay = options_get_number(global_options, "escape-time"); + if (delay == 0) + delay = 1; tv.tv_sec = delay / 1000; tv.tv_usec = (delay % 1000) * 1000L; -- cgit From 0f6227f46b1d33476ef448682a2ba0b0290e6d9b Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 2 Jun 2022 20:41:21 +0000 Subject: When deleting or renaming a buffer and a buffer name is specified, complain if the buffer doesn't exist instead of silently deleting or renaming the most recent buffer. GitHub issue 3205. --- cmd-set-buffer.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/cmd-set-buffer.c b/cmd-set-buffer.c index 9112683f..c9ffe5ed 100644 --- a/cmd-set-buffer.c +++ b/cmd-set-buffer.c @@ -69,8 +69,13 @@ cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item) pb = paste_get_name(bufname); if (cmd_get_entry(self) == &cmd_delete_buffer_entry) { - if (pb == NULL) + if (pb == NULL) { + if (bufname != NULL) { + cmdq_error(item, "unknown buffer: %s", bufname); + return (CMD_RETURN_ERROR); + } pb = paste_get_top(&bufname); + } if (pb == NULL) { cmdq_error(item, "no buffer"); return (CMD_RETURN_ERROR); @@ -80,8 +85,13 @@ cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item) } if (args_has(args, 'n')) { - if (pb == NULL) + if (pb == NULL) { + if (bufname != NULL) { + cmdq_error(item, "unknown buffer: %s", bufname); + return (CMD_RETURN_ERROR); + } pb = paste_get_top(&bufname); + } if (pb == NULL) { cmdq_error(item, "no buffer"); return (CMD_RETURN_ERROR); -- cgit From be2617036ff7f53343e24101654015b875258777 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 2 Jun 2022 21:44:28 +0100 Subject: Remove extra definition of getpeereid. From Eric N Vander Weele in GitHub issue 3209. --- compat/getpeereid.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/compat/getpeereid.c b/compat/getpeereid.c index c4988438..c194e886 100644 --- a/compat/getpeereid.c +++ b/compat/getpeereid.c @@ -38,9 +38,6 @@ getpeereid(int s, uid_t *uid, gid_t *gid) *gid = uc.gid; return (0); #elif defined(HAVE_GETPEERUCRED) -int -getpeereid(int s, uid_t *uid, gid_t *gid) -{ ucred_t *ucred = NULL; if (getpeerucred(s, &ucred) == -1) @@ -51,7 +48,6 @@ getpeereid(int s, uid_t *uid, gid_t *gid) return (-1); ucred_free(ucred); return (0); -} #else return (getuid()); #endif -- cgit From 18838fbc877b5c003449fa10df353405c024f0f5 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 2 Jun 2022 21:19:32 +0000 Subject: Do not attempt to use client in config file (it will be NULL), GitHub issue 3206. --- cmd-run-shell.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd-run-shell.c b/cmd-run-shell.c index db5774d2..560eface 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -84,7 +84,7 @@ cmd_run_shell_print(struct job *job, const char *msg) if (cdata->wp_id != -1) wp = window_pane_find_by_id(cdata->wp_id); - if (wp == NULL && cdata->item != NULL) + if (wp == NULL && cdata->item != NULL && cdata->client != NULL) wp = server_client_get_pane(cdata->client); if (wp == NULL && cmd_find_from_nothing(&fs, 0) == 0) wp = fs.wp; -- cgit From 3edda3c5e71d634efd8e1e849328a68fb9da37de Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 3 Jun 2022 08:09:16 +0000 Subject: Do not unintentionally turn off all mouse mode when button is also present. --- tty.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tty.c b/tty.c index ea10e61e..49cf9795 100644 --- a/tty.c +++ b/tty.c @@ -814,7 +814,7 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s) tty_puts(tty, "\033[?1006h"); if (mode & MODE_MOUSE_ALL) tty_puts(tty, "\033[?1000h\033[?1002h\033[?1003h"); - if (mode & MODE_MOUSE_BUTTON) + else if (mode & MODE_MOUSE_BUTTON) tty_puts(tty, "\033[?1000h\033[?1002h"); else if (mode & MODE_MOUSE_STANDARD) tty_puts(tty, "\033[?1000h"); -- cgit From 020c403dff56269d93625970ed41761520afc853 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 4 Jun 2022 07:42:07 +0000 Subject: When picking a buffer because one isn't specified by the user, ignore named buffers. GitHub issue 3212 from David le Blanc. --- cmd-choose-tree.c | 2 +- paste.c | 8 ++++++++ tmux.h | 1 + 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/cmd-choose-tree.c b/cmd-choose-tree.c index 7aa1d217..f2f4b2e3 100644 --- a/cmd-choose-tree.c +++ b/cmd-choose-tree.c @@ -100,7 +100,7 @@ cmd_choose_tree_exec(struct cmd *self, struct cmdq_item *item) const struct window_mode *mode; if (cmd_get_entry(self) == &cmd_choose_buffer_entry) { - if (paste_get_top(NULL) == NULL) + if (paste_is_empty()) return (CMD_RETURN_NORMAL); mode = &window_buffer_mode; } else if (cmd_get_entry(self) == &cmd_choose_client_entry) { diff --git a/paste.c b/paste.c index 46f218d2..5ff36854 100644 --- a/paste.c +++ b/paste.c @@ -112,6 +112,12 @@ paste_walk(struct paste_buffer *pb) return (RB_NEXT(paste_time_tree, &paste_by_time, pb)); } +int +paste_is_empty(void) +{ + return RB_ROOT(&paste_by_time) == NULL; +} + /* Get the most recent automatic buffer. */ struct paste_buffer * paste_get_top(const char **name) @@ -119,6 +125,8 @@ paste_get_top(const char **name) struct paste_buffer *pb; pb = RB_MIN(paste_time_tree, &paste_by_time); + while (pb != NULL && !pb->automatic) + pb = RB_NEXT(paste_time_tree, &paste_by_time, pb); if (pb == NULL) return (NULL); if (name != NULL) diff --git a/tmux.h b/tmux.h index dd177550..33b72d0b 100644 --- a/tmux.h +++ b/tmux.h @@ -2057,6 +2057,7 @@ u_int paste_buffer_order(struct paste_buffer *); time_t paste_buffer_created(struct paste_buffer *); const char *paste_buffer_data(struct paste_buffer *, size_t *); struct paste_buffer *paste_walk(struct paste_buffer *); +int paste_is_empty(void); struct paste_buffer *paste_get_top(const char **); struct paste_buffer *paste_get_name(const char *); void paste_free(struct paste_buffer *); -- cgit From c07d582e2476db075252998388305f11302a8b23 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 7 Jun 2022 10:02:19 +0000 Subject: Expand arguments to some commands where it makes sense, GitHub issue 3204 from Anindya Mukherjee. --- arguments.c | 102 +++++++++++++++++++++++++++++++++++++++++++++++++++++ cmd-capture-pane.c | 6 ++-- cmd-join-pane.c | 36 +++++++++++-------- cmd-send-keys.c | 3 +- cmd-split-window.c | 79 +++++++++++++++-------------------------- tmux.h | 6 ++++ 6 files changed, 165 insertions(+), 67 deletions(-) diff --git a/arguments.c b/arguments.c index c94a3a29..485af2be 100644 --- a/arguments.c +++ b/arguments.c @@ -848,6 +848,41 @@ args_strtonum(struct args *args, u_char flag, long long minval, return (ll); } +/* Convert an argument value to a number, and expand formats. */ +long long +args_strtonum_and_expand(struct args *args, u_char flag, long long minval, + long long maxval, struct cmdq_item *item, char **cause) +{ + const char *errstr; + char *formatted; + long long ll; + struct args_entry *entry; + struct args_value *value; + + if ((entry = args_find(args, flag)) == NULL) { + *cause = xstrdup("missing"); + return (0); + } + value = TAILQ_LAST(&entry->values, args_values); + if (value == NULL || + value->type != ARGS_STRING || + value->string == NULL) { + *cause = xstrdup("missing"); + return (0); + } + + formatted = format_single_from_target(item, value->string); + ll = strtonum(formatted, minval, maxval, &errstr); + free(formatted); + if (errstr != NULL) { + *cause = xstrdup(errstr); + return (0); + } + + *cause = NULL; + return (ll); +} + /* Convert an argument to a number which may be a percentage. */ long long args_percentage(struct args *args, u_char flag, long long minval, @@ -904,3 +939,70 @@ args_string_percentage(const char *value, long long minval, long long maxval, *cause = NULL; return (ll); } + +/* + * Convert an argument to a number which may be a percentage, and expand + * formats. + */ +long long +args_percentage_and_expand(struct args *args, u_char flag, long long minval, + long long maxval, long long curval, struct cmdq_item *item, char **cause) +{ + const char *value; + struct args_entry *entry; + + if ((entry = args_find(args, flag)) == NULL) { + *cause = xstrdup("missing"); + return (0); + } + value = TAILQ_LAST(&entry->values, args_values)->string; + return (args_string_percentage_and_expand(value, minval, maxval, curval, + item, cause)); +} + +/* + * Convert a string to a number which may be a percentage, and expand formats. + */ +long long +args_string_percentage_and_expand(const char *value, long long minval, + long long maxval, long long curval, struct cmdq_item *item, char **cause) +{ + const char *errstr; + long long ll; + size_t valuelen = strlen(value); + char *copy, *f; + + if (value[valuelen - 1] == '%') { + copy = xstrdup(value); + copy[valuelen - 1] = '\0'; + + f = format_single_from_target(item, copy); + ll = strtonum(f, 0, 100, &errstr); + free(f); + free(copy); + if (errstr != NULL) { + *cause = xstrdup(errstr); + return (0); + } + ll = (curval * ll) / 100; + if (ll < minval) { + *cause = xstrdup("too small"); + return (0); + } + if (ll > maxval) { + *cause = xstrdup("too large"); + return (0); + } + } else { + f = format_single_from_target(item, value); + ll = strtonum(f, minval, maxval, &errstr); + free(f); + if (errstr != NULL) { + *cause = xstrdup(errstr); + return (0); + } + } + + *cause = NULL; + return (ll); +} diff --git a/cmd-capture-pane.c b/cmd-capture-pane.c index 964f831e..a3e84c46 100644 --- a/cmd-capture-pane.c +++ b/cmd-capture-pane.c @@ -133,7 +133,8 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item, if (Sflag != NULL && strcmp(Sflag, "-") == 0) top = 0; else { - n = args_strtonum(args, 'S', INT_MIN, SHRT_MAX, &cause); + n = args_strtonum_and_expand(args, 'S', INT_MIN, SHRT_MAX, + item, &cause); if (cause != NULL) { top = gd->hsize; free(cause); @@ -149,7 +150,8 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item, if (Eflag != NULL && strcmp(Eflag, "-") == 0) bottom = gd->hsize + gd->sy - 1; else { - n = args_strtonum(args, 'E', INT_MIN, SHRT_MAX, &cause); + n = args_strtonum_and_expand(args, 'E', INT_MIN, SHRT_MAX, + item, &cause); if (cause != NULL) { bottom = gd->hsize + gd->sy - 1; free(cause); diff --git a/cmd-join-pane.c b/cmd-join-pane.c index f7f16a4e..fbe9eff2 100644 --- a/cmd-join-pane.c +++ b/cmd-join-pane.c @@ -72,10 +72,11 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item) struct window *src_w, *dst_w; struct window_pane *src_wp, *dst_wp; char *cause = NULL; - int size, percentage, dst_idx; + int size, dst_idx; int flags; enum layout_type type; struct layout_cell *lc; + u_int curval = 0; dst_s = target->s; dst_wl = target->wl; @@ -98,24 +99,31 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item) if (args_has(args, 'h')) type = LAYOUT_LEFTRIGHT; - size = -1; - if (args_has(args, 'l')) { - if (type == LAYOUT_TOPBOTTOM) { - size = args_percentage(args, 'l', 0, INT_MAX, - dst_wp->sy, &cause); + /* If the 'p' flag is dropped then this bit can be moved into 'l'. */ + if (args_has(args, 'l') || args_has(args, 'p')) { + if (args_has(args, 'f')) { + if (type == LAYOUT_TOPBOTTOM) + curval = dst_w->sy; + else + curval = dst_w->sx; } else { - size = args_percentage(args, 'l', 0, INT_MAX, - dst_wp->sx, &cause); - } - } else if (args_has(args, 'p')) { - percentage = args_strtonum(args, 'p', 0, 100, &cause); - if (cause == NULL) { if (type == LAYOUT_TOPBOTTOM) - size = (dst_wp->sy * percentage) / 100; + curval = dst_wp->sy; else - size = (dst_wp->sx * percentage) / 100; + curval = dst_wp->sx; } } + + size = -1; + if (args_has(args, 'l')) { + size = args_percentage_and_expand(args, 'l', 0, INT_MAX, curval, + item, &cause); + } else if (args_has(args, 'p')) { + size = args_strtonum_and_expand(args, 'l', 0, 100, item, + &cause); + if (cause == NULL) + size = curval * size / 100; + } if (cause != NULL) { cmdq_error(item, "size %s", cause); free(cause); diff --git a/cmd-send-keys.c b/cmd-send-keys.c index d6a95434..e22d94a6 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -151,7 +151,8 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item) char *cause = NULL; if (args_has(args, 'N')) { - np = args_strtonum(args, 'N', 1, UINT_MAX, &cause); + np = args_strtonum_and_expand(args, 'N', 1, UINT_MAX, item, + &cause); if (cause != NULL) { cmdq_error(item, "repeat count %s", cause); free(cause); diff --git a/cmd-split-window.c b/cmd-split-window.c index 4c8054ec..18df715e 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -66,67 +66,46 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) enum layout_type type; struct layout_cell *lc; struct cmd_find_state fs; - int size, percentage, flags, input; - const char *template, *errstr, *p; - char *cause, *cp, *copy; - size_t plen; + int size, flags, input; + const char *template; + char *cause = NULL, *cp; struct args_value *av; - u_int count = args_count(args); + u_int count = args_count(args), curval = 0; + type = LAYOUT_TOPBOTTOM; if (args_has(args, 'h')) type = LAYOUT_LEFTRIGHT; - else - type = LAYOUT_TOPBOTTOM; - if ((p = args_get(args, 'l')) != NULL) { - plen = strlen(p); - if (p[plen - 1] == '%') { - copy = xstrdup(p); - copy[plen - 1] = '\0'; - percentage = strtonum(copy, 0, INT_MAX, &errstr); - free(copy); - if (errstr != NULL) { - cmdq_error(item, "percentage %s", errstr); - return (CMD_RETURN_ERROR); - } - if (args_has(args, 'f')) { - if (type == LAYOUT_TOPBOTTOM) - size = (w->sy * percentage) / 100; - else - size = (w->sx * percentage) / 100; - } else { - if (type == LAYOUT_TOPBOTTOM) - size = (wp->sy * percentage) / 100; - else - size = (wp->sx * percentage) / 100; - } - } else { - size = args_strtonum(args, 'l', 0, INT_MAX, &cause); - if (cause != NULL) { - cmdq_error(item, "lines %s", cause); - free(cause); - return (CMD_RETURN_ERROR); - } - } - } else if (args_has(args, 'p')) { - percentage = args_strtonum(args, 'p', 0, INT_MAX, &cause); - if (cause != NULL) { - cmdq_error(item, "create pane failed: -p %s", cause); - free(cause); - return (CMD_RETURN_ERROR); - } + + /* If the 'p' flag is dropped then this bit can be moved into 'l'. */ + if (args_has(args, 'l') || args_has(args, 'p')) { if (args_has(args, 'f')) { if (type == LAYOUT_TOPBOTTOM) - size = (w->sy * percentage) / 100; + curval = w->sy; else - size = (w->sx * percentage) / 100; + curval = w->sx; } else { if (type == LAYOUT_TOPBOTTOM) - size = (wp->sy * percentage) / 100; + curval = wp->sy; else - size = (wp->sx * percentage) / 100; + curval = wp->sx; } - } else - size = -1; + } + + size = -1; + if (args_has(args, 'l')) { + size = args_percentage_and_expand(args, 'l', 0, INT_MAX, curval, + item, &cause); + } else if (args_has(args, 'p')) { + size = args_strtonum_and_expand(args, 'l', 0, 100, item, + &cause); + if (cause == NULL) + size = curval * size / 100; + } + if (cause != NULL) { + cmdq_error(item, "size %s", cause); + free(cause); + return (CMD_RETURN_ERROR); + } window_push_zoom(wp->window, 1, args_has(args, 'Z')); input = (args_has(args, 'I') && count == 0); diff --git a/tmux.h b/tmux.h index 33b72d0b..811a4220 100644 --- a/tmux.h +++ b/tmux.h @@ -2383,10 +2383,16 @@ struct args_value *args_first_value(struct args *, u_char); struct args_value *args_next_value(struct args_value *); long long args_strtonum(struct args *, u_char, long long, long long, char **); +long long args_strtonum_and_expand(struct args *, u_char, long long, + long long, struct cmdq_item *, char **); long long args_percentage(struct args *, u_char, long long, long long, long long, char **); long long args_string_percentage(const char *, long long, long long, long long, char **); +long long args_percentage_and_expand(struct args *, u_char, long long, + long long, long long, struct cmdq_item *, char **); +long long args_string_percentage_and_expand(const char *, long long, + long long, long long, struct cmdq_item *, char **); /* cmd-find.c */ int cmd_find_target(struct cmd_find_state *, struct cmdq_item *, -- cgit From ccc9dc3bb49ac258c856d8478346b4ce829b188e Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 9 Jun 2022 09:12:55 +0000 Subject: If an application gives the first parameter to OSC 52, validate and pass on to outside terminal. GitHub issue 3192. --- cmd-load-buffer.c | 2 +- cmd-set-buffer.c | 2 +- input.c | 11 ++++++++++- screen-write.c | 4 +++- tmux.h | 6 ++++-- tty.c | 9 +++++---- window-copy.c | 4 ++-- 7 files changed, 26 insertions(+), 12 deletions(-) diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c index 59810dea..70fd7ed9 100644 --- a/cmd-load-buffer.c +++ b/cmd-load-buffer.c @@ -77,7 +77,7 @@ cmd_load_buffer_done(__unused struct client *c, const char *path, int error, } else if (tc != NULL && tc->session != NULL && (~tc->flags & CLIENT_DEAD)) - tty_set_selection(&tc->tty, copy, bsize); + tty_set_selection(&tc->tty, "", copy, bsize); if (tc != NULL) server_client_unref(tc); } diff --git a/cmd-set-buffer.c b/cmd-set-buffer.c index c9ffe5ed..35e72955 100644 --- a/cmd-set-buffer.c +++ b/cmd-set-buffer.c @@ -131,7 +131,7 @@ cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_ERROR); } if (args_has(args, 'w') && tc != NULL) - tty_set_selection(&tc->tty, bufdata, bufsize); + tty_set_selection(&tc->tty, "", bufdata, bufsize); return (CMD_RETURN_NORMAL); } diff --git a/input.c b/input.c index 693b6f32..fa9dfcdf 100644 --- a/input.c +++ b/input.c @@ -2693,6 +2693,9 @@ input_osc_52(struct input_ctx *ictx, const char *p) int outlen, state; struct screen_write_ctx ctx; struct paste_buffer *pb; + const char* allow = "cpqs01234567"; + char flags[sizeof allow] = ""; + u_int i, j = 0; if (wp == NULL) return; @@ -2707,6 +2710,12 @@ input_osc_52(struct input_ctx *ictx, const char *p) return; log_debug("%s: %s", __func__, end); + for (i = 0; p + i != end; i++) { + if (strchr(allow, p[i]) != NULL && strchr(flags, p[i]) == NULL) + flags[j++] = p[i]; + } + log_debug("%s: %.*s %s", __func__, (int)(end - p - 1), p, flags); + if (strcmp(end, "?") == 0) { if ((pb = paste_get_top(NULL)) != NULL) buf = paste_buffer_data(pb, &len); @@ -2728,7 +2737,7 @@ input_osc_52(struct input_ctx *ictx, const char *p) } screen_write_start_pane(&ctx, wp, NULL); - screen_write_setselection(&ctx, out, outlen); + screen_write_setselection(&ctx, flags, out, outlen); screen_write_stop(&ctx); notify_pane("pane-set-clipboard", wp); diff --git a/screen-write.c b/screen-write.c index 6b6a750e..213b533c 100644 --- a/screen-write.c +++ b/screen-write.c @@ -2085,12 +2085,14 @@ screen_write_overwrite(struct screen_write_ctx *ctx, struct grid_cell *gc, /* Set external clipboard. */ void -screen_write_setselection(struct screen_write_ctx *ctx, u_char *str, u_int len) +screen_write_setselection(struct screen_write_ctx *ctx, const char *flags, + u_char *str, u_int len) { struct tty_ctx ttyctx; screen_write_initctx(ctx, &ttyctx, 0); ttyctx.ptr = str; + ttyctx.ptr2 = (void *)flags; ttyctx.num = len; tty_write(tty_cmd_setselection, &ttyctx); diff --git a/tmux.h b/tmux.h index 811a4220..0c05dcec 100644 --- a/tmux.h +++ b/tmux.h @@ -1421,6 +1421,7 @@ struct tty_ctx { u_int num; void *ptr; + void *ptr2; /* * Cursor and region position before the screen was updated - this is @@ -2278,7 +2279,7 @@ int tty_open(struct tty *, char **); void tty_close(struct tty *); void tty_free(struct tty *); void tty_update_features(struct tty *); -void tty_set_selection(struct tty *, const char *, size_t); +void tty_set_selection(struct tty *, const char *, const char *, size_t); void tty_write(void (*)(struct tty *, const struct tty_ctx *), struct tty_ctx *); void tty_cmd_alignmenttest(struct tty *, const struct tty_ctx *); @@ -2872,7 +2873,8 @@ void screen_write_collect_end(struct screen_write_ctx *); void screen_write_collect_add(struct screen_write_ctx *, const struct grid_cell *); void screen_write_cell(struct screen_write_ctx *, const struct grid_cell *); -void screen_write_setselection(struct screen_write_ctx *, u_char *, u_int); +void screen_write_setselection(struct screen_write_ctx *, const char *, + u_char *, u_int); void screen_write_rawstring(struct screen_write_ctx *, u_char *, u_int); void screen_write_alternateon(struct screen_write_ctx *, struct grid_cell *, int); diff --git a/tty.c b/tty.c index 49cf9795..7e0a6a3e 100644 --- a/tty.c +++ b/tty.c @@ -671,7 +671,7 @@ static void tty_force_cursor_colour(struct tty *tty, int c) { u_char r, g, b; - char s[13] = ""; + char s[13]; if (c != -1) c = colour_force_rgb(c); @@ -2082,11 +2082,12 @@ tty_cmd_cells(struct tty *tty, const struct tty_ctx *ctx) void tty_cmd_setselection(struct tty *tty, const struct tty_ctx *ctx) { - tty_set_selection(tty, ctx->ptr, ctx->num); + tty_set_selection(tty, ctx->ptr2, ctx->ptr, ctx->num); } void -tty_set_selection(struct tty *tty, const char *buf, size_t len) +tty_set_selection(struct tty *tty, const char *flags, const char *buf, + size_t len) { char *encoded; size_t size; @@ -2101,7 +2102,7 @@ tty_set_selection(struct tty *tty, const char *buf, size_t len) b64_ntop(buf, len, encoded, size); tty->flags |= TTY_NOBLOCK; - tty_putcode_ptr2(tty, TTYC_MS, "", encoded); + tty_putcode_ptr2(tty, TTYC_MS, flags, encoded); free(encoded); } diff --git a/window-copy.c b/window-copy.c index 03070556..09304218 100644 --- a/window-copy.c +++ b/window-copy.c @@ -4570,7 +4570,7 @@ window_copy_copy_buffer(struct window_mode_entry *wme, const char *prefix, if (options_get_number(global_options, "set-clipboard") != 0) { screen_write_start_pane(&ctx, wp, NULL); - screen_write_setselection(&ctx, buf, len); + screen_write_setselection(&ctx, "", buf, len); screen_write_stop(&ctx); notify_pane("pane-set-clipboard", wp); } @@ -4644,7 +4644,7 @@ window_copy_append_selection(struct window_mode_entry *wme) if (options_get_number(global_options, "set-clipboard") != 0) { screen_write_start_pane(&ctx, wp, NULL); - screen_write_setselection(&ctx, buf, len); + screen_write_setselection(&ctx, "", buf, len); screen_write_stop(&ctx); notify_pane("pane-set-clipboard", wp); } -- cgit From 30e06e9d85a54648dd11b107f467c608f153f62c Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 3 Jun 2022 08:09:16 +0000 Subject: Do not unintentionally turn off all mouse mode when button is also present. --- tty.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tty.c b/tty.c index ea10e61e..49cf9795 100644 --- a/tty.c +++ b/tty.c @@ -814,7 +814,7 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s) tty_puts(tty, "\033[?1006h"); if (mode & MODE_MOUSE_ALL) tty_puts(tty, "\033[?1000h\033[?1002h\033[?1003h"); - if (mode & MODE_MOUSE_BUTTON) + else if (mode & MODE_MOUSE_BUTTON) tty_puts(tty, "\033[?1000h\033[?1002h"); else if (mode & MODE_MOUSE_STANDARD) tty_puts(tty, "\033[?1000h"); -- cgit From 988e59cf3e0354209c0d068b244fb96bf1f0183f Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 2 Jun 2022 21:19:32 +0000 Subject: Do not attempt to use client in config file (it will be NULL), GitHub issue 3206. --- cmd-run-shell.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd-run-shell.c b/cmd-run-shell.c index db5774d2..560eface 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -84,7 +84,7 @@ cmd_run_shell_print(struct job *job, const char *msg) if (cdata->wp_id != -1) wp = window_pane_find_by_id(cdata->wp_id); - if (wp == NULL && cdata->item != NULL) + if (wp == NULL && cdata->item != NULL && cdata->client != NULL) wp = server_client_get_pane(cdata->client); if (wp == NULL && cmd_find_from_nothing(&fs, 0) == 0) wp = fs.wp; -- cgit From 6a9bb2a6224958474469422e3b9d667821d0df74 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 2 Jun 2022 21:44:28 +0100 Subject: Remove extra definition of getpeereid. From Eric N Vander Weele in GitHub issue 3209. --- compat/getpeereid.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/compat/getpeereid.c b/compat/getpeereid.c index c4988438..c194e886 100644 --- a/compat/getpeereid.c +++ b/compat/getpeereid.c @@ -38,9 +38,6 @@ getpeereid(int s, uid_t *uid, gid_t *gid) *gid = uc.gid; return (0); #elif defined(HAVE_GETPEERUCRED) -int -getpeereid(int s, uid_t *uid, gid_t *gid) -{ ucred_t *ucred = NULL; if (getpeerucred(s, &ucred) == -1) @@ -51,7 +48,6 @@ getpeereid(int s, uid_t *uid, gid_t *gid) return (-1); ucred_free(ucred); return (0); -} #else return (getuid()); #endif -- cgit From 0b355ae8114511e1ff6359272b164f1cdf718e80 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 9 Jun 2022 12:30:50 +0100 Subject: 3.3a. --- CHANGES | 7 +++++++ configure.ac | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 73848e7d..b72d1ec4 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,10 @@ +CHANGES FROM 3.3 TO 3.3a + +* Do not crash when run-shell produces output from a config file. + +* Do not unintentionally turn off all mouse mode when button mode is also + present. + CHANGES FROM 3.2a TO 3.3 * Add an ACL list for users connecting to the tmux socket. Users may be diff --git a/configure.ac b/configure.ac index 542ae67f..2b8b3b11 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ # configure.ac -AC_INIT([tmux], 3.3) +AC_INIT([tmux], 3.3a) AC_PREREQ([2.60]) AC_CONFIG_AUX_DIR(etc) -- cgit From 18a5835affc3fef58f673a8feeb128cf4132525e Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 10 Jun 2022 11:55:30 +0000 Subject: Ignore OSC if the first argument is not properly terminated. --- input.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/input.c b/input.c index fa9dfcdf..05654124 100644 --- a/input.c +++ b/input.c @@ -2292,6 +2292,8 @@ input_exit_osc(struct input_ctx *ictx) option = 0; while (*p >= '0' && *p <= '9') option = option * 10 + *p++ - '0'; + if (*p != ';' && *p != '\0') + return; if (*p == ';') p++; -- cgit From 42ddf02ffce2002ed5ded5e03e1a51516fc2d710 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 11 Jun 2022 16:59:33 +0000 Subject: Fix size of flags output buffer. --- input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/input.c b/input.c index 05654124..d4ffe784 100644 --- a/input.c +++ b/input.c @@ -2696,7 +2696,7 @@ input_osc_52(struct input_ctx *ictx, const char *p) struct screen_write_ctx ctx; struct paste_buffer *pb; const char* allow = "cpqs01234567"; - char flags[sizeof allow] = ""; + char flags[sizeof "cpqs01234567"] = ""; u_int i, j = 0; if (wp == NULL) -- cgit From 616bde08ac74d4be0ae06087aa3103df54833f86 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 14 Jun 2022 07:29:00 +0000 Subject: kf* terminfo capabilities are poorly defined and rxvt uses them in a different way from xterm, so add a feature flag for rxvt to make tmux ignore the capabilities and instead rely on its builtin definitions. --- options-table.c | 3 ++- tmux.1 | 6 +++++ tty-features.c | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- tty-keys.c | 61 ++++++++++++++++++++------------------------- 4 files changed, 111 insertions(+), 36 deletions(-) diff --git a/options-table.c b/options-table.c index 4e68af00..d1e65f15 100644 --- a/options-table.c +++ b/options-table.c @@ -363,7 +363,8 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_SERVER, .flags = OPTIONS_TABLE_IS_ARRAY, .default_str = "xterm*:clipboard:ccolour:cstyle:focus:title," - "screen*:title", + "screen*:title," + "rxvt*:ignorefkeys", .separator = ",", .text = "List of terminal features, used if they cannot be " "automatically detected." diff --git a/tmux.1 b/tmux.1 index c58597c6..f06a42a4 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3656,6 +3656,12 @@ Allows setting the cursor style. Supports extended keys. .It focus Supports focus reporting. +.It ignorefkeys +Ignore function keys from +.Xr terminfo 5 +and use the +.Nm +internal set only. .It margins Supports DECSLRM margins. .It mouse diff --git a/tty-features.c b/tty-features.c index 2848b4d6..477925e3 100644 --- a/tty-features.c +++ b/tty-features.c @@ -250,6 +250,80 @@ static const struct tty_feature tty_feature_rectfill = { TERM_DECFRA }; +/* Use builtin function keys only. */ +static const char *tty_feature_ignorefkeys_capabilities[] = { + "kf0@", + "kf1@", + "kf2@", + "kf3@", + "kf4@", + "kf5@", + "kf6@", + "kf7@", + "kf8@", + "kf9@", + "kf10@", + "kf11@", + "kf12@", + "kf13@", + "kf14@", + "kf15@", + "kf16@", + "kf17@", + "kf18@", + "kf19@", + "kf20@", + "kf21@", + "kf22@", + "kf23@", + "kf24@", + "kf25@", + "kf26@", + "kf27@", + "kf28@", + "kf29@", + "kf30@", + "kf31@", + "kf32@", + "kf33@", + "kf34@", + "kf35@", + "kf36@", + "kf37@", + "kf38@", + "kf39@", + "kf40@", + "kf41@", + "kf42@", + "kf43@", + "kf44@", + "kf45@", + "kf46@", + "kf47@", + "kf48@", + "kf49@", + "kf50@", + "kf51@", + "kf52@", + "kf53@", + "kf54@", + "kf55@", + "kf56@", + "kf57@", + "kf58@", + "kf59@", + "kf60@", + "kf61@", + "kf62@", + "kf63@", + NULL +}; +static const struct tty_feature tty_feature_ignorefkeys = { + "ignorefkeys", + tty_feature_ignorefkeys_capabilities, + 0 +}; + /* Available terminal features. */ static const struct tty_feature *tty_features[] = { &tty_feature_256, @@ -259,6 +333,7 @@ static const struct tty_feature *tty_features[] = { &tty_feature_cstyle, &tty_feature_extkeys, &tty_feature_focus, + &tty_feature_ignorefkeys, &tty_feature_margins, &tty_feature_mouse, &tty_feature_osc7, @@ -372,7 +447,7 @@ tty_default_features(int *feat, const char *name, u_int version) ",ccolour,cstyle,focus,overline,usstyle" }, { .name = "rxvt-unicode", - .features = "256,bpaste,ccolour,cstyle,mouse,title" + .features = "256,bpaste,ccolour,cstyle,mouse,title,ignorefkeys" }, { .name = "iTerm2", .features = TTY_FEATURES_BASE_MODERN_XTERM diff --git a/tty-keys.c b/tty-keys.c index d26dbf1e..4a712d42 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -126,7 +126,7 @@ static const struct tty_default_key_raw tty_default_raw_keys[] = { { "\033\033[C", KEYC_RIGHT|KEYC_CURSOR|KEYC_META }, { "\033\033[D", KEYC_LEFT|KEYC_CURSOR|KEYC_META }, - /* Other (xterm) "cursor" keys. */ + /* Other xterm keys. */ { "\033OH", KEYC_HOME }, { "\033OF", KEYC_END }, @@ -139,7 +139,7 @@ static const struct tty_default_key_raw tty_default_raw_keys[] = { { "\033\033[H", KEYC_HOME|KEYC_META|KEYC_IMPLIED_META }, { "\033\033[F", KEYC_END|KEYC_META|KEYC_IMPLIED_META }, - /* rxvt-style arrow + modifier keys. */ + /* rxvt arrow keys. */ { "\033Oa", KEYC_UP|KEYC_CTRL }, { "\033Ob", KEYC_DOWN|KEYC_CTRL }, { "\033Oc", KEYC_RIGHT|KEYC_CTRL }, @@ -150,7 +150,31 @@ static const struct tty_default_key_raw tty_default_raw_keys[] = { { "\033[c", KEYC_RIGHT|KEYC_SHIFT }, { "\033[d", KEYC_LEFT|KEYC_SHIFT }, - /* rxvt-style function + modifier keys (C = ^, S = $, C-S = @). */ + /* rxvt function keys. */ + { "\033[11~", KEYC_F1 }, + { "\033[12~", KEYC_F2 }, + { "\033[13~", KEYC_F3 }, + { "\033[14~", KEYC_F4 }, + { "\033[15~", KEYC_F5 }, + { "\033[17~", KEYC_F6 }, + { "\033[18~", KEYC_F7 }, + { "\033[19~", KEYC_F8 }, + { "\033[20~", KEYC_F9 }, + { "\033[21~", KEYC_F10 }, + + { "\033[23~", KEYC_F1|KEYC_SHIFT }, + { "\033[24~", KEYC_F2|KEYC_SHIFT }, + { "\033[25~", KEYC_F3|KEYC_SHIFT }, + { "\033[26~", KEYC_F4|KEYC_SHIFT }, + { "\033[28~", KEYC_F5|KEYC_SHIFT }, + { "\033[29~", KEYC_F6|KEYC_SHIFT }, + { "\033[31~", KEYC_F7|KEYC_SHIFT }, + { "\033[32~", KEYC_F8|KEYC_SHIFT }, + { "\033[33~", KEYC_F9|KEYC_SHIFT }, + { "\033[34~", KEYC_F10|KEYC_SHIFT }, + { "\033[23$", KEYC_F11|KEYC_SHIFT }, + { "\033[24$", KEYC_F12|KEYC_SHIFT }, + { "\033[11^", KEYC_F1|KEYC_CTRL }, { "\033[12^", KEYC_F2|KEYC_CTRL }, { "\033[13^", KEYC_F3|KEYC_CTRL }, @@ -163,31 +187,6 @@ static const struct tty_default_key_raw tty_default_raw_keys[] = { { "\033[21^", KEYC_F10|KEYC_CTRL }, { "\033[23^", KEYC_F11|KEYC_CTRL }, { "\033[24^", KEYC_F12|KEYC_CTRL }, - { "\033[2^", KEYC_IC|KEYC_CTRL }, - { "\033[3^", KEYC_DC|KEYC_CTRL }, - { "\033[7^", KEYC_HOME|KEYC_CTRL }, - { "\033[8^", KEYC_END|KEYC_CTRL }, - { "\033[6^", KEYC_NPAGE|KEYC_CTRL }, - { "\033[5^", KEYC_PPAGE|KEYC_CTRL }, - - { "\033[11$", KEYC_F1|KEYC_SHIFT }, - { "\033[12$", KEYC_F2|KEYC_SHIFT }, - { "\033[13$", KEYC_F3|KEYC_SHIFT }, - { "\033[14$", KEYC_F4|KEYC_SHIFT }, - { "\033[15$", KEYC_F5|KEYC_SHIFT }, - { "\033[17$", KEYC_F6|KEYC_SHIFT }, - { "\033[18$", KEYC_F7|KEYC_SHIFT }, - { "\033[19$", KEYC_F8|KEYC_SHIFT }, - { "\033[20$", KEYC_F9|KEYC_SHIFT }, - { "\033[21$", KEYC_F10|KEYC_SHIFT }, - { "\033[23$", KEYC_F11|KEYC_SHIFT }, - { "\033[24$", KEYC_F12|KEYC_SHIFT }, - { "\033[2$", KEYC_IC|KEYC_SHIFT }, - { "\033[3$", KEYC_DC|KEYC_SHIFT }, - { "\033[7$", KEYC_HOME|KEYC_SHIFT }, - { "\033[8$", KEYC_END|KEYC_SHIFT }, - { "\033[6$", KEYC_NPAGE|KEYC_SHIFT }, - { "\033[5$", KEYC_PPAGE|KEYC_SHIFT }, { "\033[11@", KEYC_F1|KEYC_CTRL|KEYC_SHIFT }, { "\033[12@", KEYC_F2|KEYC_CTRL|KEYC_SHIFT }, @@ -201,12 +200,6 @@ static const struct tty_default_key_raw tty_default_raw_keys[] = { { "\033[21@", KEYC_F10|KEYC_CTRL|KEYC_SHIFT }, { "\033[23@", KEYC_F11|KEYC_CTRL|KEYC_SHIFT }, { "\033[24@", KEYC_F12|KEYC_CTRL|KEYC_SHIFT }, - { "\033[2@", KEYC_IC|KEYC_CTRL|KEYC_SHIFT }, - { "\033[3@", KEYC_DC|KEYC_CTRL|KEYC_SHIFT }, - { "\033[7@", KEYC_HOME|KEYC_CTRL|KEYC_SHIFT }, - { "\033[8@", KEYC_END|KEYC_CTRL|KEYC_SHIFT }, - { "\033[6@", KEYC_NPAGE|KEYC_CTRL|KEYC_SHIFT }, - { "\033[5@", KEYC_PPAGE|KEYC_CTRL|KEYC_SHIFT }, /* Focus tracking. */ { "\033[I", KEYC_FOCUS_IN }, -- cgit From 42358cc5210422826c8215a4411ddde1d19ddbeb Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 15 Jun 2022 08:00:58 +0100 Subject: Typos from Bastian Venthur. --- CHANGES | 2 +- tools/ansicode.txt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index b72d1ec4..e5942216 100644 --- a/CHANGES +++ b/CHANGES @@ -1302,7 +1302,7 @@ Incompatible Changes bind -Tcopy-mode C-r command-prompt -i -p'search up' "send -X search-backward-incremental '%%'" - There are also some new commmands available with send -X, such as + There are also some new commands available with send -X, such as copy-pipe-and-cancel. * set-remain-on-exit has gone -- can be achieved with hooks instead. * Hooks: before hooks have been removed and only a selection of commands now diff --git a/tools/ansicode.txt b/tools/ansicode.txt index e77d31ae..a9c81a96 100644 --- a/tools/ansicode.txt +++ b/tools/ansicode.txt @@ -268,7 +268,7 @@ Oct Hex * (* marks function used in DEC VT series or LA series terminals) 073 3B ; 074 3C < * DECANSI - Switch from VT52 mode to VT100 mode 075 3D = * DECKPAM - Set keypad to applications mode (ESCape instead of digits) -076 3E > * DECKPNM - Set keypad to numeric mode (digits intead of ESCape seq) +076 3E > * DECKPNM - Set keypad to numeric mode (digits instead of ESCape seq) 077 3F ? DCS Device Control Strings used by DEC terminals (ends with ST) @@ -449,7 +449,7 @@ Oct Hex * (* marks function used in DEC VT series or LA series terminals) * [16h = TTM - Transmit Termination Mode, send scrolling region [17h = SATM - Send Area Transmit Mode, send entire buffer [18h = TSM - Tabulation Stop Mode, lines are independent - [19h = EBM - Editing Boundry Mode, all of memory affected + [19h = EBM - Editing Boundary Mode, all of memory affected * [20h = LNM - Linefeed Newline Mode, LF interpreted as CR LF * [?1h = DECCKM - Cursor Keys Mode, send ESC O A for cursor up * [?2h = DECANM - ANSI Mode, use ESC < to switch VT52 to ANSI -- cgit From 7cee982f909d29e7331d35bd9c21d337688b9ea1 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 16 Jun 2022 13:27:39 +0000 Subject: Keep cursor on selected item on menu (useful for blind people), GitHub issue 3225. --- menu.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/menu.c b/menu.c index 16120bed..dc3b289f 100644 --- a/menu.c +++ b/menu.c @@ -160,11 +160,16 @@ menu_free(struct menu *menu) } struct screen * -menu_mode_cb(__unused struct client *c, void *data, __unused u_int *cx, - __unused u_int *cy) +menu_mode_cb(__unused struct client *c, void *data, u_int *cx, u_int *cy) { struct menu_data *md = data; + *cx = md->px + 2; + if (md->choice == -1) + *cy = md->py; + else + *cy = md->py + 1 + md->choice; + return (&md->s); } -- cgit From d9f84854ac01c8d4f6d5507e88d1dc7bcdd99558 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 17 Jun 2022 07:28:05 +0000 Subject: Check cursor options when a pane is created, not just when they are changed. --- options.c | 15 ++++----------- tmux.h | 1 + window.c | 15 +++++++++++++++ 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/options.c b/options.c index f9cf2afd..fef5637e 100644 --- a/options.c +++ b/options.c @@ -1106,7 +1106,6 @@ options_push_changes(const char *name) struct session *s; struct window *w; struct window_pane *wp; - int c; log_debug("%s: %s", __func__, name); @@ -1119,18 +1118,12 @@ options_push_changes(const char *name) } } 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; - } + RB_FOREACH(wp, window_pane_tree, &all_window_panes) + window_pane_default_cursor(wp); } 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); - } + RB_FOREACH(wp, window_pane_tree, &all_window_panes) + window_pane_default_cursor(wp); } if (strcmp(name, "fill-character") == 0) { RB_FOREACH(w, windows, &windows) diff --git a/tmux.h b/tmux.h index 0c05dcec..ed347c80 100644 --- a/tmux.h +++ b/tmux.h @@ -2997,6 +2997,7 @@ void *window_pane_get_new_data(struct window_pane *, void window_pane_update_used_data(struct window_pane *, struct window_pane_offset *, size_t); void window_set_fill_character(struct window *); +void window_pane_default_cursor(struct window_pane *); /* layout.c */ u_int layout_count_cells(struct layout_cell *); diff --git a/window.c b/window.c index f14c3bc7..2be5d5e6 100644 --- a/window.c +++ b/window.c @@ -933,6 +933,7 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) screen_init(&wp->base, sx, sy, hlimit); wp->screen = &wp->base; + window_pane_default_cursor(wp); screen_init(&wp->status_screen, 1, 1, 0); @@ -1608,3 +1609,17 @@ window_set_fill_character(struct window *w) w->fill_character = ud; } } + +void +window_pane_default_cursor(struct window_pane *wp) +{ + struct screen *s = wp->screen; + int c; + + c = options_get_number(wp->options, "cursor-colour"); + s->default_ccolour = c; + + c = options_get_number(wp->options, "cursor-style"); + s->default_mode = 0; + screen_set_cursor_style(c, &s->default_cstyle, &s->default_mode); +} -- cgit From a888ce9963053c790c6ee9bf64cc53d95f0f9c09 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 20 Jun 2022 07:59:37 +0000 Subject: Do not display configuration file errors in a pane when in control mode, instead report them with a %config-error notification. GitHub issue 3193. --- cfg.c | 24 +++++++++++++++++++++--- cmd-new-session.c | 2 +- tmux.1 | 2 ++ 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/cfg.c b/cfg.c index d32e5ff1..e92ce36f 100644 --- a/cfg.c +++ b/cfg.c @@ -52,8 +52,7 @@ cfg_done(__unused struct cmdq_item *item, __unused void *data) return (CMD_RETURN_NORMAL); cfg_finished = 1; - if (!RB_EMPTY(&sessions)) - cfg_show_causes(RB_MIN(sessions, &sessions)); + cfg_show_causes(NULL); if (cfg_item != NULL) cmdq_continue(cfg_item); @@ -239,11 +238,29 @@ cfg_print_causes(struct cmdq_item *item) void cfg_show_causes(struct session *s) { + struct client *c = TAILQ_FIRST(&clients); struct window_pane *wp; struct window_mode_entry *wme; u_int i; - if (s == NULL || cfg_ncauses == 0) + if (cfg_ncauses == 0) + return; + + if (c != NULL && (c->flags & CLIENT_CONTROL)) { + for (i = 0; i < cfg_ncauses; i++) { + control_write(c, "%%config-error %s", cfg_causes[i]); + free(cfg_causes[i]); + } + goto out; + } + + if (s == NULL) { + if (c != NULL && c->session != NULL) + s = c->session; + else + s = RB_MIN(sessions, &sessions); + } + if (s == NULL || s->attached == 0) /* wait for an attached session */ return; wp = s->curw->window->active; @@ -255,6 +272,7 @@ cfg_show_causes(struct session *s) free(cfg_causes[i]); } +out: free(cfg_causes); cfg_causes = NULL; cfg_ncauses = 0; diff --git a/cmd-new-session.c b/cmd-new-session.c index cb9abfb3..b946f049 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -335,7 +335,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) /* * If there are still configuration file errors to display, put the new - * session's current window into more mode and display them now. + * session's current window into view mode and display them now. */ if (cfg_finished) cfg_show_causes(s); diff --git a/tmux.1 b/tmux.1 index f06a42a4..44b96eb7 100644 --- a/tmux.1 +++ b/tmux.1 @@ -6529,6 +6529,8 @@ The client is now attached to the session with ID .Ar session-id , which is named .Ar name . +.It Ic %config-error Ar error +An error has happened in a configuration file. .It Ic %continue Ar pane-id The pane has been continued after being paused (if the .Ar pause-after -- cgit From 9c89f7c2af748858e784e8c533c548460bd6b10e Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 21 Jun 2022 09:30:01 +0000 Subject: Store time lines are scrolled into history and display in copy mode. --- format.c | 13 ++++++++----- grid.c | 2 ++ server.c | 4 ++++ tmux.h | 3 +++ window-copy.c | 33 ++++++++++++++++++++------------- 5 files changed, 37 insertions(+), 18 deletions(-) diff --git a/format.c b/format.c index d085e348..69d8ef34 100644 --- a/format.c +++ b/format.c @@ -3387,12 +3387,12 @@ format_quote_style(const char *s) } /* Make a prettier time. */ -static char * -format_pretty_time(time_t t) +char * +format_pretty_time(time_t t, int seconds) { struct tm now_tm, tm; time_t now, age; - char s[6]; + char s[9]; time(&now); if (now < t) @@ -3404,7 +3404,10 @@ format_pretty_time(time_t t) /* Last 24 hours. */ if (age < 24 * 3600) { - strftime(s, sizeof s, "%H:%M", &tm); + if (seconds) + strftime(s, sizeof s, "%H:%M:%S", &tm); + else + strftime(s, sizeof s, "%H:%M", &tm); return (xstrdup(s)); } @@ -3509,7 +3512,7 @@ found: if (t == 0) return (NULL); if (modifiers & FORMAT_PRETTY) - found = format_pretty_time(t); + found = format_pretty_time(t, 0); else { if (time_format != NULL) { localtime_r(&t, &tm); diff --git a/grid.c b/grid.c index 1109ac58..ba251e70 100644 --- a/grid.c +++ b/grid.c @@ -399,6 +399,7 @@ grid_scroll_history(struct grid *gd, u_int bg) gd->hscrolled++; grid_compact_line(&gd->linedata[gd->hsize]); + gd->linedata[gd->hsize].time = current_time; gd->hsize++; } @@ -438,6 +439,7 @@ grid_scroll_history_region(struct grid *gd, u_int upper, u_int lower, u_int bg) /* Move the line into the history. */ memcpy(gl_history, gl_upper, sizeof *gl_history); + gl_history->time = current_time; /* Then move the region up and clear the bottom line. */ memmove(gl_upper, gl_upper + 1, (lower - upper) * sizeof *gl_upper); diff --git a/server.c b/server.c index 3a2580a9..5cbe3921 100644 --- a/server.c +++ b/server.c @@ -55,6 +55,8 @@ struct cmd_find_state marked_pane; static u_int message_next; struct message_list message_log; +time_t current_time; + static int server_loop(void); static void server_send_exit(void); static void server_accept(int, short, void *); @@ -257,6 +259,8 @@ server_loop(void) struct client *c; u_int items; + current_time = time (NULL); + do { items = cmdq_next(NULL); TAILQ_FOREACH(c, &clients, entry) { diff --git a/tmux.h b/tmux.h index ed347c80..c77b77c7 100644 --- a/tmux.h +++ b/tmux.h @@ -725,6 +725,7 @@ struct grid_line { u_int extdsize; int flags; + time_t time; }; /* Entire grid of cells. */ @@ -2095,6 +2096,7 @@ void format_add_cb(struct format_tree *, const char *, format_cb); void format_log_debug(struct format_tree *, const char *); void format_each(struct format_tree *, void (*)(const char *, const char *, void *), void *); +char *format_pretty_time(time_t, int); char *format_expand_time(struct format_tree *, const char *); char *format_expand(struct format_tree *, const char *); char *format_single(struct cmdq_item *, const char *, @@ -2587,6 +2589,7 @@ extern struct tmuxproc *server_proc; extern struct clients clients; extern struct cmd_find_state marked_pane; extern struct message_list message_log; +extern time_t current_time; void server_set_marked(struct session *, struct winlink *, struct window_pane *); void server_clear_marked(void); diff --git a/window-copy.c b/window-copy.c index 09304218..b955d222 100644 --- a/window-copy.c +++ b/window-copy.c @@ -4092,8 +4092,9 @@ window_copy_write_line(struct window_mode_entry *wme, struct window_copy_mode_data *data = wme->data; struct screen *s = &data->screen; struct options *oo = wp->window->options; + struct grid_line *gl; struct grid_cell gc, mgc, cgc, mkgc; - char hdr[512]; + char hdr[512], tmp[256], *t; size_t size = 0; u_int hsize = screen_hsize(data->backing); @@ -4107,23 +4108,29 @@ window_copy_write_line(struct window_mode_entry *wme, mkgc.flags |= GRID_FLAG_NOPALETTE; if (py == 0 && s->rupper < s->rlower && !data->hide_position) { + gl = grid_get_line(data->backing->grid, hsize - data->oy); + if (gl->time == 0) + xsnprintf(tmp, sizeof tmp, "[%u/%u]", data->oy, hsize); + else { + t = format_pretty_time(gl->time, 1); + xsnprintf(tmp, sizeof tmp, "%s [%u/%u]", t, data->oy, + hsize); + free(t); + } + if (data->searchmark == NULL) { if (data->timeout) { size = xsnprintf(hdr, sizeof hdr, - "(timed out) [%u/%u]", data->oy, hsize); - } else { - size = xsnprintf(hdr, sizeof hdr, - "[%u/%u]", data->oy, hsize); - } + "(timed out) %s", tmp); + } else + size = xsnprintf(hdr, sizeof hdr, "%s", tmp); } else { - if (data->searchcount == -1) { - size = xsnprintf(hdr, sizeof hdr, - "[%u/%u]", data->oy, hsize); - } else { + if (data->searchcount == -1) + size = xsnprintf(hdr, sizeof hdr, "%s", tmp); + else { size = xsnprintf(hdr, sizeof hdr, - "(%d%s results) [%u/%u]", data->searchcount, - data->searchmore ? "+" : "", data->oy, - hsize); + "(%d%s results) %s", data->searchcount, + data->searchmore ? "+" : "", tmp); } } if (size > screen_size_x(s)) -- cgit From 786cff8db9dd64ec8143a492c63051582ee41288 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 27 Jun 2022 09:14:49 +0000 Subject: Do not expand single character format aliases inside #[] since they interfere with colours. GitHub issue 3239 from Magnus Gross. --- format.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/format.c b/format.c index 69d8ef34..e10f324f 100644 --- a/format.c +++ b/format.c @@ -4606,7 +4606,7 @@ format_expand1(struct format_expand_state *es, const char *fmt) { struct format_tree *ft = es->ft; char *buf, *out, *name; - const char *ptr, *s; + const char *ptr, *s, *style_end = NULL; size_t off, len, n, outlen; int ch, brackets; char expanded[8192]; @@ -4701,13 +4701,14 @@ format_expand1(struct format_expand_state *es, const char *fmt) break; fmt += n + 1; continue; + case '[': case '#': /* * If ##[ (with two or more #s), then it is a style and * can be left for format_draw to handle. */ - ptr = fmt; - n = 2; + ptr = fmt - (ch == '['); + n = 2 - (ch == '['); while (*ptr == '#') { ptr++; n++; @@ -4721,6 +4722,7 @@ format_expand1(struct format_expand_state *es, const char *fmt) memcpy(buf + off, fmt - 2, n + 1); off += n + 1; fmt = ptr + 1; + style_end = format_skip(fmt - 2, "]"); continue; } /* FALLTHROUGH */ @@ -4735,10 +4737,12 @@ format_expand1(struct format_expand_state *es, const char *fmt) continue; default: s = NULL; - if (ch >= 'A' && ch <= 'Z') - s = format_upper[ch - 'A']; - else if (ch >= 'a' && ch <= 'z') - s = format_lower[ch - 'a']; + if (fmt > style_end) { /* skip inside #[] */ + if (ch >= 'A' && ch <= 'Z') + s = format_upper[ch - 'A']; + else if (ch >= 'a' && ch <= 'z') + s = format_lower[ch - 'a']; + } if (s == NULL) { while (len - off < 3) { buf = xreallocarray(buf, 2, len); -- cgit From b22edcf3a5e6271625141992c1a842d295c8b89f Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 27 Jun 2022 09:16:54 +0000 Subject: Tweak previous - find end of style correctly. --- format.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/format.c b/format.c index e10f324f..b97c842c 100644 --- a/format.c +++ b/format.c @@ -4714,6 +4714,7 @@ format_expand1(struct format_expand_state *es, const char *fmt) n++; } if (*ptr == '[') { + style_end = format_skip(fmt - 2, "]"); format_log(es, "found #*%zu[", n); while (len - off < n + 2) { buf = xreallocarray(buf, 2, len); @@ -4722,7 +4723,6 @@ format_expand1(struct format_expand_state *es, const char *fmt) memcpy(buf + off, fmt - 2, n + 1); off += n + 1; fmt = ptr + 1; - style_end = format_skip(fmt - 2, "]"); continue; } /* FALLTHROUGH */ -- cgit From cdacc12ce305ad2f3e65e2a01328a988e3200b51 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 30 Jun 2022 09:55:53 +0000 Subject: Add support for OSC 8 hyperlinks (a VTE extension now supported by other terminals such as iTerm2). Originally written by me then extended and completed by first Will Noble and later Jeff Chiang. GitHub issues 911, 2621, 2890, 3240. --- Makefile | 1 + cmd-capture-pane.c | 6 +- cmd-display-panes.c | 6 +- grid.c | 13 ++- hyperlinks.c | 225 ++++++++++++++++++++++++++++++++++++++++++++++++++++ input.c | 45 +++++++++++ screen-redraw.c | 2 +- screen.c | 14 ++++ server.c | 1 - style.c | 2 +- tmux.1 | 11 ++- tmux.h | 23 +++++- tty-features.c | 16 +++- tty-term.c | 1 + tty.c | 114 ++++++++++++++++++-------- 15 files changed, 432 insertions(+), 48 deletions(-) create mode 100644 hyperlinks.c diff --git a/Makefile b/Makefile index fc7ed262..e3e2fd9c 100644 --- a/Makefile +++ b/Makefile @@ -81,6 +81,7 @@ SRCS= alerts.c \ grid-reader.c \ grid-view.c \ grid.c \ + hyperlinks.c \ input-keys.c \ input.c \ job.c \ diff --git a/cmd-capture-pane.c b/cmd-capture-pane.c index a3e84c46..04a88172 100644 --- a/cmd-capture-pane.c +++ b/cmd-capture-pane.c @@ -53,8 +53,8 @@ const struct cmd_entry cmd_clear_history_entry = { .name = "clear-history", .alias = "clearhist", - .args = { "t:", 0, 0, NULL }, - .usage = CMD_TARGET_PANE_USAGE, + .args = { "Ht:", 0, 0, NULL }, + .usage = "[-H] " CMD_TARGET_PANE_USAGE, .target = { 't', CMD_FIND_PANE, 0 }, @@ -204,6 +204,8 @@ cmd_capture_pane_exec(struct cmd *self, struct cmdq_item *item) if (cmd_get_entry(self) == &cmd_clear_history_entry) { window_pane_reset_mode_all(wp); grid_clear_history(wp->base.grid); + if (args_has(args, 'H')) + screen_reset_hyperlinks(wp->screen); return (CMD_RETURN_NORMAL); } diff --git a/cmd-display-panes.c b/cmd-display-panes.c index 5773a2d0..06f6dc27 100644 --- a/cmd-display-panes.c +++ b/cmd-display-panes.c @@ -144,7 +144,7 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx, llen = 0; if (sx < len * 6 || sy < 5) { - tty_attributes(tty, &fgc, &grid_default_cell, NULL); + tty_attributes(tty, &fgc, &grid_default_cell, NULL, NULL); if (sx >= len + llen + 1) { len += llen + 1; tty_cursor(tty, xoff + px - len / 2, yoff + py); @@ -161,7 +161,7 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx, px -= len * 3; py -= 2; - tty_attributes(tty, &bgc, &grid_default_cell, NULL); + tty_attributes(tty, &bgc, &grid_default_cell, NULL, NULL); for (ptr = buf; *ptr != '\0'; ptr++) { if (*ptr < '0' || *ptr > '9') continue; @@ -179,7 +179,7 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx, if (sy <= 6) goto out; - tty_attributes(tty, &fgc, &grid_default_cell, NULL); + tty_attributes(tty, &fgc, &grid_default_cell, NULL, NULL); if (rlen != 0 && sx >= rlen) { tty_cursor(tty, xoff + sx - rlen, yoff); tty_putn(tty, rbuf, rlen, rlen); diff --git a/grid.c b/grid.c index ba251e70..828def68 100644 --- a/grid.c +++ b/grid.c @@ -37,7 +37,7 @@ /* Default grid cell data. */ const struct grid_cell grid_default_cell = { - { { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 0 + { { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 0, 0 }; /* @@ -45,12 +45,12 @@ const struct grid_cell grid_default_cell = { * appears in the grid - because of this, they are always extended cells. */ static const struct grid_cell grid_padding_cell = { - { { '!' }, 0, 0, 0 }, 0, GRID_FLAG_PADDING, 8, 8, 0 + { { '!' }, 0, 0, 0 }, 0, GRID_FLAG_PADDING, 8, 8, 0, 0 }; /* Cleared grid cell data. */ static const struct grid_cell grid_cleared_cell = { - { { ' ' }, 0, 1, 1 }, 0, GRID_FLAG_CLEARED, 8, 8, 0 + { { ' ' }, 0, 1, 1 }, 0, GRID_FLAG_CLEARED, 8, 8, 0, 0 }; static const struct grid_cell_entry grid_cleared_entry = { GRID_FLAG_CLEARED, { .data = { 0, 8, 8, ' ' } } @@ -90,6 +90,8 @@ grid_need_extended_cell(const struct grid_cell_entry *gce, return (1); if (gc->us != 0) /* only supports 256 or RGB */ return (1); + if (gc->link != 0) + return (1); return (0); } @@ -131,6 +133,7 @@ grid_extended_cell(struct grid_line *gl, struct grid_cell_entry *gce, gee->fg = gc->fg; gee->bg = gc->bg; gee->us = gc->us; + gee->link = gc->link; return (gee); } @@ -231,6 +234,8 @@ grid_cells_look_equal(const struct grid_cell *gc1, const struct grid_cell *gc2) return (0); if (gc1->attr != gc2->attr || gc1->flags != gc2->flags) return (0); + if (gc1->link != gc2->link) + return (0); return (1); } @@ -509,6 +514,7 @@ grid_get_cell1(struct grid_line *gl, u_int px, struct grid_cell *gc) gc->fg = gee->fg; gc->bg = gee->bg; gc->us = gee->us; + gc->link = gee->link; utf8_to_data(gee->data, &gc->data); } return; @@ -524,6 +530,7 @@ grid_get_cell1(struct grid_line *gl, u_int px, struct grid_cell *gc) gc->bg |= COLOUR_FLAG_256; gc->us = 0; utf8_set(&gc->data, gce->data.data); + gc->link = 0; } /* Get cell for reading. */ diff --git a/hyperlinks.c b/hyperlinks.c new file mode 100644 index 00000000..3dd3efa9 --- /dev/null +++ b/hyperlinks.c @@ -0,0 +1,225 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2021 Will + * Copyright (c) 2022 Jeff Chiang + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include +#include + +#include "tmux.h" + +/* + * OSC 8 hyperlinks, described at: + * + * https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda + * + * Each hyperlink and ID combination is assigned a number ("inner" in this + * file) which is stored in an extended grid cell and maps into a tree here. + * + * Each URI has one inner number and one external ID (which tmux uses to send + * the hyperlink to the terminal) and one internal ID (which is received from + * the sending application inside tmux). + * + * Anonymous hyperlinks are each unique and are not reused even if they have + * the same URI (terminals will not want to tie them together). + */ + +#define MAX_HYPERLINKS 5000 + +static uint64_t hyperlinks_next_external_id = 1; +static u_int global_hyperlinks_count; + +struct hyperlinks_uri { + struct hyperlinks *tree; + + u_int inner; + const char *internal_id; + const char *external_id; + const char *uri; + + TAILQ_ENTRY(hyperlinks_uri) list_entry; + RB_ENTRY(hyperlinks_uri) by_inner_entry; + RB_ENTRY(hyperlinks_uri) by_uri_entry; /* by internal ID and URI */ +}; +RB_HEAD(hyperlinks_by_uri_tree, hyperlinks_uri); +RB_HEAD(hyperlinks_by_inner_tree, hyperlinks_uri); + +TAILQ_HEAD(hyperlinks_list, hyperlinks_uri); +static struct hyperlinks_list global_hyperlinks = + TAILQ_HEAD_INITIALIZER(global_hyperlinks); + +struct hyperlinks { + u_int next_inner; + struct hyperlinks_by_inner_tree by_inner; + struct hyperlinks_by_uri_tree by_uri; +}; + +static int +hyperlinks_by_uri_cmp(struct hyperlinks_uri *left, struct hyperlinks_uri *right) +{ + int r; + + if (*left->internal_id == '\0' || *right->internal_id == '\0') { + /* + * If both URIs are anonymous, use the inner for comparison so + * that they do not match even if the URI is the same - each + * anonymous URI should be unique. + */ + if (*left->internal_id != '\0') + return (-1); + if (*right->internal_id != '\0') + return (1); + return (left->inner - right->inner); + } + + r = strcmp(left->internal_id, right->internal_id); + if (r != 0) + return (r); + return (strcmp(left->uri, right->uri)); +} +RB_PROTOTYPE_STATIC(hyperlinks_by_uri_tree, hyperlinks_uri, by_uri_entry, + hyperlinks_by_uri_cmp); +RB_GENERATE_STATIC(hyperlinks_by_uri_tree, hyperlinks_uri, by_uri_entry, + hyperlinks_by_uri_cmp); + +static int +hyperlinks_by_inner_cmp(struct hyperlinks_uri *left, + struct hyperlinks_uri *right) +{ + return (left->inner - right->inner); +} +RB_PROTOTYPE_STATIC(hyperlinks_by_inner_tree, hyperlinks_uri, by_inner_entry, + hyperlinks_by_inner_cmp); +RB_GENERATE_STATIC(hyperlinks_by_inner_tree, hyperlinks_uri, by_inner_entry, + hyperlinks_by_inner_cmp); + +/* Remove a hyperlink. */ +static void +hyperlinks_remove(struct hyperlinks_uri *hlu) +{ + struct hyperlinks *hl = hlu->tree; + + TAILQ_REMOVE(&global_hyperlinks, hlu, list_entry); + global_hyperlinks_count--; + + RB_REMOVE(hyperlinks_by_inner_tree, &hl->by_inner, hlu); + RB_REMOVE(hyperlinks_by_uri_tree, &hl->by_uri, hlu); + + free((void *)hlu->internal_id); + free((void *)hlu->external_id); + free((void *)hlu->uri); + free(hlu); +} + +/* Store a new hyperlink or return if it already exists. */ +u_int +hyperlinks_put(struct hyperlinks *hl, const char *uri_in, + const char *internal_id_in) +{ + struct hyperlinks_uri find, *hlu; + char *uri, *internal_id, *external_id; + + /* + * Anonymous URI are stored with an empty internal ID and the tree + * comparator will make sure they never match each other (so each + * anonymous URI is unique). + */ + if (internal_id_in == NULL) + internal_id_in = ""; + + utf8_stravis(&uri, uri_in, VIS_OCTAL|VIS_CSTYLE); + utf8_stravis(&internal_id, internal_id_in, VIS_OCTAL|VIS_CSTYLE); + + if (*internal_id_in != '\0') { + find.uri = uri; + find.internal_id = internal_id; + + hlu = RB_FIND(hyperlinks_by_uri_tree, &hl->by_uri, &find); + if (hlu != NULL) { + free (uri); + free (internal_id); + return (hlu->inner); + } + } + xasprintf(&external_id, "tmux%llX", hyperlinks_next_external_id++); + + hlu = xcalloc(1, sizeof *hlu); + hlu->inner = hl->next_inner++; + hlu->internal_id = internal_id; + hlu->external_id = external_id; + hlu->uri = uri; + hlu->tree = hl; + RB_INSERT(hyperlinks_by_uri_tree, &hl->by_uri, hlu); + RB_INSERT(hyperlinks_by_inner_tree, &hl->by_inner, hlu); + + TAILQ_INSERT_TAIL(&global_hyperlinks, hlu, list_entry); + if (++global_hyperlinks_count == MAX_HYPERLINKS) + hyperlinks_remove(TAILQ_FIRST(&global_hyperlinks)); + + return (hlu->inner); +} + +/* Get hyperlink by inner number. */ +int +hyperlinks_get(struct hyperlinks *hl, u_int inner, const char **uri_out, + const char **external_id_out) +{ + struct hyperlinks_uri find, *hlu; + + find.inner = inner; + + hlu = RB_FIND(hyperlinks_by_inner_tree, &hl->by_inner, &find); + if (hlu == NULL) + return (0); + *external_id_out = hlu->external_id; + *uri_out = hlu->uri; + return (1); +} + +/* Initialize hyperlink set. */ +struct hyperlinks * +hyperlinks_init(void) +{ + struct hyperlinks *hl; + + hl = xcalloc(1, sizeof *hl); + hl->next_inner = 1; + RB_INIT(&hl->by_uri); + RB_INIT(&hl->by_inner); + return (hl); +} + +/* Free all hyperlinks but not the set itself. */ +void +hyperlinks_reset(struct hyperlinks *hl) +{ + struct hyperlinks_uri *hlu, *hlu1; + + RB_FOREACH_SAFE(hlu, hyperlinks_by_inner_tree, &hl->by_inner, hlu1) + hyperlinks_remove(hlu); +} + +/* Free hyperlink set. */ +void +hyperlinks_free(struct hyperlinks *hl) +{ + hyperlinks_reset(hl); + free(hl); +} diff --git a/input.c b/input.c index d4ffe784..4252428b 100644 --- a/input.c +++ b/input.c @@ -135,6 +135,7 @@ static void input_set_state(struct input_ctx *, static void input_reset_cell(struct input_ctx *); static void input_osc_4(struct input_ctx *, const char *); +static void input_osc_8(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 *); @@ -2318,6 +2319,9 @@ input_exit_osc(struct input_ctx *ictx) } } break; + case 8: + input_osc_8(ictx, p); + break; case 10: input_osc_10(ictx, p); break; @@ -2562,6 +2566,47 @@ input_osc_4(struct input_ctx *ictx, const char *p) free(copy); } +/* Handle the OSC 8 sequence for embedding hyperlinks. */ +static void +input_osc_8(struct input_ctx *ictx, const char *p) +{ + struct hyperlinks *hl = ictx->ctx.s->hyperlinks; + struct grid_cell *gc = &ictx->cell.cell; + const char *start, *end, *uri; + char *id = NULL; + + for (start = p; (end = strpbrk(start, ":;")) != NULL; start = end + 1) { + if (end - start >= 4 && strncmp(start, "id=", 3) == 0) { + if (id != NULL) + goto bad; + id = xstrndup(start + 3, end - start - 3); + } + + /* The first ; is the end of parameters and start of the URI. */ + if (*end == ';') + break; + } + if (end == NULL || *end != ';') + goto bad; + uri = end + 1; + if (*uri == '\0') { + gc->link = 0; + free(id); + return; + } + gc->link = hyperlinks_put(hl, uri, id); + if (id == NULL) + log_debug("hyperlink (anonymous) %s = %u", uri, gc->link); + else + log_debug("hyperlink (id=%s) %s = %u", id, uri, gc->link); + free(id); + return; + +bad: + log_debug("bad OSC 8 %s", p); + free(id); +} + /* Handle the OSC 10 sequence for setting and querying foreground colour. */ static void input_osc_10(struct input_ctx *ictx, const char *p) diff --git a/screen-redraw.c b/screen-redraw.c index c4906ab8..470135cc 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -738,7 +738,7 @@ screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j) } } - tty_cell(tty, &gc, &grid_default_cell, NULL); + tty_cell(tty, &gc, &grid_default_cell, NULL, NULL); if (isolates) tty_puts(tty, START_ISOLATE); } diff --git a/screen.c b/screen.c index 87b3330a..09326b2d 100644 --- a/screen.c +++ b/screen.c @@ -90,6 +90,7 @@ screen_init(struct screen *s, u_int sx, u_int sy, u_int hlimit) s->sel = NULL; s->write_list = NULL; + s->hyperlinks = NULL; screen_reinit(s); } @@ -119,6 +120,17 @@ screen_reinit(struct screen *s) screen_clear_selection(s); screen_free_titles(s); + screen_reset_hyperlinks(s); +} + +/* Reset hyperlinks of a screen. */ +void +screen_reset_hyperlinks(struct screen *s) +{ + if (s->hyperlinks == NULL) + s->hyperlinks = hyperlinks_init(); + else + hyperlinks_reset(s->hyperlinks); } /* Destroy a screen. */ @@ -137,6 +149,8 @@ screen_free(struct screen *s) grid_destroy(s->saved_grid); grid_destroy(s->grid); + if (s->hyperlinks != NULL) + hyperlinks_free(s->hyperlinks); screen_free_titles(s); } diff --git a/server.c b/server.c index 5cbe3921..f1df1aa4 100644 --- a/server.c +++ b/server.c @@ -211,7 +211,6 @@ server_start(struct tmuxproc *client, int flags, struct event_base *base, RB_INIT(&sessions); key_bindings_init(); TAILQ_INIT(&message_log); - gettimeofday(&start_time, NULL); server_fd = server_create_socket(flags, &cause); diff --git a/style.c b/style.c index 89a4e63a..8407dc68 100644 --- a/style.c +++ b/style.c @@ -30,7 +30,7 @@ /* Default style. */ static struct style style_default = { - { { { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 0 }, + { { { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 0, 0 }, 0, 8, diff --git a/tmux.1 b/tmux.1 index 44b96eb7..c7ff6cea 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3656,6 +3656,8 @@ Allows setting the cursor style. Supports extended keys. .It focus Supports focus reporting. +.It hyperlinks +Supports OSC 8 hyperlinks. .It ignorefkeys Ignore function keys from .Xr terminfo 5 @@ -6122,9 +6124,14 @@ a format for each shortcut key; both are evaluated once for each line. 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 +.It Xo Ic clear-history +.Op Fl H +.Op Fl t Ar target-pane +.Xc .D1 Pq alias: Ic clearhist Remove and free the history for the specified pane. +.Fl H +also removes all hyperlinks. .Tg deleteb .It Ic delete-buffer Op Fl b Ar buffer-name .D1 Pq alias: Ic deleteb @@ -6412,6 +6419,8 @@ Disable and enable focus reporting. These are set automatically if the .Em XT capability is present. +.It Em \&Hls +Set or clear a hyperlink annotation. .It Em \&Rect Tell .Nm diff --git a/tmux.h b/tmux.h index c77b77c7..71245ffc 100644 --- a/tmux.h +++ b/tmux.h @@ -49,6 +49,8 @@ struct control_state; struct environ; struct format_job_tree; struct format_tree; +struct hyperlinks_uri; +struct hyperlinks; struct input_ctx; struct job; struct menu_data; @@ -365,6 +367,7 @@ enum tty_code_code { TTYC_ENFCS, TTYC_ENMG, TTYC_FSL, + TTYC_HLS, TTYC_HOME, TTYC_HPA, TTYC_ICH, @@ -689,6 +692,7 @@ struct grid_cell { int fg; int bg; int us; + u_int link; }; /* Grid extended cell entry. */ @@ -699,6 +703,7 @@ struct grid_extd_entry { int fg; int bg; int us; + u_int link; } __packed; /* Grid cell entry. */ @@ -850,6 +855,8 @@ struct screen { struct screen_sel *sel; struct screen_write_cline *write_list; + + struct hyperlinks *hyperlinks; }; /* Screen write context. */ @@ -2246,7 +2253,8 @@ void tty_update_window_offset(struct window *); void tty_update_client_offset(struct client *); void tty_raw(struct tty *, const char *); void tty_attributes(struct tty *, const struct grid_cell *, - const struct grid_cell *, struct colour_palette *); + const struct grid_cell *, struct colour_palette *, + struct hyperlinks *); void tty_reset(struct tty *); void tty_region_off(struct tty *); void tty_margin_off(struct tty *); @@ -2263,7 +2271,8 @@ void tty_puts(struct tty *, const char *); void tty_putc(struct tty *, u_char); void tty_putn(struct tty *, const void *, size_t, u_int); void tty_cell(struct tty *, const struct grid_cell *, - const struct grid_cell *, struct colour_palette *); + const struct grid_cell *, struct colour_palette *, + struct hyperlinks *); int tty_init(struct tty *, struct client *); void tty_resize(struct tty *); void tty_set_size(struct tty *, u_int, u_int, u_int, u_int); @@ -2893,6 +2902,7 @@ 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_reset_hyperlinks(struct screen *); 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 *); @@ -3298,4 +3308,13 @@ void server_acl_user_deny_write(uid_t); int server_acl_join(struct client *); uid_t server_acl_get_uid(struct server_acl_user *); +/* hyperlink.c */ +u_int hyperlinks_put(struct hyperlinks *, const char *, + const char *); +int hyperlinks_get(struct hyperlinks *, u_int, + const char **, const char **); +struct hyperlinks *hyperlinks_init(void); +void hyperlinks_reset(struct hyperlinks *); +void hyperlinks_free(struct hyperlinks *); + #endif /* TMUX_H */ diff --git a/tty-features.c b/tty-features.c index 477925e3..396a351e 100644 --- a/tty-features.c +++ b/tty-features.c @@ -87,6 +87,17 @@ static const struct tty_feature tty_feature_clipboard = { 0 }; +/* Terminal supports OSC 8 hyperlinks. */ +static const char *tty_feature_hyperlinks_capabilities[] = { + "*:Hls=\\E]8;%?%p1%l%tid=%p1%s%;;%p2%s\\E\\\\", + NULL +}; +static const struct tty_feature tty_feature_hyperlinks = { + "hyperlinks", + tty_feature_hyperlinks_capabilities, + 0 +}; + /* * Terminal supports RGB colour. This replaces setab and setaf also since * terminals with RGB have versions that do not allow setting colours from the @@ -330,6 +341,7 @@ static const struct tty_feature *tty_features[] = { &tty_feature_bpaste, &tty_feature_ccolour, &tty_feature_clipboard, + &tty_feature_hyperlinks, &tty_feature_cstyle, &tty_feature_extkeys, &tty_feature_focus, @@ -444,14 +456,14 @@ tty_default_features(int *feat, const char *name, u_int version) }, { .name = "tmux", .features = TTY_FEATURES_BASE_MODERN_XTERM - ",ccolour,cstyle,focus,overline,usstyle" + ",ccolour,cstyle,focus,overline,usstyle,hyperlinks" }, { .name = "rxvt-unicode", .features = "256,bpaste,ccolour,cstyle,mouse,title,ignorefkeys" }, { .name = "iTerm2", .features = TTY_FEATURES_BASE_MODERN_XTERM - ",cstyle,extkeys,margins,usstyle,sync,osc7" + ",cstyle,extkeys,margins,usstyle,sync,osc7,hyperlinks" }, { .name = "XTerm", /* diff --git a/tty-term.c b/tty-term.c index 61eda56d..a877ef6b 100644 --- a/tty-term.c +++ b/tty-term.c @@ -100,6 +100,7 @@ static const struct tty_term_code_entry tty_term_codes[] = { [TTYC_ENFCS] = { TTYCODE_STRING, "Enfcs" }, [TTYC_ENMG] = { TTYCODE_STRING, "Enmg" }, [TTYC_FSL] = { TTYCODE_STRING, "fsl" }, + [TTYC_HLS] = { TTYCODE_STRING, "Hls" }, [TTYC_HOME] = { TTYCODE_STRING, "home" }, [TTYC_HPA] = { TTYCODE_STRING, "hpa" }, [TTYC_ICH1] = { TTYCODE_STRING, "ich1" }, diff --git a/tty.c b/tty.c index 7e0a6a3e..78961e47 100644 --- a/tty.c +++ b/tty.c @@ -69,7 +69,7 @@ static void tty_emulate_repeat(struct tty *, enum tty_code_code, static void tty_repeat_space(struct tty *, u_int); 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); + struct colour_palette *, u_int, struct hyperlinks *); 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 *); @@ -1455,7 +1455,8 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx, tty_term_has(tty->term, TTYC_EL1) && !tty_fake_bce(tty, defaults, 8) && c->overlay_check == NULL) { - tty_default_attributes(tty, defaults, palette, 8); + tty_default_attributes(tty, defaults, palette, 8, + s->hyperlinks); tty_cursor(tty, nx - 1, aty); tty_putcode(tty, TTYC_EL1); cleared = 1; @@ -1480,9 +1481,11 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx, gcp->fg != last.fg || gcp->bg != last.bg || gcp->us != last.us || + gcp->link != last.link || ux + width + gcp->data.width > nx || (sizeof buf) - len < gcp->data.size)) { - tty_attributes(tty, &last, defaults, palette); + tty_attributes(tty, &last, defaults, palette, + s->hyperlinks); if (last.flags & GRID_FLAG_CLEARED) { log_debug("%s: %zu cleared", __func__, len); tty_clear_line(tty, defaults, aty, atx + ux, @@ -1515,7 +1518,8 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx, 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_attributes(tty, &last, defaults, palette, + s->hyperlinks); for (j = 0; j < OVERLAY_MAX_RANGES; j++) { if (r.nx[j] == 0) continue; @@ -1532,7 +1536,8 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx, } } } else if (gcp->attr & GRID_ATTR_CHARSET) { - tty_attributes(tty, &last, defaults, palette); + tty_attributes(tty, &last, defaults, palette, + s->hyperlinks); tty_cursor(tty, atx + ux, aty); for (j = 0; j < gcp->data.size; j++) tty_putc(tty, gcp->data.data[j]); @@ -1544,7 +1549,7 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx, } } if (len != 0 && ((~last.flags & GRID_FLAG_CLEARED) || last.bg != 8)) { - tty_attributes(tty, &last, defaults, palette); + tty_attributes(tty, &last, defaults, palette, s->hyperlinks); if (last.flags & GRID_FLAG_CLEARED) { log_debug("%s: %zu cleared (end)", __func__, len); tty_clear_line(tty, defaults, aty, atx + ux, width, @@ -1560,7 +1565,8 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx, if (!cleared && ux < nx) { log_debug("%s: %u to end of line (%zu cleared)", __func__, nx - ux, len); - tty_default_attributes(tty, defaults, palette, 8); + tty_default_attributes(tty, defaults, palette, 8, + s->hyperlinks); tty_clear_line(tty, defaults, aty, atx + ux, nx - ux, 8); } @@ -1646,7 +1652,8 @@ tty_cmd_insertcharacter(struct tty *tty, const struct tty_ctx *ctx) return; } - tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg); + tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg, + ctx->s->hyperlinks); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); @@ -1668,7 +1675,8 @@ tty_cmd_deletecharacter(struct tty *tty, const struct tty_ctx *ctx) return; } - tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg); + tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg, + ctx->s->hyperlinks); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); @@ -1678,7 +1686,8 @@ tty_cmd_deletecharacter(struct tty *tty, const struct tty_ctx *ctx) void tty_cmd_clearcharacter(struct tty *tty, const struct tty_ctx *ctx) { - tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg); + tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg, + ctx->s->hyperlinks); tty_clear_pane_line(tty, ctx, ctx->ocy, ctx->ocx, ctx->num, ctx->bg); } @@ -1700,7 +1709,8 @@ tty_cmd_insertline(struct tty *tty, const struct tty_ctx *ctx) return; } - tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg); + tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg, + ctx->s->hyperlinks); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); tty_margin_off(tty); @@ -1727,7 +1737,8 @@ tty_cmd_deleteline(struct tty *tty, const struct tty_ctx *ctx) return; } - tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg); + tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg, + ctx->s->hyperlinks); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); tty_margin_off(tty); @@ -1740,7 +1751,8 @@ tty_cmd_deleteline(struct tty *tty, const struct tty_ctx *ctx) void tty_cmd_clearline(struct tty *tty, const struct tty_ctx *ctx) { - tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg); + tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg, + ctx->s->hyperlinks); tty_clear_pane_line(tty, ctx, ctx->ocy, 0, ctx->sx, ctx->bg); } @@ -1750,7 +1762,8 @@ tty_cmd_clearendofline(struct tty *tty, const struct tty_ctx *ctx) { u_int nx = ctx->sx - ctx->ocx; - tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg); + tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg, + ctx->s->hyperlinks); tty_clear_pane_line(tty, ctx, ctx->ocy, ctx->ocx, nx, ctx->bg); } @@ -1758,7 +1771,8 @@ tty_cmd_clearendofline(struct tty *tty, const struct tty_ctx *ctx) void tty_cmd_clearstartofline(struct tty *tty, const struct tty_ctx *ctx) { - tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg); + tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg, + ctx->s->hyperlinks); tty_clear_pane_line(tty, ctx, ctx->ocy, 0, ctx->ocx + 1, ctx->bg); } @@ -1784,7 +1798,8 @@ tty_cmd_reverseindex(struct tty *tty, const struct tty_ctx *ctx) return; } - tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg); + tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg, + ctx->s->hyperlinks); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); tty_margin_pane(tty, ctx); @@ -1815,7 +1830,8 @@ tty_cmd_linefeed(struct tty *tty, const struct tty_ctx *ctx) return; } - tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg); + tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg, + ctx->s->hyperlinks); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); tty_margin_pane(tty, ctx); @@ -1855,7 +1871,8 @@ tty_cmd_scrollup(struct tty *tty, const struct tty_ctx *ctx) return; } - tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg); + tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg, + ctx->s->hyperlinks); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); tty_margin_pane(tty, ctx); @@ -1895,7 +1912,8 @@ tty_cmd_scrolldown(struct tty *tty, const struct tty_ctx *ctx) return; } - tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg); + tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg, + ctx->s->hyperlinks); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); tty_margin_pane(tty, ctx); @@ -1914,7 +1932,8 @@ tty_cmd_clearendofscreen(struct tty *tty, const struct tty_ctx *ctx) { u_int px, py, nx, ny; - tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg); + tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg, + ctx->s->hyperlinks); tty_region_pane(tty, ctx, 0, ctx->sy - 1); tty_margin_off(tty); @@ -1938,7 +1957,8 @@ tty_cmd_clearstartofscreen(struct tty *tty, const struct tty_ctx *ctx) { u_int px, py, nx, ny; - tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg); + tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg, + ctx->s->hyperlinks); tty_region_pane(tty, ctx, 0, ctx->sy - 1); tty_margin_off(tty); @@ -1962,7 +1982,8 @@ tty_cmd_clearscreen(struct tty *tty, const struct tty_ctx *ctx) { u_int px, py, nx, ny; - tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg); + tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg, + ctx->s->hyperlinks); tty_region_pane(tty, ctx, 0, ctx->sy - 1); tty_margin_off(tty); @@ -1985,7 +2006,8 @@ tty_cmd_alignmenttest(struct tty *tty, const struct tty_ctx *ctx) return; } - tty_attributes(tty, &grid_default_cell, &ctx->defaults, ctx->palette); + tty_attributes(tty, &grid_default_cell, &ctx->defaults, ctx->palette, + ctx->s->hyperlinks); tty_region_pane(tty, ctx, 0, ctx->sy - 1); tty_margin_off(tty); @@ -2031,7 +2053,8 @@ tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx) tty_margin_off(tty); tty_cursor_pane_unless_wrap(tty, ctx, ctx->ocx, ctx->ocy); - tty_cell(tty, ctx->cell, &ctx->defaults, ctx->palette); + tty_cell(tty, ctx->cell, &ctx->defaults, ctx->palette, + ctx->s->hyperlinks); } void @@ -2062,7 +2085,7 @@ 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); + tty_attributes(tty, ctx->cell, &ctx->defaults, ctx->palette, ctx->s->hyperlinks); /* Get tty position from pane position for overlay check. */ px = ctx->xoff + ctx->ocx - ctx->wox; @@ -2136,7 +2159,8 @@ tty_cmd_syncstart(struct tty *tty, const struct tty_ctx *ctx) void tty_cell(struct tty *tty, const struct grid_cell *gc, - const struct grid_cell *defaults, struct colour_palette *palette) + const struct grid_cell *defaults, struct colour_palette *palette, + struct hyperlinks *hl) { const struct grid_cell *gcp; @@ -2152,11 +2176,11 @@ tty_cell(struct tty *tty, const struct grid_cell *gc, /* Check the output codeset and apply attributes. */ gcp = tty_check_codeset(tty, gc); - tty_attributes(tty, gcp, defaults, palette); + tty_attributes(tty, gcp, defaults, palette, hl); /* If it is a single character, write with putc to handle ACS. */ if (gcp->data.size == 1) { - tty_attributes(tty, gcp, defaults, palette); + tty_attributes(tty, gcp, defaults, palette, hl); if (*gcp->data.data < 0x20 || *gcp->data.data == 0x7f) return; tty_putc(tty, *gcp->data.data); @@ -2173,6 +2197,8 @@ tty_reset(struct tty *tty) struct grid_cell *gc = &tty->cell; if (!grid_cells_equal(gc, &grid_default_cell)) { + if (gc->link != 0) + tty_putcode_ptr2(tty, TTYC_HLS, "", ""); if ((gc->attr & GRID_ATTR_CHARSET) && tty_acs_needed(tty)) tty_putcode(tty, TTYC_RMACS); tty_putcode(tty, TTYC_SGR0); @@ -2462,9 +2488,29 @@ out: tty->cy = cy; } +static void +tty_hyperlink(struct tty *tty, const struct grid_cell *gc, + struct hyperlinks *hl) +{ + const char *uri, *id; + + if (gc->link == tty->cell.link) + return; + tty->cell.link = gc->link; + + if (hl == NULL) + return; + + if (gc->link == 0 || !hyperlinks_get(hl, gc->link, &uri, &id)) + tty_putcode_ptr2(tty, TTYC_HLS, "", ""); + else + tty_putcode_ptr2(tty, TTYC_HLS, id, uri); +} + void tty_attributes(struct tty *tty, const struct grid_cell *gc, - const struct grid_cell *defaults, struct colour_palette *palette) + const struct grid_cell *defaults, struct colour_palette *palette, + struct hyperlinks *hl) { struct grid_cell *tc = &tty->cell, gc2; int changed; @@ -2482,7 +2528,8 @@ tty_attributes(struct tty *tty, const struct grid_cell *gc, if (gc2.attr == tty->last_cell.attr && gc2.fg == tty->last_cell.fg && gc2.bg == tty->last_cell.bg && - gc2.us == tty->last_cell.us) + gc2.us == tty->last_cell.us && + gc2.link == tty->last_cell.link) return; /* @@ -2559,6 +2606,9 @@ tty_attributes(struct tty *tty, const struct grid_cell *gc, if ((changed & GRID_ATTR_CHARSET) && tty_acs_needed(tty)) tty_putcode(tty, TTYC_SMACS); + /* Set hyperlink if any. */ + tty_hyperlink(tty, gc, hl); + memcpy(&tty->last_cell, &gc2, sizeof tty->last_cell); } @@ -2924,13 +2974,13 @@ tty_default_colours(struct grid_cell *gc, struct window_pane *wp) static void tty_default_attributes(struct tty *tty, const struct grid_cell *defaults, - struct colour_palette *palette, u_int bg) + struct colour_palette *palette, u_int bg, struct hyperlinks *hl) { struct grid_cell gc; memcpy(&gc, &grid_default_cell, sizeof gc); gc.bg = bg; - tty_attributes(tty, &gc, defaults, palette); + tty_attributes(tty, &gc, defaults, palette, hl); } static void -- cgit From c3af8f6b16eb770796fc1977ead90d02f9b82de4 Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Thu, 30 Jun 2022 16:44:43 +0100 Subject: hyperlinks: remove vis.h Not used on Linux. --- hyperlinks.c | 1 - 1 file changed, 1 deletion(-) diff --git a/hyperlinks.c b/hyperlinks.c index 3dd3efa9..a258809b 100644 --- a/hyperlinks.c +++ b/hyperlinks.c @@ -21,7 +21,6 @@ #include #include -#include #include "tmux.h" -- cgit From f08c019d419d7deefce2e8ab699aad404895355a Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 30 Jun 2022 16:46:26 +0100 Subject: Do not set Hls for hyperlinks on ncurses older than 5.9 (for example macOS). --- tty-features.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tty-features.c b/tty-features.c index 396a351e..e387f823 100644 --- a/tty-features.c +++ b/tty-features.c @@ -89,7 +89,11 @@ static const struct tty_feature tty_feature_clipboard = { /* Terminal supports OSC 8 hyperlinks. */ static const char *tty_feature_hyperlinks_capabilities[] = { +#if defined (__OpenBSD__) || (defined(NCURSES_VERSION_MAJOR) && \ + (NCURSES_VERSION_MAJOR > 5 || \ + (NCURSES_VERSION_MAJOR == 5 && NCURSES_VERSION_MINOR > 8))) "*:Hls=\\E]8;%?%p1%l%tid=%p1%s%;;%p2%s\\E\\\\", +#endif NULL }; static const struct tty_feature tty_feature_hyperlinks = { -- cgit From 9360e0ef329c12931bb97f42197a56776bb3d2f0 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 4 Jul 2022 08:39:45 +0000 Subject: Sort panes by index not by ID, GitHub issue 3249. --- window-tree.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/window-tree.c b/window-tree.c index fc21af43..00c7c0cc 100644 --- a/window-tree.c +++ b/window-tree.c @@ -272,9 +272,10 @@ window_tree_cmp_window(const void *a0, const void *b0) static int window_tree_cmp_pane(const void *a0, const void *b0) { - const struct window_pane *const *a = a0; - const struct window_pane *const *b = b0; - int result; + struct window_pane **a = (struct window_pane **)a0; + struct window_pane **b = (struct window_pane **)b0; + int result; + u_int ai, bi; if (window_tree_sort->field == WINDOW_TREE_BY_TIME) result = (*a)->active_point - (*b)->active_point; @@ -283,7 +284,9 @@ window_tree_cmp_pane(const void *a0, const void *b0) * Panes don't have names, so use number order for any other * sort field. */ - result = (*a)->id - (*b)->id; + window_pane_index(*a, &ai); + window_pane_index(*b, &bi); + result = ai - bi; } if (window_tree_sort->reversed) result = -result; @@ -1243,12 +1246,17 @@ window_tree_key(struct window_mode_entry *wme, struct client *c, item = mode_tree_get_current(data->data); finished = mode_tree_key(data->data, c, &key, m, &x, &y); + +again: if (item != (new_item = mode_tree_get_current(data->data))) { item = new_item; data->offset = 0; } - if (KEYC_IS_MOUSE(key) && m != NULL) + if (KEYC_IS_MOUSE(key) && m != NULL) { key = window_tree_mouse(data, key, x, item); + goto again; + } + switch (key) { case '<': data->offset--; -- cgit From d0d2c39decd1c342f2ffdb360e5d6b509b9bb34e Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 6 Jul 2022 07:36:36 +0000 Subject: Support hyperlinks with capture-pane -e and add a mouse_hyperlink format, GitHub issue 3247 from Jeff Chiang. --- cmd-capture-pane.c | 2 +- format.c | 39 +++++++++++++++++++++++++++++ grid-view.c | 2 +- grid.c | 72 +++++++++++++++++++++++++++++++++++++++++++++++------- hyperlinks.c | 7 ++++-- key-bindings.c | 3 +++ tmux.1 | 1 + tmux.h | 6 +++-- tty.c | 2 +- 9 files changed, 118 insertions(+), 16 deletions(-) diff --git a/cmd-capture-pane.c b/cmd-capture-pane.c index 04a88172..422f87d6 100644 --- a/cmd-capture-pane.c +++ b/cmd-capture-pane.c @@ -177,7 +177,7 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item, buf = NULL; for (i = top; i <= bottom; i++) { line = grid_string_cells(gd, 0, i, sx, &gc, with_codes, - escape_c0, !join_lines && !no_trim); + escape_c0, !join_lines && !no_trim, wp->screen); linelen = strlen(line); buf = cmd_capture_pane_append(buf, len, line, linelen); diff --git a/format.c b/format.c index b97c842c..edf4217c 100644 --- a/format.c +++ b/format.c @@ -1145,6 +1145,25 @@ format_cb_mouse_word(struct format_tree *ft) return (format_grid_word(gd, x, gd->hsize + y)); } +/* Callback for mouse_hyperlink. */ +static void * +format_cb_mouse_hyperlink(struct format_tree *ft) +{ + struct window_pane *wp; + struct grid *gd; + u_int x, y; + + if (!ft->m.valid) + return (NULL); + wp = cmd_mouse_pane(&ft->m, NULL, NULL); + if (wp == NULL) + return (NULL); + if (cmd_mouse_at(wp, &ft->m, &x, &y, 0) != 0) + return (NULL); + gd = wp->base.grid; + return (format_grid_hyperlink(gd, x, gd->hsize + y, wp->screen)); +} + /* Callback for mouse_line. */ static void * format_cb_mouse_line(struct format_tree *ft) @@ -2789,6 +2808,9 @@ static const struct format_table_entry format_table[] = { { "mouse_button_flag", FORMAT_TABLE_STRING, format_cb_mouse_button_flag }, + { "mouse_hyperlink", FORMAT_TABLE_STRING, + format_cb_mouse_hyperlink + }, { "mouse_line", FORMAT_TABLE_STRING, format_cb_mouse_line }, @@ -5064,3 +5086,20 @@ format_grid_line(struct grid *gd, u_int y) } return (s); } + +/* Return hyperlink at given coordinates. Caller frees. */ +char * +format_grid_hyperlink(struct grid *gd, u_int x, u_int y, struct screen* s) +{ + const char *uri; + struct grid_cell gc; + + grid_get_cell(gd, x, y, &gc); + if (gc.flags & GRID_FLAG_PADDING) + return (NULL); + if (s->hyperlinks == NULL || gc.link == 0) + return (NULL); + if (!hyperlinks_get(s->hyperlinks, gc.link, &uri, NULL, NULL)) + return (NULL); + return (xstrdup(uri)); +} diff --git a/grid-view.c b/grid-view.c index f230d3c8..689ac4e4 100644 --- a/grid-view.c +++ b/grid-view.c @@ -231,5 +231,5 @@ grid_view_string_cells(struct grid *gd, u_int px, u_int py, u_int nx) px = grid_view_x(gd, px); py = grid_view_y(gd, py); - return (grid_string_cells(gd, px, py, nx, NULL, 0, 0, 0)); + return (grid_string_cells(gd, px, py, nx, NULL, 0, 0, 0, NULL)); } diff --git a/grid.c b/grid.c index 828def68..b1afd398 100644 --- a/grid.c +++ b/grid.c @@ -885,18 +885,47 @@ grid_string_cells_add_code(char *buf, size_t len, u_int n, int *s, int *newc, } } +static int +grid_string_cells_add_hyperlink(char *buf, size_t len, const char *id, + const char *uri, int escape_c0) +{ + char *tmp; + + if (strlen(uri) + strlen(id) + 17 >= len) + return (0); + + if (escape_c0) + strlcat(buf, "\\033]8;", len); + else + strlcat(buf, "\033]8;", len); + if (*id != '\0') { + xasprintf(&tmp, "id=%s;", id); + strlcat(buf, tmp, len); + free(tmp); + } else + strlcat(buf, ";", len); + strlcat(buf, uri, len); + if (escape_c0) + strlcat(buf, "\\033\\\\", len); + else + strlcat(buf, "\033\\", len); + return (1); +} + /* * Returns ANSI code to set particular attributes (colour, bold and so on) * given a current state. */ static void grid_string_cells_code(const struct grid_cell *lastgc, - const struct grid_cell *gc, char *buf, size_t len, int escape_c0) + const struct grid_cell *gc, char *buf, size_t len, int escape_c0, + struct screen *sc, int *has_link) { - int oldc[64], newc[64], s[128]; - size_t noldc, nnewc, n, i; - u_int attr = gc->attr, lastattr = lastgc->attr; - char tmp[64]; + int oldc[64], newc[64], s[128]; + size_t noldc, nnewc, n, i; + u_int attr = gc->attr, lastattr = lastgc->attr; + char tmp[64]; + const char *uri, *id; struct { u_int mask; @@ -986,19 +1015,32 @@ grid_string_cells_code(const struct grid_cell *lastgc, else strlcat(buf, "\017", len); /* SI */ } + + /* Add hyperlink if changed. */ + if (sc != NULL && sc->hyperlinks != NULL && lastgc->link != gc->link) { + if (hyperlinks_get(sc->hyperlinks, gc->link, &uri, &id, NULL)) { + *has_link = grid_string_cells_add_hyperlink(buf, len, + id, uri, escape_c0); + } else if (*has_link) { + grid_string_cells_add_hyperlink(buf, len, "", "", + escape_c0); + *has_link = 0; + } + } } /* Convert cells into a string. */ char * grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx, - struct grid_cell **lastgc, int with_codes, int escape_c0, int trim) + struct grid_cell **lastgc, int with_codes, int escape_c0, int trim, + struct screen *s) { struct grid_cell gc; static struct grid_cell lastgc1; const char *data; - char *buf, code[128]; + char *buf, code[8192]; size_t len, off, size, codelen; - u_int xx; + u_int xx, has_link = 0; const struct grid_line *gl; if (lastgc != NULL && *lastgc == NULL) { @@ -1020,7 +1062,7 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx, if (with_codes) { grid_string_cells_code(*lastgc, &gc, code, sizeof code, - escape_c0); + escape_c0, s, &has_link); codelen = strlen(code); memcpy(*lastgc, &gc, sizeof **lastgc); } else @@ -1046,6 +1088,18 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx, off += size; } + if (has_link) { + grid_string_cells_add_hyperlink(code, sizeof code, "", "", + escape_c0); + codelen = strlen(code); + while (len < off + size + codelen + 1) { + buf = xreallocarray(buf, 2, len); + len *= 2; + } + memcpy(buf + off, code, codelen); + off += codelen; + } + if (trim) { while (off > 0 && buf[off - 1] == ' ') off--; diff --git a/hyperlinks.c b/hyperlinks.c index 3dd3efa9..18163cb2 100644 --- a/hyperlinks.c +++ b/hyperlinks.c @@ -179,7 +179,7 @@ hyperlinks_put(struct hyperlinks *hl, const char *uri_in, /* Get hyperlink by inner number. */ int hyperlinks_get(struct hyperlinks *hl, u_int inner, const char **uri_out, - const char **external_id_out) + const char **internal_id_out, const char **external_id_out) { struct hyperlinks_uri find, *hlu; @@ -188,7 +188,10 @@ hyperlinks_get(struct hyperlinks *hl, u_int inner, const char **uri_out, hlu = RB_FIND(hyperlinks_by_inner_tree, &hl->by_inner, &find); if (hlu == NULL) return (0); - *external_id_out = hlu->external_id; + if (internal_id_out != NULL) + *internal_id_out = hlu->internal_id; + if (external_id_out != NULL) + *external_id_out = hlu->external_id; *uri_out = hlu->uri; return (1); } diff --git a/key-bindings.c b/key-bindings.c index 95171966..bcc6004d 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -54,6 +54,9 @@ " '#{?mouse_word,Copy #[underscore]#{=/9/...:mouse_word},}' 'c' {copy-mode -q; set-buffer -- \"#{q:mouse_word}\"}" \ " '#{?mouse_line,Copy Line,}' 'l' {copy-mode -q; set-buffer -- \"#{q:mouse_line}\"}" \ " ''" \ + " '#{?mouse_hyperlink,Type #[underscore]#{=/9/...:mouse_hyperlink},}' 'C-h' {copy-mode -q; send-keys -l -- \"#{q:mouse_hyperlink}\"}" \ + " '#{?mouse_hyperlink,Copy #[underscore]#{=/9/...:mouse_hyperlink},}' 'h' {copy-mode -q; set-buffer -- \"#{q:mouse_hyperlink}\"}" \ + " ''" \ " 'Horizontal Split' 'h' {split-window -h}" \ " 'Vertical Split' 'v' {split-window -v}" \ " ''" \ diff --git a/tmux.1 b/tmux.1 index c7ff6cea..ee6740ac 100644 --- a/tmux.1 +++ b/tmux.1 @@ -5160,6 +5160,7 @@ The following variables are available, where appropriate: .It Li "mouse_all_flag" Ta "" Ta "Pane mouse all flag" .It Li "mouse_any_flag" Ta "" Ta "Pane mouse any flag" .It Li "mouse_button_flag" Ta "" Ta "Pane mouse button flag" +.It Li "mouse_hyperlink" Ta "" Ta "Hyperlink under mouse, if any" .It Li "mouse_line" Ta "" Ta "Line under mouse, if any" .It Li "mouse_sgr_flag" Ta "" Ta "Pane mouse SGR flag" .It Li "mouse_standard_flag" Ta "" Ta "Pane mouse standard flag" diff --git a/tmux.h b/tmux.h index 71245ffc..3137ca3b 100644 --- a/tmux.h +++ b/tmux.h @@ -2126,6 +2126,8 @@ void format_defaults_paste_buffer(struct format_tree *, struct paste_buffer *); void format_lost_client(struct client *); char *format_grid_word(struct grid *, u_int, u_int); +char *format_grid_hyperlink(struct grid *, u_int, u_int, + struct screen *); char *format_grid_line(struct grid *, u_int); /* format-draw.c */ @@ -2772,7 +2774,7 @@ void grid_clear_lines(struct grid *, u_int, u_int, u_int); void grid_move_lines(struct grid *, u_int, u_int, u_int, u_int); void grid_move_cells(struct grid *, u_int, u_int, u_int, u_int, u_int); char *grid_string_cells(struct grid *, u_int, u_int, u_int, - struct grid_cell **, int, int, int); + struct grid_cell **, int, int, int, struct screen *); void grid_duplicate_lines(struct grid *, u_int, struct grid *, u_int, u_int); void grid_reflow(struct grid *, u_int); @@ -3312,7 +3314,7 @@ uid_t server_acl_get_uid(struct server_acl_user *); u_int hyperlinks_put(struct hyperlinks *, const char *, const char *); int hyperlinks_get(struct hyperlinks *, u_int, - const char **, const char **); + const char **, const char **, const char **); struct hyperlinks *hyperlinks_init(void); void hyperlinks_reset(struct hyperlinks *); void hyperlinks_free(struct hyperlinks *); diff --git a/tty.c b/tty.c index 78961e47..1f373821 100644 --- a/tty.c +++ b/tty.c @@ -2501,7 +2501,7 @@ tty_hyperlink(struct tty *tty, const struct grid_cell *gc, if (hl == NULL) return; - if (gc->link == 0 || !hyperlinks_get(hl, gc->link, &uri, &id)) + if (gc->link == 0 || !hyperlinks_get(hl, gc->link, &uri, NULL, &id)) tty_putcode_ptr2(tty, TTYC_HLS, "", ""); else tty_putcode_ptr2(tty, TTYC_HLS, id, uri); -- cgit From dd602eaa61e82188313b9187021accab260f89c0 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 6 Jul 2022 07:51:37 +0000 Subject: Mention whether time is creation/activity for sort orders. --- tmux.1 | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/tmux.1 b/tmux.1 index ee6740ac..1fc2d061 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2112,9 +2112,11 @@ is not given, "detach-client -t '%%'" is used. specifies the initial sort field: one of .Ql name , .Ql size , -.Ql creation , +.Ql creation +(time), or -.Ql activity . +.Ql activity +(time). .Fl r reverses the sort order. .Fl f @@ -2196,7 +2198,8 @@ specifies the initial sort field: one of .Ql index , .Ql name , or -.Ql time . +.Ql time +(activity). .Fl r reverses the sort order. .Fl f @@ -6107,7 +6110,8 @@ is not given, "paste-buffer -b '%%'" is used. .Pp .Fl O specifies the initial sort field: one of -.Ql time , +.Ql time +(creation), .Ql name or .Ql size . -- cgit From 9e03df5500f802e0a10d52bfaf51eab493b84c70 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 6 Jul 2022 08:31:59 +0000 Subject: Defer reading from control client until the command line command has completed. --- control.c | 8 +++++++- server-client.c | 5 ++++- tmux.h | 1 + 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/control.c b/control.c index 359d0e78..f75035ef 100644 --- a/control.c +++ b/control.c @@ -776,7 +776,6 @@ control_start(struct client *c) cs->read_event = bufferevent_new(c->fd, control_read_callback, control_write_callback, control_error_callback, c); - bufferevent_enable(cs->read_event, EV_READ); if (c->flags & CLIENT_CONTROLCONTROL) cs->write_event = cs->read_event; @@ -793,6 +792,13 @@ control_start(struct client *c) } } +/* Control client ready. */ +void +control_ready(struct client *c) +{ + bufferevent_enable(c->control_state->read_event, EV_READ); +} + /* Discard all output for a client. */ void control_discard(struct client *c) diff --git a/server-client.c b/server-client.c index 7c4c2fdd..864b37b3 100644 --- a/server-client.c +++ b/server-client.c @@ -2791,8 +2791,11 @@ server_client_command_done(struct cmdq_item *item, __unused void *data) if (~c->flags & CLIENT_ATTACHED) c->flags |= CLIENT_EXIT; - else if (~c->flags & CLIENT_EXIT) + else if (~c->flags & CLIENT_EXIT) { + if (c->flags & CLIENT_CONTROL) + control_ready(c); tty_send_requests(&c->tty); + } return (CMD_RETURN_NORMAL); } diff --git a/tmux.h b/tmux.h index 3137ca3b..3f720e50 100644 --- a/tmux.h +++ b/tmux.h @@ -3134,6 +3134,7 @@ char *parse_window_name(const char *); /* control.c */ void control_discard(struct client *); void control_start(struct client *); +void control_ready(struct client *); void control_stop(struct client *); void control_set_pane_on(struct client *, struct window_pane *); void control_set_pane_off(struct client *, struct window_pane *); -- cgit -- cgit From 8e8b9865d18d1ecba7b9051fb21d759893462fb2 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 6 Jul 2022 09:02:12 +0100 Subject: Add hyperlink test, from Jeff Chiang. --- regress/capture-pane-hyperlink.sh | 30 ++++++++++++++++++++++++++++++ regress/capture-pane-sgr0.sh | 4 ++-- 2 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 regress/capture-pane-hyperlink.sh diff --git a/regress/capture-pane-hyperlink.sh b/regress/capture-pane-hyperlink.sh new file mode 100644 index 00000000..681a8e17 --- /dev/null +++ b/regress/capture-pane-hyperlink.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +# capture-pane -e for OSC 8 hyperlink + +PATH=/bin:/usr/bin +TERM=screen + +[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) +TMUX="$TEST_TMUX -Ltest" +TMP=$(mktemp) +TMP2=$(mktemp) +trap "rm -f $TMP $TMP2" 0 1 15 +$TMUX kill-server 2>/dev/null + +do_test() { + $TMUX -f/dev/null new -d " + printf '$1' + $TMUX capturep -peS0 -E1 >$TMP" + echo $2 > $TMP2 + sleep 1 + cmp $TMP $TMP2 || exit 1 + return 0 +} + +do_test '\033]8;id=1;https://github.com\033\\test1\033]8;;\033\\\n' '\033]8;id=1;https://github.com\033\\test1\033]8;;\033\\\n' || exit 1 +do_test '\033]8;;https://github.com/tmux/tmux\033\\test1\033]8;;\033\\\n' '\033]8;;https://github.com/tmux/tmux\033\\test1\033]8;;\033\\\n' || exit 1 + +$TMUX has 2>/dev/null && exit 1 + +exit 0 diff --git a/regress/capture-pane-sgr0.sh b/regress/capture-pane-sgr0.sh index 0dd9cd82..6a19ac8a 100644 --- a/regress/capture-pane-sgr0.sh +++ b/regress/capture-pane-sgr0.sh @@ -22,8 +22,8 @@ $TMUX -f/dev/null new -d " sleep 1 ( - printf '\033[1m\033[31m\033[42mabc\033[0m\033[31m\033[49mdef\033[39m\n' - printf '\033[100m bright bg \033[49m\n' + printf '\033[1m\033[31m\033[42mabc\033[0m\033[31m\033[49mdef\n' + printf '\033[39m\033[100m bright bg\n' ) | cmp - $TMP || exit 1 $TMUX has 2>/dev/null && exit 1 -- cgit From 1afe22086fb85a596eb4a20f2e80cacfbbc2f1e5 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 6 Jul 2022 08:40:52 +0000 Subject: Show config errors on attach if they were not shown when the session was created. --- cmd-attach-session.c | 3 +++ cmd-new-session.c | 10 +++------- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/cmd-attach-session.c b/cmd-attach-session.c index b92a7f2b..4e2d15da 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -158,6 +158,9 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag, c->flags |= CLIENT_ATTACHED; } + if (cfg_finished) + cfg_show_causes(s); + return (CMD_RETURN_NORMAL); } diff --git a/cmd-new-session.c b/cmd-new-session.c index b946f049..c90369bc 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -333,13 +333,6 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) server_client_set_key_table(c, NULL); } - /* - * If there are still configuration file errors to display, put the new - * session's current window into view mode and display them now. - */ - if (cfg_finished) - cfg_show_causes(s); - /* Print if requested. */ if (args_has(args, 'P')) { if ((template = args_get(args, 'F')) == NULL) @@ -357,6 +350,9 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) cmd_find_from_session(&fs, s, 0); cmdq_insert_hook(s, item, &fs, "after-new-session"); + if (cfg_finished) + cfg_show_causes(s); + if (sc.argv != NULL) cmd_free_argv(sc.argc, sc.argv); free(cwd); -- cgit From 9e19f132f2963d603a881d8e35411348638e5fc0 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 6 Jul 2022 09:54:53 +0100 Subject: Errors are now displayed on attach so use control mode to test instead. --- regress/if-shell-error.sh | 13 +++++-------- regress/kill-session-process-exit.sh | 3 ++- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/regress/if-shell-error.sh b/regress/if-shell-error.sh index 24dc578e..2896d9aa 100644 --- a/regress/if-shell-error.sh +++ b/regress/if-shell-error.sh @@ -11,16 +11,13 @@ TMUX="$TEST_TMUX -Ltest" $TMUX kill-server 2>/dev/null TMP=$(mktemp) -trap "rm -f $TMP" 0 1 15 +OUT=$(mktemp) +trap "rm -f $TMP $OUT" 0 1 15 cat <$TMP if 'true' 'wibble wobble' EOF -$TMUX -f$TMP new -d || exit 1 -sleep 1 -E=$($TMUX display -p '#{pane_in_mode}') -$TMUX kill-server 2>/dev/null -[ "$E" = "1" ] || exit 1 - -exit 0 +$TMUX -f$TMP -C new <$OUT +EOF +grep -q "^%config-error $TMP:1: $TMP:1: unknown command: wibble$" $OUT diff --git a/regress/kill-session-process-exit.sh b/regress/kill-session-process-exit.sh index 69ee27a2..82cd7412 100644 --- a/regress/kill-session-process-exit.sh +++ b/regress/kill-session-process-exit.sh @@ -8,13 +8,14 @@ TERM=screen [ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) TMUX="$TEST_TMUX -Ltest" $TMUX kill-server 2>/dev/null +sleep 1 $TMUX -f/dev/null new -d 'sleep 1000' || exit 1 P=$($TMUX display -pt0:0.0 '#{pane_pid}') $TMUX -f/dev/null new -d || exit 1 sleep 1 $TMUX kill-session -t0: -sleep 1 +sleep 3 kill -0 $P 2>/dev/null && exit 1 $TMUX kill-server 2>/dev/null -- cgit From 3c65475561b25073c3b7dcbb0b6498a0535ecd59 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 19 Jul 2022 06:46:57 +0000 Subject: Fix memory leak, from Gabriel Souza Franco. --- format.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/format.c b/format.c index edf4217c..535af061 100644 --- a/format.c +++ b/format.c @@ -3564,12 +3564,12 @@ found: } if (modifiers & FORMAT_QUOTE_SHELL) { saved = found; - found = xstrdup(format_quote_shell(saved)); + found = format_quote_shell(saved); free(saved); } if (modifiers & FORMAT_QUOTE_STYLE) { saved = found; - found = xstrdup(format_quote_style(saved)); + found = format_quote_style(saved); free(saved); } return (found); -- cgit From 697cebb4c1a28e0dc5b76037cb912eb1ace700b3 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 19 Jul 2022 07:48:48 +0100 Subject: Include curses properly for hyperlinks ifdef, from chrysn at fsfe dot org. --- tty-features.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tty-features.c b/tty-features.c index e387f823..64f0039a 100644 --- a/tty-features.c +++ b/tty-features.c @@ -21,6 +21,12 @@ #include #include +#if defined(HAVE_CURSES_H) +#include +#elif defined(HAVE_NCURSES_H) +#include +#endif + #include "tmux.h" /* -- cgit From 86dfbda0e4a2ad2638a5beed41dc4b441857362b Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 19 Jul 2022 06:51:31 +0000 Subject: Process modifiers as bits rather than using a switch, from Koichi Murase. --- tty-keys.c | 38 ++++++++++---------------------------- 1 file changed, 10 insertions(+), 28 deletions(-) diff --git a/tty-keys.c b/tty-keys.c index 4a712d42..bb9ec231 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -934,34 +934,16 @@ tty_keys_extended_key(struct tty *tty, const char *buf, size_t len, nkey = number; /* Update the modifiers. */ - switch (modifiers) { - case 2: - nkey |= KEYC_SHIFT; - break; - case 3: - nkey |= (KEYC_META|KEYC_IMPLIED_META); - break; - case 4: - nkey |= (KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META); - break; - case 5: - nkey |= KEYC_CTRL; - break; - case 6: - nkey |= (KEYC_SHIFT|KEYC_CTRL); - break; - case 7: - nkey |= (KEYC_META|KEYC_CTRL); - break; - case 8: - nkey |= (KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL); - break; - case 9: - nkey |= (KEYC_META|KEYC_IMPLIED_META); - break; - default: - *key = KEYC_NONE; - break; + if (modifiers > 0) { + modifiers--; + if (modifiers & 1) + nkey |= KEYC_SHIFT; + if (modifiers & 2) + nkey |= (KEYC_META|KEYC_IMPLIED_META); /* Alt */ + if (modifiers & 4) + nkey |= KEYC_CTRL; + if (modifiers & 8) + nkey |= (KEYC_META|KEYC_IMPLIED_META); /* Meta */ } /* -- cgit From e06c09889c8f56164ddc920d534357357769cf13 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 19 Jul 2022 07:54:11 +0100 Subject: Add permissions for workflow, GitHub issue 3202. --- .github/workflows/lock.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/lock.yml b/.github/workflows/lock.yml index c6691e46..a4d3e9b5 100644 --- a/.github/workflows/lock.yml +++ b/.github/workflows/lock.yml @@ -4,8 +4,14 @@ on: schedule: - cron: '0 0 * * *' +permissions: + contents: read + jobs: lock: + permissions: + issues: write # for dessant/lock-threads to lock issues + pull-requests: write # for dessant/lock-threads to lock PRs runs-on: ubuntu-latest steps: - uses: dessant/lock-threads@v2 -- cgit From ee431d482a8dac52f4fb16de1038e819a9e42b9a Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 19 Jul 2022 07:10:13 +0000 Subject: Do not ignore the "off" flag when checking if a pane should be stopped, GitHub issue 3250. --- server-client.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server-client.c b/server-client.c index 864b37b3..4109c1df 100644 --- a/server-client.c +++ b/server-client.c @@ -2221,7 +2221,8 @@ server_client_check_pane_buffer(struct window_pane *wp) } wpo = control_pane_offset(c, wp, &flag); if (wpo == NULL) { - off = 0; + if (!flag) + off = 0; continue; } if (!flag) -- cgit