From dd7d04be95f9c08fab3a277e0356eaecd0810fa7 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott <nicholas.marriott@gmail.com> Date: Mon, 20 Jan 2025 13:00:07 +0000 Subject: [PATCH 01/25] Update issue templates --- .github/ISSUE_TEMPLATE/custom.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/custom.md diff --git a/.github/ISSUE_TEMPLATE/custom.md b/.github/ISSUE_TEMPLATE/custom.md new file mode 100644 index 00000000..0aeab3a8 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/custom.md @@ -0,0 +1,31 @@ +--- +name: Custom issue template +about: Describe this issue template's purpose here. +title: '' +labels: '' +assignees: '' + +--- + +### Issue description + +Please read https://github.com/tmux/tmux/blob/master/.github/CONTRIBUTING.md +before opening an issue. + +If you have upgraded, make sure your issue is not covered in the CHANGES file +for your version: https://raw.githubusercontent.com/tmux/tmux/master/CHANGES + +Describe the problem and the steps to reproduce. Add a minimal tmux config if +necessary. Screenshots can be helpful, but no more than one or two. + +Do not report bugs (crashes, incorrect behaviour) without reproducing on a tmux +built from the latest code in Git. + +### Required information + +Please provide the following information: + +* tmux version (`tmux -V`). +* Platform (`uname -sp`). +* $TERM inside and outside of tmux (`echo $TERM`). +* Logs from tmux (`tmux kill-server; tmux -vv new`). From 0c9f8ff18950a70309411dd61768cb272b55c68e Mon Sep 17 00:00:00 2001 From: Nicholas Marriott <nicholas.marriott@gmail.com> Date: Mon, 20 Jan 2025 13:02:24 +0000 Subject: [PATCH 02/25] Update issue templates --- .../ISSUE_TEMPLATE/{custom.md => use-this-issue-template.md} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename .github/ISSUE_TEMPLATE/{custom.md => use-this-issue-template.md} (87%) diff --git a/.github/ISSUE_TEMPLATE/custom.md b/.github/ISSUE_TEMPLATE/use-this-issue-template.md similarity index 87% rename from .github/ISSUE_TEMPLATE/custom.md rename to .github/ISSUE_TEMPLATE/use-this-issue-template.md index 0aeab3a8..bc95fa2e 100644 --- a/.github/ISSUE_TEMPLATE/custom.md +++ b/.github/ISSUE_TEMPLATE/use-this-issue-template.md @@ -1,6 +1,6 @@ --- -name: Custom issue template -about: Describe this issue template's purpose here. +name: Use this issue template +about: Please read https://github.com/tmux/tmux/blob/master/.github/CONTRIBUTING.md title: '' labels: '' assignees: '' From 6ab268c7bb8b6941796ee6f2ad4b628f54a4f28d Mon Sep 17 00:00:00 2001 From: Nicholas Marriott <nicholas.marriott@gmail.com> Date: Mon, 20 Jan 2025 13:04:08 +0000 Subject: [PATCH 03/25] Remove old issue template. --- .github/ISSUE_TEMPLATE.md | 22 ---------------------- 1 file changed, 22 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE.md diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md deleted file mode 100644 index 8bf1e66a..00000000 --- a/.github/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,22 +0,0 @@ -### Issue description - -Please read https://github.com/tmux/tmux/blob/master/.github/CONTRIBUTING.md -before opening an issue. - -If you have upgraded, make sure your issue is not covered in the CHANGES file -for your version: https://raw.githubusercontent.com/tmux/tmux/master/CHANGES - -Describe the problem and the steps to reproduce. Add a minimal tmux config if -necessary. Screenshots can be helpful, but no more than one or two. - -Do not report bugs (crashes, incorrect behaviour) without reproducing on a tmux -built from the latest code in Git. - -### Required information - -Please provide the following information: - -* tmux version (`tmux -V`). -* Platform (`uname -sp`). -* $TERM inside and outside of tmux (`echo $TERM`). -* Logs from tmux (`tmux kill-server; tmux -vv new`). From 244bb726e26d7e590b04b7f57dd75fc431b2a492 Mon Sep 17 00:00:00 2001 From: nicm <nicm> Date: Mon, 27 Jan 2025 09:05:22 +0000 Subject: [PATCH 04/25] Add some missing spaces, from Ilya Grigoriev. --- cmd-display-menu.c | 4 ++-- cmd-split-window.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd-display-menu.c b/cmd-display-menu.c index 73728b91..ac136766 100644 --- a/cmd-display-menu.c +++ b/cmd-display-menu.c @@ -42,7 +42,7 @@ const struct cmd_entry cmd_display_menu_entry = { .args = { "b:c:C:H:s:S:MOt:T:x:y:", 1, -1, cmd_display_menu_args_parse }, .usage = "[-MO] [-b border-lines] [-c target-client] " "[-C starting-choice] [-H selected-style] [-s style] " - "[-S border-style] " CMD_TARGET_PANE_USAGE "[-T title] " + "[-S border-style] " CMD_TARGET_PANE_USAGE " [-T title] " "[-x position] [-y position] name key command ...", .target = { 't', CMD_FIND_PANE, 0 }, @@ -59,7 +59,7 @@ const struct cmd_entry cmd_display_popup_entry = { .usage = "[-BCE] [-b border-lines] [-c target-client] " "[-d start-directory] [-e environment] [-h height] " "[-s style] [-S border-style] " CMD_TARGET_PANE_USAGE - "[-T title] [-w width] [-x position] [-y position] " + " [-T title] [-w width] [-x position] [-y position] " "[shell-command]", .target = { 't', CMD_FIND_PANE, 0 }, diff --git a/cmd-split-window.c b/cmd-split-window.c index 82ff22af..128e9e8b 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -43,7 +43,7 @@ const struct cmd_entry cmd_split_window_entry = { .args = { "bc:de:fF:hIl:p:Pt:vZ", 0, -1, NULL }, .usage = "[-bdefhIPvZ] [-c start-directory] [-e environment] " "[-F format] [-l size] " CMD_TARGET_PANE_USAGE - "[shell-command]", + " [shell-command]", .target = { 't', CMD_FIND_PANE, 0 }, From 4c12ac9fb80f8f8d2096ba2463b448e9865b70cd Mon Sep 17 00:00:00 2001 From: nicm <nicm> Date: Mon, 27 Jan 2025 09:16:05 +0000 Subject: [PATCH 05/25] Make list-commands command show only one command if an argument is given, from Ilya Grigoriev in GitHub issue 4352. --- cmd-list-keys.c | 68 +++++++++++++++++++++++++++++-------------------- cmd.c | 2 +- tmux.h | 1 + 3 files changed, 43 insertions(+), 28 deletions(-) diff --git a/cmd-list-keys.c b/cmd-list-keys.c index 395b147c..b5050f09 100644 --- a/cmd-list-keys.c +++ b/cmd-list-keys.c @@ -91,7 +91,7 @@ cmd_list_keys_print_notes(struct cmdq_item *item, struct args *args, struct key_binding *bd; const char *key; char *tmp, *note; - int found = 0; + int found = 0; table = key_bindings_get_table(tablename, 0); if (table == NULL) @@ -321,6 +321,31 @@ out: return (CMD_RETURN_NORMAL); } +static void +cmd_list_single_command(const struct cmd_entry *entry, struct format_tree *ft, + const char *template, struct cmdq_item *item) +{ + const char *s; + char *line; + + format_add(ft, "command_list_name", "%s", entry->name); + if (entry->alias != NULL) + s = entry->alias; + else + s = ""; + format_add(ft, "command_list_alias", "%s", s); + if (entry->usage != NULL) + s = entry->usage; + else + s = ""; + format_add(ft, "command_list_usage", "%s", s); + + line = format_expand(ft, template); + if (*line != '\0') + cmdq_print(item, "%s", line); + free(line); +} + static enum cmd_retval cmd_list_keys_commands(struct cmd *self, struct cmdq_item *item) { @@ -328,8 +353,8 @@ cmd_list_keys_commands(struct cmd *self, struct cmdq_item *item) const struct cmd_entry **entryp; const struct cmd_entry *entry; struct format_tree *ft; - const char *template, *s, *command; - char *line; + const char *template, *command; + char *cause; if ((template = args_get(args, 'F')) == NULL) { template = "#{command_list_name}" @@ -341,30 +366,19 @@ cmd_list_keys_commands(struct cmd *self, struct cmdq_item *item) format_defaults(ft, NULL, NULL, NULL, NULL); command = args_string(args, 0); - for (entryp = cmd_table; *entryp != NULL; entryp++) { - entry = *entryp; - if (command != NULL && - (strcmp(entry->name, command) != 0 && - (entry->alias == NULL || - strcmp(entry->alias, command) != 0))) - continue; - - format_add(ft, "command_list_name", "%s", entry->name); - if (entry->alias != NULL) - s = entry->alias; - else - s = ""; - format_add(ft, "command_list_alias", "%s", s); - if (entry->usage != NULL) - s = entry->usage; - else - s = ""; - format_add(ft, "command_list_usage", "%s", s); - - line = format_expand(ft, template); - if (*line != '\0') - cmdq_print(item, "%s", line); - free(line); + if (command == NULL) { + for (entryp = cmd_table; *entryp != NULL; entryp++) + cmd_list_single_command(*entryp, ft, template, item); + } else { + entry = cmd_find(command, &cause); + if (entry != NULL) + cmd_list_single_command(entry, ft, template, item); + else { + cmdq_error(item, "%s", cause); + free(cause); + format_free(ft); + return (CMD_RETURN_ERROR); + } } format_free(ft); diff --git a/cmd.c b/cmd.c index 9e2ce036..64f63c92 100644 --- a/cmd.c +++ b/cmd.c @@ -445,7 +445,7 @@ cmd_get_alias(const char *name) } /* Look up a command entry by name. */ -static const struct cmd_entry * +const struct cmd_entry * cmd_find(const char *name, char **cause) { const struct cmd_entry **loop, *entry, *found = NULL; diff --git a/tmux.h b/tmux.h index 558bc009..1b6d889a 100644 --- a/tmux.h +++ b/tmux.h @@ -2597,6 +2597,7 @@ int cmd_find_from_nothing(struct cmd_find_state *, int); /* cmd.c */ extern const struct cmd_entry *cmd_table[]; +const struct cmd_entry *cmd_find(const char *, char **); void printflike(3, 4) cmd_log_argv(int, char **, const char *, ...); void cmd_prepend_argv(int *, char ***, const char *); void cmd_append_argv(int *, char ***, const char *); From 80eb460fc919e843c4fc53a08c2df84fa1654b98 Mon Sep 17 00:00:00 2001 From: nicm <nicm> Date: Mon, 10 Feb 2025 08:14:32 +0000 Subject: [PATCH 06/25] Add display-message -C flag to update pane while message is displayed, GitHub issue 4363 from Vitaly Ostrosablin. --- alerts.c | 8 ++++---- cmd-display-message.c | 7 ++++--- cmd-if-shell.c | 2 +- cmd-list-keys.c | 8 ++++---- cmd-queue.c | 2 +- cmd-run-shell.c | 2 +- mode-tree.c | 2 +- status.c | 6 ++++-- tmux.1 | 5 ++++- tmux.h | 2 +- window-customize.c | 4 ++-- 11 files changed, 27 insertions(+), 21 deletions(-) diff --git a/alerts.c b/alerts.c index b5ea0cf4..a2b4d654 100644 --- a/alerts.c +++ b/alerts.c @@ -316,11 +316,11 @@ alerts_set_message(struct winlink *wl, const char *type, const char *option) if (visual == VISUAL_OFF) continue; if (c->session->curw == wl) { - status_message_set(c, -1, 1, 0, "%s in current window", - type); + status_message_set(c, -1, 1, 0, 0, + "%s in current window", type); } else { - status_message_set(c, -1, 1, 0, "%s in window %d", type, - wl->idx); + status_message_set(c, -1, 1, 0, 0, + "%s in window %d", type, wl->idx); } } } diff --git a/cmd-display-message.c b/cmd-display-message.c index 512509f0..9ba6d13e 100644 --- a/cmd-display-message.c +++ b/cmd-display-message.c @@ -39,8 +39,8 @@ const struct cmd_entry cmd_display_message_entry = { .name = "display-message", .alias = "display", - .args = { "ac:d:lINpt:F:v", 0, 1, NULL }, - .usage = "[-aIlNpv] [-c target-client] [-d delay] [-F format] " + .args = { "aCc:d:lINpt:F:v", 0, 1, NULL }, + .usage = "[-aCIlNpv] [-c target-client] [-d delay] [-F format] " CMD_TARGET_PANE_USAGE " [message]", .target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL }, @@ -69,6 +69,7 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item) const char *template; char *msg, *cause; int delay = -1, flags, Nflag = args_has(args, 'N'); + int Cflag = args_has(args, 'C'); struct format_tree *ft; u_int count = args_count(args); struct evbuffer *evb; @@ -150,7 +151,7 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item) server_client_print(tc, 0, evb); evbuffer_free(evb); } else if (tc != NULL) - status_message_set(tc, delay, 0, Nflag, "%s", msg); + status_message_set(tc, delay, 0, Nflag, Cflag, "%s", msg); free(msg); format_free(ft); diff --git a/cmd-if-shell.c b/cmd-if-shell.c index 205a8ce1..81518939 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -157,7 +157,7 @@ cmd_if_shell_callback(struct job *job) if (cmdlist == NULL) { if (cdata->item == NULL) { *error = toupper((u_char)*error); - status_message_set(c, -1, 1, 0, "%s", error); + status_message_set(c, -1, 1, 0, 0, "%s", error); } else cmdq_error(cdata->item, "%s", error); free(error); diff --git a/cmd-list-keys.c b/cmd-list-keys.c index b5050f09..ddfc0e0c 100644 --- a/cmd-list-keys.c +++ b/cmd-list-keys.c @@ -114,8 +114,8 @@ cmd_list_keys_print_notes(struct cmdq_item *item, struct args *args, note = xstrdup(bd->note); tmp = utf8_padcstr(key, keywidth + 1); if (args_has(args, '1') && tc != NULL) { - status_message_set(tc, -1, 1, 0, "%s%s%s", prefix, tmp, - note); + status_message_set(tc, -1, 1, 0, 0, "%s%s%s", prefix, + tmp, note); } else cmdq_print(item, "%s%s%s", prefix, tmp, note); free(tmp); @@ -298,8 +298,8 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item) free(cp); if (args_has(args, '1') && tc != NULL) { - status_message_set(tc, -1, 1, 0, "bind-key %s", - tmp); + status_message_set(tc, -1, 1, 0, 0, + "bind-key %s", tmp); } else cmdq_print(item, "bind-key %s", tmp); free(key); diff --git a/cmd-queue.c b/cmd-queue.c index 60d685f8..a6dfc592 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -893,7 +893,7 @@ cmdq_error(struct cmdq_item *item, const char *fmt, ...) c->retval = 1; } else { *msg = toupper((u_char) *msg); - status_message_set(c, -1, 1, 0, "%s", msg); + status_message_set(c, -1, 1, 0, 0, "%s", msg); } free(msg); diff --git a/cmd-run-shell.c b/cmd-run-shell.c index 9e224c4e..be4c7cac 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -204,7 +204,7 @@ cmd_run_shell_timer(__unused int fd, __unused short events, void* arg) if (cmdlist == NULL) { if (cdata->item == NULL) { *error = toupper((u_char)*error); - status_message_set(c, -1, 1, 0, "%s", error); + status_message_set(c, -1, 1, 0, 0, "%s", error); } else cmdq_error(cdata->item, "%s", error); free(error); diff --git a/mode-tree.c b/mode-tree.c index e1170a3d..d43cf1b3 100644 --- a/mode-tree.c +++ b/mode-tree.c @@ -1297,7 +1297,7 @@ mode_tree_run_command(struct client *c, struct cmd_find_state *fs, if (status == CMD_PARSE_ERROR) { if (c != NULL) { *error = toupper((u_char)*error); - status_message_set(c, -1, 1, 0, "%s", error); + status_message_set(c, -1, 1, 0, 0, "%s", error); } free(error); } diff --git a/status.c b/status.c index 32833f8c..745ee3ae 100644 --- a/status.c +++ b/status.c @@ -470,7 +470,7 @@ status_redraw(struct client *c) /* Set a status line message. */ void status_message_set(struct client *c, int delay, int ignore_styles, - int ignore_keys, const char *fmt, ...) + int ignore_keys, int no_freeze, const char *fmt, ...) { struct timeval tv; va_list ap; @@ -514,7 +514,9 @@ status_message_set(struct client *c, int delay, int ignore_styles, c->message_ignore_keys = ignore_keys; c->message_ignore_styles = ignore_styles; - c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE); + if (!no_freeze) + c->tty.flags |= TTY_FREEZE; + c->tty.flags |= TTY_NOCURSOR; c->flags |= CLIENT_REDRAWSTATUS; } diff --git a/tmux.1 b/tmux.1 index 2cb3b3c6..6f9c68e1 100644 --- a/tmux.1 +++ b/tmux.1 @@ -6742,7 +6742,7 @@ The following keys are available in menus: .El .Tg display .It Xo Ic display-message -.Op Fl aIlNpv +.Op Fl aCIlNpv .Op Fl c Ar target-client .Op Fl d Ar delay .Op Fl t Ar target-pane @@ -6765,6 +6765,9 @@ option is used; a delay of zero waits for a key press. .Ql N ignores key presses and closes only after the delay expires. If +.Fl C +given, the pane will continue to be updated while the message is displayed. +If .Fl l is given, .Ar message diff --git a/tmux.h b/tmux.h index 1b6d889a..065bb042 100644 --- a/tmux.h +++ b/tmux.h @@ -2847,7 +2847,7 @@ struct style_range *status_get_range(struct client *, u_int, u_int); void status_init(struct client *); void status_free(struct client *); int status_redraw(struct client *); -void printflike(5, 6) status_message_set(struct client *, int, int, int, +void printflike(6, 7) status_message_set(struct client *, int, int, int, int, const char *, ...); void status_message_clear(struct client *); int status_message_redraw(struct client *); diff --git a/window-customize.c b/window-customize.c index 387254e0..c49e57af 100644 --- a/window-customize.c +++ b/window-customize.c @@ -1000,7 +1000,7 @@ window_customize_set_option_callback(struct client *c, void *itemdata, fail: *cause = toupper((u_char)*cause); - status_message_set(c, -1, 1, 0, "%s", cause); + status_message_set(c, -1, 1, 0, 0, "%s", cause); free(cause); return (0); } @@ -1203,7 +1203,7 @@ window_customize_set_command_callback(struct client *c, void *itemdata, fail: *error = toupper((u_char)*error); - status_message_set(c, -1, 1, 0, "%s", error); + status_message_set(c, -1, 1, 0, 0, "%s", error); free(error); return (0); } From 5d1a6acc84bb024aadf4288b4e3a9e7cc0289e83 Mon Sep 17 00:00:00 2001 From: nicm <nicm> Date: Mon, 10 Feb 2025 08:18:23 +0000 Subject: [PATCH 07/25] Align index numbers in trees, from David Mandelberg, GitHub issue 4360. --- mode-tree.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/mode-tree.c b/mode-tree.c index d43cf1b3..24cbea04 100644 --- a/mode-tree.c +++ b/mode-tree.c @@ -66,6 +66,7 @@ struct mode_tree_data { u_int line_size; u_int depth; + u_int maxdepth; u_int width; u_int height; @@ -196,6 +197,8 @@ mode_tree_build_lines(struct mode_tree_data *mtd, int flat = 1; mtd->depth = depth; + if (depth > mtd->maxdepth) + mtd->maxdepth = depth; TAILQ_FOREACH(mti, mtl, entry) { mtd->line_list = xreallocarray(mtd->line_list, mtd->line_size + 1, sizeof *mtd->line_list); @@ -528,6 +531,7 @@ mode_tree_build(struct mode_tree_data *mtd) TAILQ_INIT(&mtd->saved); mode_tree_clear_lines(mtd); + mtd->maxdepth = 0; mode_tree_build_lines(mtd, &mtd->children, 0); if (mtd->line_list != NULL && tag == UINT64_MAX) @@ -658,7 +662,7 @@ mode_tree_draw(struct mode_tree_data *mtd) char *text, *start, *key; const char *tag, *symbol; size_t size, n; - int keylen, pad; + int keylen, pad, namelen[mtd->maxdepth + 1]; if (mtd->line_size == 0) return; @@ -682,6 +686,15 @@ mode_tree_draw(struct mode_tree_data *mtd) keylen = mti->keylen + 3; } + for (i = 0; i < mtd->maxdepth + 1; i++) + namelen[i] = 0; + for (i = 0; i < mtd->line_size; i++) { + line = &mtd->line_list[i]; + mti = line->item; + if ((int)strlen(mti->name) > namelen[line->depth]) + namelen[line->depth] = strlen(mti->name); + } + for (i = 0; i < mtd->line_size; i++) { if (i < mtd->offset) continue; @@ -731,8 +744,9 @@ mode_tree_draw(struct mode_tree_data *mtd) tag = "*"; else tag = ""; - xasprintf(&text, "%-*s%s%s%s%s", keylen, key, start, mti->name, - tag, (mti->text != NULL) ? ": " : "" ); + xasprintf(&text, "%-*s%s%*s%s%s", keylen, key, start, + namelen[line->depth], mti->name, tag, + (mti->text != NULL) ? ": " : "" ); width = utf8_cstrwidth(text); if (width > w) width = w; From c4b9716873a0a5ecc68697c73793eaa553192d1b Mon Sep 17 00:00:00 2001 From: Nicholas Marriott <nicholas.marriott@gmail.com> Date: Thu, 13 Feb 2025 16:24:16 +0000 Subject: [PATCH 08/25] Look for imsg_add not _init now. --- configure.ac | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/configure.ac b/configure.ac index 4f90fcd5..98b67a4d 100644 --- a/configure.ac +++ b/configure.ac @@ -638,9 +638,9 @@ else AC_LIBOBJ(err) fi -# Look for imsg_init in libutil. -AC_SEARCH_LIBS(imsg_init, util, found_imsg_init=yes, found_imsg_init=no) -if test "x$found_imsg_init" = xyes; then +# Look for imsg_add in libutil. +AC_SEARCH_LIBS(imsg_add, util, found_imsg_add=yes, found_imsg_add=no) +if test "x$found_imsg_add" = xyes; then AC_DEFINE(HAVE_IMSG) else AC_LIBOBJ(imsg) From 47a56c11f26a66c97ce90af97f20a7b724dd515d Mon Sep 17 00:00:00 2001 From: nicm <nicm> Date: Thu, 13 Feb 2025 16:31:25 +0000 Subject: [PATCH 09/25] Add a note about C-r and C-s behaviour, GitHub issue 4309. Also add a missing word, from jmc@. --- tmux.1 | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tmux.1 b/tmux.1 index 6f9c68e1..ed6e7a53 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2329,6 +2329,15 @@ repeats the last search and does the same but reverses the direction (forward becomes backward and backward becomes forward). .Pp +The default incremental search key bindings, +.Ql C-r +and +.Ql C-s , +are designed to emulate +.Xr emacs 1 . +When first pressed they allow a new search term to be entered; if pressed with +an empty search term they repeat the previously used search term. +.Pp The .Ql next-prompt and @@ -6766,7 +6775,7 @@ option is used; a delay of zero waits for a key press. ignores key presses and closes only after the delay expires. If .Fl C -given, the pane will continue to be updated while the message is displayed. +is given, the pane will continue to be updated while the message is displayed. If .Fl l is given, From 250c88efdccd892bdbc99bfb565ace6fa90e5d0d Mon Sep 17 00:00:00 2001 From: Nicholas Marriott <nicholas.marriott@gmail.com> Date: Thu, 20 Feb 2025 13:30:26 +0000 Subject: [PATCH 10/25] Add .swp, from Nikola Tadic. --- .gitignore | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/.gitignore b/.gitignore index ec49a6de..bf012d5f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,23 +1,24 @@ -*.o -*~ -*.diff -*.patch *.core -core -tags +*.dSYM +*.diff +*.o +*.patch +*.swp +*~ .deps/ -compat/.dirstamp -aclocal.m4 -autom4te.cache/ -config.log -config.status -etc/ -tmux +.dirstamp Makefile Makefile.in -configure -tmux.1.* -*.dSYM +aclocal.m4 +autom4te.cache/ cmd-parse.c +compat/.dirstamp +config.log +config.status +configure +core +etc/ fuzz/*-fuzzer -.dirstamp +tags +tmux +tmux.1.* From 18331e39bf7e88132fd0f1eafb4424347b6efb4a Mon Sep 17 00:00:00 2001 From: nicm <nicm> Date: Thu, 20 Feb 2025 13:32:07 +0000 Subject: [PATCH 11/25] Reset overlay_resize pointer when clearing overlay. --- server-client.c | 1 + 1 file changed, 1 insertion(+) diff --git a/server-client.c b/server-client.c index 4581e9bc..847a8576 100644 --- a/server-client.c +++ b/server-client.c @@ -156,6 +156,7 @@ server_client_clear_overlay(struct client *c) c->overlay_draw = NULL; c->overlay_key = NULL; c->overlay_free = NULL; + c->overlay_resize = NULL; c->overlay_data = NULL; c->tty.flags &= ~(TTY_FREEZE|TTY_NOCURSOR); From 084e6ee9ec0cc58a8f3e673858c74a2b3952081b Mon Sep 17 00:00:00 2001 From: nicm <nicm> Date: Thu, 20 Feb 2025 13:39:58 +0000 Subject: [PATCH 12/25] Add a -M flag to capture-pane to use the copy mode screen, GitHub issue 4358. --- cmd-capture-pane.c | 38 ++++++++++++++++++++++++++------------ tmux.1 | 5 ++++- tmux.h | 1 + window-copy.c | 11 +++++++++++ 4 files changed, 42 insertions(+), 13 deletions(-) diff --git a/cmd-capture-pane.c b/cmd-capture-pane.c index 8f7250e8..81e1f7f9 100644 --- a/cmd-capture-pane.c +++ b/cmd-capture-pane.c @@ -39,8 +39,8 @@ const struct cmd_entry cmd_capture_pane_entry = { .name = "capture-pane", .alias = "capturep", - .args = { "ab:CeE:JNpPqS:Tt:", 0, 0, NULL }, - .usage = "[-aCeJNpPqT] " CMD_BUFFER_USAGE " [-E end-line] " + .args = { "ab:CeE:JMNpPqS:Tt:", 0, 0, NULL }, + .usage = "[-aCeJMNpPqT] " CMD_BUFFER_USAGE " [-E end-line] " "[-S start-line] " CMD_TARGET_PANE_USAGE, .target = { 't', CMD_FIND_PANE, 0 }, @@ -107,14 +107,16 @@ static char * cmd_capture_pane_history(struct args *args, struct cmdq_item *item, struct window_pane *wp, size_t *len) { - struct grid *gd; - const struct grid_line *gl; - struct grid_cell *gc = NULL; - int n, join_lines, flags = 0; - u_int i, sx, top, bottom, tmp; - char *cause, *buf, *line; - const char *Sflag, *Eflag; - size_t linelen; + struct grid *gd; + const struct grid_line *gl; + struct screen *s; + struct grid_cell *gc = NULL; + struct window_mode_entry *wme; + int n, join_lines, flags = 0; + u_int i, sx, top, bottom, tmp; + char *cause, *buf, *line; + const char *Sflag, *Eflag; + size_t linelen; sx = screen_size_x(&wp->base); if (args_has(args, 'a')) { @@ -126,8 +128,20 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item, } return (xstrdup("")); } - } else + s = &wp->base; + } else if (args_has(args, 'M')) { + wme = TAILQ_FIRST(&wp->modes); + if (wme != NULL && wme->mode->get_screen != NULL) { + s = wme->mode->get_screen (wme); + gd = s->grid; + } else { + s = &wp->base; + gd = wp->base.grid; + } + } else { + s = &wp->base; gd = wp->base.grid; + } Sflag = args_get(args, 'S'); if (Sflag != NULL && strcmp(Sflag, "-") == 0) @@ -181,7 +195,7 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item, buf = NULL; for (i = top; i <= bottom; i++) { - line = grid_string_cells(gd, 0, i, sx, &gc, flags, wp->screen); + line = grid_string_cells(gd, 0, i, sx, &gc, flags, s); linelen = strlen(line); buf = cmd_capture_pane_append(buf, len, line, linelen); diff --git a/tmux.1 b/tmux.1 index ed6e7a53..5c979758 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2554,7 +2554,7 @@ but a different format may be specified with .Fl F . .Tg capturep .It Xo Ic capture-pane -.Op Fl aAepPqCJN +.Op Fl aepPqCJMN .Op Fl b Ar buffer-name .Op Fl E Ar end-line .Op Fl S Ar start-line @@ -2573,6 +2573,9 @@ is given, the alternate screen is used, and the history is not accessible. If no alternate screen exists, an error will be returned unless .Fl q is given. +Similarly, if the pane is in a mode, +.Fl M +uses the screen for the mode. If .Fl e is given, the output includes escape sequences for text and background diff --git a/tmux.h b/tmux.h index 065bb042..cd93097e 100644 --- a/tmux.h +++ b/tmux.h @@ -1059,6 +1059,7 @@ struct window_mode { struct mouse_event *); void (*formats)(struct window_mode_entry *, struct format_tree *); + struct screen *(*get_screen)(struct window_mode_entry *); }; /* Active window mode. */ diff --git a/window-copy.c b/window-copy.c index 1cda6d39..7dcc8432 100644 --- a/window-copy.c +++ b/window-copy.c @@ -40,6 +40,7 @@ static void window_copy_free(struct window_mode_entry *); static void window_copy_resize(struct window_mode_entry *, u_int, u_int); static void window_copy_formats(struct window_mode_entry *, struct format_tree *); +static struct screen *window_copy_get_screen(struct window_mode_entry *); static void window_copy_scroll1(struct window_mode_entry *, struct window_pane *wp, int, u_int, int); static void window_copy_pageup1(struct window_mode_entry *, int); @@ -160,6 +161,7 @@ const struct window_mode window_copy_mode = { .key_table = window_copy_key_table, .command = window_copy_command, .formats = window_copy_formats, + .get_screen = window_copy_get_screen }; const struct window_mode window_view_mode = { @@ -171,6 +173,7 @@ const struct window_mode window_view_mode = { .key_table = window_copy_key_table, .command = window_copy_command, .formats = window_copy_formats, + .get_screen = window_copy_get_screen }; enum { @@ -972,6 +975,14 @@ window_copy_formats(struct window_mode_entry *wme, struct format_tree *ft) window_copy_cursor_hyperlink_cb); } +static struct screen * +window_copy_get_screen(struct window_mode_entry *wme) +{ + struct window_copy_mode_data *data = wme->data; + + return (data->backing); +} + static void window_copy_size_changed(struct window_mode_entry *wme) { From 27ee0c9c3bddcc7ab4ccc8b4b85f83a8e186781f Mon Sep 17 00:00:00 2001 From: nicm <nicm> Date: Wed, 26 Feb 2025 07:39:50 +0000 Subject: [PATCH 13/25] Add the width of the scrollbars to the calculation of the width of the window panes when finding the adjacent panes, GitHub issue 4370 from Michael Grant. --- window.c | 94 +++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 70 insertions(+), 24 deletions(-) diff --git a/window.c b/window.c index 35fc370b..a6354e20 100644 --- a/window.c +++ b/window.c @@ -1349,6 +1349,36 @@ window_pane_choose_best(struct window_pane **list, u_int size) return (best); } +/* + * Get full size and offset of a window pane including the area of the + * scrollbars if they were visible but not including the border(s). + */ +static void +window_pane_full_size_offset(struct window_pane *wp, u_int *xoff, u_int *yoff, + u_int *sx, u_int *sy) +{ + struct window *w = wp->window; + int pane_scrollbars; + u_int sb_w, sb_pos; + + pane_scrollbars = options_get_number(w->options, "pane-scrollbars"); + sb_pos = options_get_number(w->options, "pane-scrollbars-position"); + + if (window_pane_show_scrollbar(wp, pane_scrollbars)) + sb_w = wp->scrollbar_style.width + wp->scrollbar_style.pad; + else + sb_w = 0; + if (sb_pos == PANE_SCROLLBARS_LEFT) { + *xoff = wp->xoff - sb_w; + *sx = wp->sx + sb_w; + } else { /* sb_pos == PANE_SCROLLBARS_RIGHT */ + *xoff = wp->xoff; + *sx = wp->sx + sb_w; + } + *yoff = wp->yoff; + *sy = wp->sy; +} + /* * Find the pane directly above another. We build a list of those adjacent to * top edge and then choose the best. @@ -1360,6 +1390,7 @@ window_pane_find_up(struct window_pane *wp) struct window_pane *next, *best, **list; u_int edge, left, right, end, size; int status, found; + u_int xoff, yoff, sx, sy; if (wp == NULL) return (NULL); @@ -1369,7 +1400,9 @@ window_pane_find_up(struct window_pane *wp) list = NULL; size = 0; - edge = wp->yoff; + window_pane_full_size_offset(wp, &xoff, &yoff, &sx, &sy); + + edge = yoff; if (status == PANE_STATUS_TOP) { if (edge == 1) edge = w->sy + 1; @@ -1381,20 +1414,21 @@ window_pane_find_up(struct window_pane *wp) edge = w->sy + 1; } - left = wp->xoff; - right = wp->xoff + wp->sx; + left = xoff; + right = xoff + sx; TAILQ_FOREACH(next, &w->panes, entry) { + window_pane_full_size_offset(next, &xoff, &yoff, &sx, &sy); if (next == wp) continue; - if (next->yoff + next->sy + 1 != edge) + if (yoff + sy + 1 != edge) continue; - end = next->xoff + next->sx - 1; + end = xoff + sx - 1; found = 0; - if (next->xoff < left && end > right) + if (xoff < left && end > right) found = 1; - else if (next->xoff >= left && next->xoff <= right) + else if (xoff >= left && xoff <= right) found = 1; else if (end >= left && end <= right) found = 1; @@ -1417,6 +1451,7 @@ window_pane_find_down(struct window_pane *wp) struct window_pane *next, *best, **list; u_int edge, left, right, end, size; int status, found; + u_int xoff, yoff, sx, sy; if (wp == NULL) return (NULL); @@ -1426,7 +1461,9 @@ window_pane_find_down(struct window_pane *wp) list = NULL; size = 0; - edge = wp->yoff + wp->sy + 1; + window_pane_full_size_offset(wp, &xoff, &yoff, &sx, &sy); + + edge = yoff + sy + 1; if (status == PANE_STATUS_TOP) { if (edge >= w->sy) edge = 1; @@ -1442,16 +1479,17 @@ window_pane_find_down(struct window_pane *wp) right = wp->xoff + wp->sx; TAILQ_FOREACH(next, &w->panes, entry) { + window_pane_full_size_offset(next, &xoff, &yoff, &sx, &sy); if (next == wp) continue; - if (next->yoff != edge) + if (yoff != edge) continue; - end = next->xoff + next->sx - 1; + end = xoff + sx - 1; found = 0; - if (next->xoff < left && end > right) + if (xoff < left && end > right) found = 1; - else if (next->xoff >= left && next->xoff <= right) + else if (xoff >= left && xoff <= right) found = 1; else if (end >= left && end <= right) found = 1; @@ -1474,6 +1512,7 @@ window_pane_find_left(struct window_pane *wp) struct window_pane *next, *best, **list; u_int edge, top, bottom, end, size; int found; + u_int xoff, yoff, sx, sy; if (wp == NULL) return (NULL); @@ -1482,24 +1521,27 @@ window_pane_find_left(struct window_pane *wp) list = NULL; size = 0; - edge = wp->xoff; + window_pane_full_size_offset(wp, &xoff, &yoff, &sx, &sy); + + edge = xoff; if (edge == 0) edge = w->sx + 1; - top = wp->yoff; - bottom = wp->yoff + wp->sy; + top = yoff; + bottom = yoff + sy; TAILQ_FOREACH(next, &w->panes, entry) { + window_pane_full_size_offset(next, &xoff, &yoff, &sx, &sy); if (next == wp) continue; - if (next->xoff + next->sx + 1 != edge) + if (xoff + sx + 1 != edge) continue; - end = next->yoff + next->sy - 1; + end = yoff + sy - 1; found = 0; - if (next->yoff < top && end > bottom) + if (yoff < top && end > bottom) found = 1; - else if (next->yoff >= top && next->yoff <= bottom) + else if (yoff >= top && yoff <= bottom) found = 1; else if (end >= top && end <= bottom) found = 1; @@ -1522,6 +1564,7 @@ window_pane_find_right(struct window_pane *wp) struct window_pane *next, *best, **list; u_int edge, top, bottom, end, size; int found; + u_int xoff, yoff, sx, sy; if (wp == NULL) return (NULL); @@ -1530,7 +1573,9 @@ window_pane_find_right(struct window_pane *wp) list = NULL; size = 0; - edge = wp->xoff + wp->sx + 1; + window_pane_full_size_offset(wp, &xoff, &yoff, &sx, &sy); + + edge = xoff + sx + 1; if (edge >= w->sx) edge = 0; @@ -1538,16 +1583,17 @@ window_pane_find_right(struct window_pane *wp) bottom = wp->yoff + wp->sy; TAILQ_FOREACH(next, &w->panes, entry) { + window_pane_full_size_offset(next, &xoff, &yoff, &sx, &sy); if (next == wp) continue; - if (next->xoff != edge) + if (xoff != edge) continue; - end = next->yoff + next->sy - 1; + end = yoff + sy - 1; found = 0; - if (next->yoff < top && end > bottom) + if (yoff < top && end > bottom) found = 1; - else if (next->yoff >= top && next->yoff <= bottom) + else if (yoff >= top && yoff <= bottom) found = 1; else if (end >= top && end <= bottom) found = 1; From 9a8f46e554d848c9b1114a98e14fd17d705f5f84 Mon Sep 17 00:00:00 2001 From: nicm <nicm> Date: Wed, 26 Feb 2025 07:42:52 +0000 Subject: [PATCH 14/25] Fix colouring of pane border when scrollbars are enabled, GitHub issue 4378 from Michael Grant. --- screen-redraw.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/screen-redraw.c b/screen-redraw.c index c1e1481c..fb530007 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -433,11 +433,15 @@ screen_redraw_make_pane_status(struct client *c, struct window_pane *wp, const char *fmt; struct format_tree *ft; char *expanded; - int pane_status = rctx->pane_status; + int pane_status = rctx->pane_status, sb_w = 0; + int pane_scrollbars = rctx->pane_scrollbars; u_int width, i, cell_type, px, py; struct screen_write_ctx ctx; struct screen old; + if (window_pane_show_scrollbar(wp, pane_scrollbars)) + sb_w = wp->scrollbar_style.width + wp->scrollbar_style.pad; + ft = format_create(c, NULL, FORMAT_PANE|wp->id, FORMAT_STATUS); format_defaults(ft, c, c->session, c->session->curw, wp); @@ -451,7 +455,7 @@ screen_redraw_make_pane_status(struct client *c, struct window_pane *wp, if (wp->sx < 4) wp->status_size = width = 0; else - wp->status_size = width = wp->sx - 4; + wp->status_size = width = wp->sx + sb_w - 2; memcpy(&old, &wp->status_screen, sizeof old); screen_init(&wp->status_screen, width, 1, 0); From d938ab5dd7698e9f4dd92986d5b9746b2cd12834 Mon Sep 17 00:00:00 2001 From: nicm <nicm> Date: Wed, 26 Feb 2025 07:47:46 +0000 Subject: [PATCH 15/25] If command parsing fails in the client, report the error rather than trying to send the command to the server. GitHub issue 4372 from Nikola Tadic. --- client.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/client.c b/client.c index fcc49c39..9d16e1e9 100644 --- a/client.c +++ b/client.c @@ -267,8 +267,13 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags, if (cmd_list_any_have(pr->cmdlist, CMD_STARTSERVER)) flags |= CLIENT_STARTSERVER; cmd_list_free(pr->cmdlist); - } else + } else { + fprintf(stderr, "%s\n", pr->error); + args_free_values(values, argc); + free(values); free(pr->error); + return 1; + } args_free_values(values, argc); free(values); } From f224d61f3781a246a75e48f222755246920adcb9 Mon Sep 17 00:00:00 2001 From: nicm <nicm> Date: Wed, 26 Feb 2025 07:50:36 +0000 Subject: [PATCH 16/25] Document the use of ';' as a modifier separator, from Matt Liggett in GitHub issue 4384. --- tmux.1 | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tmux.1 b/tmux.1 index 5c979758..e17a92b3 100644 --- a/tmux.1 +++ b/tmux.1 @@ -5867,6 +5867,12 @@ with .Ql bar/ throughout. .Pp +Multiple modifiers may be separated with a semicolon (;) as in +.Ql #{T;=10:status-left} , +which limits the resulting +.Xr strftime 3 -expanded +string to at most 10 characters. +.Pp In addition, the last line of a shell command's output may be inserted using .Ql #() . For example, From 21f7db4c4dbbc1837667a7d813663036f9288ea6 Mon Sep 17 00:00:00 2001 From: nicm <nicm> Date: Wed, 26 Feb 2025 08:55:27 +0000 Subject: [PATCH 17/25] Do not allow meta prefix on paste start and end sequences, GitHub issue 4387. --- server-client.c | 4 ++-- tmux.h | 3 ++- tty-keys.c | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/server-client.c b/server-client.c index 847a8576..7b017697 100644 --- a/server-client.c +++ b/server-client.c @@ -2247,13 +2247,13 @@ out: static int server_client_is_bracket_paste(struct client *c, key_code key) { - if (key == KEYC_PASTE_START) { + if ((key & KEYC_MASK_KEY) == KEYC_PASTE_START) { c->flags |= CLIENT_BRACKETPASTING; log_debug("%s: bracket paste on", c->name); return (0); } - if (key == KEYC_PASTE_END) { + if ((key & KEYC_MASK_KEY) == KEYC_PASTE_END) { c->flags &= ~CLIENT_BRACKETPASTING; log_debug("%s: bracket paste off", c->name); return (0); diff --git a/tmux.h b/tmux.h index cd93097e..52b85cb1 100644 --- a/tmux.h +++ b/tmux.h @@ -167,7 +167,8 @@ struct winlink; /* Is this a paste key? */ #define KEYC_IS_PASTE(key) \ - ((key) == KEYC_PASTE_START || (key) == KEYC_PASTE_END) + (((key) & KEYC_MASK_KEY) == KEYC_PASTE_START || \ + ((key) & KEYC_MASK_KEY) == KEYC_PASTE_END) /* Multiple click timeout. */ #define KEYC_CLICK_TIMEOUT 300 diff --git a/tty-keys.c b/tty-keys.c index 0de31c5d..77ab4ae1 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -208,8 +208,8 @@ static const struct tty_default_key_raw tty_default_raw_keys[] = { { "\033[O", KEYC_FOCUS_OUT }, /* Paste keys. */ - { "\033[200~", KEYC_PASTE_START }, - { "\033[201~", KEYC_PASTE_END }, + { "\033[200~", KEYC_PASTE_START|KEYC_IMPLIED_META }, + { "\033[201~", KEYC_PASTE_END|KEYC_IMPLIED_META }, /* Extended keys. */ { "\033[1;5Z", '\011'|KEYC_CTRL|KEYC_SHIFT }, From 91c0de60b4fb0962699eaf7c1b32ae441767e963 Mon Sep 17 00:00:00 2001 From: nicm <nicm> Date: Wed, 26 Feb 2025 09:02:00 +0000 Subject: [PATCH 18/25] Also need the implied meta paste keys in the list for output. --- input-keys.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/input-keys.c b/input-keys.c index fa4e3f8e..900dea07 100644 --- a/input-keys.c +++ b/input-keys.c @@ -54,9 +54,15 @@ static struct input_key_entry input_key_defaults[] = { { .key = KEYC_PASTE_START, .data = "\033[200~" }, + { .key = KEYC_PASTE_START|KEYC_IMPLIED_META, + .data = "\033[200~" + }, { .key = KEYC_PASTE_END, .data = "\033[201~" }, + { .key = KEYC_PASTE_END|KEYC_IMPLIED_META, + .data = "\033[201~" + }, /* Function keys. */ { .key = KEYC_F1, From 3543d79048f4fc0e72c57f109a201baf4ebfaf3e Mon Sep 17 00:00:00 2001 From: nicm <nicm> Date: Tue, 4 Mar 2025 08:03:19 +0000 Subject: [PATCH 19/25] Free fill character string if it cannot be used, GitHub issue 4394. --- window.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/window.c b/window.c index a6354e20..dc220462 100644 --- a/window.c +++ b/window.c @@ -1759,6 +1759,8 @@ window_set_fill_character(struct window *w) ud = utf8_fromcstr(value); if (ud != NULL && ud[0].width == 1) w->fill_character = ud; + else + free(ud); } } From eaf70c955b37560de5a147674b55400c72c27f1e Mon Sep 17 00:00:00 2001 From: nicm <nicm> Date: Tue, 4 Mar 2025 08:45:04 +0000 Subject: [PATCH 20/25] Add mode 2031 support to automatically report dark or light theme. tmux will guess the theme from the background colour on terminals which do not themselves support the escape sequence. Written by Jonathan Slenders, GitHub issue 4353. --- cmd-break-pane.c | 2 +- cmd-join-pane.c | 2 +- cmd-select-pane.c | 8 ++- cmd-send-keys.c | 2 +- cmd-swap-pane.c | 4 +- colour.c | 40 ++++++++++++ format.c | 20 ++++++ input-keys.c | 6 ++ input.c | 139 +++++++++++++-------------------------- options-table.c | 2 + options.c | 2 +- server-client.c | 38 ++++++++++- session.c | 13 ++++ tmux.h | 34 +++++++++- tty-keys.c | 6 ++ tty.c | 8 +++ window.c | 161 +++++++++++++++++++++++++++++++++++++++++++++- 17 files changed, 380 insertions(+), 107 deletions(-) diff --git a/cmd-break-pane.c b/cmd-break-pane.c index 9c4b1508..a5582e46 100644 --- a/cmd-break-pane.c +++ b/cmd-break-pane.c @@ -99,7 +99,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item) w = wp->window = window_create(w->sx, w->sy, w->xpixel, w->ypixel); options_set_parent(wp->options, w->options); - wp->flags |= PANE_STYLECHANGED; + wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED); TAILQ_INSERT_HEAD(&w->panes, wp, entry); w->active = wp; w->latest = tc; diff --git a/cmd-join-pane.c b/cmd-join-pane.c index 627424ec..3300498f 100644 --- a/cmd-join-pane.c +++ b/cmd-join-pane.c @@ -150,7 +150,7 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item) src_wp->window = dst_w; options_set_parent(src_wp->options, dst_w->options); - src_wp->flags |= PANE_STYLECHANGED; + src_wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED); if (flags & SPAWN_BEFORE) TAILQ_INSERT_BEFORE(dst_wp, src_wp, entry); else diff --git a/cmd-select-pane.c b/cmd-select-pane.c index 135729f5..3cabe07e 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -149,12 +149,14 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) markedwp = marked_pane.wp; if (lastwp != NULL) { - lastwp->flags |= (PANE_REDRAW|PANE_STYLECHANGED); + lastwp->flags |= (PANE_REDRAW|PANE_STYLECHANGED| + PANE_THEMECHANGED); server_redraw_window_borders(lastwp->window); server_status_window(lastwp->window); } if (markedwp != NULL) { - markedwp->flags |= (PANE_REDRAW|PANE_STYLECHANGED); + markedwp->flags |= (PANE_REDRAW|PANE_STYLECHANGED| + PANE_THEMECHANGED); server_redraw_window_borders(markedwp->window); server_status_window(markedwp->window); } @@ -169,7 +171,7 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_ERROR); } options_set_string(oo, "window-active-style", 0, "%s", style); - wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED); + wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED|PANE_THEMECHANGED); } if (args_has(args, 'g')) { cmdq_print(item, "%s", options_get_string(oo, "window-style")); diff --git a/cmd-send-keys.c b/cmd-send-keys.c index c270fbd1..35b3f140 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -217,7 +217,7 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item) if (args_has(args, 'R')) { colour_palette_clear(&wp->palette); input_reset(wp->ictx, 1); - wp->flags |= (PANE_STYLECHANGED|PANE_REDRAW); + wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED|PANE_REDRAW); } if (count == 0) { diff --git a/cmd-swap-pane.c b/cmd-swap-pane.c index 6931bd16..4680f598 100644 --- a/cmd-swap-pane.c +++ b/cmd-swap-pane.c @@ -101,10 +101,10 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item) src_wp->window = dst_w; options_set_parent(src_wp->options, dst_w->options); - src_wp->flags |= PANE_STYLECHANGED; + src_wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED); dst_wp->window = src_w; options_set_parent(dst_wp->options, src_w->options); - dst_wp->flags |= PANE_STYLECHANGED; + dst_wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED); sx = src_wp->sx; sy = src_wp->sy; xoff = src_wp->xoff; yoff = src_wp->yoff; diff --git a/colour.c b/colour.c index 5fd91c9c..903090d3 100644 --- a/colour.c +++ b/colour.c @@ -182,6 +182,46 @@ colour_tostring(int c) return ("invalid"); } +/* Convert background colour to theme. */ +enum client_theme +colour_totheme(int c) +{ + int r, g, b, brightness; + + if (c == -1) + return (THEME_UNKNOWN); + + if (c & COLOUR_FLAG_RGB) { + r = (c >> 16) & 0xff; + g = (c >> 8) & 0xff; + b = (c >> 0) & 0xff; + + brightness = r + g + b; + if (brightness > 382) + return (THEME_LIGHT); + return (THEME_DARK); + } + + if (c & COLOUR_FLAG_256) + return (colour_totheme(colour_256toRGB(c))); + + switch (c) { + case 0: + case 90: + return (THEME_DARK); + case 7: + case 97: + return (THEME_LIGHT); + default: + if (c >= 0 && c <= 7) + return (colour_totheme(colour_256toRGB(c))); + if (c >= 90 && c <= 97) + return (colour_totheme(colour_256toRGB(8 + c - 90))); + break; + } + return (THEME_UNKNOWN); +} + /* Convert colour from string. */ int colour_fromstring(const char *s) diff --git a/format.c b/format.c index 7cd5b2a8..39a4a9a7 100644 --- a/format.c +++ b/format.c @@ -1544,6 +1544,23 @@ format_cb_client_written(struct format_tree *ft) return (NULL); } +/* Callback for client_theme. */ +static void * +format_cb_client_theme(struct format_tree *ft) +{ + if (ft->c != NULL) { + switch (ft->c->theme) { + case THEME_DARK: + return (xstrdup("dark")); + case THEME_LIGHT: + return (xstrdup("light")); + case THEME_UNKNOWN: + return (NULL); + } + } + return (NULL); +} + /* Callback for config_files. */ static void * format_cb_config_files(__unused struct format_tree *ft) @@ -2877,6 +2894,9 @@ static const struct format_table_entry format_table[] = { { "client_termtype", FORMAT_TABLE_STRING, format_cb_client_termtype }, + { "client_theme", FORMAT_TABLE_STRING, + format_cb_client_theme + }, { "client_tty", FORMAT_TABLE_STRING, format_cb_client_tty }, diff --git a/input-keys.c b/input-keys.c index 900dea07..91415064 100644 --- a/input-keys.c +++ b/input-keys.c @@ -314,6 +314,12 @@ static struct input_key_entry input_key_defaults[] = { { .key = KEYC_DC|KEYC_BUILD_MODIFIERS, .data = "\033[3;_~" }, + { .key = KEYC_REPORT_DARK_THEME, + .data = "\033[?997;1n" + }, + { .key = KEYC_REPORT_LIGHT_THEME, + .data = "\033[?997;2n" + }, }; static const key_code input_key_modifiers[] = { 0, diff --git a/input.c b/input.c index b6eef671..02bd5458 100644 --- a/input.c +++ b/input.c @@ -133,7 +133,7 @@ static void printflike(2, 3) input_reply(struct input_ctx *, const char *, ...); static void input_set_state(struct input_ctx *, const struct input_transition *); static void input_reset_cell(struct input_ctx *); - +static void input_report_current_theme(struct input_ctx *); static void input_osc_4(struct input_ctx *, const char *); static void input_osc_8(struct input_ctx *, const char *); static void input_osc_10(struct input_ctx *, const char *); @@ -243,6 +243,7 @@ enum input_csi_type { INPUT_CSI_DECSTBM, INPUT_CSI_DL, INPUT_CSI_DSR, + INPUT_CSI_DSR_PRIVATE, INPUT_CSI_ECH, INPUT_CSI_ED, INPUT_CSI_EL, @@ -251,6 +252,7 @@ enum input_csi_type { INPUT_CSI_IL, INPUT_CSI_MODOFF, INPUT_CSI_MODSET, + INPUT_CSI_QUERY_PRIVATE, INPUT_CSI_RCP, INPUT_CSI_REP, INPUT_CSI_RM, @@ -259,8 +261,8 @@ enum input_csi_type { INPUT_CSI_SD, INPUT_CSI_SGR, INPUT_CSI_SM, - INPUT_CSI_SM_PRIVATE, INPUT_CSI_SM_GRAPHICS, + INPUT_CSI_SM_PRIVATE, INPUT_CSI_SU, INPUT_CSI_TBC, INPUT_CSI_VPA, @@ -304,6 +306,8 @@ static const struct input_table_entry input_csi_table[] = { { 'm', ">", INPUT_CSI_MODSET }, { 'n', "", INPUT_CSI_DSR }, { 'n', ">", INPUT_CSI_MODOFF }, + { 'n', "?", INPUT_CSI_DSR_PRIVATE }, + { 'p', "?$", INPUT_CSI_QUERY_PRIVATE }, { 'q', " ", INPUT_CSI_DECSCUSR }, { 'q', ">", INPUT_CSI_XDA }, { 'r', "", INPUT_CSI_DECSTBM }, @@ -1527,6 +1531,20 @@ input_csi_dispatch(struct input_ctx *ictx) if (n != -1) screen_write_deleteline(sctx, n, bg); break; + case INPUT_CSI_DSR_PRIVATE: + switch (input_get(ictx, 0, 0, 0)) { + case 996: + input_report_current_theme(ictx); + break; + } + break; + case INPUT_CSI_QUERY_PRIVATE: + switch (input_get(ictx, 0, 0, 0)) { + case 2031: + input_reply(ictx, "\033[?2031;2$y"); + break; + } + break; case INPUT_CSI_DSR: switch (input_get(ictx, 0, 0, 0)) { case -1: @@ -1777,6 +1795,9 @@ input_csi_dispatch_rm_private(struct input_ctx *ictx) case 2004: screen_write_mode_clear(sctx, MODE_BRACKETPASTE); break; + case 2031: + screen_write_mode_clear(sctx, MODE_THEME_UPDATES); + break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; @@ -1872,6 +1893,9 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx) case 2004: screen_write_mode_set(sctx, MODE_BRACKETPASTE); break; + case 2031: + screen_write_mode_set(sctx, MODE_THEME_UPDATES); + break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; @@ -2660,84 +2684,6 @@ bad: free(id); } -/* - * Get a client with a foreground for the pane. There isn't much to choose - * between them so just use the first. - */ -static int -input_get_fg_client(struct window_pane *wp) -{ - struct window *w = wp->window; - struct client *loop; - - TAILQ_FOREACH(loop, &clients, entry) { - if (loop->flags & CLIENT_UNATTACHEDFLAGS) - continue; - if (loop->session == NULL || !session_has(loop->session, w)) - continue; - if (loop->tty.fg == -1) - continue; - return (loop->tty.fg); - } - return (-1); -} - -/* Get a client with a background for the pane. */ -static int -input_get_bg_client(struct window_pane *wp) -{ - struct window *w = wp->window; - struct client *loop; - - TAILQ_FOREACH(loop, &clients, entry) { - if (loop->flags & CLIENT_UNATTACHEDFLAGS) - continue; - if (loop->session == NULL || !session_has(loop->session, w)) - continue; - if (loop->tty.bg == -1) - continue; - return (loop->tty.bg); - } - return (-1); -} - -/* - * If any control mode client exists that has provided a bg color, return it. - * Otherwise, return -1. - */ -static int -input_get_bg_control_client(struct window_pane *wp) -{ - struct client *c; - - if (wp->control_bg == -1) - return (-1); - - TAILQ_FOREACH(c, &clients, entry) { - if (c->flags & CLIENT_CONTROL) - return (wp->control_bg); - } - return (-1); -} - -/* - * If any control mode client exists that has provided a fg color, return it. - * Otherwise, return -1. - */ -static int -input_get_fg_control_client(struct window_pane *wp) -{ - struct client *c; - - if (wp->control_fg == -1) - return (-1); - - TAILQ_FOREACH(c, &clients, entry) { - if (c->flags & CLIENT_CONTROL) - return (wp->control_fg); - } - return (-1); -} /* Handle the OSC 10 sequence for setting and querying foreground colour. */ static void @@ -2750,11 +2696,11 @@ input_osc_10(struct input_ctx *ictx, const char *p) if (strcmp(p, "?") == 0) { if (wp == NULL) return; - c = input_get_fg_control_client(wp); + c = window_pane_get_fg_control_client(wp); if (c == -1) { tty_default_colours(&defaults, wp); if (COLOUR_DEFAULT(defaults.fg)) - c = input_get_fg_client(wp); + c = window_pane_get_fg(wp); else c = defaults.fg; } @@ -2795,20 +2741,12 @@ static void input_osc_11(struct input_ctx *ictx, const char *p) { struct window_pane *wp = ictx->wp; - struct grid_cell defaults; int c; if (strcmp(p, "?") == 0) { if (wp == NULL) return; - c = input_get_bg_control_client(wp); - if (c == -1) { - tty_default_colours(&defaults, wp); - if (COLOUR_DEFAULT(defaults.bg)) - c = input_get_bg_client(wp); - else - c = defaults.bg; - } + c = window_pane_get_bg(wp); input_osc_colour_reply(ictx, 11, c); return; } @@ -2820,7 +2758,7 @@ input_osc_11(struct input_ctx *ictx, const char *p) if (ictx->palette != NULL) { ictx->palette->bg = c; if (wp != NULL) - wp->flags |= PANE_STYLECHANGED; + wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED); screen_write_fullredraw(&ictx->ctx); } } @@ -2836,7 +2774,7 @@ input_osc_111(struct input_ctx *ictx, const char *p) if (ictx->palette != NULL) { ictx->palette->bg = 8; if (wp != NULL) - wp->flags |= PANE_STYLECHANGED; + wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED); screen_write_fullredraw(&ictx->ctx); } } @@ -3027,3 +2965,18 @@ input_set_buffer_size(size_t buffer_size) log_debug("%s: %lu -> %lu", __func__, input_buffer_size, buffer_size); input_buffer_size = buffer_size; } + +static void +input_report_current_theme(struct input_ctx *ictx) +{ + switch (window_pane_get_theme(ictx->wp)) { + case THEME_DARK: + input_reply(ictx, "\033[?997;1n"); + break; + case THEME_LIGHT: + input_reply(ictx, "\033[?997;2n"); + break; + case THEME_UNKNOWN: + break; + } +} diff --git a/options-table.c b/options-table.c index bea0bc4c..6de2fb02 100644 --- a/options-table.c +++ b/options-table.c @@ -1455,6 +1455,8 @@ const struct options_table_entry options_table[] = { OPTIONS_TABLE_HOOK("client-focus-out", ""), OPTIONS_TABLE_HOOK("client-resized", ""), OPTIONS_TABLE_HOOK("client-session-changed", ""), + OPTIONS_TABLE_HOOK("client-light-theme", ""), + OPTIONS_TABLE_HOOK("client-dark-theme", ""), OPTIONS_TABLE_HOOK("command-error", ""), OPTIONS_TABLE_PANE_HOOK("pane-died", ""), OPTIONS_TABLE_PANE_HOOK("pane-exited", ""), diff --git a/options.c b/options.c index 4beb9898..5541a376 100644 --- a/options.c +++ b/options.c @@ -1165,7 +1165,7 @@ options_push_changes(const char *name) if (strcmp(name, "window-style") == 0 || strcmp(name, "window-active-style") == 0) { RB_FOREACH(wp, window_pane_tree, &all_window_panes) - wp->flags |= PANE_STYLECHANGED; + wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED); } if (strcmp(name, "pane-colours") == 0) { RB_FOREACH(wp, window_pane_tree, &all_window_panes) diff --git a/server-client.c b/server-client.c index 7b017697..8ab00fbf 100644 --- a/server-client.c +++ b/server-client.c @@ -60,11 +60,11 @@ 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 void server_client_update_latest(struct client *); - static void server_client_dispatch(struct imsg *, void *); static void server_client_dispatch_command(struct client *, struct imsg *); static void server_client_dispatch_identify(struct client *, struct imsg *); static void server_client_dispatch_shell(struct client *); +static void server_client_report_theme(struct client *, enum client_theme); /* Compare client windows. */ static int @@ -304,6 +304,7 @@ server_client_create(int fd) c->tty.sx = 80; c->tty.sy = 24; + c->theme = THEME_UNKNOWN; status_init(c); c->flags |= CLIENT_FOCUSED; @@ -405,6 +406,7 @@ server_client_set_session(struct client *c, struct session *s) recalculate_sizes(); window_update_focus(s->curw->window); session_update_activity(s, NULL); + session_theme_changed(s); gettimeofday(&s->last_attached_time, NULL); s->curw->flags &= ~WINLINK_ALERTFLAGS; s->curw->window->latest = c; @@ -2388,6 +2390,16 @@ server_client_key_callback(struct cmdq_item *item, void *data) event->key = key; } + /* Handle theme reporting keys. */ + if (key == KEYC_REPORT_LIGHT_THEME) { + server_client_report_theme(c, THEME_LIGHT); + goto out; + } + if (key == KEYC_REPORT_DARK_THEME) { + server_client_report_theme(c, THEME_DARK); + goto out; + } + /* Find affected pane. */ if (!KEYC_IS_MOUSE(key) || cmd_find_from_mouse(&fs, m, 0) != 0) cmd_find_from_client(&fs, c, 0); @@ -2678,6 +2690,12 @@ server_client_loop(void) } check_window_name(w); } + + /* Send theme updates. */ + RB_FOREACH(w, windows, &windows) { + TAILQ_FOREACH(wp, &w->panes, entry) + window_pane_send_theme_update(wp); + } } /* Check if window needs to be resized. */ @@ -3909,3 +3927,21 @@ out: if (!parse) free(msg); } + +static void +server_client_report_theme(struct client *c, enum client_theme theme) +{ + if (theme == THEME_LIGHT) { + c->theme = THEME_LIGHT; + notify_client("client-light-theme", c); + } else { + c->theme = THEME_DARK; + notify_client("client-dark-theme", c); + } + + /* + * Request foreground and background colour again. Don't forward 2031 to + * panes until a response is received. + */ + tty_puts(&c->tty, "\033]10;?\033\\\033]11;?\033\\"); +} diff --git a/session.c b/session.c index a3adafdd..e9664183 100644 --- a/session.c +++ b/session.c @@ -753,3 +753,16 @@ session_renumber_windows(struct session *s) RB_FOREACH_SAFE(wl, winlinks, &old_wins, wl1) winlink_remove(&old_wins, wl); } + +/* Set the PANE_THEMECHANGED flag for every pane in this session. */ +void +session_theme_changed(struct session *s) +{ + struct window_pane *wp; + struct winlink *wl; + + RB_FOREACH(wl, winlinks, &s->windows) { + TAILQ_FOREACH(wp, &wl->window->panes, entry) + wp->flags |= PANE_THEMECHANGED; + } +} diff --git a/tmux.h b/tmux.h index 52b85cb1..9d239510 100644 --- a/tmux.h +++ b/tmux.h @@ -369,6 +369,10 @@ enum { KEYC_KP_ZERO, KEYC_KP_PERIOD, + /* Theme reporting. */ + KEYC_REPORT_DARK_THEME, + KEYC_REPORT_LIGHT_THEME, + /* End of special keys. */ KEYC_BASE_END }; @@ -636,6 +640,7 @@ enum tty_code_code { #define MODE_CURSOR_VERY_VISIBLE 0x10000 #define MODE_CURSOR_BLINKING_SET 0x20000 #define MODE_KEYS_EXTENDED_2 0x40000 +#define MODE_THEME_UPDATES 0x80000 #define ALL_MODES 0xffffff #define ALL_MOUSE_MODES (MODE_MOUSE_STANDARD|MODE_MOUSE_BUTTON|MODE_MOUSE_ALL) @@ -1125,8 +1130,9 @@ struct window_pane { #define PANE_STATUSDRAWN 0x400 #define PANE_EMPTY 0x800 #define PANE_STYLECHANGED 0x1000 -#define PANE_UNSEENCHANGES 0x2000 -#define PANE_REDRAWSCROLLBAR 0x4000 +#define PANE_THEMECHANGED 0x2000 +#define PANE_UNSEENCHANGES 0x4000 +#define PANE_REDRAWSCROLLBAR 0x8000 u_int sb_slider_y; u_int sb_slider_h; @@ -1831,6 +1837,16 @@ struct overlay_ranges { u_int nx[OVERLAY_MAX_RANGES]; }; +/* + * Client theme, this is worked out from the background colour if not reported + * by terminal. + */ +enum client_theme { + THEME_UNKNOWN, + THEME_LIGHT, + THEME_DARK +}; + /* Client connection. */ typedef int (*prompt_input_cb)(struct client *, void *, const char *, int); typedef void (*prompt_free_cb)(void *); @@ -1890,6 +1906,7 @@ struct client { struct mouse_event click_event; struct status_line status; + enum client_theme theme; #define CLIENT_TERMINAL 0x1 #define CLIENT_LOGIN 0x2 @@ -2887,6 +2904,8 @@ void input_parse_screen(struct input_ctx *, struct screen *, void input_reply_clipboard(struct bufferevent *, const char *, size_t, const char *); void input_set_buffer_size(size_t); +int input_get_bg_client(struct window_pane *); +int input_get_bg_control_client(struct window_pane *); /* input-key.c */ void input_key_build(void); @@ -2901,7 +2920,8 @@ int colour_join_rgb(u_char, u_char, u_char); void colour_split_rgb(int, u_char *, u_char *, u_char *); int colour_force_rgb(int); const char *colour_tostring(int); -int colour_fromstring(const char *s); +enum client_theme colour_totheme(int); +int colour_fromstring(const char *); int colour_256toRGB(int); int colour_256to16(int); int colour_byname(const char *); @@ -3198,6 +3218,13 @@ void window_set_fill_character(struct window *); void window_pane_default_cursor(struct window_pane *); int window_pane_mode(struct window_pane *); int window_pane_show_scrollbar(struct window_pane *, int); +int window_pane_get_bg(struct window_pane *); +int window_pane_get_fg(struct window_pane *); +int window_pane_get_fg_control_client(struct window_pane *); +int window_pane_get_bg_control_client(struct window_pane *); +int window_get_bg_client(struct window_pane *); +enum client_theme window_pane_get_theme(struct window_pane *); +void window_pane_send_theme_update(struct window_pane *); /* layout.c */ u_int layout_count_cells(struct layout_cell *); @@ -3395,6 +3422,7 @@ void session_group_synchronize_from(struct session *); u_int session_group_count(struct session_group *); u_int session_group_attached_count(struct session_group *); void session_renumber_windows(struct session *); +void session_theme_changed(struct session *); /* utf8.c */ enum utf8_state utf8_towc (const struct utf8_data *, wchar_t *); diff --git a/tty-keys.c b/tty-keys.c index 77ab4ae1..45175171 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -213,6 +213,10 @@ static const struct tty_default_key_raw tty_default_raw_keys[] = { /* Extended keys. */ { "\033[1;5Z", '\011'|KEYC_CTRL|KEYC_SHIFT }, + + /* Theme reporting. */ + { "\033[?997;1n", KEYC_REPORT_DARK_THEME }, + { "\033[?997;2n", KEYC_REPORT_LIGHT_THEME }, }; /* Default xterm keys. */ @@ -791,10 +795,12 @@ tty_keys_next(struct tty *tty) switch (tty_keys_colours(tty, buf, len, &size, &tty->fg, &tty->bg)) { case 0: /* yes */ key = KEYC_UNKNOWN; + session_theme_changed(tty->client->session); goto complete_key; case -1: /* no, or not valid */ break; case 1: /* partial */ + session_theme_changed(tty->client->session); goto partial_key; } diff --git a/tty.c b/tty.c index 0080b7c6..fc74a164 100644 --- a/tty.c +++ b/tty.c @@ -351,6 +351,11 @@ tty_start_tty(struct tty *tty) if (tty_term_has(tty->term, TTYC_ENBP)) tty_putcode(tty, TTYC_ENBP); + if (tty->term->flags & TERM_VT100LIKE) { + /* Subscribe to theme changes and request theme now. */ + tty_puts(tty, "\033[?2031h\033[?996n"); + } + evtimer_set(&tty->start_timer, tty_start_timer_callback, tty); evtimer_add(&tty->start_timer, &tv); @@ -463,6 +468,9 @@ tty_stop_tty(struct tty *tty) tty_raw(tty, tty_term_string(tty->term, TTYC_DSMG)); tty_raw(tty, tty_term_string(tty->term, TTYC_RMCUP)); + if (tty->term->flags & TERM_VT100LIKE) + tty_raw(tty, "\033[?2031l"); + setblocking(c->fd, 1); } diff --git a/window.c b/window.c index dc220462..3bb67f00 100644 --- a/window.c +++ b/window.c @@ -943,7 +943,7 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) wp = xcalloc(1, sizeof *wp); wp->window = w; wp->options = options_create(w->options); - wp->flags = PANE_STYLECHANGED; + wp->flags = (PANE_STYLECHANGED|PANE_THEMECHANGED); wp->id = next_window_pane_id++; RB_INSERT(window_pane_tree, &all_window_panes, wp); @@ -1794,3 +1794,162 @@ window_pane_show_scrollbar(struct window_pane *wp, int sb_option) return (1); return (0); } + +int +window_pane_get_bg(struct window_pane *wp) +{ + int c; + struct grid_cell defaults; + + c = window_pane_get_bg_control_client(wp); + if (c == -1) { + tty_default_colours(&defaults, wp); + if (COLOUR_DEFAULT(defaults.bg)) + c = window_get_bg_client(wp); + else + c = defaults.bg; + } + return (c); +} + +/* Get a client with a background for the pane. */ +int +window_get_bg_client(struct window_pane *wp) +{ + struct window *w = wp->window; + struct client *loop; + + TAILQ_FOREACH(loop, &clients, entry) { + if (loop->flags & CLIENT_UNATTACHEDFLAGS) + continue; + if (loop->session == NULL || !session_has(loop->session, w)) + continue; + if (loop->tty.bg == -1) + continue; + return (loop->tty.bg); + } + return (-1); +} + +/* + * If any control mode client exists that has provided a bg color, return it. + * Otherwise, return -1. + */ +int +window_pane_get_bg_control_client(struct window_pane *wp) +{ + struct client *c; + + if (wp->control_bg == -1) + return (-1); + + TAILQ_FOREACH(c, &clients, entry) { + if (c->flags & CLIENT_CONTROL) + return (wp->control_bg); + } + return (-1); +} + +/* + * Get a client with a foreground for the pane. There isn't much to choose + * between them so just use the first. + */ +int +window_pane_get_fg(struct window_pane *wp) +{ + struct window *w = wp->window; + struct client *loop; + + TAILQ_FOREACH(loop, &clients, entry) { + if (loop->flags & CLIENT_UNATTACHEDFLAGS) + continue; + if (loop->session == NULL || !session_has(loop->session, w)) + continue; + if (loop->tty.fg == -1) + continue; + return (loop->tty.fg); + } + return (-1); +} + +/* + * If any control mode client exists that has provided a fg color, return it. + * Otherwise, return -1. + */ +int +window_pane_get_fg_control_client(struct window_pane *wp) +{ + struct client *c; + + if (wp->control_fg == -1) + return (-1); + + TAILQ_FOREACH(c, &clients, entry) { + if (c->flags & CLIENT_CONTROL) + return (wp->control_fg); + } + return (-1); +} + +enum client_theme +window_pane_get_theme(struct window_pane *wp) +{ + struct window *w = wp->window; + struct client *loop; + enum client_theme theme; + int found_light = 0, found_dark = 0; + + /* + * Derive theme from pane background color, if it's not the default + * colour. + */ + theme = colour_totheme(window_pane_get_bg(wp)); + if (theme != THEME_UNKNOWN) + return (theme); + + /* Try to find a client that has a theme. */ + TAILQ_FOREACH(loop, &clients, entry) { + if (loop->flags & CLIENT_UNATTACHEDFLAGS) + continue; + if (loop->session == NULL || !session_has(loop->session, w)) + continue; + switch (loop->theme) { + case THEME_LIGHT: + found_light = 1; + break; + case THEME_DARK: + found_dark = 1; + break; + case THEME_UNKNOWN: + break; + } + } + + if (found_dark && !found_light) + return (THEME_DARK); + if (found_light && !found_dark) + return (THEME_LIGHT); + return (THEME_UNKNOWN); +} + +void +window_pane_send_theme_update(struct window_pane *wp) +{ + if (~wp->flags & PANE_THEMECHANGED) + return; + if (~wp->screen->mode & MODE_THEME_UPDATES) + return; + + switch (window_pane_get_theme(wp)) { + case THEME_LIGHT: + input_key_pane(wp, KEYC_REPORT_LIGHT_THEME, NULL); + break; + case THEME_DARK: + input_key_pane(wp, KEYC_REPORT_DARK_THEME, NULL); + break; + case THEME_UNKNOWN: + break; + } + + wp->flags &= ~PANE_THEMECHANGED; +} From 4e4fe3eb391e5614b2cc1c7c5086df0f96165ad3 Mon Sep 17 00:00:00 2001 From: nicm <nicm> Date: Tue, 11 Mar 2025 08:14:26 +0000 Subject: [PATCH 21/25] Cleanup window_get_active_at function. GitHub issue 4401 from Michael Grant. --- window.c | 27 +++++---------------------- 1 file changed, 5 insertions(+), 22 deletions(-) diff --git a/window.c b/window.c index 3bb67f00..b1691ac6 100644 --- a/window.c +++ b/window.c @@ -72,6 +72,8 @@ struct window_pane_input_data { static struct window_pane *window_pane_create(struct window *, u_int, u_int, u_int); static void window_pane_destroy(struct window_pane *); +static void window_pane_full_size_offset(struct window_pane *wp, + u_int *xoff, u_int *yoff, u_int *sx, u_int *sy); RB_GENERATE(windows, window, entry, window_cmp); RB_GENERATE(winlinks, winlink, entry, winlink_cmp); @@ -584,34 +586,15 @@ struct window_pane * window_get_active_at(struct window *w, u_int x, u_int y) { struct window_pane *wp; - int pane_scrollbars; - u_int sb_pos, sb_w, xoff, sx; - - pane_scrollbars = options_get_number(w->options, "pane-scrollbars"); - sb_pos = options_get_number(w->options, "pane-scrollbars-position"); + u_int xoff, yoff, sx, sy; TAILQ_FOREACH(wp, &w->panes, entry) { if (!window_pane_visible(wp)) continue; - - if (pane_scrollbars == PANE_SCROLLBARS_ALWAYS || - (pane_scrollbars == PANE_SCROLLBARS_MODAL && - window_pane_mode(wp) != WINDOW_PANE_NO_MODE)) { - sb_w = wp->scrollbar_style.width + - wp->scrollbar_style.pad; - } else - sb_w = 0; - - if (sb_pos == PANE_SCROLLBARS_LEFT) { - xoff = wp->xoff - sb_w; - sx = wp->sx + sb_w; - } else { /* sb_pos == PANE_SCROLLBARS_RIGHT */ - xoff = wp->xoff; - sx = wp->sx + sb_w; - } + window_pane_full_size_offset(wp, &xoff, &yoff, &sx, &sy); if (x < xoff || x > xoff + sx) continue; - if (y < wp->yoff || y > wp->yoff + wp->sy) + if (y < yoff || y > yoff + sy) continue; return (wp); } From 5eb30c1543a4f9201a1c6d6039d38c32646bcf0a Mon Sep 17 00:00:00 2001 From: nicm <nicm> Date: Mon, 17 Mar 2025 20:33:20 +0000 Subject: [PATCH 22/25] Handle padding cells correctly for regular expression searching, GitHub issue 4399 from github at jyn dot dev. --- window-copy.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/window-copy.c b/window-copy.c index 7dcc8432..3a4f59bb 100644 --- a/window-copy.c +++ b/window-copy.c @@ -4263,6 +4263,8 @@ window_copy_match_at_cursor(struct window_copy_mode_data *data) buf = xrealloc(buf, len + 2); buf[len] = '\t'; len++; + } else if (gc.flags & GRID_FLAG_PADDING) { + /* nothing to do */ } else { buf = xrealloc(buf, len + gc.data.size + 1); memcpy(buf + len, gc.data.data, gc.data.size); From 817b621d2078137b4ddea78835f609a9d7bac339 Mon Sep 17 00:00:00 2001 From: nicm <nicm> Date: Mon, 17 Mar 2025 20:43:29 +0000 Subject: [PATCH 23/25] If there is an active query, set escape time temporarily to a higher value (the old default - 500). Some Windows terminals are very slow to respond, or the network may be slow. From github at jyn dot dev. --- tty-keys.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tty-keys.c b/tty-keys.c index 45175171..7b0da5a2 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -937,6 +937,11 @@ partial_key: delay = options_get_number(global_options, "escape-time"); if (delay == 0) delay = 1; + if ((tty->flags & TTY_ALL_REQUEST_FLAGS) != TTY_ALL_REQUEST_FLAGS) { + log_debug("%s: increasing delay for active DA query", c->name); + if (delay < 500) + delay = 500; + } tv.tv_sec = delay / 1000; tv.tv_usec = (delay % 1000) * 1000L; From 111e16e7726dc7ab63baf47adf8531c8601f9657 Mon Sep 17 00:00:00 2001 From: jsg <jsg> Date: Fri, 21 Mar 2025 02:10:42 +0000 Subject: [PATCH 24/25] remove prototypes for removed functions --- tmux.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/tmux.h b/tmux.h index 9d239510..3a437165 100644 --- a/tmux.h +++ b/tmux.h @@ -2904,8 +2904,6 @@ void input_parse_screen(struct input_ctx *, struct screen *, void input_reply_clipboard(struct bufferevent *, const char *, size_t, const char *); void input_set_buffer_size(size_t); -int input_get_bg_client(struct window_pane *); -int input_get_bg_control_client(struct window_pane *); /* input-key.c */ void input_key_build(void); @@ -3427,7 +3425,6 @@ void session_theme_changed(struct session *); /* utf8.c */ enum utf8_state utf8_towc (const struct utf8_data *, wchar_t *); enum utf8_state utf8_fromwc(wchar_t wc, struct utf8_data *); -int utf8_in_table(wchar_t, const wchar_t *, u_int); void utf8_update_width_cache(void); utf8_char utf8_build_one(u_char); enum utf8_state utf8_from_data(const struct utf8_data *, utf8_char *); From 969f6a60c3f4c521ba25aea72d130b3353fd95da Mon Sep 17 00:00:00 2001 From: Thomas Adam <thomas.adam22@gmail.com> Date: Fri, 21 Mar 2025 12:46:06 +0000 Subject: [PATCH 25/25] portable: SYNCING: correct tmux-openbsd The tmux-openbsd repository is called tmux-obsd. Noticed via Github issue #4419 --- SYNCING | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/SYNCING b/SYNCING index 07be40c4..51b78010 100644 --- a/SYNCING +++ b/SYNCING @@ -1,17 +1,17 @@ Preamble ======== -Tmux portable relies on repositories "tmux" and "tmux-openbsd". +Tmux portable relies on repositories "tmux" and "tmux-obsd". Here's a description of them: * "tmux" is the portable version, the one which contains code for other operating systems, and autotools, etc., which isn't found or needed in the OpenBSD base system. -* "tmux-openbsd" is the version of tmux in OpenBSD base system which provides +* "tmux-obsd" is the version of tmux in OpenBSD base system which provides the basis of the portable tmux version. -Note: The "tmux-openbsd" repository is actually handled by "git cvsimport" +Note: The "tmux-obsd" repository is actually handled by "git cvsimport" running at 15 minute intervals, so a commit made to OpenBSD's tmux CVS repository will take at least that long to appear in this git repository. (It might take longer, depending on the CVS mirror used to import the @@ -34,11 +34,11 @@ this information has ever been set before. Cloning repositories ==================== -This involves having both tmux and tmux-openbsd cloned, as in: +This involves having both tmux and tmux-obsd cloned, as in: % cd /some/where/useful % git clone https://github.com/tmux/tmux.git -% git clone https://github.com/ThomasAdam/tmux-openbsd.git +% git clone https://github.com/ThomasAdam/tmux-obsd.git Note that you do not need additional checkouts to manage the sync -- an existing clone of either repositories will suffice. So if you already have @@ -47,30 +47,30 @@ these checkouts existing, skip that. Adding in git-remotes ===================== -Because the portable "tmux" git repository and the "tmux-openbsd" +Because the portable "tmux" git repository and the "tmux-obsd" repository do not inherently share any history between each other, the history has been faked between them. This "faking of history" is something which has to be told to git for the purposes of comparing the "tmux" and -"tmux-openbsd" repositories for syncing. To do this, we must reference the -clone of the "tmux-openbsd" repository from the "tmux" repository, as +"tmux-obsd" repositories for syncing. To do this, we must reference the +clone of the "tmux-obsd" repository from the "tmux" repository, as shown by the following command: % cd /path/to/tmux -% git remote add obsd-tmux file:///path/to/tmux-openbsd +% git remote add obsd-tmux file:///path/to/tmux-obsd So that now, the remote "obsd-tmux" can be used to reference branches and -commits from the "tmux-openbsd" repository, but from the context of the +commits from the "tmux-obsd" repository, but from the context of the portable "tmux" repository, which makes sense because it's the "tmux" repository which will have the updates applied to them. Fetching updates ================ -To ensure the latest commits from "tmux-openbsd" can be found from within -"tmux", we have to ensure the "master" branch from "tmux-openbsd" is +To ensure the latest commits from "tmux-obsd" can be found from within +"tmux", we have to ensure the "master" branch from "tmux-obsd" is up-to-date first, and then reference that update in "tmux", as in: -% cd /path/to/tmux-openbsd +% cd /path/to/tmux-obsd % git checkout master % git pull @@ -82,9 +82,9 @@ Then back in "tmux": Creating the necessary branches =============================== -Now that "tmux" can see commits and branches from "tmux-openbsd" by way +Now that "tmux" can see commits and branches from "tmux-obsd" by way of the remote name "obsd-tmux", we can now create the master branch from -"tmux-openbsd" in the "tmux" repository: +"tmux-obsd" in the "tmux" repository: % git checkout -b obsd-master obsd-tmux/master @@ -92,7 +92,7 @@ Adding in the fake history points ================================= To tie both the "master" branch from "tmux" and the "obsd-master" -branch from "tmux-openbsd" together, the fake history points added to the +branch from "tmux-obsd" together, the fake history points added to the "tmux" repository need to be added. To do this, we must add an additional refspec line, as in: