This commit is contained in:
Michael Grant
2026-04-05 19:40:56 -04:00
35 changed files with 1256 additions and 1654 deletions

View File

@@ -240,9 +240,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.

View File

@@ -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)

2
cmd.c
View File

@@ -313,6 +313,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';

View File

@@ -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);
}
@@ -1116,7 +1121,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)

View File

@@ -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
@@ -1009,7 +1015,7 @@ static void *
format_cb_pane_flags(struct format_tree *ft)
{
if (ft->wp != NULL)
return (xstrdup(window_pane_printable_flags(ft->wp, 1)));
return (xstrdup(window_pane_printable_flags(ft->wp)));
return (NULL);
}
@@ -1344,6 +1350,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);
}
@@ -1378,6 +1386,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)
@@ -1616,9 +1636,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);
}
@@ -2288,6 +2312,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)
@@ -3063,10 +3119,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);
}
@@ -3101,6 +3160,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
},
@@ -3395,6 +3457,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
},
@@ -4256,6 +4324,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 &&
@@ -4816,7 +4886,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]);
@@ -4961,8 +5031,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], -FORMAT_MAX_WIDTH,
FORMAT_MAX_WIDTH, &errstr);
if (errstr != NULL)
limit = 0;
if (fm->argc >= 2 && fm->argv[1] != NULL)
@@ -4971,8 +5041,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], -FORMAT_MAX_WIDTH,
FORMAT_MAX_WIDTH, &errstr);
if (errstr != NULL)
width = 0;
break;
@@ -5195,7 +5265,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 {
@@ -5390,6 +5460,7 @@ done:
if (marker != NULL && strcmp(new, value) != 0) {
free(value);
xasprintf(&value, "%s%s", new, marker);
free(new);
} else {
free(value);
value = new;
@@ -5400,6 +5471,7 @@ done:
if (marker != NULL && strcmp(new, value) != 0) {
free(value);
xasprintf(&value, "%s%s", marker, new);
free(new);
} else {
free(value);
value = new;
@@ -5512,7 +5584,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++;

83
fuzz/cmd-parse-fuzzer.c Normal file
View File

@@ -0,0 +1,83 @@
/*
* Copyright (c) 2026 David Korczynski <david@adalogics.com>
*
* 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 <stddef.h>
#include <string.h>
#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;
}

133
fuzz/cmd-parse-fuzzer.dict Normal file
View File

@@ -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"

View File

@@ -0,0 +1,2 @@
[libfuzzer]
max_len = 2048

88
fuzz/format-fuzzer.c Normal file
View File

@@ -0,0 +1,88 @@
/*
* Copyright (c) 2026 David Korczynski <david@adalogics.com>
*
* 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 <stddef.h>
#include <string.h>
#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;
}

71
fuzz/format-fuzzer.dict Normal file
View File

@@ -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:"

View File

@@ -0,0 +1,2 @@
[libfuzzer]
max_len = 2048

79
fuzz/style-fuzzer.c Normal file
View File

@@ -0,0 +1,79 @@
/*
* Copyright (c) 2026 David Korczynski <david@adalogics.com>
*
* 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 <stddef.h>
#include <string.h>
#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;
}

73
fuzz/style-fuzzer.dict Normal file
View File

@@ -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
","
" "

View File

@@ -0,0 +1,2 @@
[libfuzzer]
max_len = 512

10
grid.c
View File

@@ -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);
}
}

View File

@@ -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;

View File

@@ -673,8 +673,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);
}

57
input.c
View File

@@ -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 *);
@@ -2673,6 +2674,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;
@@ -2941,6 +2945,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
@@ -3151,7 +3206,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);

View File

@@ -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;

View File

@@ -91,7 +91,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", "keypress", NULL
"off", "on", "failed", "key", NULL
};
static const char *options_table_destroy_unattached_list[] = {
"off", "on", "keep-last", "keep-group", NULL
@@ -1429,8 +1429,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",

View File

@@ -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';
@@ -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);

View File

@@ -602,6 +602,7 @@ screen_redraw_make_pane_status(struct client *c, struct window_pane *wp,
struct grid_cell gc;
const char *fmt, *border_opt;
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;
@@ -660,12 +661,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);

View File

@@ -131,6 +131,7 @@ screen_reinit(struct screen *s)
image_free_all(s);
#endif
screen_set_progress_bar(s, PROGRESS_BAR_HIDDEN, 0);
screen_reset_hyperlinks(s);
}
@@ -296,6 +297,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,
@@ -772,8 +786,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;
@@ -789,6 +804,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;

File diff suppressed because it is too large Load Diff

View File

@@ -339,14 +339,13 @@ server_destroy_pane(struct window_pane *wp, int notify)
switch (remain_on_exit) {
case 0:
break;
case 3: /* keypress — fall through to draw remain-on-exit-format message */
/* FALLTHROUGH */
case 2:
if (remain_on_exit == 2 &&
WIFEXITED(wp->status) && WEXITSTATUS(wp->status) == 0)
break;
/* FALLTHROUGH */
case 1:
case 3:
if (wp->flags & PANE_STATUSDRAWN)
return;
wp->flags |= PANE_STATUSDRAWN;

1
sort.c
View File

@@ -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;

View File

@@ -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);

47
style.c
View File

@@ -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);
}

31
tmux.1
View File

@@ -5769,7 +5769,7 @@ 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 | keypress
.Op Ic on | off | failed | key
.Xc
A pane with this flag set is not destroyed when the program running in it
exits.
@@ -5777,9 +5777,8 @@ If set to
.Ic failed ,
then only when the program exit status is not zero.
If set to
.Ic keypress ,
the pane stays open and closes when the user presses any key (other than
a mouse button or paste).
.Ic key ,
the pane stays open and closes when a key is pressed.
The pane may be reactivated with the
.Ic respawn\-pane
command.
@@ -6013,8 +6012,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 ""
@@ -6475,6 +6479,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"
@@ -6600,6 +6605,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"
@@ -6828,6 +6835,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
@@ -6874,6 +6882,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.
@@ -7403,7 +7424,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

299
tmux.h
View File

@@ -129,18 +129,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
@@ -156,45 +144,122 @@ 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_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 */
};
/* 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), \
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), \
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 }, \
@@ -204,7 +269,17 @@ struct winlink;
{ #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
@@ -250,8 +325,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. */
@@ -261,77 +343,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,
@@ -384,8 +395,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. */
@@ -880,7 +905,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;
@@ -894,6 +920,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
@@ -952,6 +984,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;
@@ -993,6 +1039,7 @@ struct screen {
struct screen_write_cline *write_list;
struct hyperlinks *hyperlinks;
struct progress_bar progress_bar;
};
/* Screen write context. */
@@ -1216,8 +1263,8 @@ struct window_pane {
#define PANE_DROP 0x2
#define PANE_FOCUSED 0x4
#define PANE_VISITED 0x8
/* 0x10 unused */
/* 0x20 unused */
#define PANE_ZOOMED 0x10
#define PANE_FLOATING 0x20
#define PANE_INPUTOFF 0x40
#define PANE_CHANGED 0x80
#define PANE_EXITED 0x100
@@ -1228,9 +1275,7 @@ struct window_pane {
#define PANE_THEMECHANGED 0x2000
#define PANE_UNSEENCHANGES 0x4000
#define PANE_REDRAWSCROLLBAR 0x8000
#define PANE_FLOATING 0x10000
#define PANE_MINIMISED 0x20000
#define PANE_ZOOMED 0x40000
#define PANE_SAVED_FLOAT 0x80000 /* saved_float_* fields are valid */
/* Last floating position/size, saved when the pane is tiled. */
@@ -1268,6 +1313,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;
@@ -1892,10 +1938,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;
@@ -1904,7 +1946,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. */
@@ -1969,6 +2011,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;
@@ -2009,7 +2052,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;
@@ -3312,6 +3355,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,
@@ -3324,7 +3368,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;
@@ -3404,7 +3448,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 *, 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 *);
@@ -3435,6 +3479,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 *);
@@ -3756,6 +3802,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 **);

View File

@@ -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,19 +158,24 @@ 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);
/* 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.
@@ -195,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;
@@ -209,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;
@@ -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;
@@ -331,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);
}

View File

@@ -474,23 +474,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",
/*
@@ -498,8 +521,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;

View File

@@ -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);

View File

@@ -1034,6 +1034,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)
@@ -2747,6 +2749,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);
@@ -3340,7 +3344,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)
@@ -3362,9 +3366,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;

View File

@@ -653,10 +653,10 @@ struct window_pane *
window_get_active_at(struct window *w, u_int x, u_int y)
{
struct window_pane *wp;
int status, xoff, yoff;
int pane_status, xoff, yoff;
u_int sx, sy;
status = options_get_number(w->options, "pane-border-status");
pane_status = options_get_number(w->options, "pane-border-status");
TAILQ_FOREACH(wp, &w->z_index, zentry) {
if (!window_pane_visible(wp))
@@ -667,7 +667,7 @@ window_get_active_at(struct window *w, u_int x, u_int y)
right border. */
if ((int)x < xoff || x > xoff + sx)
continue;
if (status == PANE_STATUS_TOP) {
if (pane_status == PANE_STATUS_TOP) {
if ((int)y < yoff - 1 || y > yoff + sy)
continue;
} else {
@@ -678,8 +678,13 @@ window_get_active_at(struct window *w, u_int x, u_int y)
/* Floating, include top or or left border. */
if ((int)x < xoff - 1 || x > xoff + sx)
continue;
if ((int)y < yoff - 1 || y > yoff + sy)
if (pane_status == PANE_STATUS_TOP) {
if ((int)y <= yoff - 2 || y > yoff + sy - 1)
continue;
} else {
if ((int)y < yoff - 1 || y > yoff + sy)
continue;
}
}
return (wp);
}
@@ -990,9 +995,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)
@@ -1015,7 +1019,7 @@ window_printable_flags(struct winlink *wl, int escape)
}
const char *
window_pane_printable_flags(struct window_pane *wp, __unused int escape)
window_pane_printable_flags(struct window_pane *wp)
{
static char flags[32];
struct window *w = wp->window;
@@ -1099,6 +1103,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);
@@ -1151,6 +1156,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);
}
@@ -2121,3 +2127,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));
}