From db3aabcc34464940c286d7ce3abc8f3edc94b7bc Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 10 Dec 2021 12:42:37 +0000 Subject: [PATCH 01/57] 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); } From 1f9aad2bb40fe90bec38288b08e8b152f13e04ad Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 10 Dec 2021 12:45:32 +0000 Subject: [PATCH 02/57] 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 From 9c1633a8659c4045b8303eb99d5f89b4f0dcb784 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 10 Dec 2021 12:51:11 +0000 Subject: [PATCH 03/57] 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 From c6149adf55a1ab108a9336ba15a82bd11a809cb3 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 13 Dec 2021 09:42:20 +0000 Subject: [PATCH 04/57] 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) From e4856de8bf23108ee9d335a22d7e16dd8ad855d8 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 20 Dec 2021 09:02:12 +0000 Subject: [PATCH 05/57] 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; From ff64aafeabe0c9b51b33a10313e2027ae1686bc4 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 21 Dec 2021 13:07:53 +0000 Subject: [PATCH 06/57] 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 { From 9aad945f7e58112c3f8819f658e379d672842d73 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 21 Dec 2021 14:57:28 +0000 Subject: [PATCH 07/57] 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", /* From e6e3c75ed70adece8b1cf93017f6f359d022bdb0 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 31 Dec 2021 11:35:49 +0000 Subject: [PATCH 08/57] 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) From b2b94dcba73a62e2949fb98aee097dbaea658760 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 6 Jan 2022 08:20:00 +0000 Subject: [PATCH 09/57] 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 { From e5e4df7a2241298b7ea3ba77b5fe1fdef6df0e08 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 6 Jan 2022 08:23:42 +0000 Subject: [PATCH 10/57] 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. From a6b361e775e0bf6301284391e75dd9af7af340da Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 17 Jan 2022 10:40:03 +0000 Subject: [PATCH 11/57] 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++; From 770d28b8c517fd87621ea3e8d6d7bdc4f35b3a94 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 1 Feb 2022 11:52:08 +0000 Subject: [PATCH 12/57] 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); From 7a4ba6d4a5458f4e2450024d72f0e16905dd5c64 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 1 Feb 2022 12:05:42 +0000 Subject: [PATCH 13/57] 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 From 7e34645fcbe93984461343d67373a29e552fec20 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 1 Feb 2022 14:46:41 +0000 Subject: [PATCH 14/57] 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; From 97900d0442252aa4b76f89745718038f39717ecd Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 1 Feb 2022 18:12:20 +0000 Subject: [PATCH 15/57] 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 From 5080acc12714862ef7a66286d7c3bce538c6d74f Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 3 Feb 2022 07:26:43 +0000 Subject: [PATCH 16/57] 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, From c401c91ad9f1ef8ea50b11762856a458de525bf9 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 3 Feb 2022 07:38:17 +0000 Subject: [PATCH 17/57] 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; From 57396b55a2a979925def85cf142f087c5ad340a6 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 3 Feb 2022 07:50:06 +0000 Subject: [PATCH 18/57] 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 From 948d2fad0ae9723263554115164c412aa095fe7a Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 3 Feb 2022 10:07:11 +0000 Subject: [PATCH 19/57] 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; From 3276f0c18473703a734d53495202f50a2c7d6b34 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 3 Feb 2022 11:06:11 +0000 Subject: [PATCH 20/57] 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); } From 07e2d88c205db5620154c6124c8ec7686b8c9028 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 4 Feb 2022 11:57:22 +0000 Subject: [PATCH 21/57] 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); From 040164555a0e41d23082b74a2a22ff370e8193c2 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 14 Feb 2022 09:10:48 +0000 Subject: [PATCH 22/57] 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); From f85208602ddd43ba55f29beae8d7a05cbfedefc1 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 15 Feb 2022 13:03:02 +0000 Subject: [PATCH 23/57] 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); +} + From 5076beb009f761999a3b218a1a8d7cbfbc80ee03 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 15 Feb 2022 13:11:29 +0000 Subject: [PATCH 24/57] 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 From c7c7e875a0f71451564746a20db8d84c418ad7d5 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 16 Feb 2022 12:26:23 +0000 Subject: [PATCH 25/57] 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" From 0027ee13a089efe7d1db1a4cfedb9b801635ded8 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 16 Feb 2022 18:55:05 +0000 Subject: [PATCH 26/57] 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); From 818b2176ef52a13867905d3d3e0269dc1b4d6fb4 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 17 Feb 2022 09:58:47 +0000 Subject: [PATCH 27/57] 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; } From a9b880921dce2836611873a4ec85e9425dc08b40 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 22 Feb 2022 11:01:57 +0000 Subject: [PATCH 28/57] 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); From 0fd01f8873ccae655b0f997ca0dc46c42ebe399d Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 22 Feb 2022 11:07:25 +0000 Subject: [PATCH 29/57] 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++ = '#'; From fa71e9a07911715da596d618fe045943337c596b Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 22 Feb 2022 11:10:41 +0000 Subject: [PATCH 30/57] 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 *); From d54b18ca2bb1383b120e920412e1a7340c4b1416 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 22 Feb 2022 13:31:18 +0000 Subject: [PATCH 31/57] 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); From e8d6d53a7b4af435806fefa5c9f6cef4387f3451 Mon Sep 17 00:00:00 2001 From: deraadt Date: Tue, 22 Feb 2022 17:35:01 +0000 Subject: [PATCH 32/57] 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 From 2e59ff2db95d573f56237776a10314b412721491 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 28 Feb 2022 09:24:22 +0000 Subject: [PATCH 33/57] 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; From 2da096d828f2b02f9d7fc329474f98aa6740328b Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 28 Feb 2022 09:34:57 +0000 Subject: [PATCH 34/57] 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) { From 047c011a1524a2d699da098ac423ef16f8a11bd6 Mon Sep 17 00:00:00 2001 From: topcat001 Date: Mon, 28 Feb 2022 13:11:28 -0800 Subject: [PATCH 35/57] 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) From f39a71aaade384d47917349f9d0b8dd709e8262e Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 1 Mar 2022 15:20:22 +0000 Subject: [PATCH 36/57] 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; } From 7d4224f2073ca4111968573e35997b65a0d5787a Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 3 Mar 2022 08:24:12 +0000 Subject: [PATCH 37/57] 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); From a731b1a9162bfa8157c474d411a4d0d6f131439d Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 7 Mar 2022 11:52:09 +0000 Subject: [PATCH 38/57] 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) From 54d22497166de02d50b93e922d762698fb82d25b Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 7 Mar 2022 15:21:39 +0000 Subject: [PATCH 39/57] 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) From afd9b68d102fc4a7614885267e99299247a031d8 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 8 Mar 2022 11:02:17 +0000 Subject: [PATCH 40/57] 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, From 9ed1226a66ee7e097ef858f7bc4b992fbcda2dc7 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 8 Mar 2022 11:04:15 +0000 Subject: [PATCH 41/57] 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); From 98cd8e4cad3afbc542de665e9d025d12b560bc6b Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 8 Mar 2022 11:28:40 +0000 Subject: [PATCH 42/57] 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); +} From 04952f15df1bc81d1878833533d344a6b1d1326c Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 8 Mar 2022 11:35:06 +0000 Subject: [PATCH 43/57] 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 From ad9b8059836d424f70a8579d28e28e0186cdbaa6 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 8 Mar 2022 12:01:19 +0000 Subject: [PATCH 44/57] 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 | 158 +++++++++++++++++++++++-------------------- tty-keys.c | 28 ++++++-- tty.c | 17 +++-- 7 files changed, 191 insertions(+), 114 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; + } exit_type; + enum msgtype exit_msgtype; + char *exit_session; + char *exit_message; - struct key_table *keytable; + struct key_table *keytable; - uint64_t redraw_panes; + uint64_t redraw_panes; - int message_ignore_keys; - int message_ignore_styles; - char *message_string; - struct event message_timer; + 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; + 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; - TAILQ_ENTRY(client) entry; + u_int *clipboard_panes; + u_int clipboard_npanes; + + 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); + evtimer_set(&tty->clipboard_timer, tty_clipboard_query_callback, tty); + evtimer_add(&tty->clipboard_timer, &tv); } - From a3d920930bf77cafa5260e25584dd3ba1d26f9cb Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 8 Mar 2022 18:31:46 +0000 Subject: [PATCH 45/57] Add remain-on-exit-format to set text shown when pane is dead. --- format.c | 37 +++++++++++++++++++++++++++++++++++++ options-table.c | 13 +++++++++++++ server-fn.c | 38 +++++++++++++++++--------------------- tmux.1 | 7 +++++++ tmux.h | 1 + 5 files changed, 75 insertions(+), 21 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); + 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); - time(&t); - ctime_r(&t, tim); - tim[strcspn(tim, "\n")] = '\0'; + expanded = format_single(NULL, s, NULL, NULL, NULL, wp); + format_draw(&ctx, &gc, sx, expanded, NULL, 0); + free(expanded); - 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); + 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; From cf217fa6181013bb70f77d6a50565e086205c5f4 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 8 Mar 2022 21:58:37 +0000 Subject: [PATCH 46/57] Fix user hooks (which are strings not arrays). --- notify.c | 67 ++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 50 insertions(+), 17 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); - } - a = options_array_next(a); } - cmdq_free_state(new_state); + 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); + } + } + + cmdq_free_state(state); } static enum cmd_retval From bfbe972225d10be7f6eaca22d88d90df4fae09c9 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 8 Mar 2022 22:14:25 +0000 Subject: [PATCH 47/57] 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; From e6e737ac0bf9a5be729b5c71f3a582355432d041 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 16 Mar 2022 17:00:17 +0000 Subject: [PATCH 48/57] 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; + } +} From 10d689e7354f789f951016f7f4d57a0c2d14e124 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 17 Mar 2022 11:35:37 +0000 Subject: [PATCH 49/57] 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 From 6a1706a62fb2442326cd37d84ff8b0d39bf0b7a6 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 17 Mar 2022 13:39:13 +0000 Subject: [PATCH 50/57] 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); From 792d13af49f2550a9a8d11b0099528628957a1a0 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 24 Mar 2022 09:05:57 +0000 Subject: [PATCH 51/57] 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) { From 938130bc6925808681cbc003a392ccce2a4455c1 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 24 Mar 2022 12:07:25 +0000 Subject: [PATCH 52/57] 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; From 422fcd294a6de4ba940144ede9ebb86a9ab6e41f Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 25 Mar 2022 06:14:42 +0000 Subject: [PATCH 53/57] 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); From 207b1bc385480cf453f2994a12f65706977b6c0f Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 28 Mar 2022 07:40:57 +0000 Subject: [PATCH 54/57] 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); From fc7f1e7acb3539a43df46136e971f770515e0b0d Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 28 Mar 2022 08:42:13 +0100 Subject: [PATCH 55/57] 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); From ded695504faad8f5d6430731f0bcb9beb0e1430a Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 30 Mar 2022 07:05:26 +0000 Subject: [PATCH 56/57] 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) From 2d9f4ca9a1368f47ddd6e121c9b1a7822884a31a Mon Sep 17 00:00:00 2001 From: naddy Date: Thu, 31 Mar 2022 17:27:27 +0000 Subject: [PATCH 57/57] 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,