From 1cf3e02bfb189f5b6f112960229b57e072e27bd7 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 27 Mar 2026 08:40:26 +0000 Subject: [PATCH 01/28] Add a format flag for bracket paste, from George Nachman in GitHub issue 4951. --- format.c | 15 +++++++++++++++ tmux.1 | 1 + 2 files changed, 16 insertions(+) diff --git a/format.c b/format.c index bdde54a3..031d38e1 100644 --- a/format.c +++ b/format.c @@ -1341,6 +1341,18 @@ format_cb_alternate_saved_y(struct format_tree *ft) return (NULL); } +/* Callback for bracket_paste_flag. */ +static void * +format_cb_bracket_paste_flag(struct format_tree *ft) +{ + if (ft->wp != NULL && ft->wp->screen != NULL) { + if (ft->wp->screen->mode & MODE_BRACKETPASTE) + return (xstrdup("1")); + return (xstrdup("0")); + } + return (NULL); +} + /* Callback for buffer_name. */ static void * format_cb_buffer_name(struct format_tree *ft) @@ -3031,6 +3043,9 @@ static const struct format_table_entry format_table[] = { { "alternate_saved_y", FORMAT_TABLE_STRING, format_cb_alternate_saved_y }, + { "bracket_paste_flag", FORMAT_TABLE_STRING, + format_cb_bracket_paste_flag + }, { "buffer_created", FORMAT_TABLE_TIME, format_cb_buffer_created }, diff --git a/tmux.1 b/tmux.1 index e03c37fa..a6f3afca 100644 --- a/tmux.1 +++ b/tmux.1 @@ -6257,6 +6257,7 @@ The following variables are available, where appropriate: .It Li "alternate_on" Ta "" Ta "1 if pane is in alternate screen" .It Li "alternate_saved_x" Ta "" Ta "Saved cursor X in alternate screen" .It Li "alternate_saved_y" Ta "" Ta "Saved cursor Y in alternate screen" +.It Li "bracket_paste_flag" Ta "" Ta "Pane bracketed paste flag" .It Li "buffer_created" Ta "" Ta "Time buffer created" .It Li "buffer_full" Ta "" Ta "Full buffer content" .It Li "buffer_name" Ta "" Ta "Name of buffer" From e0237c6b8cd29b021060fa7816b02beb6538c234 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 27 Mar 2026 08:41:54 +0000 Subject: [PATCH 02/28] Ql -> Fl, from arza at arza dot us in GitHub issue 4949. --- tmux.1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tmux.1 b/tmux.1 index a6f3afca..998a2cbd 100644 --- a/tmux.1 +++ b/tmux.1 @@ -7182,7 +7182,7 @@ If is not given, the .Ic display\-time option is used; a delay of zero waits for a key press. -.Ql N +.Fl N ignores key presses and closes only after the delay expires. If .Fl C From 7620c03b7278ed631efa98455c5e116538c23ed7 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 30 Mar 2026 10:19:03 +0100 Subject: [PATCH 03/28] Add new fuzzers for command parsing, formats and styles, from David Korczynski in GitHub issue 4957. --- Makefile.am | 12 ++- fuzz/cmd-parse-fuzzer.c | 83 +++++++++++++++++++++ fuzz/cmd-parse-fuzzer.dict | 133 ++++++++++++++++++++++++++++++++++ fuzz/cmd-parse-fuzzer.options | 2 + fuzz/format-fuzzer.c | 88 ++++++++++++++++++++++ fuzz/format-fuzzer.dict | 71 ++++++++++++++++++ fuzz/format-fuzzer.options | 2 + fuzz/style-fuzzer.c | 79 ++++++++++++++++++++ fuzz/style-fuzzer.dict | 73 +++++++++++++++++++ fuzz/style-fuzzer.options | 2 + 10 files changed, 544 insertions(+), 1 deletion(-) create mode 100644 fuzz/cmd-parse-fuzzer.c create mode 100644 fuzz/cmd-parse-fuzzer.dict create mode 100644 fuzz/cmd-parse-fuzzer.options create mode 100644 fuzz/format-fuzzer.c create mode 100644 fuzz/format-fuzzer.dict create mode 100644 fuzz/format-fuzzer.options create mode 100644 fuzz/style-fuzzer.c create mode 100644 fuzz/style-fuzzer.dict create mode 100644 fuzz/style-fuzzer.options diff --git a/Makefile.am b/Makefile.am index 7dddbe49..3ec467f4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -237,9 +237,19 @@ dist_tmux_SOURCES += image.c image-sixel.c endif if NEED_FUZZING -check_PROGRAMS = fuzz/input-fuzzer +check_PROGRAMS = \ + fuzz/input-fuzzer \ + fuzz/cmd-parse-fuzzer \ + fuzz/format-fuzzer \ + fuzz/style-fuzzer fuzz_input_fuzzer_LDFLAGS = $(FUZZING_LIBS) fuzz_input_fuzzer_LDADD = $(LDADD) $(tmux_OBJECTS) +fuzz_cmd_parse_fuzzer_LDFLAGS = $(FUZZING_LIBS) +fuzz_cmd_parse_fuzzer_LDADD = $(LDADD) $(tmux_OBJECTS) +fuzz_format_fuzzer_LDFLAGS = $(FUZZING_LIBS) +fuzz_format_fuzzer_LDADD = $(LDADD) $(tmux_OBJECTS) +fuzz_style_fuzzer_LDFLAGS = $(FUZZING_LIBS) +fuzz_style_fuzzer_LDADD = $(LDADD) $(tmux_OBJECTS) endif # Install tmux.1 in the right format. diff --git a/fuzz/cmd-parse-fuzzer.c b/fuzz/cmd-parse-fuzzer.c new file mode 100644 index 00000000..2e135422 --- /dev/null +++ b/fuzz/cmd-parse-fuzzer.c @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2026 David Korczynski + * + * 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. + */ + +/* + * Fuzz the tmux command parser (cmd_parse_from_buffer). + * + * This exercises: + * - cmd-parse.y (yacc grammar, lexer, command building) + * - cmd.c (command lookup and validation) + * - arguments.c (argument parsing and flag handling) + * - cmd-find.c (target resolution) + * - options.c (option name lookups during parsing) + */ + +#include +#include + +#include "tmux.h" + +struct event_base *libevent; + +int +LLVMFuzzerTestOneInput(const u_char *data, size_t size) +{ + struct cmd_parse_input pi; + struct cmd_parse_result *pr; + + if (size > 2048 || size == 0) + return 0; + + memset(&pi, 0, sizeof pi); + pi.flags = CMD_PARSE_QUIET; + + pr = cmd_parse_from_buffer(data, size, &pi); + switch (pr->status) { + case CMD_PARSE_SUCCESS: + cmd_list_free(pr->cmdlist); + break; + case CMD_PARSE_ERROR: + free(pr->error); + break; + default: + break; + } + + return 0; +} + +int +LLVMFuzzerInitialize(__unused int *argc, __unused char ***argv) +{ + const struct options_table_entry *oe; + + global_environ = environ_create(); + global_options = options_create(NULL); + global_s_options = options_create(NULL); + global_w_options = options_create(NULL); + for (oe = options_table; oe->name != NULL; oe++) { + if (oe->scope & OPTIONS_TABLE_SERVER) + options_default(global_options, oe); + if (oe->scope & OPTIONS_TABLE_SESSION) + options_default(global_s_options, oe); + if (oe->scope & OPTIONS_TABLE_WINDOW) + options_default(global_w_options, oe); + } + libevent = osdep_event_init(); + socket_path = xstrdup("dummy"); + + return 0; +} diff --git a/fuzz/cmd-parse-fuzzer.dict b/fuzz/cmd-parse-fuzzer.dict new file mode 100644 index 00000000..cbd933f9 --- /dev/null +++ b/fuzz/cmd-parse-fuzzer.dict @@ -0,0 +1,133 @@ +# tmux command names +"set-option" +"bind-key" +"unbind-key" +"send-keys" +"new-session" +"new-window" +"split-window" +"select-window" +"select-pane" +"kill-pane" +"kill-window" +"kill-session" +"kill-server" +"list-sessions" +"list-windows" +"list-panes" +"list-keys" +"list-buffers" +"list-clients" +"list-commands" +"attach-session" +"detach-client" +"switch-client" +"rename-session" +"rename-window" +"resize-pane" +"resize-window" +"display-message" +"display-menu" +"display-popup" +"display-panes" +"copy-mode" +"paste-buffer" +"capture-pane" +"save-buffer" +"load-buffer" +"set-buffer" +"delete-buffer" +"show-buffer" +"choose-buffer" +"choose-tree" +"choose-client" +"if-shell" +"run-shell" +"source-file" +"command-prompt" +"confirm-before" +"pipe-pane" +"wait-for" +"set-environment" +"show-environment" +"set-hook" +"show-hooks" +"show-messages" +"show-options" +"set-window-option" +"show-window-options" +"clear-history" +"clock-mode" +"find-window" +"join-pane" +"move-pane" +"break-pane" +"swap-pane" +"swap-window" +"move-window" +"link-window" +"unlink-window" +"rotate-window" +"next-window" +"previous-window" +"last-window" +"last-pane" +"next-layout" +"previous-layout" +"select-layout" +"customize-mode" +"refresh-client" +"suspend-client" +"lock-client" +"lock-server" +"lock-session" +"respawn-pane" +"respawn-window" +"start-server" +"server-access" +"send-prefix" +"clear-prompt-history" +"show-prompt-history" + +# Common flags and syntax +"-t" +"-s" +"-g" +"-w" +"-p" +"-a" +"-o" +"-q" +"-u" +"-F" +"-f" +"-b" +"-d" +"-e" +"-n" +"-r" + +# Targets and option names +"status" +"default" +"mouse" +"on" +"off" +"vi" +"emacs" +"set -g" +"set -s" +"setw" +"bind" +"unbind" + +# Syntax elements +"{" +"}" +";" +"#{" +"%if" +"%elif" +"%else" +"%endif" +"%hidden" diff --git a/fuzz/cmd-parse-fuzzer.options b/fuzz/cmd-parse-fuzzer.options new file mode 100644 index 00000000..60bd9b0b --- /dev/null +++ b/fuzz/cmd-parse-fuzzer.options @@ -0,0 +1,2 @@ +[libfuzzer] +max_len = 2048 diff --git a/fuzz/format-fuzzer.c b/fuzz/format-fuzzer.c new file mode 100644 index 00000000..d447c506 --- /dev/null +++ b/fuzz/format-fuzzer.c @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2026 David Korczynski + * + * 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. + */ + +/* + * Fuzz the tmux format string expander (format_expand). + * + * This exercises: + * - format.c (format parsing, modifier chains, conditionals, math, regex) + * - colour.c (colour name and RGB parsing within formats) + * - utf8.c (UTF-8 width calculations in format padding) + */ + +#include +#include + +#include "tmux.h" + +struct event_base *libevent; + +int +LLVMFuzzerTestOneInput(const u_char *data, size_t size) +{ + struct format_tree *ft; + char *buf, *expanded; + + if (size > 2048 || size == 0) + return 0; + + /* Null-terminate the input for format_expand. */ + buf = malloc(size + 1); + if (buf == NULL) + return 0; + memcpy(buf, data, size); + buf[size] = '\0'; + + ft = format_create(NULL, NULL, 0, FORMAT_NOJOBS); + format_add(ft, "session_name", "%s", "fuzz-session"); + format_add(ft, "window_index", "%d", 0); + format_add(ft, "window_name", "%s", "fuzz-window"); + format_add(ft, "pane_index", "%d", 0); + format_add(ft, "pane_id", "%s", "%%0"); + format_add(ft, "host", "%s", "fuzzhost"); + format_add(ft, "pane_width", "%d", 80); + format_add(ft, "pane_height", "%d", 25); + + expanded = format_expand(ft, buf); + free(expanded); + format_free(ft); + + free(buf); + return 0; +} + +int +LLVMFuzzerInitialize(__unused int *argc, __unused char ***argv) +{ + const struct options_table_entry *oe; + + global_environ = environ_create(); + global_options = options_create(NULL); + global_s_options = options_create(NULL); + global_w_options = options_create(NULL); + for (oe = options_table; oe->name != NULL; oe++) { + if (oe->scope & OPTIONS_TABLE_SERVER) + options_default(global_options, oe); + if (oe->scope & OPTIONS_TABLE_SESSION) + options_default(global_s_options, oe); + if (oe->scope & OPTIONS_TABLE_WINDOW) + options_default(global_w_options, oe); + } + libevent = osdep_event_init(); + socket_path = xstrdup("dummy"); + + return 0; +} diff --git a/fuzz/format-fuzzer.dict b/fuzz/format-fuzzer.dict new file mode 100644 index 00000000..fb79cf9a --- /dev/null +++ b/fuzz/format-fuzzer.dict @@ -0,0 +1,71 @@ +# Format expansion syntax +"#{" +"}" +"#{?" +"#{==" +"#{!=" +"#{<" +"#{>" +"#{m:" +"#{C:" +"#{s/" +"#{t:" +"#{T:" +"#{E:" +"#{S:" +"#{W:" +"#{P:" +"##" + +# Common format modifiers +"b:" +"d:" +"q:" +"l:" +"e:" +"t:" +"p:" +"w:" +"n:" +"a:" +"=:" +"||" +"&&" +"," +"#{e|" +"#{p-1:" +"#{=/10/...:" + +# Format variables +"session_name" +"window_index" +"window_name" +"pane_index" +"pane_id" +"host" +"pane_width" +"pane_height" +"pane_current_command" +"pane_pid" +"session_id" +"window_id" +"client_name" +"version" +"line" + +# Conditionals +"#{?pane_active," +",}" +"#{?#{==:" + +# Math and comparison +"#{e|+:" +"#{e|-:" +"#{e|*:" +"#{e|/:" +"#{e|%:" + +# Nested +"#{=" +"#{" +"#{l:" diff --git a/fuzz/format-fuzzer.options b/fuzz/format-fuzzer.options new file mode 100644 index 00000000..60bd9b0b --- /dev/null +++ b/fuzz/format-fuzzer.options @@ -0,0 +1,2 @@ +[libfuzzer] +max_len = 2048 diff --git a/fuzz/style-fuzzer.c b/fuzz/style-fuzzer.c new file mode 100644 index 00000000..f9d40e61 --- /dev/null +++ b/fuzz/style-fuzzer.c @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2026 David Korczynski + * + * 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. + */ + +/* + * Fuzz the tmux style parser (style_parse). + * + * This exercises: + * - style.c (style string parsing, alignment, ranges) + * - colour.c (colour name, RGB, and indexed colour parsing) + */ + +#include +#include + +#include "tmux.h" + +struct event_base *libevent; + +int +LLVMFuzzerTestOneInput(const u_char *data, size_t size) +{ + struct style sy; + struct grid_cell gc; + char *buf; + + if (size > 512 || size == 0) + return 0; + + /* Null-terminate the input for style_parse. */ + buf = malloc(size + 1); + if (buf == NULL) + return 0; + memcpy(buf, data, size); + buf[size] = '\0'; + + memset(&gc, 0, sizeof gc); + style_set(&sy, &gc); + + style_parse(&sy, &gc, buf); + + free(buf); + return 0; +} + +int +LLVMFuzzerInitialize(__unused int *argc, __unused char ***argv) +{ + const struct options_table_entry *oe; + + global_environ = environ_create(); + global_options = options_create(NULL); + global_s_options = options_create(NULL); + global_w_options = options_create(NULL); + for (oe = options_table; oe->name != NULL; oe++) { + if (oe->scope & OPTIONS_TABLE_SERVER) + options_default(global_options, oe); + if (oe->scope & OPTIONS_TABLE_SESSION) + options_default(global_s_options, oe); + if (oe->scope & OPTIONS_TABLE_WINDOW) + options_default(global_w_options, oe); + } + libevent = osdep_event_init(); + socket_path = xstrdup("dummy"); + + return 0; +} diff --git a/fuzz/style-fuzzer.dict b/fuzz/style-fuzzer.dict new file mode 100644 index 00000000..2834734c --- /dev/null +++ b/fuzz/style-fuzzer.dict @@ -0,0 +1,73 @@ +# Style attributes +"default" +"ignore" +"nodefaults" +"bright" +"bold" +"dim" +"underscore" +"blink" +"reverse" +"hidden" +"italics" +"overline" +"strikethrough" +"double-underscore" +"curly-underscore" +"dotted-underscore" +"dashed-underscore" +"nobright" +"nobold" +"nodim" +"nounderscore" +"noblink" +"noreverse" +"nohidden" +"noitalics" +"nooverline" +"nostrikethrough" + +# Colours +"fg=" +"bg=" +"us=" +"fill=" +"colour0" +"colour255" +"red" +"green" +"blue" +"yellow" +"cyan" +"magenta" +"white" +"black" + +# RGB and hex +"#ff0000" +"#00ff00" +"#0000ff" + +# Alignment and ranges +"align=left" +"align=centre" +"align=right" +"align=absolute-centre" +"list=on" +"list=focus" +"list=left-marker" +"list=right-marker" +"range=left" +"range=right" +"range=pane" +"range=window" +"range=session" +"range=user" + +# Width and padding +"width=" +"pad=" + +# Delimiters +"," +" " diff --git a/fuzz/style-fuzzer.options b/fuzz/style-fuzzer.options new file mode 100644 index 00000000..5d468bc6 --- /dev/null +++ b/fuzz/style-fuzzer.options @@ -0,0 +1,2 @@ +[libfuzzer] +max_len = 512 From 022b5cf193ffed6f3feaee741987f5860e78ed61 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 30 Mar 2026 09:23:40 +0000 Subject: [PATCH 04/28] When in copy mode with a large scroll offset and the window is resized so that history shrinks, data->oy can exceed screen_hsize causing an unsigned integer underflow in the py computation. Clamp data->oy in window_copy_resize and window_copy_cmd_refresh_from_pane before the subtraction. From futpib at gmail dot com in GitHub issue 4958. --- window-copy.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/window-copy.c b/window-copy.c index bf265b5b..cc9f3e5d 100644 --- a/window-copy.c +++ b/window-copy.c @@ -1026,6 +1026,8 @@ window_copy_resize(struct window_mode_entry *wme, u_int sx, u_int sy) screen_resize(s, sx, sy, 0); cx = data->cx; + if (data->oy > gd->hsize + data->cy) + data->oy = gd->hsize + data->cy; cy = gd->hsize + data->cy - data->oy; reflow = (gd->sx != sx); if (reflow) @@ -2736,6 +2738,8 @@ window_copy_cmd_refresh_from_pane(struct window_copy_cmd_state *cs) if (data->viewmode) return (WINDOW_COPY_CMD_NOTHING); + if (data->oy > screen_hsize(data->backing)) + data->oy = screen_hsize(data->backing); oy_from_top = screen_hsize(data->backing) - data->oy; screen_free(data->backing); From 2ff0dd3fef9d2f81d9108aac5917329804a28468 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 31 Mar 2026 11:46:43 +0000 Subject: [PATCH 05/28] Reorganize structure of key_code so that it can be built directly by bitshifts rather than a load of huge switches, from Dane Jensen in GitHub issue 4953. --- input-keys.c | 3 +- key-string.c | 4 +- server-client.c | 1490 ++++------------------------------------------- tmux.h | 219 ++++--- 4 files changed, 209 insertions(+), 1507 deletions(-) diff --git a/input-keys.c b/input-keys.c index 6a872731..963824c7 100644 --- a/input-keys.c +++ b/input-keys.c @@ -674,8 +674,7 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key) } /* Ignore internal function key codes. */ - if ((key >= KEYC_BASE && key < KEYC_BASE_END) || - (key >= KEYC_USER && key < KEYC_USER_END)) { + if (KEYC_IS_USER(key) || KEYC_IS_SPECIAL(key) || KEYC_IS_MOUSE(key)) { log_debug("%s: ignoring key 0x%llx", __func__, key); return (0); } diff --git a/key-string.c b/key-string.c index 8b9b5604..3df9da4b 100644 --- a/key-string.c +++ b/key-string.c @@ -202,7 +202,7 @@ key_string_search_table(const char *string) return (key_string_table[i].key); } - if (sscanf(string, "User%u", &user) == 1 && user < KEYC_NUSER) + if (sscanf(string, "User%u", &user) == 1 && user <= KEYC_NUSER) return (KEYC_USER + user); return (KEYC_UNKNOWN); @@ -418,7 +418,7 @@ key_string_lookup_key(key_code key, int with_flags) s = "MouseMoveBorder"; goto append; } - if (key >= KEYC_USER && key < KEYC_USER_END) { + if (KEYC_IS_USER(key)) { snprintf(tmp, sizeof tmp, "User%u", (u_int)(key - KEYC_USER)); strlcat(out, tmp, sizeof out); goto out; diff --git a/server-client.c b/server-client.c index 8c730e35..95f6076c 100644 --- a/server-client.c +++ b/server-client.c @@ -33,19 +33,6 @@ #include "tmux.h" -enum mouse_where { - NOWHERE, - PANE, - STATUS, - STATUS_LEFT, - STATUS_RIGHT, - STATUS_DEFAULT, - BORDER, - SCROLLBAR_UP, - SCROLLBAR_SLIDER, - SCROLLBAR_DOWN -}; - static void server_client_free(int, short, void *); static void server_client_check_pane_resize(struct window_pane *); static void server_client_check_pane_buffer(struct window_pane *); @@ -613,7 +600,7 @@ server_client_exec(struct client *c, const char *cmd) free(msg); } -static enum mouse_where +static enum key_code_mouse_location server_client_check_mouse_in_pane(struct window_pane *wp, u_int px, u_int py, u_int *sl_mpos) { @@ -660,16 +647,16 @@ server_client_check_mouse_in_pane(struct window_pane *wp, u_int px, u_int py, sl_bottom = (wp->yoff + wp->sb_slider_y + wp->sb_slider_h - 1); if (py < sl_top) - return (SCROLLBAR_UP); + return (KEYC_MOUSE_LOCATION_SCROLLBAR_UP); else if (py >= sl_top && py <= sl_bottom) { *sl_mpos = (py - wp->sb_slider_y - wp->yoff); - return (SCROLLBAR_SLIDER); + return (KEYC_MOUSE_LOCATION_SCROLLBAR_SLIDER); } else /* py > sl_bottom */ - return (SCROLLBAR_DOWN); + return (KEYC_MOUSE_LOCATION_SCROLLBAR_DOWN); } else { /* Must be inside the pane. */ - return (PANE); + return (KEYC_MOUSE_LOCATION_PANE); } } else if (~w->flags & WINDOW_ZOOMED) { /* Try the pane borders if not zoomed. */ @@ -686,42 +673,35 @@ server_client_check_mouse_in_pane(struct window_pane *wp, u_int px, u_int py, break; } if (fwp != NULL) - return (BORDER); + return (KEYC_MOUSE_LOCATION_BORDER); } - return (NOWHERE); + return (KEYC_MOUSE_LOCATION_NOWHERE); } /* Check for mouse keys. */ static key_code server_client_check_mouse(struct client *c, struct key_event *event) { - struct mouse_event *m = &event->m; - struct session *s = c->session, *fs; - struct window *w = s->curw->window; - struct winlink *fwl; - struct window_pane *wp, *fwp; - u_int x, y, b, sx, sy, px, py, sl_mpos = 0; - int ignore = 0; - key_code key; - struct timeval tv; - struct style_range *sr; - enum { NOTYPE, - MOVE, - DOWN, - UP, - DRAG, - WHEEL, - SECOND, - DOUBLE, - TRIPLE } type = NOTYPE; - enum mouse_where where = NOWHERE; + struct mouse_event *m = &event->m; + struct session *s = c->session, *fs; + struct window *w = s->curw->window; + struct winlink *fwl; + struct window_pane *wp, *fwp; + u_int x, y, sx, sy, px, py, sl_mpos = 0; + u_int b, bn; + int ignore = 0; + key_code key; + struct timeval tv; + struct style_range *sr; + enum key_code_type type = KEYC_TYPE_NOTYPE; + enum key_code_mouse_location loc = KEYC_MOUSE_LOCATION_NOWHERE; log_debug("%s mouse %02x at %u,%u (last %u,%u) (%d)", c->name, m->b, m->x, m->y, m->lx, m->ly, c->tty.mouse_drag_flag); /* What type of event is this? */ if (event->key == KEYC_DOUBLECLICK) { - type = DOUBLE; + type = KEYC_TYPE_DOUBLECLICK; x = m->x, y = m->y, b = m->b; ignore = 1; log_debug("double-click at %u,%u", x, y); @@ -732,11 +712,11 @@ server_client_check_mouse(struct client *c, struct key_event *event) MOUSE_DRAG(m->b) && MOUSE_RELEASE(m->b) && MOUSE_RELEASE(m->lb))) { - type = MOVE; + type = KEYC_TYPE_MOUSEMOVE; x = m->x, y = m->y, b = 0; log_debug("move at %u,%u", x, y); } else if (MOUSE_DRAG(m->b)) { - type = DRAG; + type = KEYC_TYPE_MOUSEDRAG; if (c->tty.mouse_drag_flag) { x = m->x, y = m->y, b = m->b; if (x == m->lx && y == m->ly) @@ -747,11 +727,14 @@ server_client_check_mouse(struct client *c, struct key_event *event) log_debug("drag start at %u,%u", x, y); } } else if (MOUSE_WHEEL(m->b)) { - type = WHEEL; + if ((m->b & MOUSE_MASK_BUTTONS) == MOUSE_WHEEL_UP) + type = KEYC_TYPE_WHEELUP; + else + type = KEYC_TYPE_WHEELDOWN; x = m->x, y = m->y, b = m->b; log_debug("wheel at %u,%u", x, y); } else if (MOUSE_RELEASE(m->b)) { - type = UP; + type = KEYC_TYPE_MOUSEUP; x = m->x, y = m->y, b = m->lb; if (m->sgr_type == 'm') b = m->sgr_b; @@ -760,22 +743,22 @@ server_client_check_mouse(struct client *c, struct key_event *event) if (c->flags & CLIENT_DOUBLECLICK) { evtimer_del(&c->click_timer); c->flags &= ~CLIENT_DOUBLECLICK; - type = SECOND; + type = KEYC_TYPE_SECONDCLICK; x = m->x, y = m->y, b = m->b; log_debug("second-click at %u,%u", x, y); c->flags |= CLIENT_TRIPLECLICK; } else if (c->flags & CLIENT_TRIPLECLICK) { evtimer_del(&c->click_timer); c->flags &= ~CLIENT_TRIPLECLICK; - type = TRIPLE; + type = KEYC_TYPE_TRIPLECLICK; x = m->x, y = m->y, b = m->b; log_debug("triple-click at %u,%u", x, y); goto have_event; } /* DOWN is the only remaining event type. */ - if (type == NOTYPE) { - type = DOWN; + if (type == KEYC_TYPE_NOTYPE) { + type = KEYC_TYPE_MOUSEDOWN; x = m->x, y = m->y, b = m->b; log_debug("down at %u,%u", x, y); c->flags |= CLIENT_DOUBLECLICK; @@ -783,7 +766,7 @@ server_client_check_mouse(struct client *c, struct key_event *event) } have_event: - if (type == NOTYPE) + if (type == KEYC_TYPE_NOTYPE) return (KEYC_UNKNOWN); /* Save the session. */ @@ -800,18 +783,18 @@ have_event: y < m->statusat + m->statuslines) { sr = status_get_range(c, x, y - m->statusat); if (sr == NULL) { - where = STATUS_DEFAULT; + loc = KEYC_MOUSE_LOCATION_STATUS_DEFAULT; } else { switch (sr->type) { case STYLE_RANGE_NONE: return (KEYC_UNKNOWN); case STYLE_RANGE_LEFT: log_debug("mouse range: left"); - where = STATUS_LEFT; + loc = KEYC_MOUSE_LOCATION_STATUS_LEFT; break; case STYLE_RANGE_RIGHT: log_debug("mouse range: right"); - where = STATUS_RIGHT; + loc = KEYC_MOUSE_LOCATION_STATUS_RIGHT; break; case STYLE_RANGE_PANE: fwp = window_pane_find_by_id(sr->argument); @@ -820,7 +803,7 @@ have_event: m->wp = sr->argument; log_debug("mouse range: pane %%%u", m->wp); - where = STATUS; + loc = KEYC_MOUSE_LOCATION_STATUS; break; case STYLE_RANGE_WINDOW: fwl = winlink_find_by_index(&s->windows, @@ -830,7 +813,7 @@ have_event: m->w = fwl->window->id; log_debug("mouse range: window @%u", m->w); - where = STATUS; + loc = KEYC_MOUSE_LOCATION_STATUS; break; case STYLE_RANGE_SESSION: fs = session_find_by_id(sr->argument); @@ -839,10 +822,10 @@ have_event: m->s = sr->argument; log_debug("mouse range: session $%u", m->s); - where = STATUS; + loc = KEYC_MOUSE_LOCATION_STATUS; break; case STYLE_RANGE_USER: - where = STATUS; + loc = KEYC_MOUSE_LOCATION_STATUS; break; } } @@ -852,9 +835,9 @@ have_event: * Not on status line. Adjust position and check for border, pane, or * scrollbar. */ - if (where == NOWHERE) { + if (loc == KEYC_MOUSE_LOCATION_NOWHERE) { if (c->tty.mouse_scrolling_flag) - where = SCROLLBAR_SLIDER; + loc = KEYC_MOUSE_LOCATION_SCROLLBAR_SLIDER; else { px = x; if (m->statusat == 0 && y >= m->statuslines) @@ -876,17 +859,17 @@ have_event: wp = window_get_active_at(w, px, py); if (wp == NULL) return (KEYC_UNKNOWN); - where = server_client_check_mouse_in_pane(wp, px, py, + loc = server_client_check_mouse_in_pane(wp, px, py, &sl_mpos); - if (where == PANE) { + if (loc == KEYC_MOUSE_LOCATION_PANE) { log_debug("mouse %u,%u on pane %%%u", x, y, wp->id); - } else if (where == BORDER) + } else if (loc == KEYC_MOUSE_LOCATION_BORDER) log_debug("mouse on pane %%%u border", wp->id); - else if (where == SCROLLBAR_UP || - where == SCROLLBAR_SLIDER || - where == SCROLLBAR_DOWN) { + else if (loc == KEYC_MOUSE_LOCATION_SCROLLBAR_UP || + loc == KEYC_MOUSE_LOCATION_SCROLLBAR_SLIDER || + loc == KEYC_MOUSE_LOCATION_SCROLLBAR_DOWN) { log_debug("mouse on pane %%%u scrollbar", wp->id); } @@ -896,23 +879,23 @@ have_event: } /* Reset click type or add a click timer if needed. */ - if (type == DOWN || - type == SECOND || - type == TRIPLE) { - if (type != DOWN && + if (type == KEYC_TYPE_MOUSEDOWN || + type == KEYC_TYPE_SECONDCLICK || + type == KEYC_TYPE_TRIPLECLICK) { + if (type != KEYC_TYPE_MOUSEDOWN && (m->b != c->click_button || - where != (enum mouse_where)c->click_where || + loc != (enum key_code_mouse_location)c->click_loc || m->wp != c->click_wp)) { - type = DOWN; + type = KEYC_TYPE_MOUSEDOWN; log_debug("click sequence reset at %u,%u", x, y); c->flags &= ~CLIENT_TRIPLECLICK; c->flags |= CLIENT_DOUBLECLICK; } - if (type != TRIPLE && KEYC_CLICK_TIMEOUT != 0) { + if (type != KEYC_TYPE_TRIPLECLICK && KEYC_CLICK_TIMEOUT != 0) { memcpy(&c->click_event, m, sizeof c->click_event); c->click_button = m->b; - c->click_where = where; + c->click_loc = loc; c->click_wp = m->wp; log_debug("click timer started"); @@ -923,11 +906,14 @@ have_event: } } + key = KEYC_UNKNOWN; + /* Stop dragging if needed. */ - if (type != DRAG && - type != WHEEL && - type != DOUBLE && - type != TRIPLE && + if (type != KEYC_TYPE_MOUSEDRAG && + type != KEYC_TYPE_WHEELUP && + type != KEYC_TYPE_WHEELDOWN && + type != KEYC_TYPE_DOUBLECLICK && + type != KEYC_TYPE_TRIPLECLICK && c->tty.mouse_drag_flag != 0) { if (c->tty.mouse_drag_release != NULL) c->tty.mouse_drag_release(c, m); @@ -940,374 +926,24 @@ 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 - 1) { - case MOUSE_BUTTON_1: - if (where == PANE) - key = KEYC_MOUSEDRAGEND1_PANE; - if (where == STATUS) - key = KEYC_MOUSEDRAGEND1_STATUS; - if (where == STATUS_LEFT) - key = KEYC_MOUSEDRAGEND1_STATUS_LEFT; - if (where == STATUS_RIGHT) - key = KEYC_MOUSEDRAGEND1_STATUS_RIGHT; - if (where == STATUS_DEFAULT) - key = KEYC_MOUSEDRAGEND1_STATUS_DEFAULT; - if (where == SCROLLBAR_SLIDER) - key = KEYC_MOUSEDRAGEND1_SCROLLBAR_SLIDER; - if (where == BORDER) - key = KEYC_MOUSEDRAGEND1_BORDER; - break; - case MOUSE_BUTTON_2: - if (where == PANE) - key = KEYC_MOUSEDRAGEND2_PANE; - if (where == STATUS) - key = KEYC_MOUSEDRAGEND2_STATUS; - if (where == STATUS_LEFT) - key = KEYC_MOUSEDRAGEND2_STATUS_LEFT; - if (where == STATUS_RIGHT) - key = KEYC_MOUSEDRAGEND2_STATUS_RIGHT; - if (where == STATUS_DEFAULT) - key = KEYC_MOUSEDRAGEND2_STATUS_DEFAULT; - if (where == SCROLLBAR_SLIDER) - key = KEYC_MOUSEDRAGEND2_SCROLLBAR_SLIDER; - if (where == BORDER) - key = KEYC_MOUSEDRAGEND2_BORDER; - break; - case MOUSE_BUTTON_3: - if (where == PANE) - key = KEYC_MOUSEDRAGEND3_PANE; - if (where == STATUS) - key = KEYC_MOUSEDRAGEND3_STATUS; - if (where == STATUS_LEFT) - key = KEYC_MOUSEDRAGEND3_STATUS_LEFT; - if (where == STATUS_RIGHT) - key = KEYC_MOUSEDRAGEND3_STATUS_RIGHT; - if (where == STATUS_DEFAULT) - key = KEYC_MOUSEDRAGEND3_STATUS_DEFAULT; - if (where == SCROLLBAR_SLIDER) - key = KEYC_MOUSEDRAGEND3_SCROLLBAR_SLIDER; - 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 == SCROLLBAR_SLIDER) - key = KEYC_MOUSEDRAGEND6_SCROLLBAR_SLIDER; - 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 == SCROLLBAR_SLIDER) - key = KEYC_MOUSEDRAGEND7_SCROLLBAR_SLIDER; - 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 == SCROLLBAR_SLIDER) - key = KEYC_MOUSEDRAGEND8_SCROLLBAR_SLIDER; - 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 == SCROLLBAR_SLIDER) - key = KEYC_MOUSEDRAGEND9_SCROLLBAR_SLIDER; - 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 == SCROLLBAR_SLIDER) - key = KEYC_MOUSEDRAGEND10_SCROLLBAR_SLIDER; - 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 == SCROLLBAR_SLIDER) - key = KEYC_MOUSEDRAGEND11_SCROLLBAR_SLIDER; - if (where == BORDER) - key = KEYC_MOUSEDRAGEND11_BORDER; - break; - default: - key = KEYC_MOUSE; - break; - } c->tty.mouse_drag_flag = 0; c->tty.mouse_slider_mpos = -1; - goto out; } /* Convert to a key binding. */ - key = KEYC_UNKNOWN; - switch (type) { - case NOTYPE: - break; - case MOVE: - if (where == PANE) { - key = KEYC_MOUSEMOVE_PANE; - if (wp != NULL && - wp != w->active && - options_get_number(s->options, "focus-follows-mouse")) { - window_set_active_pane(w, wp, 1); - server_redraw_window_borders(w); - server_status_window(w); - } + if (type == KEYC_TYPE_MOUSEMOVE && loc == KEYC_MOUSE_LOCATION_PANE) { + key = KEYC_MOUSEMOVE_PANE; + if (wp != NULL && + wp != w->active && + options_get_number(s->options, "focus-follows-mouse")) { + window_set_active_pane(w, wp, 1); + server_redraw_window_borders(w); + server_status_window(w); } - if (where == STATUS) - key = KEYC_MOUSEMOVE_STATUS; - if (where == STATUS_LEFT) - key = KEYC_MOUSEMOVE_STATUS_LEFT; - if (where == STATUS_RIGHT) - key = KEYC_MOUSEMOVE_STATUS_RIGHT; - if (where == STATUS_DEFAULT) - key = KEYC_MOUSEMOVE_STATUS_DEFAULT; - if (where == BORDER) - key = KEYC_MOUSEMOVE_BORDER; - break; - case DRAG: + } + if (type == KEYC_TYPE_MOUSEDRAG) { if (c->tty.mouse_drag_update != NULL) key = KEYC_DRAGGING; - else { - switch (MOUSE_BUTTONS(b)) { - case MOUSE_BUTTON_1: - if (where == PANE) - key = KEYC_MOUSEDRAG1_PANE; - if (where == STATUS) - key = KEYC_MOUSEDRAG1_STATUS; - if (where == STATUS_LEFT) - key = KEYC_MOUSEDRAG1_STATUS_LEFT; - if (where == STATUS_RIGHT) - key = KEYC_MOUSEDRAG1_STATUS_RIGHT; - if (where == STATUS_DEFAULT) - key = KEYC_MOUSEDRAG1_STATUS_DEFAULT; - if (where == SCROLLBAR_UP) - key = KEYC_MOUSEDRAG1_SCROLLBAR_UP; - if (where == SCROLLBAR_SLIDER) - key = KEYC_MOUSEDRAG1_SCROLLBAR_SLIDER; - if (where == SCROLLBAR_DOWN) - key = KEYC_MOUSEDRAG1_SCROLLBAR_DOWN; - if (where == BORDER) - key = KEYC_MOUSEDRAG1_BORDER; - break; - case MOUSE_BUTTON_2: - if (where == PANE) - key = KEYC_MOUSEDRAG2_PANE; - if (where == STATUS) - key = KEYC_MOUSEDRAG2_STATUS; - if (where == STATUS_LEFT) - key = KEYC_MOUSEDRAG2_STATUS_LEFT; - if (where == STATUS_RIGHT) - key = KEYC_MOUSEDRAG2_STATUS_RIGHT; - if (where == STATUS_DEFAULT) - key = KEYC_MOUSEDRAG2_STATUS_DEFAULT; - if (where == SCROLLBAR_UP) - key = KEYC_MOUSEDRAG2_SCROLLBAR_UP; - if (where == SCROLLBAR_SLIDER) - key = KEYC_MOUSEDRAG2_SCROLLBAR_SLIDER; - if (where == SCROLLBAR_DOWN) - key = KEYC_MOUSEDRAG2_SCROLLBAR_DOWN; - if (where == BORDER) - key = KEYC_MOUSEDRAG2_BORDER; - break; - case MOUSE_BUTTON_3: - if (where == PANE) - key = KEYC_MOUSEDRAG3_PANE; - if (where == STATUS) - key = KEYC_MOUSEDRAG3_STATUS; - if (where == STATUS_LEFT) - key = KEYC_MOUSEDRAG3_STATUS_LEFT; - if (where == STATUS_RIGHT) - key = KEYC_MOUSEDRAG3_STATUS_RIGHT; - if (where == STATUS_DEFAULT) - key = KEYC_MOUSEDRAG3_STATUS_DEFAULT; - if (where == SCROLLBAR_UP) - key = KEYC_MOUSEDRAG3_SCROLLBAR_UP; - if (where == SCROLLBAR_SLIDER) - key = KEYC_MOUSEDRAG3_SCROLLBAR_SLIDER; - if (where == SCROLLBAR_DOWN) - key = KEYC_MOUSEDRAG3_SCROLLBAR_DOWN; - 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 == SCROLLBAR_UP) - key = KEYC_MOUSEDRAG6_SCROLLBAR_UP; - if (where == SCROLLBAR_SLIDER) - key = KEYC_MOUSEDRAG6_SCROLLBAR_SLIDER; - if (where == SCROLLBAR_DOWN) - key = KEYC_MOUSEDRAG6_SCROLLBAR_DOWN; - 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 == SCROLLBAR_UP) - key = KEYC_MOUSEDRAG7_SCROLLBAR_UP; - if (where == SCROLLBAR_SLIDER) - key = KEYC_MOUSEDRAG7_SCROLLBAR_SLIDER; - if (where == SCROLLBAR_DOWN) - key = KEYC_MOUSEDRAG7_SCROLLBAR_DOWN; - 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 == SCROLLBAR_UP) - key = KEYC_MOUSEDRAG8_SCROLLBAR_UP; - if (where == SCROLLBAR_SLIDER) - key = KEYC_MOUSEDRAG8_SCROLLBAR_SLIDER; - if (where == SCROLLBAR_DOWN) - key = KEYC_MOUSEDRAG8_SCROLLBAR_DOWN; - 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 == SCROLLBAR_UP) - key = KEYC_MOUSEDRAG9_SCROLLBAR_UP; - if (where == SCROLLBAR_SLIDER) - key = KEYC_MOUSEDRAG9_SCROLLBAR_SLIDER; - if (where == SCROLLBAR_DOWN) - key = KEYC_MOUSEDRAG9_SCROLLBAR_DOWN; - 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 == SCROLLBAR_UP) - key = KEYC_MOUSEDRAG10_SCROLLBAR_UP; - if (where == SCROLLBAR_SLIDER) - key = KEYC_MOUSEDRAG10_SCROLLBAR_SLIDER; - if (where == SCROLLBAR_DOWN) - key = KEYC_MOUSEDRAG10_SCROLLBAR_DOWN; - 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 == SCROLLBAR_UP) - key = KEYC_MOUSEDRAG11_SCROLLBAR_UP; - if (where == SCROLLBAR_SLIDER) - key = KEYC_MOUSEDRAG11_SCROLLBAR_SLIDER; - if (where == SCROLLBAR_DOWN) - key = KEYC_MOUSEDRAG11_SCROLLBAR_DOWN; - if (where == BORDER) - key = KEYC_MOUSEDRAG11_BORDER; - break; - } - } /* * Begin a drag by setting the flag to a non-zero value that @@ -1317,7 +953,7 @@ have_event: */ c->tty.mouse_drag_flag = MOUSE_BUTTONS(b) + 1; if (c->tty.mouse_scrolling_flag == 0 && - where == SCROLLBAR_SLIDER) { + loc == KEYC_MOUSE_LOCATION_SCROLLBAR_SLIDER) { c->tty.mouse_scrolling_flag = 1; if (m->statusat == 0) { c->tty.mouse_slider_mpos = sl_mpos + @@ -1325,961 +961,33 @@ have_event: } else c->tty.mouse_slider_mpos = sl_mpos; } - break; - case WHEEL: - if (MOUSE_BUTTONS(b) == MOUSE_WHEEL_UP) { - if (where == PANE) - key = KEYC_WHEELUP_PANE; - if (where == STATUS) - key = KEYC_WHEELUP_STATUS; - if (where == STATUS_LEFT) - key = KEYC_WHEELUP_STATUS_LEFT; - if (where == STATUS_RIGHT) - key = KEYC_WHEELUP_STATUS_RIGHT; - if (where == STATUS_DEFAULT) - key = KEYC_WHEELUP_STATUS_DEFAULT; - if (where == BORDER) - key = KEYC_WHEELUP_BORDER; - } else { - if (where == PANE) - key = KEYC_WHEELDOWN_PANE; - if (where == STATUS) - key = KEYC_WHEELDOWN_STATUS; - if (where == STATUS_LEFT) - key = KEYC_WHEELDOWN_STATUS_LEFT; - if (where == STATUS_RIGHT) - key = KEYC_WHEELDOWN_STATUS_RIGHT; - if (where == STATUS_DEFAULT) - key = KEYC_WHEELDOWN_STATUS_DEFAULT; - if (where == BORDER) - key = KEYC_WHEELDOWN_BORDER; - } - break; - case UP: - switch (MOUSE_BUTTONS(b)) { - case MOUSE_BUTTON_1: - 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 == SCROLLBAR_UP) - key = KEYC_MOUSEUP1_SCROLLBAR_UP; - if (where == SCROLLBAR_SLIDER) - key = KEYC_MOUSEUP1_SCROLLBAR_SLIDER; - if (where == SCROLLBAR_DOWN) - key = KEYC_MOUSEUP1_SCROLLBAR_DOWN; - if (where == BORDER) - key = KEYC_MOUSEUP1_BORDER; - break; - case MOUSE_BUTTON_2: - if (where == PANE) - key = KEYC_MOUSEUP2_PANE; - if (where == STATUS) - key = KEYC_MOUSEUP2_STATUS; - if (where == STATUS_LEFT) - key = KEYC_MOUSEUP2_STATUS_LEFT; - if (where == STATUS_RIGHT) - key = KEYC_MOUSEUP2_STATUS_RIGHT; - if (where == STATUS_DEFAULT) - key = KEYC_MOUSEUP2_STATUS_DEFAULT; - if (where == SCROLLBAR_UP) - key = KEYC_MOUSEUP2_SCROLLBAR_UP; - if (where == SCROLLBAR_SLIDER) - key = KEYC_MOUSEUP2_SCROLLBAR_SLIDER; - if (where == SCROLLBAR_DOWN) - key = KEYC_MOUSEUP2_SCROLLBAR_DOWN; - if (where == BORDER) - key = KEYC_MOUSEUP2_BORDER; - break; - case MOUSE_BUTTON_3: - if (where == PANE) - key = KEYC_MOUSEUP3_PANE; - if (where == STATUS) - key = KEYC_MOUSEUP3_STATUS; - if (where == STATUS_LEFT) - key = KEYC_MOUSEUP3_STATUS_LEFT; - if (where == STATUS_RIGHT) - key = KEYC_MOUSEUP3_STATUS_RIGHT; - if (where == STATUS_DEFAULT) - key = KEYC_MOUSEUP3_STATUS_DEFAULT; - if (where == SCROLLBAR_UP) - key = KEYC_MOUSEUP3_SCROLLBAR_UP; - if (where == SCROLLBAR_SLIDER) - key = KEYC_MOUSEUP3_SCROLLBAR_SLIDER; - if (where == SCROLLBAR_DOWN) - key = KEYC_MOUSEUP3_SCROLLBAR_DOWN; - 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 == SCROLLBAR_UP) - key = KEYC_MOUSEUP6_SCROLLBAR_UP; - if (where == SCROLLBAR_SLIDER) - key = KEYC_MOUSEUP6_SCROLLBAR_SLIDER; - if (where == SCROLLBAR_DOWN) - key = KEYC_MOUSEUP6_SCROLLBAR_DOWN; - 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 == SCROLLBAR_UP) - key = KEYC_MOUSEUP7_SCROLLBAR_UP; - if (where == SCROLLBAR_SLIDER) - key = KEYC_MOUSEUP7_SCROLLBAR_SLIDER; - if (where == SCROLLBAR_DOWN) - key = KEYC_MOUSEUP7_SCROLLBAR_DOWN; - 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 == SCROLLBAR_UP) - key = KEYC_MOUSEUP8_SCROLLBAR_UP; - if (where == SCROLLBAR_SLIDER) - key = KEYC_MOUSEUP8_SCROLLBAR_SLIDER; - if (where == SCROLLBAR_DOWN) - key = KEYC_MOUSEUP8_SCROLLBAR_DOWN; - 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 == SCROLLBAR_UP) - key = KEYC_MOUSEUP9_SCROLLBAR_UP; - if (where == SCROLLBAR_SLIDER) - key = KEYC_MOUSEUP9_SCROLLBAR_SLIDER; - if (where == SCROLLBAR_DOWN) - key = KEYC_MOUSEUP9_SCROLLBAR_DOWN; - 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_MOUSEUP10_STATUS_DEFAULT; - if (where == SCROLLBAR_UP) - key = KEYC_MOUSEUP10_SCROLLBAR_UP; - if (where == SCROLLBAR_SLIDER) - key = KEYC_MOUSEUP10_SCROLLBAR_SLIDER; - if (where == SCROLLBAR_DOWN) - key = KEYC_MOUSEUP1_SCROLLBAR_DOWN; - 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 == SCROLLBAR_UP) - key = KEYC_MOUSEUP11_SCROLLBAR_UP; - if (where == SCROLLBAR_SLIDER) - key = KEYC_MOUSEUP11_SCROLLBAR_SLIDER; - if (where == SCROLLBAR_DOWN) - key = KEYC_MOUSEUP11_SCROLLBAR_DOWN; - if (where == BORDER) - key = KEYC_MOUSEUP11_BORDER; - break; - } - break; - case DOWN: - switch (MOUSE_BUTTONS(b)) { - case MOUSE_BUTTON_1: - if (where == PANE) - key = KEYC_MOUSEDOWN1_PANE; - if (where == STATUS) - key = KEYC_MOUSEDOWN1_STATUS; - if (where == STATUS_LEFT) - key = KEYC_MOUSEDOWN1_STATUS_LEFT; - if (where == STATUS_RIGHT) - key = KEYC_MOUSEDOWN1_STATUS_RIGHT; - if (where == STATUS_DEFAULT) - key = KEYC_MOUSEDOWN1_STATUS_DEFAULT; - if (where == SCROLLBAR_UP) - key = KEYC_MOUSEDOWN1_SCROLLBAR_UP; - if (where == SCROLLBAR_SLIDER) - key = KEYC_MOUSEDOWN1_SCROLLBAR_SLIDER; - if (where == SCROLLBAR_DOWN) - key = KEYC_MOUSEDOWN1_SCROLLBAR_DOWN; - if (where == BORDER) - key = KEYC_MOUSEDOWN1_BORDER; - break; - case MOUSE_BUTTON_2: - if (where == PANE) - key = KEYC_MOUSEDOWN2_PANE; - if (where == STATUS) - key = KEYC_MOUSEDOWN2_STATUS; - if (where == STATUS_LEFT) - key = KEYC_MOUSEDOWN2_STATUS_LEFT; - if (where == STATUS_RIGHT) - key = KEYC_MOUSEDOWN2_STATUS_RIGHT; - if (where == STATUS_DEFAULT) - key = KEYC_MOUSEDOWN2_STATUS_DEFAULT; - if (where == SCROLLBAR_UP) - key = KEYC_MOUSEDOWN2_SCROLLBAR_UP; - if (where == SCROLLBAR_SLIDER) - key = KEYC_MOUSEDOWN2_SCROLLBAR_SLIDER; - if (where == SCROLLBAR_DOWN) - key = KEYC_MOUSEDOWN2_SCROLLBAR_DOWN; - if (where == BORDER) - key = KEYC_MOUSEDOWN2_BORDER; - break; - case MOUSE_BUTTON_3: - if (where == PANE) - key = KEYC_MOUSEDOWN3_PANE; - if (where == STATUS) - key = KEYC_MOUSEDOWN3_STATUS; - if (where == STATUS_LEFT) - key = KEYC_MOUSEDOWN3_STATUS_LEFT; - if (where == STATUS_RIGHT) - key = KEYC_MOUSEDOWN3_STATUS_RIGHT; - if (where == STATUS_DEFAULT) - key = KEYC_MOUSEDOWN3_STATUS_DEFAULT; - if (where == SCROLLBAR_UP) - key = KEYC_MOUSEDOWN3_SCROLLBAR_UP; - if (where == SCROLLBAR_SLIDER) - key = KEYC_MOUSEDOWN3_SCROLLBAR_SLIDER; - if (where == SCROLLBAR_DOWN) - key = KEYC_MOUSEDOWN3_SCROLLBAR_DOWN; - 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 == SCROLLBAR_UP) - key = KEYC_MOUSEDOWN6_SCROLLBAR_UP; - if (where == SCROLLBAR_SLIDER) - key = KEYC_MOUSEDOWN6_SCROLLBAR_SLIDER; - if (where == SCROLLBAR_DOWN) - key = KEYC_MOUSEDOWN6_SCROLLBAR_DOWN; - 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 == SCROLLBAR_UP) - key = KEYC_MOUSEDOWN7_SCROLLBAR_UP; - if (where == SCROLLBAR_SLIDER) - key = KEYC_MOUSEDOWN7_SCROLLBAR_SLIDER; - if (where == SCROLLBAR_DOWN) - key = KEYC_MOUSEDOWN7_SCROLLBAR_DOWN; - 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 == SCROLLBAR_UP) - key = KEYC_MOUSEDOWN8_SCROLLBAR_UP; - if (where == SCROLLBAR_SLIDER) - key = KEYC_MOUSEDOWN8_SCROLLBAR_SLIDER; - if (where == SCROLLBAR_DOWN) - key = KEYC_MOUSEDOWN8_SCROLLBAR_DOWN; - 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 == SCROLLBAR_UP) - key = KEYC_MOUSEDOWN9_SCROLLBAR_UP; - if (where == SCROLLBAR_SLIDER) - key = KEYC_MOUSEDOWN9_SCROLLBAR_SLIDER; - if (where == SCROLLBAR_DOWN) - key = KEYC_MOUSEDOWN9_SCROLLBAR_DOWN; - 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 == SCROLLBAR_UP) - key = KEYC_MOUSEDOWN10_SCROLLBAR_UP; - if (where == SCROLLBAR_SLIDER) - key = KEYC_MOUSEDOWN10_SCROLLBAR_SLIDER; - if (where == SCROLLBAR_DOWN) - key = KEYC_MOUSEDOWN10_SCROLLBAR_DOWN; - 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 == SCROLLBAR_UP) - key = KEYC_MOUSEDOWN11_SCROLLBAR_UP; - if (where == SCROLLBAR_SLIDER) - key = KEYC_MOUSEDOWN11_SCROLLBAR_SLIDER; - if (where == SCROLLBAR_DOWN) - key = KEYC_MOUSEDOWN11_SCROLLBAR_DOWN; - if (where == BORDER) - key = KEYC_MOUSEDOWN11_BORDER; - break; - } - break; - case SECOND: - switch (MOUSE_BUTTONS(b)) { - case MOUSE_BUTTON_1: - if (where == PANE) - key = KEYC_SECONDCLICK1_PANE; - if (where == STATUS) - key = KEYC_SECONDCLICK1_STATUS; - if (where == STATUS_LEFT) - key = KEYC_SECONDCLICK1_STATUS_LEFT; - if (where == STATUS_RIGHT) - key = KEYC_SECONDCLICK1_STATUS_RIGHT; - if (where == STATUS_DEFAULT) - key = KEYC_SECONDCLICK1_STATUS_DEFAULT; - if (where == SCROLLBAR_UP) - key = KEYC_SECONDCLICK1_SCROLLBAR_UP; - if (where == SCROLLBAR_SLIDER) - key = KEYC_SECONDCLICK1_SCROLLBAR_SLIDER; - if (where == SCROLLBAR_DOWN) - key = KEYC_SECONDCLICK1_SCROLLBAR_DOWN; - if (where == BORDER) - key = KEYC_SECONDCLICK1_BORDER; - break; - case MOUSE_BUTTON_2: - if (where == PANE) - key = KEYC_SECONDCLICK2_PANE; - if (where == STATUS) - key = KEYC_SECONDCLICK2_STATUS; - if (where == STATUS_LEFT) - key = KEYC_SECONDCLICK2_STATUS_LEFT; - if (where == STATUS_RIGHT) - key = KEYC_SECONDCLICK2_STATUS_RIGHT; - if (where == STATUS_DEFAULT) - key = KEYC_SECONDCLICK2_STATUS_DEFAULT; - if (where == SCROLLBAR_UP) - key = KEYC_SECONDCLICK2_SCROLLBAR_UP; - if (where == SCROLLBAR_SLIDER) - key = KEYC_SECONDCLICK2_SCROLLBAR_SLIDER; - if (where == SCROLLBAR_DOWN) - key = KEYC_SECONDCLICK2_SCROLLBAR_DOWN; - if (where == BORDER) - key = KEYC_SECONDCLICK2_BORDER; - break; - case MOUSE_BUTTON_3: - if (where == PANE) - key = KEYC_SECONDCLICK3_PANE; - if (where == STATUS) - key = KEYC_SECONDCLICK3_STATUS; - if (where == STATUS_LEFT) - key = KEYC_SECONDCLICK3_STATUS_LEFT; - if (where == STATUS_RIGHT) - key = KEYC_SECONDCLICK3_STATUS_RIGHT; - if (where == STATUS_DEFAULT) - key = KEYC_SECONDCLICK3_STATUS_DEFAULT; - if (where == SCROLLBAR_UP) - key = KEYC_SECONDCLICK3_SCROLLBAR_UP; - if (where == SCROLLBAR_SLIDER) - key = KEYC_SECONDCLICK3_SCROLLBAR_SLIDER; - if (where == SCROLLBAR_DOWN) - key = KEYC_SECONDCLICK3_SCROLLBAR_DOWN; - 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 == SCROLLBAR_UP) - key = KEYC_SECONDCLICK6_SCROLLBAR_UP; - if (where == SCROLLBAR_SLIDER) - key = KEYC_SECONDCLICK6_SCROLLBAR_SLIDER; - if (where == SCROLLBAR_DOWN) - key = KEYC_SECONDCLICK6_SCROLLBAR_DOWN; - 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 == SCROLLBAR_UP) - key = KEYC_SECONDCLICK7_SCROLLBAR_UP; - if (where == SCROLLBAR_SLIDER) - key = KEYC_SECONDCLICK7_SCROLLBAR_SLIDER; - if (where == SCROLLBAR_DOWN) - key = KEYC_SECONDCLICK7_SCROLLBAR_DOWN; - 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 == SCROLLBAR_UP) - key = KEYC_SECONDCLICK8_SCROLLBAR_UP; - if (where == SCROLLBAR_SLIDER) - key = KEYC_SECONDCLICK8_SCROLLBAR_SLIDER; - if (where == SCROLLBAR_DOWN) - key = KEYC_SECONDCLICK8_SCROLLBAR_DOWN; - 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 == SCROLLBAR_UP) - key = KEYC_SECONDCLICK9_SCROLLBAR_UP; - if (where == SCROLLBAR_SLIDER) - key = KEYC_SECONDCLICK9_SCROLLBAR_SLIDER; - if (where == SCROLLBAR_DOWN) - key = KEYC_SECONDCLICK9_SCROLLBAR_DOWN; - 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 == SCROLLBAR_UP) - key = KEYC_SECONDCLICK10_SCROLLBAR_UP; - if (where == SCROLLBAR_SLIDER) - key = KEYC_SECONDCLICK10_SCROLLBAR_SLIDER; - if (where == SCROLLBAR_DOWN) - key = KEYC_SECONDCLICK10_SCROLLBAR_DOWN; - 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 == SCROLLBAR_UP) - key = KEYC_SECONDCLICK11_SCROLLBAR_UP; - if (where == SCROLLBAR_SLIDER) - key = KEYC_SECONDCLICK11_SCROLLBAR_SLIDER; - if (where == SCROLLBAR_DOWN) - key = KEYC_SECONDCLICK11_SCROLLBAR_DOWN; - if (where == BORDER) - key = KEYC_SECONDCLICK11_BORDER; - break; - } - break; - case DOUBLE: - switch (MOUSE_BUTTONS(b)) { - case MOUSE_BUTTON_1: - if (where == PANE) - key = KEYC_DOUBLECLICK1_PANE; - if (where == STATUS) - key = KEYC_DOUBLECLICK1_STATUS; - if (where == STATUS_LEFT) - key = KEYC_DOUBLECLICK1_STATUS_LEFT; - if (where == STATUS_RIGHT) - key = KEYC_DOUBLECLICK1_STATUS_RIGHT; - if (where == STATUS_DEFAULT) - key = KEYC_DOUBLECLICK1_STATUS_DEFAULT; - if (where == SCROLLBAR_UP) - key = KEYC_DOUBLECLICK1_SCROLLBAR_UP; - if (where == SCROLLBAR_SLIDER) - key = KEYC_DOUBLECLICK1_SCROLLBAR_SLIDER; - if (where == SCROLLBAR_DOWN) - key = KEYC_DOUBLECLICK1_SCROLLBAR_DOWN; - if (where == BORDER) - key = KEYC_DOUBLECLICK1_BORDER; - break; - case MOUSE_BUTTON_2: - if (where == PANE) - key = KEYC_DOUBLECLICK2_PANE; - if (where == STATUS) - key = KEYC_DOUBLECLICK2_STATUS; - if (where == STATUS_LEFT) - key = KEYC_DOUBLECLICK2_STATUS_LEFT; - if (where == STATUS_RIGHT) - key = KEYC_DOUBLECLICK2_STATUS_RIGHT; - if (where == STATUS_DEFAULT) - key = KEYC_DOUBLECLICK2_STATUS_DEFAULT; - if (where == SCROLLBAR_UP) - key = KEYC_DOUBLECLICK2_SCROLLBAR_UP; - if (where == SCROLLBAR_SLIDER) - key = KEYC_DOUBLECLICK2_SCROLLBAR_SLIDER; - if (where == SCROLLBAR_DOWN) - key = KEYC_DOUBLECLICK2_SCROLLBAR_DOWN; - if (where == BORDER) - key = KEYC_DOUBLECLICK2_BORDER; - break; - case MOUSE_BUTTON_3: - if (where == PANE) - key = KEYC_DOUBLECLICK3_PANE; - if (where == STATUS) - key = KEYC_DOUBLECLICK3_STATUS; - if (where == STATUS_LEFT) - key = KEYC_DOUBLECLICK3_STATUS_LEFT; - if (where == STATUS_RIGHT) - key = KEYC_DOUBLECLICK3_STATUS_RIGHT; - if (where == STATUS_DEFAULT) - key = KEYC_DOUBLECLICK3_STATUS_DEFAULT; - if (where == SCROLLBAR_UP) - key = KEYC_DOUBLECLICK3_SCROLLBAR_UP; - if (where == SCROLLBAR_SLIDER) - key = KEYC_DOUBLECLICK3_SCROLLBAR_SLIDER; - if (where == SCROLLBAR_DOWN) - key = KEYC_DOUBLECLICK3_SCROLLBAR_DOWN; - 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 == SCROLLBAR_UP) - key = KEYC_DOUBLECLICK6_SCROLLBAR_UP; - if (where == SCROLLBAR_SLIDER) - key = KEYC_DOUBLECLICK6_SCROLLBAR_SLIDER; - if (where == SCROLLBAR_DOWN) - key = KEYC_DOUBLECLICK6_SCROLLBAR_DOWN; - 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 == SCROLLBAR_UP) - key = KEYC_DOUBLECLICK7_SCROLLBAR_UP; - if (where == SCROLLBAR_SLIDER) - key = KEYC_DOUBLECLICK7_SCROLLBAR_SLIDER; - if (where == SCROLLBAR_DOWN) - key = KEYC_DOUBLECLICK7_SCROLLBAR_DOWN; - 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 == SCROLLBAR_UP) - key = KEYC_DOUBLECLICK8_SCROLLBAR_UP; - if (where == SCROLLBAR_SLIDER) - key = KEYC_DOUBLECLICK8_SCROLLBAR_SLIDER; - if (where == SCROLLBAR_DOWN) - key = KEYC_DOUBLECLICK8_SCROLLBAR_DOWN; - 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 == SCROLLBAR_UP) - key = KEYC_DOUBLECLICK9_SCROLLBAR_UP; - if (where == SCROLLBAR_SLIDER) - key = KEYC_DOUBLECLICK9_SCROLLBAR_SLIDER; - if (where == SCROLLBAR_DOWN) - key = KEYC_DOUBLECLICK9_SCROLLBAR_DOWN; - 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 == SCROLLBAR_UP) - key = KEYC_DOUBLECLICK10_SCROLLBAR_UP; - if (where == SCROLLBAR_SLIDER) - key = KEYC_DOUBLECLICK10_SCROLLBAR_SLIDER; - if (where == SCROLLBAR_DOWN) - key = KEYC_DOUBLECLICK10_SCROLLBAR_DOWN; - 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 == SCROLLBAR_UP) - key = KEYC_DOUBLECLICK11_SCROLLBAR_UP; - if (where == SCROLLBAR_SLIDER) - key = KEYC_DOUBLECLICK11_SCROLLBAR_SLIDER; - if (where == SCROLLBAR_DOWN) - key = KEYC_DOUBLECLICK11_SCROLLBAR_DOWN; - if (where == BORDER) - key = KEYC_DOUBLECLICK11_BORDER; - break; - } - break; - case TRIPLE: - switch (MOUSE_BUTTONS(b)) { - case MOUSE_BUTTON_1: - if (where == PANE) - key = KEYC_TRIPLECLICK1_PANE; - if (where == STATUS) - key = KEYC_TRIPLECLICK1_STATUS; - if (where == STATUS_LEFT) - key = KEYC_TRIPLECLICK1_STATUS_LEFT; - if (where == STATUS_RIGHT) - key = KEYC_TRIPLECLICK1_STATUS_RIGHT; - if (where == STATUS_DEFAULT) - key = KEYC_TRIPLECLICK1_STATUS_DEFAULT; - if (where == SCROLLBAR_UP) - key = KEYC_TRIPLECLICK1_SCROLLBAR_UP; - if (where == SCROLLBAR_SLIDER) - key = KEYC_TRIPLECLICK1_SCROLLBAR_SLIDER; - if (where == SCROLLBAR_DOWN) - key = KEYC_TRIPLECLICK1_SCROLLBAR_DOWN; - if (where == BORDER) - key = KEYC_TRIPLECLICK1_BORDER; - break; - case MOUSE_BUTTON_2: - if (where == PANE) - key = KEYC_TRIPLECLICK2_PANE; - if (where == STATUS) - key = KEYC_TRIPLECLICK2_STATUS; - if (where == STATUS_LEFT) - key = KEYC_TRIPLECLICK2_STATUS_LEFT; - if (where == STATUS_RIGHT) - key = KEYC_TRIPLECLICK2_STATUS_RIGHT; - if (where == STATUS_DEFAULT) - key = KEYC_TRIPLECLICK2_STATUS_DEFAULT; - if (where == SCROLLBAR_UP) - key = KEYC_TRIPLECLICK2_SCROLLBAR_UP; - if (where == SCROLLBAR_SLIDER) - key = KEYC_TRIPLECLICK2_SCROLLBAR_SLIDER; - if (where == SCROLLBAR_DOWN) - key = KEYC_TRIPLECLICK2_SCROLLBAR_DOWN; - if (where == BORDER) - key = KEYC_TRIPLECLICK2_BORDER; - break; - case MOUSE_BUTTON_3: - if (where == PANE) - key = KEYC_TRIPLECLICK3_PANE; - if (where == STATUS) - key = KEYC_TRIPLECLICK3_STATUS; - if (where == STATUS_LEFT) - key = KEYC_TRIPLECLICK3_STATUS_LEFT; - if (where == STATUS_RIGHT) - key = KEYC_TRIPLECLICK3_STATUS_RIGHT; - if (where == STATUS_DEFAULT) - key = KEYC_TRIPLECLICK3_STATUS_DEFAULT; - if (where == SCROLLBAR_UP) - key = KEYC_TRIPLECLICK3_SCROLLBAR_UP; - if (where == SCROLLBAR_SLIDER) - key = KEYC_TRIPLECLICK3_SCROLLBAR_SLIDER; - if (where == SCROLLBAR_DOWN) - key = KEYC_TRIPLECLICK3_SCROLLBAR_DOWN; - 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 == SCROLLBAR_UP) - key = KEYC_TRIPLECLICK6_SCROLLBAR_UP; - if (where == SCROLLBAR_SLIDER) - key = KEYC_TRIPLECLICK6_SCROLLBAR_SLIDER; - if (where == SCROLLBAR_DOWN) - key = KEYC_TRIPLECLICK6_SCROLLBAR_DOWN; - 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 == SCROLLBAR_UP) - key = KEYC_TRIPLECLICK7_SCROLLBAR_UP; - if (where == SCROLLBAR_SLIDER) - key = KEYC_TRIPLECLICK7_SCROLLBAR_SLIDER; - if (where == SCROLLBAR_DOWN) - key = KEYC_TRIPLECLICK7_SCROLLBAR_DOWN; - 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 == SCROLLBAR_UP) - key = KEYC_TRIPLECLICK8_SCROLLBAR_UP; - if (where == SCROLLBAR_SLIDER) - key = KEYC_TRIPLECLICK8_SCROLLBAR_SLIDER; - if (where == SCROLLBAR_DOWN) - key = KEYC_TRIPLECLICK8_SCROLLBAR_DOWN; - 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 == SCROLLBAR_UP) - key = KEYC_TRIPLECLICK9_SCROLLBAR_UP; - if (where == SCROLLBAR_SLIDER) - key = KEYC_TRIPLECLICK9_SCROLLBAR_SLIDER; - if (where == SCROLLBAR_DOWN) - key = KEYC_TRIPLECLICK9_SCROLLBAR_DOWN; - 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 == SCROLLBAR_UP) - key = KEYC_TRIPLECLICK10_SCROLLBAR_UP; - if (where == SCROLLBAR_SLIDER) - key = KEYC_TRIPLECLICK10_SCROLLBAR_SLIDER; - if (where == SCROLLBAR_DOWN) - key = KEYC_TRIPLECLICK10_SCROLLBAR_DOWN; - 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 == SCROLLBAR_UP) - key = KEYC_TRIPLECLICK11_SCROLLBAR_UP; - if (where == SCROLLBAR_SLIDER) - key = KEYC_TRIPLECLICK11_SCROLLBAR_SLIDER; - if (where == SCROLLBAR_DOWN) - key = KEYC_TRIPLECLICK11_SCROLLBAR_DOWN; - if (where == BORDER) - key = KEYC_TRIPLECLICK11_BORDER; - break; - } - break; } - if (key == KEYC_UNKNOWN) - return (KEYC_UNKNOWN); -out: + if (key == KEYC_UNKNOWN) { + /* Adjust the button number */ + if (b == MOUSE_BUTTON_1) + bn = 1; + else if (b == MOUSE_BUTTON_2) + bn = 2; + else if (b == MOUSE_BUTTON_3) + bn = 3; + else if (b == MOUSE_BUTTON_6) + bn = 6; + else if (b == MOUSE_BUTTON_7) + bn = 7; + else if (b == MOUSE_BUTTON_8) + bn = 8; + else if (b == MOUSE_BUTTON_9) + bn = 9; + else if (b == MOUSE_BUTTON_10) + bn = 10; + else if (b == MOUSE_BUTTON_11) + bn = 11; + else + bn = 0; + key = KEYC_MAKE_MOUSE_KEY(type, bn, loc); + } + /* Apply modifiers if any. */ if (b & MOUSE_MASK_META) key |= KEYC_META; diff --git a/tmux.h b/tmux.h index 55f3618b..8d631ff9 100644 --- a/tmux.h +++ b/tmux.h @@ -120,18 +120,6 @@ struct winlink; #define VISUAL_ON 1 #define VISUAL_BOTH 2 -/* No key or unknown key. */ -#define KEYC_NONE 0x000ff000000000ULL -#define KEYC_UNKNOWN 0x000fe000000000ULL - -/* - * Base for special (that is, not Unicode) keys. An enum must be at most a - * signed int, so these are based in the highest Unicode PUA. - */ -#define KEYC_BASE 0x0000000010e000ULL -#define KEYC_USER 0x0000000010f000ULL -#define KEYC_USER_END (KEYC_USER + KEYC_NUSER) - /* Key modifier bits. */ #define KEYC_META 0x00100000000000ULL #define KEYC_CTRL 0x00200000000000ULL @@ -147,45 +135,102 @@ struct winlink; #define KEYC_SENT 0x40000000000000ULL /* Masks for key bits. */ -#define KEYC_MASK_MODIFIERS 0x00f00000000000ULL +#define KEYC_MASK_TYPE 0x0000ff00000000ULL +#define KEYC_MASK_MODIFIERS 0x00ff0000000000ULL #define KEYC_MASK_FLAGS 0xff000000000000ULL -#define KEYC_MASK_KEY 0x000fffffffffffULL +#define KEYC_MASK_KEY 0x0000ffffffffffULL -/* Available user keys. */ -#define KEYC_NUSER 1000 +#define KEYC_NUSER 1000 +#define KEYC_SHIFT_TYPE(t) ((unsigned long long)(t) << 32) +#define KEYC_IS_TYPE(k, t) (((k) & KEYC_MASK_TYPE) == KEYC_SHIFT_TYPE(t)) +enum key_code_type { + KEYC_TYPE_UNICODE, + KEYC_TYPE_USER, + KEYC_TYPE_FUNCTION, + KEYC_TYPE_MOUSEMOVE, + KEYC_TYPE_MOUSEDOWN, + KEYC_TYPE_MOUSEUP, + KEYC_TYPE_MOUSEDRAG, + KEYC_TYPE_MOUSEDRAGEND, + KEYC_TYPE_WHEELDOWN, + KEYC_TYPE_WHEELUP, + KEYC_TYPE_SECONDCLICK, + KEYC_TYPE_DOUBLECLICK, + KEYC_TYPE_TRIPLECLICK, + KEYC_TYPE_NOTYPE /* end */ +}; -/* Is this a mouse key? */ -#define KEYC_IS_MOUSE(key) \ - (((key) & KEYC_MASK_KEY) >= KEYC_MOUSE && \ - ((key) & KEYC_MASK_KEY) < KEYC_BSPACE) +enum key_code_mouse_location { + KEYC_MOUSE_LOCATION_PANE, + KEYC_MOUSE_LOCATION_STATUS, + KEYC_MOUSE_LOCATION_STATUS_LEFT, + KEYC_MOUSE_LOCATION_STATUS_RIGHT, + KEYC_MOUSE_LOCATION_STATUS_DEFAULT, + KEYC_MOUSE_LOCATION_BORDER, + KEYC_MOUSE_LOCATION_SCROLLBAR_UP, + KEYC_MOUSE_LOCATION_SCROLLBAR_SLIDER, + KEYC_MOUSE_LOCATION_SCROLLBAR_DOWN, + KEYC_MOUSE_LOCATION_NOWHERE /* end */ +}; /* Is this a Unicode key? */ #define KEYC_IS_UNICODE(key) \ - (((key) & KEYC_MASK_KEY) > 0x7f && \ - (((key) & KEYC_MASK_KEY) < KEYC_BASE || \ - ((key) & KEYC_MASK_KEY) >= KEYC_BASE_END) && \ - (((key) & KEYC_MASK_KEY) < KEYC_USER || \ - ((key) & KEYC_MASK_KEY) >= KEYC_USER_END)) + (((key) & KEYC_MASK_TYPE) == KEYC_SHIFT_TYPE(KEYC_TYPE_UNICODE) && \ + ((key) & KEYC_MASK_KEY) > 0x7f) + +/* Is this a user key? */ +#define KEYC_IS_USER(key) \ + (((key) & KEYC_MASK_TYPE) == KEYC_SHIFT_TYPE(KEYC_TYPE_USER)) + +/* Is this a function key? */ +#define KEYC_IS_SPECIAL(key) \ + (((key) & KEYC_MASK_TYPE) == KEYC_SHIFT_TYPE(KEYC_TYPE_FUNCTION)) + +/* Is this a mouse key? */ +#define KEYC_IS_MOUSE(key) \ + (((key) & KEYC_MASK_KEY) == KEYC_MOUSE || \ + (((key) & KEYC_MASK_TYPE) >= KEYC_SHIFT_TYPE(KEYC_TYPE_MOUSEMOVE) && \ + ((key) & KEYC_MASK_TYPE) <= KEYC_SHIFT_TYPE(KEYC_TYPE_TRIPLECLICK))) /* Is this a paste key? */ #define KEYC_IS_PASTE(key) \ - (((key) & KEYC_MASK_KEY) == KEYC_PASTE_START || \ - ((key) & KEYC_MASK_KEY) == KEYC_PASTE_END) + (((key) & KEYC_MASK_TYPE) == KEYC_SHIFT_TYPE(KEYC_TYPE_FUNCTION) && \ + (((key) & KEYC_MASK_KEY) == KEYC_PASTE_START || \ + ((key) & KEYC_MASK_KEY) == KEYC_PASTE_END)) /* Multiple click timeout. */ #define KEYC_CLICK_TIMEOUT 300 +/* Bit shift for mouse events. */ +#define KEYC_MOUSE_LOCATION_SHIFT 0 +#define KEYC_MOUSE_BUTTON_SHIFT 8 + /* Mouse key codes. */ -#define KEYC_MOUSE_KEY(name) \ - KEYC_ ## name ## _PANE, \ - KEYC_ ## name ## _STATUS, \ - KEYC_ ## name ## _STATUS_LEFT, \ - KEYC_ ## name ## _STATUS_RIGHT, \ - KEYC_ ## name ## _STATUS_DEFAULT, \ - KEYC_ ## name ## _SCROLLBAR_UP, \ - KEYC_ ## name ## _SCROLLBAR_SLIDER, \ - KEYC_ ## name ## _SCROLLBAR_DOWN, \ - KEYC_ ## name ## _BORDER +#define KEYC_MOUSE_KEYS(t) \ + KEYC_MOUSE_KEY(KEYC_ ## t, KEYC_TYPE_ ## t, PANE), \ + KEYC_MOUSE_KEY(KEYC_ ## t, KEYC_TYPE_ ## t, STATUS), \ + KEYC_MOUSE_KEY(KEYC_ ## t, KEYC_TYPE_ ## t, STATUS_LEFT), \ + KEYC_MOUSE_KEY(KEYC_ ## t, KEYC_TYPE_ ## t, STATUS_RIGHT), \ + KEYC_MOUSE_KEY(KEYC_ ## t, KEYC_TYPE_ ## t, STATUS_DEFAULT), \ + KEYC_MOUSE_KEY(KEYC_ ## t, KEYC_TYPE_ ## t, BORDER), \ + KEYC_MOUSE_KEY(KEYC_ ## t, KEYC_TYPE_ ## t, SCROLLBAR_UP), \ + KEYC_MOUSE_KEY(KEYC_ ## t, KEYC_TYPE_ ## t, SCROLLBAR_SLIDER), \ + KEYC_MOUSE_KEY(KEYC_ ## t, KEYC_TYPE_ ## t, SCROLLBAR_DOWN) +#define KEYC_MOUSE_KEY(p, t, l) \ + p ## _ ## l = KEYC_MAKE_MOUSE_KEY(t, 0, KEYC_MOUSE_LOCATION_ ## l), \ + p ## 1_ ## l = KEYC_MAKE_MOUSE_KEY(t, 1, KEYC_MOUSE_LOCATION_ ## l), \ + p ## 2_ ## l = KEYC_MAKE_MOUSE_KEY(t, 2, KEYC_MOUSE_LOCATION_ ## l), \ + p ## 3_ ## l = KEYC_MAKE_MOUSE_KEY(t, 3, KEYC_MOUSE_LOCATION_ ## l), \ + p ## 6_ ## l = KEYC_MAKE_MOUSE_KEY(t, 6, KEYC_MOUSE_LOCATION_ ## l), \ + p ## 7_ ## l = KEYC_MAKE_MOUSE_KEY(t, 7, KEYC_MOUSE_LOCATION_ ## l), \ + p ## 8_ ## l = KEYC_MAKE_MOUSE_KEY(t, 8, KEYC_MOUSE_LOCATION_ ## l), \ + p ## 9_ ## l = KEYC_MAKE_MOUSE_KEY(t, 9, KEYC_MOUSE_LOCATION_ ## l), \ + p ## 10_ ## l = KEYC_MAKE_MOUSE_KEY(t, 10, KEYC_MOUSE_LOCATION_ ## l), \ + p ## 11_ ## l = KEYC_MAKE_MOUSE_KEY(t, 11, KEYC_MOUSE_LOCATION_ ## l) +#define KEYC_MAKE_MOUSE_KEY(t, b, l) \ + ((KEYC_SHIFT_TYPE(t)) | \ + ((unsigned long long)(b) << KEYC_MOUSE_BUTTON_SHIFT) | \ + ((unsigned long long)(l) << KEYC_MOUSE_LOCATION_SHIFT)) #define KEYC_MOUSE_STRING(name, s) \ { #s "Pane", KEYC_ ## name ## _PANE }, \ { #s "Status", KEYC_ ## name ## _STATUS }, \ @@ -241,8 +286,15 @@ enum { /* Special key codes. */ enum { + /* User key code range. */ + KEYC_USER = KEYC_SHIFT_TYPE(KEYC_TYPE_USER), + + /* Functional key code range. */ + KEYC_NONE = KEYC_SHIFT_TYPE(KEYC_TYPE_FUNCTION), + KEYC_UNKNOWN, + /* Focus events. */ - KEYC_FOCUS_IN = KEYC_BASE, + KEYC_FOCUS_IN, KEYC_FOCUS_OUT, /* "Any" key, used if not found in key table. */ @@ -252,77 +304,6 @@ enum { KEYC_PASTE_START, KEYC_PASTE_END, - /* Mouse keys. */ - KEYC_MOUSE, /* unclassified mouse event */ - KEYC_DRAGGING, /* dragging in progress */ - KEYC_DOUBLECLICK, /* double click complete */ - KEYC_MOUSE_KEY(MOUSEMOVE), - 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, @@ -375,8 +356,22 @@ enum { KEYC_REPORT_DARK_THEME, KEYC_REPORT_LIGHT_THEME, - /* End of special keys. */ - KEYC_BASE_END + /* Mouse state. */ + KEYC_MOUSE, /* unclassified mouse event */ + KEYC_DRAGGING, /* dragging in progress */ + KEYC_DOUBLECLICK, /* double click complete */ + + /* Mouse key code ranges. Must be at the end. */ + KEYC_MOUSE_KEYS(MOUSEMOVE), + KEYC_MOUSE_KEYS(WHEELDOWN), + KEYC_MOUSE_KEYS(WHEELUP), + KEYC_MOUSE_KEYS(MOUSEDOWN), + KEYC_MOUSE_KEYS(MOUSEUP), + KEYC_MOUSE_KEYS(MOUSEDRAG), + KEYC_MOUSE_KEYS(MOUSEDRAGEND), + KEYC_MOUSE_KEYS(SECONDCLICK), + KEYC_MOUSE_KEYS(DOUBLECLICK), + KEYC_MOUSE_KEYS(TRIPLECLICK), }; /* Termcap codes. */ @@ -1954,7 +1949,7 @@ struct client { struct event repeat_timer; struct event click_timer; - int click_where; + int click_loc; int click_wp; u_int click_button; struct mouse_event click_event; From 7af58f74b0a33c14927e1555c3b3f7cd21659293 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 1 Apr 2026 08:10:02 +0000 Subject: [PATCH 06/28] Fixed missing drag end detection and mouse button detection with modifiers. From Dane Jensen, reported by Joseph Tyson and Mark Kelly. --- server-client.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/server-client.c b/server-client.c index 95f6076c..8a4fe491 100644 --- a/server-client.c +++ b/server-client.c @@ -926,6 +926,7 @@ have_event: * End a mouse drag by passing a MouseDragEnd key corresponding * to the button that started the drag. */ + type = KEYC_TYPE_MOUSEDRAGEND; c->tty.mouse_drag_flag = 0; c->tty.mouse_slider_mpos = -1; } @@ -934,8 +935,8 @@ have_event: if (type == KEYC_TYPE_MOUSEMOVE && loc == KEYC_MOUSE_LOCATION_PANE) { key = KEYC_MOUSEMOVE_PANE; if (wp != NULL && - wp != w->active && - options_get_number(s->options, "focus-follows-mouse")) { + wp != w->active && + options_get_number(s->options, "focus-follows-mouse")) { window_set_active_pane(w, wp, 1); server_redraw_window_borders(w); server_status_window(w); @@ -964,24 +965,24 @@ have_event: } if (key == KEYC_UNKNOWN) { - /* Adjust the button number */ - if (b == MOUSE_BUTTON_1) + /* Adjust the button number. */ + if (MOUSE_BUTTONS(b) == MOUSE_BUTTON_1) bn = 1; - else if (b == MOUSE_BUTTON_2) + else if (MOUSE_BUTTONS(b) == MOUSE_BUTTON_2) bn = 2; - else if (b == MOUSE_BUTTON_3) + else if (MOUSE_BUTTONS(b) == MOUSE_BUTTON_3) bn = 3; - else if (b == MOUSE_BUTTON_6) + else if (MOUSE_BUTTONS(b) == MOUSE_BUTTON_6) bn = 6; - else if (b == MOUSE_BUTTON_7) + else if (MOUSE_BUTTONS(b) == MOUSE_BUTTON_7) bn = 7; - else if (b == MOUSE_BUTTON_8) + else if (MOUSE_BUTTONS(b) == MOUSE_BUTTON_8) bn = 8; - else if (b == MOUSE_BUTTON_9) + else if (MOUSE_BUTTONS(b) == MOUSE_BUTTON_9) bn = 9; - else if (b == MOUSE_BUTTON_10) + else if (MOUSE_BUTTONS(b) == MOUSE_BUTTON_10) bn = 10; - else if (b == MOUSE_BUTTON_11) + else if (MOUSE_BUTTONS(b) == MOUSE_BUTTON_11) bn = 11; else bn = 0; From cbb49e8c846d05707103946c4f53783d99e2e322 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 2 Apr 2026 08:01:47 +0000 Subject: [PATCH 07/28] Fix NULL dereference in sort.c, from Dane Jensen. --- sort.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sort.c b/sort.c index 1b18d370..8a8f7028 100644 --- a/sort.c +++ b/sort.c @@ -505,6 +505,7 @@ sort_get_panes_session(struct session *s, u_int *n, i = 0; RB_FOREACH(wl, winlinks, &s->windows) { + w = wl->window; TAILQ_FOREACH(wp, &w->panes, entry) { if (lsz <= i) { lsz += 100; From 8b51abef08b9b63bd14526d3c9834795c93bdc38 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 2 Apr 2026 08:37:14 +0000 Subject: [PATCH 08/28] Check for \0 after skipping # not before in format_expand1, from ossfuzz. --- format.c | 2 +- window-copy.c | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/format.c b/format.c index 031d38e1..d84dfd35 100644 --- a/format.c +++ b/format.c @@ -5444,7 +5444,7 @@ format_expand1(struct format_expand_state *es, const char *fmt) buf[off++] = *fmt++; continue; } - if (*fmt++ == '\0') + if (*++fmt == '\0') break; ch = (u_char)*fmt++; diff --git a/window-copy.c b/window-copy.c index cc9f3e5d..16626563 100644 --- a/window-copy.c +++ b/window-copy.c @@ -3333,7 +3333,7 @@ window_copy_command(struct window_mode_entry *wme, struct client *c, enum window_copy_cmd_clear clear = WINDOW_COPY_CMD_CLEAR_NEVER; const char *command; u_int i, count = args_count(args); - int keys; + int keys, flags; char *error = NULL; if (count == 0) @@ -3355,9 +3355,10 @@ window_copy_command(struct window_mode_entry *wme, struct client *c, action = WINDOW_COPY_CMD_NOTHING; for (i = 0; i < nitems(window_copy_cmd_table); i++) { if (strcmp(window_copy_cmd_table[i].command, command) == 0) { - if (c->flags & CLIENT_READONLY && - (~window_copy_cmd_table[i].flags & - WINDOW_COPY_CMD_FLAG_READONLY)) { + flags = window_copy_cmd_table[i].flags; + if (c != NULL && + c->flags & CLIENT_READONLY && + (~flags & WINDOW_COPY_CMD_FLAG_READONLY)) { status_message_set(c, -1, 1, 0, 0, "client is read-only"); return; From 1a51193899293367201e0698ce08a94f404f059a Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 2 Apr 2026 08:45:35 +0000 Subject: [PATCH 09/28] Handle empty regular expression in substitution, found by ossfuzz. --- regsub.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/regsub.c b/regsub.c index 61a9c324..91be3994 100644 --- a/regsub.c +++ b/regsub.c @@ -68,6 +68,8 @@ regsub(const char *pattern, const char *with, const char *text, int flags) if (*text == '\0') return (xstrdup("")); + if (*pattern == '\0') + return (xstrdup(text)); if (regcomp(&r, pattern, flags) != 0) return (NULL); From 2d5736f2971f712a30c320c8e5a170523fae2787 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 2 Apr 2026 09:11:39 +0000 Subject: [PATCH 10/28] Limit argc to between 0 and 1000 to prevent fatal from MSG_COMMAND, from Michal Majchrowicz. --- cmd.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmd.c b/cmd.c index e5ec9cc5..04a4caf0 100644 --- a/cmd.c +++ b/cmd.c @@ -304,6 +304,8 @@ cmd_unpack_argv(char *buf, size_t len, int argc, char ***argv) if (argc == 0) return (0); + if (argc < 0 || argc > 1000) + return (-1); *argv = xcalloc(argc, sizeof **argv); buf[len - 1] = '\0'; From be2c6f3b5e6de66f132fe7934de5cc5d9486836b Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 2 Apr 2026 09:28:22 +0000 Subject: [PATCH 11/28] Use INT_MIN + 1 as strtonum lower limits in formats so -ve works, found by ossfuzz. --- format.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/format.c b/format.c index d84dfd35..3dadbf34 100644 --- a/format.c +++ b/format.c @@ -4189,6 +4189,8 @@ format_build_modifiers(struct format_expand_state *es, const char **s, /* Skip any separator character. */ if (*cp == ';') cp++; + if (*cp == '\0') + break; /* Check single character modifiers with no arguments. */ if (strchr("labcdnwETSWPL!<>", cp[0]) != NULL && @@ -4749,7 +4751,7 @@ format_replace_expression(struct format_modifier *mexp, /* The third argument may be precision. */ if (argc >= 3) { - prec = strtonum(mexp->argv[2], INT_MIN, INT_MAX, &errstr); + prec = strtonum(mexp->argv[2], INT_MIN + 1, INT_MAX, &errstr); if (errstr != NULL) { format_log(es, "expression precision %s: %s", errstr, mexp->argv[2]); @@ -4894,8 +4896,8 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen, case '=': if (fm->argc < 1) break; - limit = strtonum(fm->argv[0], INT_MIN, INT_MAX, - &errstr); + limit = strtonum(fm->argv[0], INT_MIN + 1, + INT_MAX, &errstr); if (errstr != NULL) limit = 0; if (fm->argc >= 2 && fm->argv[1] != NULL) @@ -4904,8 +4906,8 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen, case 'p': if (fm->argc < 1) break; - width = strtonum(fm->argv[0], INT_MIN, INT_MAX, - &errstr); + width = strtonum(fm->argv[0], INT_MIN + 1, + INT_MAX, &errstr); if (errstr != NULL) width = 0; break; From c95d34122086627d72ea21aa0d5e0099142f4669 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 2 Apr 2026 09:35:46 +0000 Subject: [PATCH 12/28] Do not leak trimmed string when expanding, found by ossfuzz. --- format-draw.c | 2 +- format.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/format-draw.c b/format-draw.c index c8cb74b6..03fad05f 100644 --- a/format-draw.c +++ b/format-draw.c @@ -1116,7 +1116,7 @@ format_width(const char *expanded) /* * Trim on the left, taking #[] into account. Note, we copy the whole set of * unescaped #s, but only add their escaped size to width. This is because the - * format_draw function will actually do the escaping when it runs + * format_draw function will actually do the escaping. */ char * format_trim_left(const char *expanded, u_int limit) diff --git a/format.c b/format.c index 3dadbf34..e30c67d8 100644 --- a/format.c +++ b/format.c @@ -5325,6 +5325,7 @@ done: if (marker != NULL && strcmp(new, value) != 0) { free(value); xasprintf(&value, "%s%s", new, marker); + free(new); } else { free(value); value = new; @@ -5335,6 +5336,7 @@ done: if (marker != NULL && strcmp(new, value) != 0) { free(value); xasprintf(&value, "%s%s", marker, new); + free(new); } else { free(value); value = new; From 483683c197575c37a3bfae90b707bb2e9b646829 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 2 Apr 2026 09:58:14 +0000 Subject: [PATCH 13/28] Do not leak command in list-keys, reported by tb@. Also tidy up some function names while here. --- cmd-list-keys.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/cmd-list-keys.c b/cmd-list-keys.c index 4f2c9dd3..405c03c7 100644 --- a/cmd-list-keys.c +++ b/cmd-list-keys.c @@ -93,7 +93,7 @@ cmd_list_keys_get_table_width(struct key_binding **l, u_int n) } static struct key_binding ** -cmd_get_root_and_prefix(u_int *n, struct sort_criteria *sort_crit) +cmd_list_keys_get_root_and_prefix(u_int *n, struct sort_criteria *sort_crit) { const char *tables[] = { "prefix", "root" }; struct key_table *t; @@ -119,7 +119,7 @@ cmd_get_root_and_prefix(u_int *n, struct sort_criteria *sort_crit) } static void -cmd_filter_key_list(int filter_notes, int filter_key, key_code only, +cmd_list_keys_filter_key_list(int filter_notes, int filter_key, key_code only, struct key_binding **l, u_int *n) { key_code key; @@ -137,10 +137,10 @@ cmd_filter_key_list(int filter_notes, int filter_key, key_code only, } static void -cmd_format_add_key_binding(struct format_tree *ft, +cmd_list_keys_format_add_key_binding(struct format_tree *ft, const struct key_binding *bd, const char *prefix) { - const char *s; + char *s; if (bd->flags & KEY_BINDING_REPEAT) format_add(ft, "key_repeat", "1"); @@ -155,12 +155,12 @@ cmd_format_add_key_binding(struct format_tree *ft, format_add(ft, "key_prefix", "%s", prefix); format_add(ft, "key_table", "%s", bd->tablename); - s = key_string_lookup_key(bd->key, 0); - format_add(ft, "key_string", "%s", s); + format_add(ft, "key_string", "%s", key_string_lookup_key(bd->key, 0)); - s = cmd_list_print(bd->cmdlist, - CMD_LIST_PRINT_ESCAPED|CMD_LIST_PRINT_NO_GROUPS); + s = cmd_list_print(bd->cmdlist, CMD_LIST_PRINT_ESCAPED| + CMD_LIST_PRINT_NO_GROUPS); format_add(ft, "key_command", "%s", s); + free(s); } static enum cmd_retval @@ -213,14 +213,16 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item) if (table) l = sort_get_key_bindings_table(table, &n, &sort_crit); else if (notes_only) - l = cmd_get_root_and_prefix(&n, &sort_crit); + l = cmd_list_keys_get_root_and_prefix(&n, &sort_crit); else l = sort_get_key_bindings(&n, &sort_crit); filter_notes = notes_only && !args_has(args, 'a'); filter_key = only != KEYC_UNKNOWN; - if (filter_notes || filter_key) - cmd_filter_key_list(filter_notes, filter_key, only, l, &n); + if (filter_notes || filter_key) { + cmd_list_keys_filter_key_list(filter_notes, filter_key, only, l, + &n); + } if (single) n = 1; @@ -232,7 +234,7 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item) format_add(ft, "key_table_width", "%u", cmd_list_keys_get_table_width(l, n)); for (i = 0; i < n; i++) { - cmd_format_add_key_binding(ft, l[i], prefix); + cmd_list_keys_format_add_key_binding(ft, l[i], prefix); line = format_expand(ft, template); if ((single && tc != NULL) || n == 1) From 7497db6e3717b25c41280d5cb99a250af01771ab Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 3 Apr 2026 08:23:10 +0000 Subject: [PATCH 14/28] Increase b64_pton buffer to allow for Base64 without padding, from Michal Majchrowicz. --- tty-keys.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tty-keys.c b/tty-keys.c index 8fc51174..05ec63c8 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -1383,7 +1383,7 @@ tty_keys_clipboard(struct tty *tty, const char *buf, size_t len, size_t *size) copy[end] = '\0'; /* Convert from base64. */ - needed = (end / 4) * 3; + needed = ((end + 3) / 4) * 3; if (needed == 0) { free(copy); return (0); From bdd78ce38e8708006f87c424cd93437db2881abc Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 3 Apr 2026 09:14:27 +0000 Subject: [PATCH 15/28] Handle OSC 9;4 progress bar sequence and store in format variables, from Eric Dorland in GitHub issue 4954. --- format.c | 38 +++++++++++++++++++++++++++++++++++++ input.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- tmux.1 | 2 ++ tmux.h | 16 ++++++++++++++++ 4 files changed, 112 insertions(+), 1 deletion(-) diff --git a/format.c b/format.c index e30c67d8..9feb0f46 100644 --- a/format.c +++ b/format.c @@ -2248,6 +2248,38 @@ format_cb_pane_pipe_pid(struct format_tree *ft) return (value); } +/* Callback for pane_pb_progress. */ +static void * +format_cb_pane_pb_progress(struct format_tree *ft) +{ + char *value = NULL; + + if (ft->wp != NULL) + xasprintf(&value, "%d", ft->wp->base.progress_bar.progress); + return (value); +} + +/* Callback for pane_pb_state. */ +static void * +format_cb_pane_pb_state(struct format_tree *ft) +{ + if (ft->wp != NULL) { + switch (ft->wp->base.progress_bar.state) { + case PROGRESS_BAR_HIDDEN: + return xstrdup("hidden"); + case PROGRESS_BAR_NORMAL: + return xstrdup("normal"); + case PROGRESS_BAR_ERROR: + return xstrdup("error"); + case PROGRESS_BAR_INDETERMINATE: + return xstrdup("indeterminate"); + case PROGRESS_BAR_PAUSED: + return xstrdup("paused"); + } + } + return (NULL); +} + /* Callback for pane_right. */ static void * format_cb_pane_right(struct format_tree *ft) @@ -3331,6 +3363,12 @@ static const struct format_table_entry format_table[] = { { "pane_path", FORMAT_TABLE_STRING, format_cb_pane_path }, + { "pane_pb_progress", FORMAT_TABLE_STRING, + format_cb_pane_pb_progress + }, + { "pane_pb_state", FORMAT_TABLE_STRING, + format_cb_pane_pb_state + }, { "pane_pid", FORMAT_TABLE_STRING, format_cb_pane_pid }, diff --git a/input.c b/input.c index 24467144..11d481fa 100644 --- a/input.c +++ b/input.c @@ -164,6 +164,7 @@ 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_9(struct input_ctx *, const char *); static void input_osc_10(struct input_ctx *, const char *); static void input_osc_11(struct input_ctx *, const char *); static void input_osc_12(struct input_ctx *, const char *); @@ -2636,6 +2637,9 @@ input_exit_osc(struct input_ctx *ictx) case 8: input_osc_8(ictx, p); break; + case 9: + input_osc_9(ictx, p); + break; case 10: input_osc_10(ictx, p); break; @@ -2904,6 +2908,57 @@ bad: free(id); } +/* Helper to handle setting the progress bar and redrawing. */ +static void +input_set_progress_bar(struct input_ctx *ictx, enum progress_bar_state state, + int p) +{ + screen_set_progress_bar(ictx->ctx.s, state, p); + if (ictx->wp != NULL) { + server_redraw_window_borders(ictx->wp->window); + server_status_window(ictx->wp->window); + } +} + +/* Handle the OSC 9;4 sequence for progress bars. */ +static void +input_osc_9(struct input_ctx *ictx, const char *p) +{ + const char *pb = p; + enum progress_bar_state state; + int progress = 0; + + if (*pb++ != '4') + return; + if (*pb == '\0' || (*pb == ';' && pb[1] == '\0')) + return; + + if (*pb++ != ';') + return; + if (*pb < '0' || *pb > '4') + goto bad; + state = *pb++ - '0'; + + if (*pb == '\0' || (*pb == ';' && pb[1] == '\0')) { + input_set_progress_bar(ictx, state, -1); + return; + } + + if (*pb++ != ';') + goto bad; + while (*pb >= '0' && *pb <= '9') { + if (progress > 100) + goto bad; + progress = progress * 10 + *pb++ - '0'; + } + if (*pb != '\0' || progress < 0 || progress > 100) + goto bad; + input_set_progress_bar(ictx, state, progress); + return; + +bad: + log_debug("bad OSC 9;4 %s", p); +} /* Handle the OSC 10 sequence for setting and querying foreground colour. */ static void @@ -3114,7 +3169,7 @@ input_osc_52_parse(struct input_ctx *ictx, const char *p, u_char **out, return (0); } - len = (strlen(end) / 4) * 3; + len = ((strlen(end) + 3) / 4) * 3; if (len == 0) return (0); diff --git a/tmux.1 b/tmux.1 index 998a2cbd..80824d3b 100644 --- a/tmux.1 +++ b/tmux.1 @@ -6380,6 +6380,8 @@ The following variables are available, where appropriate: .It Li "pane_pid" Ta "" Ta "PID of first process in pane" .It Li "pane_pipe" Ta "" Ta "1 if pane is being piped" .It Li "pane_pipe_pid" Ta "" Ta "PID of pipe process, if any" +.It Li "pane_pb_state" Ta "" Ta "Pane progress bar state, one of hidden, normal, error, indeterminate, paused (can be set by application)" +.It Li "pane_pb_progress" Ta "" Ta "Pane progress bar progress percentage (can be set by application)" .It Li "pane_right" Ta "" Ta "Right of pane" .It Li "pane_search_string" Ta "" Ta "Last search string in copy mode" .It Li "pane_start_command" Ta "" Ta "Command pane started with" diff --git a/tmux.h b/tmux.h index 8d631ff9..cab5b694 100644 --- a/tmux.h +++ b/tmux.h @@ -920,6 +920,20 @@ enum screen_cursor_style { SCREEN_CURSOR_BAR }; + +/* Progress bar, OSC 9;4. */ +enum progress_bar_state { + PROGRESS_BAR_HIDDEN = 0, + PROGRESS_BAR_NORMAL = 1, + PROGRESS_BAR_ERROR = 2, + PROGRESS_BAR_INDETERMINATE = 3, + PROGRESS_BAR_PAUSED = 4 +}; +struct progress_bar { + enum progress_bar_state state; + int progress; +}; + /* Virtual screen. */ struct screen_sel; struct screen_titles; @@ -956,6 +970,7 @@ struct screen { struct screen_write_cline *write_list; struct hyperlinks *hyperlinks; + struct progress_bar progress_bar; }; /* Screen write context. */ @@ -3233,6 +3248,7 @@ int screen_set_title(struct screen *, const char *); void screen_set_path(struct screen *, const char *); void screen_push_title(struct screen *); void screen_pop_title(struct screen *); +void screen_set_progress_bar(struct screen *, enum progress_bar_state, int); void screen_resize(struct screen *, u_int, u_int, int); void screen_resize_cursor(struct screen *, u_int, u_int, int, int, int); void screen_set_selection(struct screen *, u_int, u_int, u_int, u_int, From 0917cd7010fb05437a27f67ad1d2cbdfc08d158d Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 3 Apr 2026 09:18:24 +0000 Subject: [PATCH 16/28] Missed a line from previous. --- screen.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/screen.c b/screen.c index 4f382ded..7ba70ea0 100644 --- a/screen.c +++ b/screen.c @@ -122,6 +122,7 @@ screen_reinit(struct screen *s) screen_clear_selection(s); screen_free_titles(s); + screen_set_progress_bar(s, PROGRESS_BAR_HIDDEN, 0); screen_reset_hyperlinks(s); } @@ -283,6 +284,19 @@ screen_pop_title(struct screen *s) } } +/* + * Set the progress bar state and progress. The progress will not be updated + * if p is negative. + */ +void +screen_set_progress_bar(struct screen *s, enum progress_bar_state pbs, int p) +{ + s->progress_bar.state = pbs; + if (p >= 0 && pbs != PROGRESS_BAR_INDETERMINATE) + s->progress_bar.progress = p; +} + + /* Resize screen with options. */ void screen_resize_cursor(struct screen *s, u_int sx, u_int sy, int reflow, From 6b056eb53f870c9bad9723749e53b4d7eada1f98 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 3 Apr 2026 10:13:20 +0000 Subject: [PATCH 17/28] Add some new mouse ranges called "control0" to "control9", will be used for controls on floating panes, from Dane Jensen, with some bits from Michael Grant. --- format-draw.c | 5 +++ format.c | 2 ++ screen-redraw.c | 10 ++++-- server-client.c | 83 ++++++++++++++++++++++++++++++++----------------- status.c | 27 +++------------- style.c | 47 +++++++++++++++++++++++++++- tmux.h | 57 +++++++++++++++++++++++++++------ window.c | 48 +++++++++++++++++++++++++--- 8 files changed, 212 insertions(+), 67 deletions(-) diff --git a/format-draw.c b/format-draw.c index 03fad05f..510f28e0 100644 --- a/format-draw.c +++ b/format-draw.c @@ -49,6 +49,7 @@ format_is_type(struct format_range *fr, struct style *sy) case STYLE_RANGE_NONE: case STYLE_RANGE_LEFT: case STYLE_RANGE_RIGHT: + case STYLE_RANGE_CONTROL: return (1); case STYLE_RANGE_PANE: case STYLE_RANGE_WINDOW: @@ -1065,6 +1066,10 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base, log_debug("%s: range user|%u at %u-%u", __func__, sr->argument, sr->start, sr->end); break; + case STYLE_RANGE_CONTROL: + log_debug("%s: range control|%u at %u-%u", __func__, + sr->argument, sr->start, sr->end); + break; } format_free_range(&frs, fr); } diff --git a/format.c b/format.c index 9feb0f46..a9310ff4 100644 --- a/format.c +++ b/format.c @@ -1307,6 +1307,8 @@ format_cb_mouse_status_range(struct format_tree *ft) return (xstrdup("session")); case STYLE_RANGE_USER: return (xstrdup(sr->string)); + case STYLE_RANGE_CONTROL: + return (xstrdup("control")); } return (NULL); } diff --git a/screen-redraw.c b/screen-redraw.c index 88653f34..8b77564a 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -450,6 +450,7 @@ screen_redraw_make_pane_status(struct client *c, struct window_pane *wp, struct grid_cell gc; const char *fmt; struct format_tree *ft; + struct style_line_entry *sle = &wp->border_status_line; char *expanded; int pane_status = rctx->pane_status, sb_w = 0; int pane_scrollbars = rctx->pane_scrollbars; @@ -494,12 +495,15 @@ screen_redraw_make_pane_status(struct client *c, struct window_pane *wp, gc.attr &= ~GRID_ATTR_CHARSET; screen_write_cursormove(&ctx, 0, 0, 0); - format_draw(&ctx, &gc, width, expanded, NULL, 0); - screen_write_stop(&ctx); + style_ranges_free(&sle->ranges); + format_draw(&ctx, &gc, width, expanded, &sle->ranges, 0); - free(expanded); + screen_write_stop(&ctx); format_free(ft); + free(sle->expanded); + sle->expanded = expanded; + if (grid_compare(wp->status_screen.grid, old.grid) == 0) { screen_free(&old); return (0); diff --git a/server-client.c b/server-client.c index 8a4fe491..ca57d397 100644 --- a/server-client.c +++ b/server-client.c @@ -608,7 +608,8 @@ server_client_check_mouse_in_pane(struct window_pane *wp, u_int px, u_int py, struct options *wo = w->options; struct window_pane *fwp; int pane_status, sb, sb_pos, sb_w, sb_pad; - u_int line, sl_top, sl_bottom; + int pane_status_line, sl_top, sl_bottom; + int bdr_bottom, bdr_top, bdr_right; sb = options_get_number(wo, "pane-scrollbars"); sb_pos = options_get_number(wo, "pane-scrollbars-position"); @@ -621,35 +622,38 @@ server_client_check_mouse_in_pane(struct window_pane *wp, u_int px, u_int py, sb_w = 0; sb_pad = 0; } + if (pane_status == PANE_STATUS_TOP) - line = wp->yoff - 1; + pane_status_line = wp->yoff - 1; else if (pane_status == PANE_STATUS_BOTTOM) - line = wp->yoff + wp->sy; + pane_status_line = wp->yoff + wp->sy; + else + pane_status_line = -1; /* not used */ /* Check if point is within the pane or scrollbar. */ if (((pane_status != PANE_STATUS_OFF && - py != line && py != wp->yoff + wp->sy) || + (int)py != pane_status_line && py != wp->yoff + wp->sy) || (wp->yoff == 0 && py < wp->sy) || (py >= wp->yoff && py < wp->yoff + wp->sy)) && ((sb_pos == PANE_SCROLLBARS_RIGHT && - px < wp->xoff + wp->sx + sb_pad + sb_w) || + (int)px < (int)wp->xoff + (int)wp->sx + sb_pad + sb_w) || (sb_pos == PANE_SCROLLBARS_LEFT && - px < wp->xoff + wp->sx - sb_pad - sb_w))) { + (int)px < (int)wp->xoff + (int)wp->sx - sb_pad - sb_w))) { /* Check if in the scrollbar. */ if ((sb_pos == PANE_SCROLLBARS_RIGHT && - (px >= wp->xoff + wp->sx + sb_pad && - px < wp->xoff + wp->sx + sb_pad + sb_w)) || + ((int)px >= (int)wp->xoff + (int)wp->sx + sb_pad && + (int)px < (int)wp->xoff + (int)wp->sx + sb_pad + sb_w)) || (sb_pos == PANE_SCROLLBARS_LEFT && - (px >= wp->xoff - sb_pad - sb_w && - px < wp->xoff - sb_pad))) { + ((int)px >= (int)wp->xoff - sb_pad - sb_w && + (int)px < (int)wp->xoff - sb_pad))) { /* Check where inside the scrollbar. */ sl_top = wp->yoff + wp->sb_slider_y; sl_bottom = (wp->yoff + wp->sb_slider_y + wp->sb_slider_h - 1); - if (py < sl_top) + if ((int)py < sl_top) return (KEYC_MOUSE_LOCATION_SCROLLBAR_UP); - else if (py >= sl_top && - py <= sl_bottom) { + else if ((int)py >= sl_top && + (int)py <= sl_bottom) { *sl_mpos = (py - wp->sb_slider_y - wp->yoff); return (KEYC_MOUSE_LOCATION_SCROLLBAR_SLIDER); } else /* py > sl_bottom */ @@ -658,19 +662,30 @@ server_client_check_mouse_in_pane(struct window_pane *wp, u_int px, u_int py, /* Must be inside the pane. */ return (KEYC_MOUSE_LOCATION_PANE); } - } else if (~w->flags & WINDOW_ZOOMED) { - /* Try the pane borders if not zoomed. */ + } else { + /* Try the pane borders. */ TAILQ_FOREACH(fwp, &w->panes, entry) { - if ((((sb_pos == PANE_SCROLLBARS_RIGHT && - fwp->xoff + fwp->sx + sb_pad + sb_w == px) || - (sb_pos == PANE_SCROLLBARS_LEFT && - fwp->xoff + fwp->sx == px)) && - fwp->yoff <= 1 + py && - fwp->yoff + fwp->sy >= py) || - (fwp->yoff + fwp->sy == py && - fwp->xoff <= 1 + px && - fwp->xoff + fwp->sx >= px)) - break; + if ((w->flags & WINDOW_ZOOMED) && + (~fwp->flags & PANE_ZOOMED)) + continue; + bdr_top = fwp->yoff - 1; + bdr_bottom = fwp->yoff + (int)fwp->sy; + if (sb_pos == PANE_SCROLLBARS_LEFT) + bdr_right = fwp->xoff + (int)fwp->sx; + else + /* PANE_SCROLLBARS_RIGHT or none. */ + bdr_right = fwp->xoff + (int)fwp->sx + + sb_pad + sb_w; + if ((int)py >= (int)fwp->yoff - 1 && + py <= fwp->yoff + fwp->sy) { + if ((int)px == bdr_right) + break; + } + if ((int)px >= (int)fwp->xoff - 1 && + px <= fwp->xoff + fwp->sx) { + if ((int)py == bdr_bottom || (int)py == bdr_top) + break; + } } if (fwp != NULL) return (KEYC_MOUSE_LOCATION_BORDER); @@ -687,7 +702,7 @@ server_client_check_mouse(struct client *c, struct key_event *event) struct window *w = s->curw->window; struct winlink *fwl; struct window_pane *wp, *fwp; - u_int x, y, sx, sy, px, py, sl_mpos = 0; + u_int x, y, sx, sy, px, py, n, sl_mpos = 0; u_int b, bn; int ignore = 0; key_code key; @@ -825,8 +840,14 @@ have_event: loc = KEYC_MOUSE_LOCATION_STATUS; break; case STYLE_RANGE_USER: + log_debug("mouse range: user"); loc = KEYC_MOUSE_LOCATION_STATUS; break; + case STYLE_RANGE_CONTROL: + n = sr->argument; /* parsing keeps this < 10 */ + log_debug("mouse range: control %u", n); + loc = KEYC_MOUSE_LOCATION_CONTROL0 + n; + break; } } } @@ -865,9 +886,15 @@ have_event: if (loc == KEYC_MOUSE_LOCATION_PANE) { log_debug("mouse %u,%u on pane %%%u", x, y, wp->id); - } else if (loc == KEYC_MOUSE_LOCATION_BORDER) + } else if (loc == KEYC_MOUSE_LOCATION_BORDER) { + sr = window_pane_border_status_get_range(wp, px, + py); + if (sr != NULL) { + n = sr->argument; + loc = KEYC_MOUSE_LOCATION_CONTROL0 + n; + } log_debug("mouse on pane %%%u border", wp->id); - else if (loc == KEYC_MOUSE_LOCATION_SCROLLBAR_UP || + } else if (loc == KEYC_MOUSE_LOCATION_SCROLLBAR_UP || loc == KEYC_MOUSE_LOCATION_SCROLLBAR_SLIDER || loc == KEYC_MOUSE_LOCATION_SCROLLBAR_DOWN) { log_debug("mouse on pane %%%u scrollbar", diff --git a/status.c b/status.c index cd8c62be..c39d0d8f 100644 --- a/status.c +++ b/status.c @@ -284,27 +284,10 @@ struct style_range * status_get_range(struct client *c, u_int x, u_int y) { struct status_line *sl = &c->status; - struct style_range *sr; if (y >= nitems(sl->entries)) return (NULL); - TAILQ_FOREACH(sr, &sl->entries[y].ranges, entry) { - if (x >= sr->start && x < sr->end) - return (sr); - } - return (NULL); -} - -/* Free all ranges. */ -static void -status_free_ranges(struct style_ranges *srs) -{ - struct style_range *sr, *sr1; - - TAILQ_FOREACH_SAFE(sr, srs, entry, sr1) { - TAILQ_REMOVE(srs, sr, entry); - free(sr); - } + return (style_ranges_get_range(&sl->entries[y].ranges, x)); } /* Save old status line. */ @@ -341,7 +324,7 @@ status_init(struct client *c) u_int i; for (i = 0; i < nitems(sl->entries); i++) - TAILQ_INIT(&sl->entries[i].ranges); + style_ranges_init(&sl->entries[i].ranges); screen_init(&sl->screen, c->tty.sx, 1, 0); sl->active = &sl->screen; @@ -355,7 +338,7 @@ status_free(struct client *c) u_int i; for (i = 0; i < nitems(sl->entries); i++) { - status_free_ranges(&sl->entries[i].ranges); + style_ranges_free(&sl->entries[i].ranges); free((void *)sl->entries[i].expanded); } @@ -374,7 +357,7 @@ int status_redraw(struct client *c) { struct status_line *sl = &c->status; - struct status_line_entry *sle; + struct style_line_entry *sle; struct session *s = c->session; struct screen_write_ctx ctx; struct grid_cell gc; @@ -454,7 +437,7 @@ status_redraw(struct client *c) screen_write_putc(&ctx, &gc, ' '); screen_write_cursormove(&ctx, 0, i, 0); - status_free_ranges(&sle->ranges); + style_ranges_free(&sle->ranges); format_draw(&ctx, &gc, width, expanded, &sle->ranges, 0); diff --git a/style.c b/style.c index 394eaf32..586e9f93 100644 --- a/style.c +++ b/style.c @@ -137,6 +137,15 @@ style_parse(struct style *sy, const struct grid_cell *base, const char *in) sy->range_type = STYLE_RANGE_RIGHT; sy->range_argument = 0; style_set_range_string(sy, ""); + } else if (strcasecmp(tmp + 6, "control") == 0) { + if (found == NULL) + goto error; + n = strtonum(found, 0, 9, &errstr); + if (errstr != NULL) + goto error; + sy->range_type = STYLE_RANGE_CONTROL; + sy->range_argument = n; + style_set_range_string(sy, ""); } else if (strcasecmp(tmp + 6, "pane") == 0) { if (found == NULL) goto error; @@ -426,8 +435,10 @@ style_copy(struct style *dst, struct style *src) memcpy(dst, src, sizeof *dst); } +/* Set scrollbar style from an option. */ void -style_set_scrollbar_style_from_option(struct style *sb_style, struct options *oo) +style_set_scrollbar_style_from_option(struct style *sb_style, + struct options *oo) { struct style *sy; @@ -446,3 +457,37 @@ style_set_scrollbar_style_from_option(struct style *sb_style, struct options *oo utf8_set(&sb_style->gc.data, PANE_SCROLLBARS_CHARACTER); } } + +/* Initialize style ranges. */ +void +style_ranges_init(struct style_ranges *srs) +{ + TAILQ_INIT(srs); +} + +/* Free style ranges. */ +void +style_ranges_free(struct style_ranges *srs) +{ + struct style_range *sr, *sr1; + + TAILQ_FOREACH_SAFE(sr, srs, entry, sr1) { + TAILQ_REMOVE(srs, sr, entry); + free(sr); + } +} + +/* Get range for position from style ranges. */ +struct style_range * +style_ranges_get_range(struct style_ranges *srs, u_int x) +{ + struct style_range *sr; + + if (srs == NULL) + return (NULL); + TAILQ_FOREACH(sr, srs, entry) { + if (x >= sr->start && x < sr->end) + return (sr); + } + return (NULL); +} diff --git a/tmux.h b/tmux.h index cab5b694..3f55c30f 100644 --- a/tmux.h +++ b/tmux.h @@ -170,6 +170,16 @@ enum key_code_mouse_location { KEYC_MOUSE_LOCATION_SCROLLBAR_UP, KEYC_MOUSE_LOCATION_SCROLLBAR_SLIDER, KEYC_MOUSE_LOCATION_SCROLLBAR_DOWN, + KEYC_MOUSE_LOCATION_CONTROL0, /* keep order */ + KEYC_MOUSE_LOCATION_CONTROL1, + KEYC_MOUSE_LOCATION_CONTROL2, + KEYC_MOUSE_LOCATION_CONTROL3, + KEYC_MOUSE_LOCATION_CONTROL4, + KEYC_MOUSE_LOCATION_CONTROL5, + KEYC_MOUSE_LOCATION_CONTROL6, + KEYC_MOUSE_LOCATION_CONTROL7, + KEYC_MOUSE_LOCATION_CONTROL8, + KEYC_MOUSE_LOCATION_CONTROL9, KEYC_MOUSE_LOCATION_NOWHERE /* end */ }; @@ -215,7 +225,17 @@ enum key_code_mouse_location { KEYC_MOUSE_KEY(KEYC_ ## t, KEYC_TYPE_ ## t, BORDER), \ KEYC_MOUSE_KEY(KEYC_ ## t, KEYC_TYPE_ ## t, SCROLLBAR_UP), \ KEYC_MOUSE_KEY(KEYC_ ## t, KEYC_TYPE_ ## t, SCROLLBAR_SLIDER), \ - KEYC_MOUSE_KEY(KEYC_ ## t, KEYC_TYPE_ ## t, SCROLLBAR_DOWN) + KEYC_MOUSE_KEY(KEYC_ ## t, KEYC_TYPE_ ## t, SCROLLBAR_DOWN), \ + KEYC_MOUSE_KEY(KEYC_ ## t, KEYC_TYPE_ ## t, CONTROL0), \ + KEYC_MOUSE_KEY(KEYC_ ## t, KEYC_TYPE_ ## t, CONTROL1), \ + KEYC_MOUSE_KEY(KEYC_ ## t, KEYC_TYPE_ ## t, CONTROL2), \ + KEYC_MOUSE_KEY(KEYC_ ## t, KEYC_TYPE_ ## t, CONTROL3), \ + KEYC_MOUSE_KEY(KEYC_ ## t, KEYC_TYPE_ ## t, CONTROL4), \ + KEYC_MOUSE_KEY(KEYC_ ## t, KEYC_TYPE_ ## t, CONTROL5), \ + KEYC_MOUSE_KEY(KEYC_ ## t, KEYC_TYPE_ ## t, CONTROL6), \ + KEYC_MOUSE_KEY(KEYC_ ## t, KEYC_TYPE_ ## t, CONTROL7), \ + KEYC_MOUSE_KEY(KEYC_ ## t, KEYC_TYPE_ ## t, CONTROL8), \ + KEYC_MOUSE_KEY(KEYC_ ## t, KEYC_TYPE_ ## t, CONTROL9) #define KEYC_MOUSE_KEY(p, t, l) \ p ## _ ## l = KEYC_MAKE_MOUSE_KEY(t, 0, KEYC_MOUSE_LOCATION_ ## l), \ p ## 1_ ## l = KEYC_MAKE_MOUSE_KEY(t, 1, KEYC_MOUSE_LOCATION_ ## l), \ @@ -240,7 +260,17 @@ enum key_code_mouse_location { { #s "ScrollbarUp", KEYC_ ## name ## _SCROLLBAR_UP }, \ { #s "ScrollbarSlider", KEYC_ ## name ## _SCROLLBAR_SLIDER }, \ { #s "ScrollbarDown", KEYC_ ## name ## _SCROLLBAR_DOWN }, \ - { #s "Border", KEYC_ ## name ## _BORDER } + { #s "Border", KEYC_ ## name ## _BORDER }, \ + { #s "Control0", KEYC_ ## name ## _CONTROL0 }, \ + { #s "Control1", KEYC_ ## name ## _CONTROL1 }, \ + { #s "Control2", KEYC_ ## name ## _CONTROL2 }, \ + { #s "Control3", KEYC_ ## name ## _CONTROL3 }, \ + { #s "Control4", KEYC_ ## name ## _CONTROL4 }, \ + { #s "Control5", KEYC_ ## name ## _CONTROL5 }, \ + { #s "Control6", KEYC_ ## name ## _CONTROL6 }, \ + { #s "Control7", KEYC_ ## name ## _CONTROL7 }, \ + { #s "Control8", KEYC_ ## name ## _CONTROL8 }, \ + { #s "Control9", KEYC_ ## name ## _CONTROL9 } /* * A single key. This can be ASCII or Unicode or one of the keys between @@ -866,7 +896,8 @@ enum style_range_type { STYLE_RANGE_PANE, STYLE_RANGE_WINDOW, STYLE_RANGE_SESSION, - STYLE_RANGE_USER + STYLE_RANGE_USER, + STYLE_RANGE_CONTROL }; struct style_range { enum style_range_type type; @@ -880,6 +911,12 @@ struct style_range { }; TAILQ_HEAD(style_ranges, style_range); +/* Ranges connected with a status line. */ +struct style_line_entry { + char *expanded; + struct style_ranges ranges; +}; + /* Default style width and pad. */ #define STYLE_WIDTH_DEFAULT -1 #define STYLE_PAD_DEFAULT -1 @@ -1180,7 +1217,7 @@ struct window_pane { #define PANE_DROP 0x2 #define PANE_FOCUSED 0x4 #define PANE_VISITED 0x8 -/* 0x10 unused */ +#define PANE_ZOOMED 0x10 /* 0x20 unused */ #define PANE_INPUTOFF 0x40 #define PANE_CHANGED 0x80 @@ -1222,6 +1259,7 @@ struct window_pane { struct grid_cell cached_active_gc; struct colour_palette palette; enum client_theme last_theme; + struct style_line_entry border_status_line; int pipe_fd; pid_t pipe_pid; @@ -1847,10 +1885,6 @@ struct cmd_entry { /* Status line. */ #define STATUS_LINES_LIMIT 5 -struct status_line_entry { - char *expanded; - struct style_ranges ranges; -}; struct status_line { struct event timer; @@ -1859,7 +1893,7 @@ struct status_line { int references; struct grid_cell style; - struct status_line_entry entries[STATUS_LINES_LIMIT]; + struct style_line_entry entries[STATUS_LINES_LIMIT]; }; /* Prompt type. */ @@ -3368,6 +3402,8 @@ 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 *); +struct style_range *window_pane_border_status_get_range(struct window_pane *, + u_int, u_int); /* layout.c */ u_int layout_count_cells(struct layout_cell *); @@ -3684,6 +3720,9 @@ void style_set(struct style *, const struct grid_cell *); void style_copy(struct style *, struct style *); void style_set_scrollbar_style_from_option(struct style *, struct options *); +void style_ranges_init(struct style_ranges *); +void style_ranges_free(struct style_ranges *); +struct style_range *style_ranges_get_range(struct style_ranges *, u_int); /* spawn.c */ struct winlink *spawn_window(struct spawn_context *, char **); diff --git a/window.c b/window.c index 60f2204a..7a70205e 100644 --- a/window.c +++ b/window.c @@ -589,16 +589,24 @@ struct window_pane * window_get_active_at(struct window *w, u_int x, u_int y) { struct window_pane *wp; - u_int xoff, yoff, sx, sy; + int pane_status, xoff, yoff; + u_int sx, sy; + + pane_status = options_get_number(w->options, "pane-border-status"); TAILQ_FOREACH(wp, &w->panes, entry) { if (!window_pane_visible(wp)) continue; window_pane_full_size_offset(wp, &xoff, &yoff, &sx, &sy); - if (x < xoff || x > xoff + sx) - continue; - if (y < yoff || y > yoff + sy) + if ((int)x < xoff || x > xoff + sx) continue; + if (pane_status == PANE_STATUS_TOP) { + if ((int)y <= yoff - 2 || y > yoff + sy - 1) + continue; + } else { + if ((int)y < yoff || y > yoff + sy) + continue; + } return (wp); } return (NULL); @@ -659,6 +667,7 @@ window_zoom(struct window_pane *wp) if (w->active != wp) window_set_active_pane(w, wp, 1); + wp->flags |= PANE_ZOOMED; TAILQ_FOREACH(wp1, &w->panes, entry) { wp1->saved_layout_cell = wp1->layout_cell; @@ -689,6 +698,7 @@ window_unzoom(struct window *w, int notify) TAILQ_FOREACH(wp, &w->panes, entry) { wp->layout_cell = wp->saved_layout_cell; wp->saved_layout_cell = NULL; + wp->flags ^= PANE_ZOOMED; } layout_fix_panes(w, NULL); @@ -959,6 +969,7 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) window_pane_default_cursor(wp); screen_init(&wp->status_screen, 1, 1, 0); + style_ranges_init(&wp->border_status_line.ranges); if (gethostname(host, sizeof host) == 0) screen_set_title(&wp->base, host); @@ -1007,6 +1018,7 @@ window_pane_destroy(struct window_pane *wp) free(wp->shell); cmd_free_argv(wp->argc, wp->argv); colour_palette_free(&wp->palette); + style_ranges_free(&wp->border_status_line.ranges); free(wp); } @@ -1958,3 +1970,31 @@ window_pane_send_theme_update(struct window_pane *wp) break; } } + +struct style_range * +window_pane_border_status_get_range(struct window_pane *wp, u_int x, u_int y) +{ + struct style_ranges *srs; + struct window *w = wp->window; + struct options *wo = w->options; + u_int line; + int pane_status; + + if (wp == NULL) + return (NULL); + srs = &wp->border_status_line.ranges; + + pane_status = options_get_number(wo, "pane-border-status"); + if (pane_status == PANE_STATUS_TOP) + line = wp->yoff - 1; + else if (pane_status == PANE_STATUS_BOTTOM) + line = wp->yoff + wp->sy; + if (pane_status == PANE_STATUS_OFF || line != y) + return (NULL); + + /* + * The border formats start 2 off but that isn't reflected in + * the stored bounds of the range. + */ + return (style_ranges_get_range(srs, x - wp->xoff - 2)); +} From 95e40115a2cedbd1a0bd29fb58e4a66e5ab01ea1 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sat, 4 Apr 2026 12:11:25 +0100 Subject: [PATCH 18/28] Don't just resize buffer once, repeat until it is big enough. Also use reallocarray. Reported by tuannguyenduc228 at gmail dot com. --- image-sixel.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/image-sixel.c b/image-sixel.c index f6eaade3..c37e2cce 100644 --- a/image-sixel.c +++ b/image-sixel.c @@ -487,9 +487,9 @@ static void sixel_print_add(char **buf, size_t *len, size_t *used, const char *s, size_t slen) { - if (*used + slen >= *len + 1) { + while (*used + slen >= *len + 1) { + *buf = xreallocarray(*buf, 2, *len); (*len) *= 2; - *buf = xrealloc(*buf, *len); } memcpy(*buf + *used, s, slen); (*used) += slen; From cd60de443e294a6aa5c967832372f01855b82eca Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 4 Apr 2026 11:20:01 +0000 Subject: [PATCH 19/28] Clamp width to terminal width, also change calculation of end of screen (it is OK to be outside the screen). Fixes problem reported by Dane Jensen in GitHub issue 4969. --- tty-draw.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/tty-draw.c b/tty-draw.c index bd643f98..d0a74fca 100644 --- a/tty-draw.c +++ b/tty-draw.c @@ -143,6 +143,14 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx, log_debug("%s: px=%u py=%u nx=%u atx=%u aty=%u", __func__, px, py, nx, atx, aty); + /* There is no point in drawing more than the end of the terminal. */ + if (atx >= tty->sx) + return; + if (atx + nx >= tty->sx) + nx = tty->sx - atx; + if (nx == 0) + return; + /* * Clamp the width to cellsize - note this is not cellused, because * there may be empty background cells after it (from BCE). @@ -150,15 +158,8 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx, cellsize = grid_get_line(gd, gd->hsize + py)->cellsize; if (screen_size_x(s) > cellsize) ex = cellsize; - else { + else ex = screen_size_x(s); - if (px > ex) - return; - if (px + nx > ex) - nx = ex - px; - } - if (ex < nx) - ex = nx; log_debug("%s: drawing %u-%u,%u (end %u) at %u,%u; defaults: fg=%d, " "bg=%d", __func__, px, px + nx, py, ex, atx, aty, defaults->fg, defaults->bg); @@ -252,7 +253,7 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx, } /* Work out the the empty width. */ - if (i >= ex) + if (px >= ex || i >= ex - px) empty = 1; else if (gcp->bg != last.bg) empty = 0; From 3badbc50e0d1c45ebfca2d2d1fad059a055584a5 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 4 Apr 2026 11:32:05 +0000 Subject: [PATCH 20/28] Do not read off end of buffer if it ends in \ when expanding \c, found by ossfuzz. --- regsub.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/regsub.c b/regsub.c index 91be3994..a863f9fd 100644 --- a/regsub.c +++ b/regsub.c @@ -41,7 +41,7 @@ regsub_expand(char **buf, ssize_t *len, const char *with, const char *text, u_int i; for (cp = with; *cp != '\0'; cp++) { - if (*cp == '\\') { + if (cp[0] == '\\' && cp[1] != '\0') { cp++; if (*cp >= '0' && *cp <= '9') { i = *cp - '0'; From a827725a8d6afb73ac27a4f316c48605afde57ea Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 4 Apr 2026 11:39:49 +0000 Subject: [PATCH 21/28] Set up default and last cell before clearing padding, reported by someone on GitHub. --- tty-draw.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/tty-draw.c b/tty-draw.c index d0a74fca..b5ecbe50 100644 --- a/tty-draw.c +++ b/tty-draw.c @@ -164,6 +164,18 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx, "bg=%d", __func__, px, px + nx, py, ex, atx, aty, defaults->fg, defaults->bg); + /* Turn off cursor while redrawing and reset region and margins. */ + flags = (tty->flags & TTY_NOCURSOR); + tty->flags |= TTY_NOCURSOR; + tty_update_mode(tty, tty->mode, s); + tty_region_off(tty); + tty_margin_off(tty); + + /* Start with the default cell as the last cell. */ + memcpy(&last, &grid_default_cell, sizeof last); + last.bg = defaults->bg; + tty_default_attributes(tty, defaults, palette, 8, s->hyperlinks); + /* * If there is padding at the start, we must have truncated a wide * character. Clear it. @@ -196,7 +208,7 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx, log_debug("%s: clearing %u padding cells", __func__, cx); tty_draw_line_clear(tty, atx, aty, cx, defaults, bg, 0); if (cx == ex) - return; + goto out; atx += cx; px += cx; nx -= cx; @@ -210,18 +222,6 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx, wrapped = 1; } - /* Turn off cursor while redrawing and reset region and margins. */ - flags = (tty->flags & TTY_NOCURSOR); - tty->flags |= TTY_NOCURSOR; - tty_update_mode(tty, tty->mode, s); - tty_region_off(tty); - tty_margin_off(tty); - - /* Start with the default cell as the last cell. */ - memcpy(&last, &grid_default_cell, sizeof last); - last.bg = defaults->bg; - tty_default_attributes(tty, defaults, palette, 8, s->hyperlinks); - /* Loop over each character in the range. */ last_i = i = 0; len = 0; @@ -332,6 +332,7 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx, i += gcp->data.width; } +out: tty->flags = (tty->flags & ~TTY_NOCURSOR)|flags; tty_update_mode(tty, tty->mode, s); } From d2d86ac36029e851820487b9908db71d250652ce Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 4 Apr 2026 11:48:00 +0000 Subject: [PATCH 22/28] Document control ranges, from Dane Jensen. --- tmux.1 | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tmux.1 b/tmux.1 index 80824d3b..dd0f7c4a 100644 --- a/tmux.1 +++ b/tmux.1 @@ -5795,8 +5795,13 @@ and a location suffix, one of the following: .It Li "ScrollbarSlider" Ta "the scrollbar slider" .It Li "ScrollbarUp" Ta "above the scrollbar slider" .It Li "ScrollbarDown" Ta "below the scrollbar slider" +.It Li "ControlN" Ta "on control range N" .El .Pp +See the +.Sx STYLES +section for information on control ranges. +.Pp The following mouse events are available: .Bl -column "MouseDown1" "MouseDrag1" "WheelDown" -offset indent .It Li "WheelUp" Ta "WheelDown" Ta "" @@ -6609,6 +6614,7 @@ replaces the previous saved default). .Ic range=window|X , .Ic range=pane|X , .Ic range=user|X , +.Ic range=control|N , .Ic norange .Xc Mark a range for mouse events in the @@ -6655,6 +6661,19 @@ will be available in the format variable. .Ql X must be at most 15 bytes in length. +.Pp +.Ic range=control|N +is a set of ranges for users to define custom behavior. +.Ql N +can be 0 through 9. +When a mouse event occurs in the +.Ic range=control|N +range, the +.Ql ControlN +key binding is triggered. +See +.Sx MOUSE SUPPORT +for details. .It Ic set\-default Set the current colours and attributes as the default, overwriting any previous default. From 87aaff5fae2ef9d07dac8532650ad728662640b4 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 4 Apr 2026 16:40:27 +0000 Subject: [PATCH 23/28] Bring some new formats from the floating panes work: pane_zoomed_flag, pane_flags, pane_floating_flag. By Michael Grant. --- cmd-select-layout.c | 2 +- format.c | 52 ++++++++++++++++++++++++++++++++++++++++++--- layout-custom.c | 2 +- tmux.1 | 3 +++ tmux.h | 5 +++-- window.c | 22 +++++++++++++++++-- 6 files changed, 77 insertions(+), 9 deletions(-) diff --git a/cmd-select-layout.c b/cmd-select-layout.c index 6dfe2b6a..41779747 100644 --- a/cmd-select-layout.c +++ b/cmd-select-layout.c @@ -90,7 +90,7 @@ cmd_select_layout_exec(struct cmd *self, struct cmdq_item *item) previous = 1; oldlayout = w->old_layout; - w->old_layout = layout_dump(w->layout_root); + w->old_layout = layout_dump(w, w->layout_root); if (next || previous) { if (next) diff --git a/format.c b/format.c index a9310ff4..2ebfe588 100644 --- a/format.c +++ b/format.c @@ -811,8 +811,8 @@ format_cb_window_layout(struct format_tree *ft) return (NULL); if (w->saved_layout_root != NULL) - return (layout_dump(w->saved_layout_root)); - return (layout_dump(w->layout_root)); + return (layout_dump(w, w->saved_layout_root)); + return (layout_dump(w, w->layout_root)); } /* Callback for window_visible_layout. */ @@ -824,7 +824,7 @@ format_cb_window_visible_layout(struct format_tree *ft) if (w == NULL) return (NULL); - return (layout_dump(w->layout_root)); + return (layout_dump(w, w->layout_root)); } /* Callback for pane_start_command. */ @@ -990,6 +990,29 @@ format_cb_pane_fg(struct format_tree *ft) return (xstrdup(colour_tostring(gc.fg))); } +/* Callback for pane_flags. */ +static void * +format_cb_pane_flags(struct format_tree *ft) +{ + if (ft->wp != NULL) + return (xstrdup(window_pane_printable_flags(ft->wp))); + return (NULL); +} + +/* Callback for pane_floating_flag. */ +static void * +format_cb_pane_floating_flag(struct format_tree *ft) +{ + struct window_pane *wp = ft->wp; + + if (wp != NULL) { + if (wp->flags & PANE_FLOATING) + return (xstrdup("1")); + return (xstrdup("0")); + } + return (NULL); +} + /* Callback for pane_bg. */ static void * format_cb_pane_bg(struct format_tree *ft) @@ -2351,6 +2374,20 @@ format_cb_pane_width(struct format_tree *ft) return (NULL); } +/* Callback for pane_zoomed_flag. */ +static void * +format_cb_pane_zoomed_flag(struct format_tree *ft) +{ + struct window_pane *wp = ft->wp; + + if (wp != NULL) { + if (wp->flags & PANE_ZOOMED) + return (xstrdup("1")); + return (xstrdup("0")); + } + return (NULL); +} + /* Callback for scroll_region_lower. */ static void * format_cb_scroll_region_lower(struct format_tree *ft) @@ -3326,6 +3363,12 @@ static const struct format_table_entry format_table[] = { { "pane_fg", FORMAT_TABLE_STRING, format_cb_pane_fg }, + { "pane_flags", FORMAT_TABLE_STRING, + format_cb_pane_flags + }, + { "pane_floating_flag", FORMAT_TABLE_STRING, + format_cb_pane_floating_flag + }, { "pane_format", FORMAT_TABLE_STRING, format_cb_pane_format }, @@ -3413,6 +3456,9 @@ static const struct format_table_entry format_table[] = { { "pane_width", FORMAT_TABLE_STRING, format_cb_pane_width }, + { "pane_zoomed_flag", FORMAT_TABLE_STRING, + format_cb_pane_zoomed_flag + }, { "pid", FORMAT_TABLE_STRING, format_cb_pid }, diff --git a/layout-custom.c b/layout-custom.c index 2bfb6d89..22de0bdd 100644 --- a/layout-custom.c +++ b/layout-custom.c @@ -58,7 +58,7 @@ layout_checksum(const char *layout) /* Dump layout as a string. */ char * -layout_dump(struct layout_cell *root) +layout_dump(__unused struct window *w, struct layout_cell *root) { char layout[8192], *out; diff --git a/tmux.1 b/tmux.1 index dd0f7c4a..9c2932ad 100644 --- a/tmux.1 +++ b/tmux.1 @@ -6369,6 +6369,8 @@ The following variables are available, where appropriate: .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_flags" Ta "" Ta "Pane flags" +.It Li "pane_floating_flag" Ta "" Ta "1 if pane is floating" .It Li "pane_format" Ta "" Ta "1 if format is for a pane" .It Li "pane_height" Ta "" Ta "Height of pane" .It Li "pane_id" Ta "#D" Ta "Unique pane ID" @@ -6398,6 +6400,7 @@ The following variables are available, where appropriate: .It Li "pane_tty" Ta "" Ta "Pseudo terminal of pane" .It Li "pane_unseen_changes" Ta "" Ta "1 if there were changes in pane while in mode" .It Li "pane_width" Ta "" Ta "Width of pane" +.It Li "pane_floating_flag" Ta "" Ta "1 if pane is floating" .It Li "pid" Ta "" Ta "Server PID" .It Li "prev_window_active" Ta "" Ta "1 if previous window in W: loop is active" .It Li "prev_window_index" Ta "" Ta "Index of previous window in W: loop" diff --git a/tmux.h b/tmux.h index 3f55c30f..27f016de 100644 --- a/tmux.h +++ b/tmux.h @@ -1218,7 +1218,7 @@ struct window_pane { #define PANE_FOCUSED 0x4 #define PANE_VISITED 0x8 #define PANE_ZOOMED 0x10 -/* 0x20 unused */ +#define PANE_FLOATING 0x20 #define PANE_INPUTOFF 0x40 #define PANE_CHANGED 0x80 #define PANE_EXITED 0x100 @@ -3372,6 +3372,7 @@ int window_pane_exited(struct window_pane *); u_int window_pane_search(struct window_pane *, const char *, int, int); const char *window_printable_flags(struct winlink *, int); +const char *window_pane_printable_flags(struct window_pane *); struct window_pane *window_pane_find_up(struct window_pane *); struct window_pane *window_pane_find_down(struct window_pane *); struct window_pane *window_pane_find_left(struct window_pane *); @@ -3439,7 +3440,7 @@ int layout_spread_cell(struct window *, struct layout_cell *); void layout_spread_out(struct window_pane *); /* layout-custom.c */ -char *layout_dump(struct layout_cell *); +char *layout_dump(struct window *, struct layout_cell *); int layout_parse(struct window *, const char *, char **); /* layout-set.c */ diff --git a/window.c b/window.c index 7a70205e..c97a3875 100644 --- a/window.c +++ b/window.c @@ -882,9 +882,8 @@ window_printable_flags(struct winlink *wl, int escape) { struct session *s = wl->session; static char flags[32]; - int pos; + u_int pos = 0; - pos = 0; if (wl->flags & WINLINK_ACTIVITY) { flags[pos++] = '#'; if (escape) @@ -906,6 +905,25 @@ window_printable_flags(struct winlink *wl, int escape) return (flags); } +const char * +window_pane_printable_flags(struct window_pane *wp) +{ + struct window *w = wp->window; + static char flags[32]; + u_int pos = 0; + + if (wp == w->active) + flags[pos++] = '*'; + if (wp == TAILQ_FIRST(&w->last_panes)) + flags[pos++] = '-'; + if (wp->flags & PANE_ZOOMED) + flags[pos++] = 'Z'; + if (wp->flags & PANE_FLOATING) + flags[pos++] = 'F'; + flags[pos] = '\0'; + return (flags); +} + struct window_pane * window_pane_find_by_id_str(const char *s) { From 7f2ac9c8718355a88d3c21ef127fd0292e7d3013 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 4 Apr 2026 17:00:33 +0000 Subject: [PATCH 24/28] Add remain-on-exit key to keep pane around until a key is pressed, from Michael Grant. --- options-table.c | 7 ++++--- server-client.c | 9 +++++++++ server-fn.c | 1 + tmux.1 | 5 ++++- 4 files changed, 18 insertions(+), 4 deletions(-) diff --git a/options-table.c b/options-table.c index 8101a92c..55abe679 100644 --- a/options-table.c +++ b/options-table.c @@ -92,7 +92,7 @@ static const char *options_table_window_size_list[] = { "largest", "smallest", "manual", "latest", NULL }; static const char *options_table_remain_on_exit_list[] = { - "off", "on", "failed", NULL + "off", "on", "failed", "key", NULL }; static const char *options_table_destroy_unattached_list[] = { "off", "on", "keep-last", "keep-group", NULL @@ -1408,8 +1408,9 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, .choices = options_table_remain_on_exit_list, .default_num = 0, - .text = "Whether panes should remain ('on') or be automatically " - "killed ('off' or 'failed') when the program inside exits." + .text = "Whether panes should remain ('on'), remain until a key is " + "pressed ('key') or be automatically killed ('off' or " + "'failed') when the program inside exits." }, { .name = "remain-on-exit-format", diff --git a/server-client.c b/server-client.c index ca57d397..88de60d0 100644 --- a/server-client.c +++ b/server-client.c @@ -1359,6 +1359,15 @@ try_again: } forward_key: + if (wp != NULL && + (wp->flags & PANE_EXITED) && + !KEYC_IS_MOUSE(key) && + !KEYC_IS_PASTE(key) && + options_get_number(wp->options, "remain-on-exit") == 3) { + options_set_number(wp->options, "remain-on-exit", 0); + server_destroy_pane(wp, 0); + goto out; + } if (c->flags & CLIENT_READONLY) goto out; if (wp != NULL) diff --git a/server-fn.c b/server-fn.c index c36dc74f..6c4c9c61 100644 --- a/server-fn.c +++ b/server-fn.c @@ -341,6 +341,7 @@ server_destroy_pane(struct window_pane *wp, int notify) break; /* FALLTHROUGH */ case 1: + case 3: if (wp->flags & PANE_STATUSDRAWN) return; wp->flags |= PANE_STATUSDRAWN; diff --git a/tmux.1 b/tmux.1 index 9c2932ad..5a457420 100644 --- a/tmux.1 +++ b/tmux.1 @@ -5569,13 +5569,16 @@ uses when the colour with that index is requested. The index may be from zero to 255. .Pp .It Xo Ic remain\-on\-exit -.Op Ic on | off | failed +.Op Ic on | off | failed | key .Xc A pane with this flag set is not destroyed when the program running in it exits. If set to .Ic failed , then only when the program exit status is not zero. +If set to +.Ic key , +the pane stays open and closes when a key is pressed. The pane may be reactivated with the .Ic respawn\-pane command. From cad282ebb77b5c037e3bc5281eab95f077ba31af Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 4 Apr 2026 17:13:07 +0000 Subject: [PATCH 25/28] Cache user from getpwuid because it can be very expensive on some platforms. From Ben Maurer in GitHub issue 4973. --- format.c | 15 +++++++++++---- server-client.c | 1 + tmux.h | 1 + 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/format.c b/format.c index 2ebfe588..1dba66a6 100644 --- a/format.c +++ b/format.c @@ -1616,9 +1616,13 @@ format_cb_client_user(struct format_tree *ft) struct passwd *pw; if (ft->c != NULL) { + if (ft->c->user != NULL) + return (xstrdup(ft->c->user)); uid = proc_get_peer_uid(ft->c->peer); - if (uid != (uid_t)-1 && (pw = getpwuid(uid)) != NULL) - return (xstrdup(pw->pw_name)); + if (uid != (uid_t)-1 && (pw = getpwuid(uid)) != NULL) { + ft->c->user = xstrdup(pw->pw_name); + return (xstrdup(ft->c->user)); + } } return (NULL); } @@ -3076,10 +3080,13 @@ format_cb_uid(__unused struct format_tree *ft) static void * format_cb_user(__unused struct format_tree *ft) { + static char *cached; struct passwd *pw; - if ((pw = getpwuid(getuid())) != NULL) - return (xstrdup(pw->pw_name)); + if (cached == NULL && (pw = getpwuid(getuid())) != NULL) + cached = xstrdup(pw->pw_name); + if (cached != NULL) + return (xstrdup(cached)); return (NULL); } diff --git a/server-client.c b/server-client.c index 88de60d0..d6a6159e 100644 --- a/server-client.c +++ b/server-client.c @@ -537,6 +537,7 @@ server_client_free(__unused int fd, __unused short events, void *arg) if (c->references == 0) { free((void *)c->name); + free((void *)c->user); free(c); } } diff --git a/tmux.h b/tmux.h index 27f016de..87bcac78 100644 --- a/tmux.h +++ b/tmux.h @@ -1958,6 +1958,7 @@ typedef void (*overlay_resize_cb)(struct client *, void *); struct client { const char *name; struct tmuxpeer *peer; + const char *user; struct cmdq_list *queue; struct client_windows windows; From d5ed967f581b09fcb670d86574146c705d924513 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 5 Apr 2026 13:24:02 +0000 Subject: [PATCH 26/28] Set less crazy limits (than INT_MAX) for pad and trim, makes ossfuzz happier. --- format.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/format.c b/format.c index 1dba66a6..db769668 100644 --- a/format.c +++ b/format.c @@ -84,6 +84,12 @@ format_job_cmp(struct format_job *fj1, struct format_job *fj2) return (strcmp(fj1->cmd, fj2->cmd)); } +/* Maimum pad and trim width. */ +#define FORMAT_MAX_WIDTH 10000 + +/* Maimum repeat size. */ +#define FORMAT_MAX_REPEAT 10000 + /* Format modifiers. */ #define FORMAT_TIMESTRING 0x1 #define FORMAT_BASENAME 0x2 @@ -4989,8 +4995,8 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen, case '=': if (fm->argc < 1) break; - limit = strtonum(fm->argv[0], INT_MIN + 1, - INT_MAX, &errstr); + limit = strtonum(fm->argv[0], -FORMAT_MAX_WIDTH, + FORMAT_MAX_WIDTH, &errstr); if (errstr != NULL) limit = 0; if (fm->argc >= 2 && fm->argv[1] != NULL) @@ -4999,8 +5005,8 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen, case 'p': if (fm->argc < 1) break; - width = strtonum(fm->argv[0], INT_MIN + 1, - INT_MAX, &errstr); + width = strtonum(fm->argv[0], -FORMAT_MAX_WIDTH, + FORMAT_MAX_WIDTH, &errstr); if (errstr != NULL) width = 0; break; @@ -5223,7 +5229,7 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen, format_log(es, "repeat syntax error: %s", copy); goto fail; } - nrep = strtonum(right, 1, 10000, &errstr); + nrep = strtonum(right, 1, FORMAT_MAX_REPEAT, &errstr); if (errstr != NULL) value = xstrdup(""); else { From 21da45e5c3ec26a42d8c6a6de3556d47acdef0a9 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 5 Apr 2026 14:29:04 +0000 Subject: [PATCH 27/28] Add extkeys feature to tmux itself so nested tmux works, GitHub issue 4960. --- tty-features.c | 48 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/tty-features.c b/tty-features.c index 18922956..623e8f20 100644 --- a/tty-features.c +++ b/tty-features.c @@ -464,23 +464,46 @@ tty_default_features(int *feat, const char *name, u_int version) #define TTY_FEATURES_BASE_MODERN_XTERM \ "256,RGB,bpaste,clipboard,mouse,strikethrough,title" { .name = "mintty", - .features = TTY_FEATURES_BASE_MODERN_XTERM - ",ccolour,cstyle,extkeys,margins,overline,usstyle" + .features = TTY_FEATURES_BASE_MODERN_XTERM "," + "ccolour," + "cstyle," + "extkeys," + "margins," + "overline," + "usstyle" }, { .name = "tmux", - .features = TTY_FEATURES_BASE_MODERN_XTERM - ",ccolour,cstyle,focus,overline,usstyle,hyperlinks" + .features = TTY_FEATURES_BASE_MODERN_XTERM "," + "ccolour," + "cstyle," + "extkeys," + "focus," + "overline," + "usstyle," + "hyperlinks" }, { .name = "rxvt-unicode", - .features = "256,bpaste,ccolour,cstyle,mouse,title,ignorefkeys" + .features = "256," + "bpaste," + "ccolour," + "cstyle," + "mouse," + "title," + "ignorefkeys" }, { .name = "iTerm2", - .features = TTY_FEATURES_BASE_MODERN_XTERM - ",cstyle,extkeys,margins,usstyle,sync,osc7,hyperlinks" + .features = TTY_FEATURES_BASE_MODERN_XTERM "," + "cstyle," + "extkeys," + "margins," + "usstyle," + "sync," + "osc7,hyperlinks" }, { .name = "foot", - .features = TTY_FEATURES_BASE_MODERN_XTERM - ",cstyle,extkeys" + .features = TTY_FEATURES_BASE_MODERN_XTERM "," + "cstyle," + "extkeys" }, { .name = "XTerm", /* @@ -488,8 +511,11 @@ tty_default_features(int *feat, const char *name, u_int version) * disabled so not set it here - they will be added if * secondary DA shows VT420. */ - .features = TTY_FEATURES_BASE_MODERN_XTERM - ",ccolour,cstyle,extkeys,focus" + .features = TTY_FEATURES_BASE_MODERN_XTERM "," + "ccolour," + "cstyle," + "extkeys," + "focus" } }; u_int i; From 4b0ff07bcbc86bb0312dfb8c6f82ec55a184476f Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 5 Apr 2026 15:43:17 +0000 Subject: [PATCH 28/28] When a cell is cleared after having been moved, we cannot reuse its extended data, because that may still be in use. Add a flag to grid_clear_cell to indicate this. Fixes irritating problems with ICH (CSI @) mostly visible in emacs. --- grid.c | 10 +++++----- screen.c | 5 ++++- tmux.h | 2 +- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/grid.c b/grid.c index d5d2b37f..0d1db844 100644 --- a/grid.c +++ b/grid.c @@ -200,7 +200,7 @@ grid_adjust_lines(struct grid *gd, u_int lines) /* Copy default into a cell. */ static void -grid_clear_cell(struct grid *gd, u_int px, u_int py, u_int bg) +grid_clear_cell(struct grid *gd, u_int px, u_int py, u_int bg, int moved) { struct grid_line *gl = &gd->linedata[py]; struct grid_cell_entry *gce = &gl->celldata[px]; @@ -209,7 +209,7 @@ grid_clear_cell(struct grid *gd, u_int px, u_int py, u_int bg) int had_extd = (gce->flags & GRID_FLAG_EXTENDED); memcpy(gce, &grid_cleared_entry, sizeof *gce); - if (had_extd && old_offset < gl->extdsize) { + if (!moved && had_extd && old_offset < gl->extdsize) { gce->flags |= GRID_FLAG_EXTENDED; gce->offset = old_offset; gee = grid_extended_cell(gl, gce, &grid_cleared_cell); @@ -515,7 +515,7 @@ grid_expand_line(struct grid *gd, u_int py, u_int sx, u_int bg) (sx - gl->cellsize) * sizeof *gl->celldata); } for (xx = gl->cellsize; xx < sx; xx++) - grid_clear_cell(gd, xx, py, bg); + grid_clear_cell(gd, xx, py, bg, 0); gl->cellsize = sx; } @@ -683,7 +683,7 @@ grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny, u_int bg) grid_expand_line(gd, yy, px + ox, 8); /* default bg first */ for (xx = px; xx < px + ox; xx++) - grid_clear_cell(gd, xx, yy, bg); + grid_clear_cell(gd, xx, yy, bg, 0); } } @@ -777,7 +777,7 @@ grid_move_cells(struct grid *gd, u_int dx, u_int px, u_int py, u_int nx, for (xx = px; xx < px + nx; xx++) { if (xx >= dx && xx < dx + nx) continue; - grid_clear_cell(gd, xx, py, bg); + grid_clear_cell(gd, xx, py, bg, 1); } } diff --git a/screen.c b/screen.c index 7ba70ea0..617ac24c 100644 --- a/screen.c +++ b/screen.c @@ -760,8 +760,9 @@ screen_mode_to_string(int mode) return (tmp); } +/* Convert screen to a string. */ const char * -screen_print(struct screen *s) +screen_print(struct screen *s, int line) { static char *buf; static size_t len = 16384; @@ -777,6 +778,8 @@ screen_print(struct screen *s) buf = xmalloc(len); for (y = 0; y < screen_hsize(s) + s->grid->sy; y++) { + if (line >= 0 && y != (u_int)line) + continue; n = snprintf(buf + last, len - last, "%.4d \"", y); if (n <= 0 || (u_int)n >= len - last) goto out; diff --git a/tmux.h b/tmux.h index 87bcac78..49519d0e 100644 --- a/tmux.h +++ b/tmux.h @@ -3296,7 +3296,7 @@ int screen_select_cell(struct screen *, struct grid_cell *, void screen_alternate_on(struct screen *, struct grid_cell *, int); void screen_alternate_off(struct screen *, struct grid_cell *, int); const char *screen_mode_to_string(int); -const char *screen_print(struct screen *); +const char *screen_print(struct screen *, int); /* window.c */ extern struct windows windows;