Merge branch 'master' into sixel

This commit is contained in:
topcat001 2023-03-30 14:21:09 -07:00
commit 64368a1a63
41 changed files with 1664 additions and 354 deletions

View File

@ -37,6 +37,10 @@ struct args_entry {
u_char flag; u_char flag;
struct args_values values; struct args_values values;
u_int count; u_int count;
int flags;
#define ARGS_ENTRY_OPTIONAL_VALUE 0x1
RB_ENTRY(args_entry) entry; RB_ENTRY(args_entry) entry;
}; };
@ -94,6 +98,22 @@ args_copy_value(struct args_value *to, struct args_value *from)
} }
} }
/* Type to string. */
static const char *
args_type_to_string (enum args_type type)
{
switch (type)
{
case ARGS_NONE:
return "NONE";
case ARGS_STRING:
return "STRING";
case ARGS_COMMANDS:
return "COMMANDS";
}
return "INVALID";
}
/* Get value as string. */ /* Get value as string. */
static const char * static const char *
args_value_as_string(struct args_value *value) args_value_as_string(struct args_value *value)
@ -122,6 +142,99 @@ args_create(void)
return (args); return (args);
} }
/* Parse a single flag. */
static int
args_parse_flag_argument(struct args_value *values, u_int count, char **cause,
struct args *args, u_int *i, const char *string, int flag,
int optional_argument)
{
struct args_value *argument, *new;
const char *s;
new = xcalloc(1, sizeof *new);
if (*string != '\0') {
new->type = ARGS_STRING;
new->string = xstrdup(string);
goto out;
}
if (*i == count)
argument = NULL;
else {
argument = &values[*i];
if (argument->type != ARGS_STRING) {
xasprintf(cause, "-%c argument must be a string", flag);
return (-1);
}
}
if (argument == NULL) {
if (optional_argument) {
log_debug("%s: -%c (optional)", __func__, flag);
args_set(args, flag, NULL, ARGS_ENTRY_OPTIONAL_VALUE);
return (0); /* either - or end */
}
xasprintf(cause, "-%c expects an argument", flag);
return (-1);
}
args_copy_value(new, argument);
(*i)++;
out:
s = args_value_as_string(new);
log_debug("%s: -%c = %s", __func__, flag, s);
args_set(args, flag, new, 0);
return (0);
}
/* Parse flags argument. */
static int
args_parse_flags(const struct args_parse *parse, struct args_value *values,
u_int count, char **cause, struct args *args, int *i)
{
struct args_value *value;
u_char flag;
const char *found, *string;
int optional_argument;
value = &values[*i];
if (value->type != ARGS_STRING)
return (1);
string = value->string;
log_debug("%s: next %s", __func__, string);
if (*string++ != '-' || *string == '\0')
return (1);
(*i)++;
if (string[0] == '-' && string[1] == '\0')
return (1);
for (;;) {
flag = *string++;
if (flag == '\0')
return (0);
if (flag == '?')
return (-1);
if (!isalnum(flag)) {
xasprintf(cause, "invalid flag -%c", flag);
return (-1);
}
found = strchr(parse->template, flag);
if (found == NULL) {
xasprintf(cause, "unknown flag -%c", flag);
return (-1);
}
if (found[1] != ':') {
log_debug("%s: -%c", __func__, flag);
args_set(args, flag, NULL, 0);
continue;
}
optional_argument = (found[2] == ':');
return (args_parse_flag_argument(values, count, cause, args, i,
string, flag, optional_argument));
}
}
/* Parse arguments into a new argument set. */ /* Parse arguments into a new argument set. */
struct args * struct args *
args_parse(const struct args_parse *parse, struct args_value *values, args_parse(const struct args_parse *parse, struct args_value *values,
@ -131,86 +244,21 @@ args_parse(const struct args_parse *parse, struct args_value *values,
u_int i; u_int i;
enum args_parse_type type; enum args_parse_type type;
struct args_value *value, *new; struct args_value *value, *new;
u_char flag; const char *s;
const char *found, *string, *s; int stop;
int optional_argument;
if (count == 0) if (count == 0)
return (args_create()); return (args_create());
args = args_create(); args = args_create();
for (i = 1; i < count; /* nothing */) { for (i = 1; i < count; /* nothing */) {
value = &values[i]; stop = args_parse_flags(parse, values, count, cause, args, &i);
if (value->type != ARGS_STRING) if (stop == -1) {
break; args_free(args);
return (NULL);
string = value->string;
if (*string++ != '-' || *string == '\0')
break;
i++;
if (string[0] == '-' && string[1] == '\0')
break;
for (;;) {
flag = *string++;
if (flag == '\0')
break;
if (flag == '?') {
args_free(args);
return (NULL);
}
if (!isalnum(flag)) {
xasprintf(cause, "invalid flag -%c", flag);
args_free(args);
return (NULL);
}
found = strchr(parse->template, flag);
if (found == NULL) {
xasprintf(cause, "unknown flag -%c", flag);
args_free(args);
return (NULL);
}
if (*++found != ':') {
log_debug("%s: -%c", __func__, flag);
args_set(args, flag, NULL);
continue;
}
if (*found == ':') {
optional_argument = 1;
found++;
}
new = xcalloc(1, sizeof *new);
if (*string != '\0') {
new->type = ARGS_STRING;
new->string = xstrdup(string);
} else {
if (i == count) {
if (optional_argument) {
log_debug("%s: -%c", __func__,
flag);
args_set(args, flag, NULL);
continue;
}
xasprintf(cause,
"-%c expects an argument",
flag);
args_free(args);
return (NULL);
}
if (values[i].type != ARGS_STRING) {
xasprintf(cause,
"-%c argument must be a string",
flag);
args_free(args);
return (NULL);
}
args_copy_value(new, &values[i++]);
}
s = args_value_as_string(new);
log_debug("%s: -%c = %s", __func__, flag, s);
args_set(args, flag, new);
break;
} }
if (stop == 1)
break;
} }
log_debug("%s: flags end at %u of %u", __func__, i, count); log_debug("%s: flags end at %u of %u", __func__, i, count);
if (i != count) { if (i != count) {
@ -218,8 +266,8 @@ args_parse(const struct args_parse *parse, struct args_value *values,
value = &values[i]; value = &values[i];
s = args_value_as_string(value); s = args_value_as_string(value);
log_debug("%s: %u = %s (type %d)", __func__, i, s, log_debug("%s: %u = %s (type %s)", __func__, i, s,
value->type); args_type_to_string (value->type));
if (parse->cb != NULL) { if (parse->cb != NULL) {
type = parse->cb(args, args->count, cause); type = parse->cb(args, args->count, cause);
@ -323,13 +371,13 @@ args_copy(struct args *args, int argc, char **argv)
RB_FOREACH(entry, args_tree, &args->tree) { RB_FOREACH(entry, args_tree, &args->tree) {
if (TAILQ_EMPTY(&entry->values)) { if (TAILQ_EMPTY(&entry->values)) {
for (i = 0; i < entry->count; i++) for (i = 0; i < entry->count; i++)
args_set(new_args, entry->flag, NULL); args_set(new_args, entry->flag, NULL, 0);
continue; continue;
} }
TAILQ_FOREACH(value, &entry->values, entry) { TAILQ_FOREACH(value, &entry->values, entry) {
new_value = xcalloc(1, sizeof *new_value); new_value = xcalloc(1, sizeof *new_value);
args_copy_copy_value(new_value, value, argc, argv); args_copy_copy_value(new_value, value, argc, argv);
args_set(new_args, entry->flag, new_value); args_set(new_args, entry->flag, new_value, 0);
} }
} }
if (args->count == 0) if (args->count == 0)
@ -487,6 +535,7 @@ args_print(struct args *args)
char *buf; char *buf;
u_int i, j; u_int i, j;
struct args_entry *entry; struct args_entry *entry;
struct args_entry *last = NULL;
struct args_value *value; struct args_value *value;
len = 1; len = 1;
@ -494,6 +543,8 @@ args_print(struct args *args)
/* Process the flags first. */ /* Process the flags first. */
RB_FOREACH(entry, args_tree, &args->tree) { RB_FOREACH(entry, args_tree, &args->tree) {
if (entry->flags & ARGS_ENTRY_OPTIONAL_VALUE)
continue;
if (!TAILQ_EMPTY(&entry->values)) if (!TAILQ_EMPTY(&entry->values))
continue; continue;
@ -505,6 +556,16 @@ args_print(struct args *args)
/* Then the flags with arguments. */ /* Then the flags with arguments. */
RB_FOREACH(entry, args_tree, &args->tree) { RB_FOREACH(entry, args_tree, &args->tree) {
if (entry->flags & ARGS_ENTRY_OPTIONAL_VALUE) {
if (*buf != '\0')
args_print_add(&buf, &len, " -%c", entry->flag);
else
args_print_add(&buf, &len, "-%c", entry->flag);
last = entry;
continue;
}
if (TAILQ_EMPTY(&entry->values))
continue;
TAILQ_FOREACH(value, &entry->values, entry) { TAILQ_FOREACH(value, &entry->values, entry) {
if (*buf != '\0') if (*buf != '\0')
args_print_add(&buf, &len, " -%c", entry->flag); args_print_add(&buf, &len, " -%c", entry->flag);
@ -512,7 +573,10 @@ args_print(struct args *args)
args_print_add(&buf, &len, "-%c", entry->flag); args_print_add(&buf, &len, "-%c", entry->flag);
args_print_add_value(&buf, &len, value); args_print_add_value(&buf, &len, value);
} }
last = entry;
} }
if (last && (last->flags & ARGS_ENTRY_OPTIONAL_VALUE))
args_print_add(&buf, &len, " --");
/* And finally the argument vector. */ /* And finally the argument vector. */
for (i = 0; i < args->count; i++) for (i = 0; i < args->count; i++)
@ -582,7 +646,7 @@ args_has(struct args *args, u_char flag)
/* Set argument value in the arguments tree. */ /* Set argument value in the arguments tree. */
void void
args_set(struct args *args, u_char flag, struct args_value *value) args_set(struct args *args, u_char flag, struct args_value *value, int flags)
{ {
struct args_entry *entry; struct args_entry *entry;
@ -591,6 +655,7 @@ args_set(struct args *args, u_char flag, struct args_value *value)
entry = xcalloc(1, sizeof *entry); entry = xcalloc(1, sizeof *entry);
entry->flag = flag; entry->flag = flag;
entry->count = 1; entry->count = 1;
entry->flags = flags;
TAILQ_INIT(&entry->values); TAILQ_INIT(&entry->values);
RB_INSERT(args_tree, &args->tree, entry); RB_INSERT(args_tree, &args->tree, entry);
} else } else
@ -747,6 +812,8 @@ args_make_commands(struct args_command_state *state, int argc, char **argv,
} }
cmd = xstrdup(state->cmd); cmd = xstrdup(state->cmd);
log_debug("%s: %s", __func__, cmd);
cmd_log_argv(argc, argv, __func__);
for (i = 0; i < argc; i++) { for (i = 0; i < argc; i++) {
new_cmd = cmd_template_replace(cmd, argv[i], i + 1); new_cmd = cmd_template_replace(cmd, argv[i], i + 1);
log_debug("%s: %%%u %s: %s", __func__, i + 1, argv[i], new_cmd); log_debug("%s: %%%u %s: %s", __func__, i + 1, argv[i], new_cmd);

View File

@ -700,6 +700,9 @@ client_dispatch_wait(struct imsg *imsg)
!(client_flags & CLIENT_CONTROL), client_file_check_cb, !(client_flags & CLIENT_CONTROL), client_file_check_cb,
NULL); NULL);
break; break;
case MSG_READ_CANCEL:
file_read_cancel(&client_files, imsg);
break;
case MSG_WRITE_OPEN: case MSG_WRITE_OPEN:
file_write_open(&client_files, client_peer, imsg, 1, file_write_open(&client_files, client_peer, imsg, 1,
!(client_flags & CLIENT_CONTROL), client_file_check_cb, !(client_flags & CLIENT_CONTROL), client_file_check_cb,

View File

@ -115,6 +115,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
layout_init(w, wp); layout_init(w, wp);
wp->flags |= PANE_CHANGED; wp->flags |= PANE_CHANGED;
colour_palette_from_option(&wp->palette, wp->options);
if (idx == -1) if (idx == -1)
idx = -1 - options_get_number(dst_s->options, "base-index"); idx = -1 - options_get_number(dst_s->options, "base-index");

View File

@ -179,10 +179,10 @@ cmd_command_prompt_callback(struct client *c, void *data, const char *s,
if (s == NULL) if (s == NULL)
goto out; goto out;
if (done) { if (done) {
if (cdata->flags & PROMPT_INCREMENTAL) if (cdata->flags & PROMPT_INCREMENTAL)
goto out; goto out;
cmd_append_argv(&cdata->argc, &cdata->argv, s); cmd_append_argv(&cdata->argc, &cdata->argv, s);
if (++cdata->current != cdata->count) { if (++cdata->current != cdata->count) {
prompt = &cdata->prompts[cdata->current]; prompt = &cdata->prompts[cdata->current];
@ -193,8 +193,11 @@ cmd_command_prompt_callback(struct client *c, void *data, const char *s,
argc = cdata->argc; argc = cdata->argc;
argv = cmd_copy_argv(cdata->argc, cdata->argv); argv = cmd_copy_argv(cdata->argc, cdata->argv);
cmd_append_argv(&argc, &argv, s); if (!done)
cmd_append_argv(&argc, &argv, s);
if (done) { if (done) {
cmd_free_argv(cdata->argc, cdata->argv);
cdata->argc = argc; cdata->argc = argc;
cdata->argv = cmd_copy_argv(argc, argv); cdata->argv = cmd_copy_argv(argc, argv);
} }

View File

@ -38,9 +38,10 @@ const struct cmd_entry cmd_display_menu_entry = {
.name = "display-menu", .name = "display-menu",
.alias = "menu", .alias = "menu",
.args = { "c:t:OT:x:y:", 1, -1, cmd_display_menu_args_parse }, .args = { "c:t:S:OT:x:y:", 1, -1, cmd_display_menu_args_parse },
.usage = "[-O] [-c target-client] " CMD_TARGET_PANE_USAGE " [-T title] " .usage = "[-O] [-c target-client] [-S starting-choice] "
"[-x position] [-y position] name key command ...", CMD_TARGET_PANE_USAGE " [-T title] [-x position] "
"[-y position] name key command ...",
.target = { 't', CMD_FIND_PANE, 0 }, .target = { 't', CMD_FIND_PANE, 0 },
@ -274,6 +275,7 @@ cmd_display_menu_get_position(struct client *tc, struct cmdq_item *item,
log_debug("%s: -y: %s = %s = %u (-h %u)", __func__, yp, p, *py, h); log_debug("%s: -y: %s = %s = %u (-h %u)", __func__, yp, p, *py, h);
free(p); free(p);
format_free(ft);
return (1); return (1);
} }
@ -287,13 +289,27 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
struct menu *menu = NULL; struct menu *menu = NULL;
struct menu_item menu_item; struct menu_item menu_item;
const char *key, *name; const char *key, *name;
char *title; char *title, *cause;
int flags = 0; int flags = 0, starting_choice = 0;
u_int px, py, i, count = args_count(args); u_int px, py, i, count = args_count(args);
if (tc->overlay_draw != NULL) if (tc->overlay_draw != NULL)
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
if (args_has(args, 'S')) {
if (strcmp(args_get(args, 'S'), "-") == 0)
starting_choice = -1;
else {
starting_choice = args_strtonum(args, 'S', 0, UINT_MAX,
&cause);
if (cause != NULL) {
cmdq_error(item, "starting choice %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
}
}
if (args_has(args, 'T')) if (args_has(args, 'T'))
title = format_single_from_target(item, args_get(args, 'T')); title = format_single_from_target(item, args_get(args, 'T'));
else else
@ -340,8 +356,8 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
flags |= MENU_STAYOPEN; flags |= MENU_STAYOPEN;
if (!event->m.valid) if (!event->m.valid)
flags |= MENU_NOMOUSE; flags |= MENU_NOMOUSE;
if (menu_display(menu, flags, item, px, py, tc, target, NULL, if (menu_display(menu, flags, starting_choice, item, px, py, tc, target,
NULL) != 0) NULL, NULL) != 0)
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
return (CMD_RETURN_WAIT); return (CMD_RETURN_WAIT);
} }
@ -454,11 +470,13 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item)
cmd_free_argv(argc, argv); cmd_free_argv(argc, argv);
if (env != NULL) if (env != NULL)
environ_free(env); environ_free(env);
free(cwd);
free(title); free(title);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }
if (env != NULL) if (env != NULL)
environ_free(env); environ_free(env);
free(cwd);
free(title); free(title);
cmd_free_argv(argc, argv); cmd_free_argv(argc, argv);
return (CMD_RETURN_WAIT); return (CMD_RETURN_WAIT);

View File

@ -68,9 +68,10 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item)
struct window_pane *wp = target->wp; struct window_pane *wp = target->wp;
const char *template; const char *template;
char *msg, *cause; char *msg, *cause;
int delay = -1, flags; int delay = -1, flags, Nflag = args_has(args, 'N');
struct format_tree *ft; struct format_tree *ft;
u_int count = args_count(args); u_int count = args_count(args);
struct evbuffer *evb;
if (args_has(args, 'I')) { if (args_has(args, 'I')) {
if (wp == NULL) if (wp == NULL)
@ -141,10 +142,15 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item)
cmdq_error(item, "%s", msg); cmdq_error(item, "%s", msg);
else if (args_has(args, 'p')) else if (args_has(args, 'p'))
cmdq_print(item, "%s", msg); cmdq_print(item, "%s", msg);
else if (tc != NULL) { else if (tc != NULL && (tc->flags & CLIENT_CONTROL)) {
status_message_set(tc, delay, 0, args_has(args, 'N'), "%s", evb = evbuffer_new();
msg); if (evb == NULL)
} fatalx("out of memory");
evbuffer_add_printf(evb, "%%message %s", msg);
server_client_print(tc, 0, evb);
evbuffer_free(evb);
} else if (tc != NULL)
status_message_set(tc, delay, 0, Nflag, "%s", msg);
free(msg); free(msg);
format_free(ft); format_free(ft);

View File

@ -103,8 +103,8 @@ cmd_find_window_exec(struct cmd *self, struct cmdq_item *item)
new_args = args_create(); new_args = args_create();
if (args_has(args, 'Z')) if (args_has(args, 'Z'))
args_set(new_args, 'Z', NULL); args_set(new_args, 'Z', NULL, 0);
args_set(new_args, 'f', filter); args_set(new_args, 'f', filter, 0);
window_pane_set_mode(wp, NULL, &window_tree_mode, target, new_args); window_pane_set_mode(wp, NULL, &window_tree_mode, target, new_args);
args_free(new_args); args_free(new_args);

View File

@ -155,6 +155,7 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
else else
TAILQ_INSERT_AFTER(&dst_w->panes, dst_wp, src_wp, entry); TAILQ_INSERT_AFTER(&dst_w->panes, dst_wp, src_wp, entry);
layout_assign_pane(lc, src_wp, 0); layout_assign_pane(lc, src_wp, 0);
colour_palette_from_option(&src_wp->palette, src_wp->options);
recalculate_sizes(); recalculate_sizes();

View File

@ -41,8 +41,8 @@ const struct cmd_entry cmd_list_clients_entry = {
.name = "list-clients", .name = "list-clients",
.alias = "lsc", .alias = "lsc",
.args = { "F:t:", 0, 0, NULL }, .args = { "F:f:t:", 0, 0, NULL },
.usage = "[-F format] " CMD_TARGET_SESSION_USAGE, .usage = "[-F format] [-f filter] " CMD_TARGET_SESSION_USAGE,
.target = { 't', CMD_FIND_SESSION, 0 }, .target = { 't', CMD_FIND_SESSION, 0 },
@ -58,9 +58,10 @@ cmd_list_clients_exec(struct cmd *self, struct cmdq_item *item)
struct client *c; struct client *c;
struct session *s; struct session *s;
struct format_tree *ft; struct format_tree *ft;
const char *template; const char *template, *filter;
u_int idx; u_int idx;
char *line; char *line, *expanded;
int flag;
if (args_has(args, 't')) if (args_has(args, 't'))
s = target->s; s = target->s;
@ -69,6 +70,7 @@ cmd_list_clients_exec(struct cmd *self, struct cmdq_item *item)
if ((template = args_get(args, 'F')) == NULL) if ((template = args_get(args, 'F')) == NULL)
template = LIST_CLIENTS_TEMPLATE; template = LIST_CLIENTS_TEMPLATE;
filter = args_get(args, 'f');
idx = 0; idx = 0;
TAILQ_FOREACH(c, &clients, entry) { TAILQ_FOREACH(c, &clients, entry) {
@ -79,9 +81,17 @@ cmd_list_clients_exec(struct cmd *self, struct cmdq_item *item)
format_add(ft, "line", "%u", idx); format_add(ft, "line", "%u", idx);
format_defaults(ft, c, NULL, NULL, NULL); format_defaults(ft, c, NULL, NULL, NULL);
line = format_expand(ft, template); if (filter != NULL) {
cmdq_print(item, "%s", line); expanded = format_expand(ft, filter);
free(line); flag = format_true(expanded);
free(expanded);
} else
flag = 1;
if (flag) {
line = format_expand(ft, template);
cmdq_print(item, "%s", line);
free(line);
}
format_free(ft); format_free(ft);

View File

@ -148,6 +148,7 @@ static enum cmd_retval
cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item) cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
{ {
struct args *args = cmd_get_args(self); struct args *args = cmd_get_args(self);
struct client *tc = cmdq_get_target_client(item);
struct key_table *table; struct key_table *table;
struct key_binding *bd; struct key_binding *bd;
const char *tablename, *r, *keystr; const char *tablename, *r, *keystr;
@ -296,9 +297,15 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
strlcat(tmp, cp, tmpsize); strlcat(tmp, cp, tmpsize);
free(cp); free(cp);
cmdq_print(item, "bind-key %s", tmp); if (args_has(args, '1') && tc != NULL) {
status_message_set(tc, -1, 1, 0, "bind-key %s",
tmp);
} else
cmdq_print(item, "bind-key %s", tmp);
free(key); free(key);
if (args_has(args, '1'))
break;
bd = key_bindings_next(table, bd); bd = key_bindings_next(table, bd);
} }
table = key_bindings_next_table(table); table = key_bindings_next_table(table);

View File

@ -1615,13 +1615,24 @@ yylex_token(int ch)
for (;;) { for (;;) {
/* EOF or \n are always the end of the token. */ /* EOF or \n are always the end of the token. */
if (ch == EOF || (state == NONE && ch == '\n')) if (ch == EOF) {
log_debug("%s: end at EOF", __func__);
break; break;
}
if (state == NONE && ch == '\n') {
log_debug("%s: end at EOL", __func__);
break;
}
/* Whitespace or ; or } ends a token unless inside quotes. */ /* Whitespace or ; or } ends a token unless inside quotes. */
if ((ch == ' ' || ch == '\t' || ch == ';' || ch == '}') && if (state == NONE && (ch == ' ' || ch == '\t')) {
state == NONE) log_debug("%s: end at WS", __func__);
break; break;
}
if (state == NONE && (ch == ';' || ch == '}')) {
log_debug("%s: end at %c", __func__, ch);
break;
}
/* /*
* Spaces and comments inside quotes after \n are removed but * Spaces and comments inside quotes after \n are removed but

View File

@ -821,45 +821,30 @@ cmdq_guard(struct cmdq_item *item, const char *guard, int flags)
control_write(c, "%%%s %ld %u %d", guard, t, number, flags); control_write(c, "%%%s %ld %u %d", guard, t, number, flags);
} }
/* Show message from command. */
void
cmdq_print_data(struct cmdq_item *item, int parse, struct evbuffer *evb)
{
server_client_print(item->client, parse, evb);
}
/* Show message from command. */ /* Show message from command. */
void void
cmdq_print(struct cmdq_item *item, const char *fmt, ...) cmdq_print(struct cmdq_item *item, const char *fmt, ...)
{ {
struct client *c = item->client; va_list ap;
struct window_pane *wp; struct evbuffer *evb;
struct window_mode_entry *wme;
va_list ap; evb = evbuffer_new();
char *tmp, *msg; if (evb == NULL)
fatalx("out of memory");
va_start(ap, fmt); va_start(ap, fmt);
xvasprintf(&msg, fmt, ap); evbuffer_add_vprintf(evb, fmt, ap);
va_end(ap); va_end(ap);
log_debug("%s: %s", __func__, msg); cmdq_print_data(item, 0, evb);
evbuffer_free(evb);
if (c == NULL)
/* nothing */;
else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) {
if (~c->flags & CLIENT_UTF8) {
tmp = msg;
msg = utf8_sanitize(tmp);
free(tmp);
}
if (c->flags & CLIENT_CONTROL)
control_write(c, "%s", msg);
else
file_print(c, "%s\n", msg);
} else {
wp = server_client_get_pane(c);
wme = TAILQ_FIRST(&wp->modes);
if (wme == NULL || wme->mode != &window_view_mode) {
window_pane_set_mode(wp, NULL, &window_view_mode, NULL,
NULL);
}
window_copy_add(wp, 0, "%s", msg);
}
free(msg);
} }
/* Show error from command. */ /* Show error from command. */

View File

@ -78,7 +78,8 @@ cmd_save_buffer_exec(struct cmd *self, struct cmdq_item *item)
int flags; int flags;
const char *bufname = args_get(args, 'b'), *bufdata; const char *bufname = args_get(args, 'b'), *bufdata;
size_t bufsize; size_t bufsize;
char *path, *tmp; char *path;
struct evbuffer *evb;
if (bufname == NULL) { if (bufname == NULL) {
if ((pb = paste_get_top(NULL)) == NULL) { if ((pb = paste_get_top(NULL)) == NULL) {
@ -96,10 +97,12 @@ cmd_save_buffer_exec(struct cmd *self, struct cmdq_item *item)
if (cmd_get_entry(self) == &cmd_show_buffer_entry) { if (cmd_get_entry(self) == &cmd_show_buffer_entry) {
if (c->session != NULL || (c->flags & CLIENT_CONTROL)) { if (c->session != NULL || (c->flags & CLIENT_CONTROL)) {
utf8_stravisx(&tmp, bufdata, bufsize, evb = evbuffer_new();
VIS_OCTAL|VIS_CSTYLE|VIS_TAB); if (evb == NULL)
cmdq_print(item, "%s", tmp); fatalx("out of memory");
free(tmp); evbuffer_add(evb, bufdata, bufsize);
cmdq_print_data(item, 1, evb);
evbuffer_free(evb);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }
path = xstrdup("-"); path = xstrdup("-");

View File

@ -33,13 +33,13 @@ const struct cmd_entry cmd_send_keys_entry = {
.name = "send-keys", .name = "send-keys",
.alias = "send", .alias = "send",
.args = { "FHlMN:Rt:X", 0, -1, NULL }, .args = { "c:FHKlMN:Rt:X", 0, -1, NULL },
.usage = "[-FHlMRX] [-N repeat-count] " CMD_TARGET_PANE_USAGE .usage = "[-FHKlMRX] [-c target-client] [-N repeat-count] "
" key ...", CMD_TARGET_PANE_USAGE " key ...",
.target = { 't', CMD_FIND_PANE, 0 }, .target = { 't', CMD_FIND_PANE, 0 },
.flags = CMD_AFTERHOOK, .flags = CMD_AFTERHOOK|CMD_CLIENT_CFLAG|CMD_CLIENT_CANFAIL,
.exec = cmd_send_keys_exec .exec = cmd_send_keys_exec
}; };
@ -58,7 +58,7 @@ const struct cmd_entry cmd_send_prefix_entry = {
static struct cmdq_item * static struct cmdq_item *
cmd_send_keys_inject_key(struct cmdq_item *item, struct cmdq_item *after, cmd_send_keys_inject_key(struct cmdq_item *item, struct cmdq_item *after,
key_code key) struct args *args, key_code key)
{ {
struct cmd_find_state *target = cmdq_get_target(item); struct cmd_find_state *target = cmdq_get_target(item);
struct client *tc = cmdq_get_target_client(item); struct client *tc = cmdq_get_target_client(item);
@ -66,8 +66,20 @@ cmd_send_keys_inject_key(struct cmdq_item *item, struct cmdq_item *after,
struct winlink *wl = target->wl; struct winlink *wl = target->wl;
struct window_pane *wp = target->wp; struct window_pane *wp = target->wp;
struct window_mode_entry *wme; struct window_mode_entry *wme;
struct key_table *table; struct key_table *table = NULL;
struct key_binding *bd; struct key_binding *bd;
struct key_event *event;
if (args_has(args, 'K')) {
if (tc == NULL)
return (item);
event = xmalloc(sizeof *event);
event->key = key|KEYC_SENT;
memset(&event->m, 0, sizeof event->m);
if (server_client_handle_key(tc, event) == 0)
free(event);
return (item);
}
wme = TAILQ_FIRST(&wp->modes); wme = TAILQ_FIRST(&wp->modes);
if (wme == NULL || wme->mode->key_table == NULL) { if (wme == NULL || wme->mode->key_table == NULL) {
@ -102,14 +114,16 @@ cmd_send_keys_inject_string(struct cmdq_item *item, struct cmdq_item *after,
n = strtol(s, &endptr, 16); n = strtol(s, &endptr, 16);
if (*s =='\0' || n < 0 || n > 0xff || *endptr != '\0') if (*s =='\0' || n < 0 || n > 0xff || *endptr != '\0')
return (item); return (item);
return (cmd_send_keys_inject_key(item, after, KEYC_LITERAL|n)); return (cmd_send_keys_inject_key(item, after, args,
KEYC_LITERAL|n));
} }
literal = args_has(args, 'l'); literal = args_has(args, 'l');
if (!literal) { if (!literal) {
key = key_string_lookup_string(s); key = key_string_lookup_string(s);
if (key != KEYC_NONE && key != KEYC_UNKNOWN) { if (key != KEYC_NONE && key != KEYC_UNKNOWN) {
after = cmd_send_keys_inject_key(item, after, key); after = cmd_send_keys_inject_key(item, after, args,
key);
if (after != NULL) if (after != NULL)
return (after); return (after);
} }
@ -125,7 +139,8 @@ cmd_send_keys_inject_string(struct cmdq_item *item, struct cmdq_item *after,
continue; continue;
key = uc; key = uc;
} }
after = cmd_send_keys_inject_key(item, after, key); after = cmd_send_keys_inject_key(item, after, args,
key);
} }
free(ud); free(ud);
} }
@ -193,7 +208,7 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item)
key = options_get_number(s->options, "prefix2"); key = options_get_number(s->options, "prefix2");
else else
key = options_get_number(s->options, "prefix"); key = options_get_number(s->options, "prefix");
cmd_send_keys_inject_key(item, item, key); cmd_send_keys_inject_key(item, item, args, key);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }
@ -207,7 +222,7 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item)
if (args_has(args, 'N') || args_has(args, 'R')) if (args_has(args, 'N') || args_has(args, 'R'))
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
for (; np != 0; np--) for (; np != 0; np--)
cmd_send_keys_inject_key(item, NULL, event->key); cmd_send_keys_inject_key(item, NULL, args, event->key);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }

View File

@ -132,6 +132,8 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item)
src_w->last = NULL; src_w->last = NULL;
if (dst_w->last == dst_wp) if (dst_w->last == dst_wp)
dst_w->last = NULL; dst_w->last = NULL;
colour_palette_from_option(&src_wp->palette, src_wp->options);
colour_palette_from_option(&dst_wp->palette, dst_wp->options);
} }
server_redraw_window(src_w); server_redraw_window(src_w);
server_redraw_window(dst_w); server_redraw_window(dst_w);

View File

@ -960,6 +960,47 @@ colour_byname(const char *name)
return (-1); return (-1);
} }
/* Parse colour from an X11 string. */
int
colour_parseX11(const char *p)
{
double c, m, y, k = 0;
u_int r, g, b;
size_t len = strlen(p);
int colour = -1;
char *copy;
if ((len == 12 && sscanf(p, "rgb:%02x/%02x/%02x", &r, &g, &b) == 3) ||
(len == 7 && sscanf(p, "#%02x%02x%02x", &r, &g, &b) == 3) ||
sscanf(p, "%d,%d,%d", &r, &g, &b) == 3)
colour = colour_join_rgb(r, g, b);
else if ((len == 18 &&
sscanf(p, "rgb:%04x/%04x/%04x", &r, &g, &b) == 3) ||
(len == 13 && sscanf(p, "#%04x%04x%04x", &r, &g, &b) == 3))
colour = colour_join_rgb(r >> 8, g >> 8, b >> 8);
else if ((sscanf(p, "cmyk:%lf/%lf/%lf/%lf", &c, &m, &y, &k) == 4 ||
sscanf(p, "cmy:%lf/%lf/%lf", &c, &m, &y) == 3) &&
c >= 0 && c <= 1 && m >= 0 && m <= 1 &&
y >= 0 && y <= 1 && k >= 0 && k <= 1) {
colour = colour_join_rgb(
(1 - c) * (1 - k) * 255,
(1 - m) * (1 - k) * 255,
(1 - y) * (1 - k) * 255);
} else {
while (len != 0 && *p == ' ') {
p++;
len--;
}
while (len != 0 && p[len - 1] == ' ')
len--;
copy = xstrndup(p, len);
colour = colour_byname(copy);
free(copy);
}
log_debug("%s: %s = %s", __func__, p, colour_tostring(colour));
return (colour);
}
/* Initialize palette. */ /* Initialize palette. */
void void
colour_palette_init(struct colour_palette *p) colour_palette_init(struct colour_palette *p)
@ -1069,5 +1110,4 @@ colour_palette_from_option(struct colour_palette *p, struct options *oo)
} }
a = options_array_next(a); a = options_array_next(a);
} }
} }

44
file.c
View File

@ -149,7 +149,8 @@ file_fire_done_cb(__unused int fd, __unused short events, void *arg)
struct client_file *cf = arg; struct client_file *cf = arg;
struct client *c = cf->c; struct client *c = cf->c;
if (cf->cb != NULL && (c == NULL || (~c->flags & CLIENT_DEAD))) if (cf->cb != NULL &&
(cf->closed || c == NULL || (~c->flags & CLIENT_DEAD)))
cf->cb(c, cf->path, cf->error, 1, cf->buffer, cf->data); cf->cb(c, cf->path, cf->error, 1, cf->buffer, cf->data);
file_free(cf); file_free(cf);
} }
@ -352,7 +353,7 @@ done:
} }
/* Read a file. */ /* Read a file. */
void struct client_file *
file_read(struct client *c, const char *path, client_file_cb cb, void *cbdata) file_read(struct client *c, const char *path, client_file_cb cb, void *cbdata)
{ {
struct client_file *cf; struct client_file *cf;
@ -420,10 +421,27 @@ skip:
goto done; goto done;
} }
free(msg); free(msg);
return; return cf;
done: done:
file_fire_done(cf); file_fire_done(cf);
return NULL;
}
/* Cancel a file read. */
void
file_cancel(struct client_file *cf)
{
struct msg_read_cancel msg;
log_debug("read cancel file %d", cf->stream);
if (cf->closed)
return;
cf->closed = 1;
msg.stream = cf->stream;
proc_send(cf->peer, MSG_READ_CANCEL, -1, &msg, sizeof msg);
} }
/* Push event, fired if there is more writing to be done. */ /* Push event, fired if there is more writing to be done. */
@ -757,6 +775,24 @@ reply:
proc_send(peer, MSG_READ_DONE, -1, &reply, sizeof reply); proc_send(peer, MSG_READ_DONE, -1, &reply, sizeof reply);
} }
/* Handle a read cancel message (client). */
void
file_read_cancel(struct client_files *files, struct imsg *imsg)
{
struct msg_read_cancel *msg = imsg->data;
size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
struct client_file find, *cf;
if (msglen != sizeof *msg)
fatalx("bad MSG_READ_CANCEL size");
find.stream = msg->stream;
if ((cf = RB_FIND(client_files, files, &find)) == NULL)
fatalx("unknown stream number");
log_debug("cancel file %d", cf->stream);
file_read_error_callback(NULL, 0, cf);
}
/* Handle a write ready message (server). */ /* Handle a write ready message (server). */
void void
file_write_ready(struct client_files *files, struct imsg *imsg) file_write_ready(struct client_files *files, struct imsg *imsg)
@ -794,7 +830,7 @@ file_read_data(struct client_files *files, struct imsg *imsg)
return; return;
log_debug("file %d read %zu bytes", cf->stream, bsize); log_debug("file %d read %zu bytes", cf->stream, bsize);
if (cf->error == 0) { if (cf->error == 0 && !cf->closed) {
if (evbuffer_add(cf->buffer, bdata, bsize) != 0) { if (evbuffer_add(cf->buffer, bdata, bsize) != 0) {
cf->error = ENOMEM; cf->error = ENOMEM;
file_fire_done(cf); file_fire_done(cf);

View File

@ -103,6 +103,7 @@ format_job_cmp(struct format_job *fj1, struct format_job *fj2)
#define FORMAT_SESSION_NAME 0x8000 #define FORMAT_SESSION_NAME 0x8000
#define FORMAT_CHARACTER 0x10000 #define FORMAT_CHARACTER 0x10000
#define FORMAT_COLOUR 0x20000 #define FORMAT_COLOUR 0x20000
#define FORMAT_CLIENTS 0x40000
/* Limit on recursion. */ /* Limit on recursion. */
#define FORMAT_LOOP_LIMIT 100 #define FORMAT_LOOP_LIMIT 100
@ -3747,7 +3748,7 @@ format_build_modifiers(struct format_expand_state *es, const char **s,
cp++; cp++;
/* Check single character modifiers with no arguments. */ /* Check single character modifiers with no arguments. */
if (strchr("labcdnwETSWP<>", cp[0]) != NULL && if (strchr("labcdnwETSWPL<>", cp[0]) != NULL &&
format_is_end(cp[1])) { format_is_end(cp[1])) {
format_add_modifier(&list, count, cp, 1, NULL, 0); format_add_modifier(&list, count, cp, 1, NULL, 0);
cp++; cp++;
@ -4075,6 +4076,40 @@ format_loop_panes(struct format_expand_state *es, const char *fmt)
return (value); return (value);
} }
/* Loop over clients. */
static char *
format_loop_clients(struct format_expand_state *es, const char *fmt)
{
struct format_tree *ft = es->ft;
struct client *c = ft->client;
struct cmdq_item *item = ft->item;
struct format_tree *nft;
struct format_expand_state next;
char *expanded, *value;
size_t valuelen;
value = xcalloc(1, 1);
valuelen = 1;
TAILQ_FOREACH(c, &clients, entry) {
format_log(es, "client loop: %s", c->name);
nft = format_create(c, item, 0, ft->flags);
format_defaults(nft, c, ft->s, ft->wl, ft->wp);
format_copy_state(&next, es, 0);
next.ft = nft;
expanded = format_expand1(&next, fmt);
format_free(nft);
valuelen += strlen(expanded);
value = xrealloc(value, valuelen);
strlcat(value, expanded, valuelen);
free(expanded);
}
return (value);
}
static char * static char *
format_replace_expression(struct format_modifier *mexp, format_replace_expression(struct format_modifier *mexp,
struct format_expand_state *es, const char *copy) struct format_expand_state *es, const char *copy)
@ -4349,6 +4384,9 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
case 'P': case 'P':
modifiers |= FORMAT_PANES; modifiers |= FORMAT_PANES;
break; break;
case 'L':
modifiers |= FORMAT_CLIENTS;
break;
} }
} else if (fm->size == 2) { } else if (fm->size == 2) {
if (strcmp(fm->modifier, "||") == 0 || if (strcmp(fm->modifier, "||") == 0 ||
@ -4405,6 +4443,10 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
value = format_loop_panes(es, copy); value = format_loop_panes(es, copy);
if (value == NULL) if (value == NULL)
goto fail; goto fail;
} else if (modifiers & FORMAT_CLIENTS) {
value = format_loop_clients(es, copy);
if (value == NULL)
goto fail;
} else if (modifiers & FORMAT_WINDOW_NAME) { } else if (modifiers & FORMAT_WINDOW_NAME) {
value = format_window_name(es, copy); value = format_window_name(es, copy);
if (value == NULL) if (value == NULL)

View File

@ -496,6 +496,9 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key)
ike = input_key_get(key & ~KEYC_EXTENDED); ike = input_key_get(key & ~KEYC_EXTENDED);
if (ike != NULL) { if (ike != NULL) {
log_debug("found key 0x%llx: \"%s\"", key, ike->data); log_debug("found key 0x%llx: \"%s\"", key, ike->data);
if ((key == KEYC_PASTE_START || key == KEYC_PASTE_END) &&
(~s->mode & MODE_BRACKETPASTE))
return (0);
if ((key & KEYC_META) && (~key & KEYC_IMPLIED_META)) if ((key & KEYC_META) && (~key & KEYC_IMPLIED_META))
input_key_write(__func__, bev, "\033", 1); input_key_write(__func__, bev, "\033", 1);
input_key_write(__func__, bev, ike->data, strlen(ike->data)); input_key_write(__func__, bev, ike->data, strlen(ike->data));

115
input.c
View File

@ -1086,6 +1086,7 @@ input_reply(struct input_ctx *ictx, const char *fmt, ...)
xvasprintf(&reply, fmt, ap); xvasprintf(&reply, fmt, ap);
va_end(ap); va_end(ap);
log_debug("%s: %s", __func__, reply);
bufferevent_write(bev, reply, strlen(reply)); bufferevent_write(bev, reply, strlen(reply));
free(reply); free(reply);
} }
@ -2466,47 +2467,6 @@ input_top_bit_set(struct input_ctx *ictx)
return (0); return (0);
} }
/* Parse colour from OSC. */
static int
input_osc_parse_colour(const char *p)
{
double c, m, y, k = 0;
u_int r, g, b;
size_t len = strlen(p);
int colour = -1;
char *copy;
if ((len == 12 && sscanf(p, "rgb:%02x/%02x/%02x", &r, &g, &b) == 3) ||
(len == 7 && sscanf(p, "#%02x%02x%02x", &r, &g, &b) == 3) ||
sscanf(p, "%d,%d,%d", &r, &g, &b) == 3)
colour = colour_join_rgb(r, g, b);
else if ((len == 18 &&
sscanf(p, "rgb:%04x/%04x/%04x", &r, &g, &b) == 3) ||
(len == 13 && sscanf(p, "#%04x%04x%04x", &r, &g, &b) == 3))
colour = colour_join_rgb(r >> 8, g >> 8, b >> 8);
else if ((sscanf(p, "cmyk:%lf/%lf/%lf/%lf", &c, &m, &y, &k) == 4 ||
sscanf(p, "cmy:%lf/%lf/%lf", &c, &m, &y) == 3) &&
c >= 0 && c <= 1 && m >= 0 && m <= 1 &&
y >= 0 && y <= 1 && k >= 0 && k <= 1) {
colour = colour_join_rgb(
(1 - c) * (1 - k) * 255,
(1 - m) * (1 - k) * 255,
(1 - y) * (1 - k) * 255);
} else {
while (len != 0 && *p == ' ') {
p++;
len--;
}
while (len != 0 && p[len - 1] == ' ')
len--;
copy = xstrndup(p, len);
colour = colour_byname(copy);
free(copy);
}
log_debug("%s: %s = %s", __func__, p, colour_tostring(colour));
return (colour);
}
/* Reply to a colour request. */ /* Reply to a colour request. */
static void static void
input_osc_colour_reply(struct input_ctx *ictx, u_int n, int c) input_osc_colour_reply(struct input_ctx *ictx, u_int n, int c)
@ -2555,7 +2515,7 @@ input_osc_4(struct input_ctx *ictx, const char *p)
input_osc_colour_reply(ictx, 4, c); input_osc_colour_reply(ictx, 4, c);
continue; continue;
} }
if ((c = input_osc_parse_colour(s)) == -1) { if ((c = colour_parseX11(s)) == -1) {
s = next; s = next;
continue; continue;
} }
@ -2611,6 +2571,47 @@ bad:
free(id); free(id);
} }
/*
* Get a client with a foreground for the pane. There isn't much to choose
* between them so just use the first.
*/
static int
input_get_fg_client(struct window_pane *wp)
{
struct window *w = wp->window;
struct client *loop;
TAILQ_FOREACH(loop, &clients, entry) {
if (loop->flags & CLIENT_UNATTACHEDFLAGS)
continue;
if (loop->session == NULL || !session_has(loop->session, w))
continue;
if (loop->tty.fg == -1)
continue;
return (loop->tty.fg);
}
return (-1);
}
/* Get a client with a background for the pane. */
static int
input_get_bg_client(struct window_pane *wp)
{
struct window *w = wp->window;
struct client *loop;
TAILQ_FOREACH(loop, &clients, entry) {
if (loop->flags & CLIENT_UNATTACHEDFLAGS)
continue;
if (loop->session == NULL || !session_has(loop->session, w))
continue;
if (loop->tty.bg == -1)
continue;
return (loop->tty.bg);
}
return (-1);
}
/* Handle the OSC 10 sequence for setting and querying foreground colour. */ /* Handle the OSC 10 sequence for setting and querying foreground colour. */
static void static void
input_osc_10(struct input_ctx *ictx, const char *p) input_osc_10(struct input_ctx *ictx, const char *p)
@ -2620,14 +2621,18 @@ input_osc_10(struct input_ctx *ictx, const char *p)
int c; int c;
if (strcmp(p, "?") == 0) { if (strcmp(p, "?") == 0) {
if (wp != NULL) { if (wp == NULL)
tty_default_colours(&defaults, wp); return;
input_osc_colour_reply(ictx, 10, defaults.fg); tty_default_colours(&defaults, wp);
} if (COLOUR_DEFAULT(defaults.fg))
c = input_get_fg_client(wp);
else
c = defaults.fg;
input_osc_colour_reply(ictx, 10, c);
return; return;
} }
if ((c = input_osc_parse_colour(p)) == -1) { if ((c = colour_parseX11(p)) == -1) {
log_debug("bad OSC 10: %s", p); log_debug("bad OSC 10: %s", p);
return; return;
} }
@ -2664,14 +2669,18 @@ input_osc_11(struct input_ctx *ictx, const char *p)
int c; int c;
if (strcmp(p, "?") == 0) { if (strcmp(p, "?") == 0) {
if (wp != NULL) { if (wp == NULL)
tty_default_colours(&defaults, wp); return;
input_osc_colour_reply(ictx, 11, defaults.bg); tty_default_colours(&defaults, wp);
} if (COLOUR_DEFAULT(defaults.bg))
c = input_get_bg_client(wp);
else
c = defaults.bg;
input_osc_colour_reply(ictx, 11, c);
return; return;
} }
if ((c = input_osc_parse_colour(p)) == -1) { if ((c = colour_parseX11(p)) == -1) {
log_debug("bad OSC 11: %s", p); log_debug("bad OSC 11: %s", p);
return; return;
} }
@ -2716,7 +2725,7 @@ input_osc_12(struct input_ctx *ictx, const char *p)
return; return;
} }
if ((c = input_osc_parse_colour(p)) == -1) { if ((c = colour_parseX11(p)) == -1) {
log_debug("bad OSC 12: %s", p); log_debug("bad OSC 12: %s", p);
return; return;
} }

View File

@ -377,7 +377,7 @@ key_bindings_init(void)
"bind -N 'Move to the previously active pane' \\; { last-pane }", "bind -N 'Move to the previously active pane' \\; { last-pane }",
"bind -N 'Choose a paste buffer from a list' = { choose-buffer -Z }", "bind -N 'Choose a paste buffer from a list' = { choose-buffer -Z }",
"bind -N 'List key bindings' ? { list-keys -N }", "bind -N 'List key bindings' ? { list-keys -N }",
"bind -N 'Choose a client from a list' D { choose-client -Z }", "bind -N 'Choose and detach a client from a list' D { choose-client -Z }",
"bind -N 'Spread panes out evenly' E { select-layout -E }", "bind -N 'Spread panes out evenly' E { select-layout -E }",
"bind -N 'Switch to the last client' L { switch-client -l }", "bind -N 'Switch to the last client' L { switch-client -l }",
"bind -N 'Clear the marked pane' M { select-pane -M }", "bind -N 'Clear the marked pane' M { select-pane -M }",

View File

@ -462,6 +462,8 @@ out:
strlcat(out, "B", sizeof out); strlcat(out, "B", sizeof out);
if (saved & KEYC_EXTENDED) if (saved & KEYC_EXTENDED)
strlcat(out, "E", sizeof out); strlcat(out, "E", sizeof out);
if (saved & KEYC_SENT)
strlcat(out, "S", sizeof out);
strlcat(out, "]", sizeof out); strlcat(out, "]", sizeof out);
} }
return (out); return (out);

View File

@ -162,8 +162,10 @@ layout_parse(struct window *w, const char *layout, char **cause)
u_short csum; u_short csum;
/* Check validity. */ /* Check validity. */
if (sscanf(layout, "%hx,", &csum) != 1) if (sscanf(layout, "%hx,", &csum) != 1) {
*cause = xstrdup("invalid layout");
return (-1); return (-1);
}
layout += 5; layout += 5;
if (csum != layout_checksum(layout)) { if (csum != layout_checksum(layout)) {
*cause = xstrdup("invalid layout"); *cause = xstrdup("invalid layout");

59
menu.c
View File

@ -64,6 +64,8 @@ menu_add_item(struct menu *menu, const struct menu_item *item,
line = (item == NULL || item->name == NULL || *item->name == '\0'); line = (item == NULL || item->name == NULL || *item->name == '\0');
if (line && menu->count == 0) if (line && menu->count == 0)
return; return;
if (line && menu->items[menu->count - 1].name == NULL)
return;
menu->items = xreallocarray(menu->items, menu->count + 1, menu->items = xreallocarray(menu->items, menu->count + 1,
sizeof *menu->items); sizeof *menu->items);
@ -427,12 +429,12 @@ chosen:
} }
struct menu_data * struct menu_data *
menu_prepare(struct menu *menu, int flags, struct cmdq_item *item, u_int px, menu_prepare(struct menu *menu, int flags, int starting_choice,
u_int py, struct client *c, struct cmd_find_state *fs, menu_choice_cb cb, struct cmdq_item *item, u_int px, u_int py, struct client *c,
void *data) struct cmd_find_state *fs, menu_choice_cb cb, void *data)
{ {
struct menu_data *md; struct menu_data *md;
u_int i; int choice;
const char *name; const char *name;
if (c->tty.sx < menu->width + 4 || c->tty.sy < menu->count + 2) if (c->tty.sx < menu->width + 4 || c->tty.sy < menu->count + 2)
@ -457,18 +459,38 @@ menu_prepare(struct menu *menu, int flags, struct cmdq_item *item, u_int px,
md->py = py; md->py = py;
md->menu = menu; md->menu = menu;
md->choice = -1;
if (md->flags & MENU_NOMOUSE) { if (md->flags & MENU_NOMOUSE) {
for (i = 0; i < menu->count; i++) { if (starting_choice >= (int)menu->count) {
name = menu->items[i].name; starting_choice = menu->count - 1;
if (name != NULL && *name != '-') choice = starting_choice + 1;
break; for (;;) {
name = menu->items[choice - 1].name;
if (name != NULL && *name != '-') {
md->choice = choice - 1;
break;
}
if (--choice == 0)
choice = menu->count;
if (choice == starting_choice + 1)
break;
}
} else if (starting_choice >= 0) {
choice = starting_choice;
for (;;) {
name = menu->items[choice].name;
if (name != NULL && *name != '-') {
md->choice = choice;
break;
}
if (++choice == (int)menu->count)
choice = 0;
if (choice == starting_choice)
break;
}
} }
if (i != menu->count) }
md->choice = i;
else
md->choice = -1;
} else
md->choice = -1;
md->cb = cb; md->cb = cb;
md->data = data; md->data = data;
@ -476,13 +498,14 @@ menu_prepare(struct menu *menu, int flags, struct cmdq_item *item, u_int px,
} }
int int
menu_display(struct menu *menu, int flags, struct cmdq_item *item, u_int px, menu_display(struct menu *menu, int flags, int starting_choice,
u_int py, struct client *c, struct cmd_find_state *fs, menu_choice_cb cb, struct cmdq_item *item, u_int px, u_int py, struct client *c,
void *data) struct cmd_find_state *fs, menu_choice_cb cb, void *data)
{ {
struct menu_data *md; struct menu_data *md;
md = menu_prepare(menu, flags, item, px, py, c, fs, cb, data); md = menu_prepare(menu, flags, starting_choice, item, px, py, c, fs, cb,
data);
if (md == NULL) if (md == NULL)
return (-1); return (-1);
server_client_set_overlay(c, 0, NULL, menu_mode_cb, menu_draw_cb, server_client_set_overlay(c, 0, NULL, menu_mode_cb, menu_draw_cb,

View File

@ -962,8 +962,8 @@ mode_tree_display_menu(struct mode_tree_data *mtd, struct client *c, u_int x,
x -= (menu->width + 4) / 2; x -= (menu->width + 4) / 2;
else else
x = 0; x = 0;
if (menu_display(menu, 0, NULL, x, y, c, NULL, mode_tree_menu_callback, if (menu_display(menu, 0, 0, NULL, x, y, c, NULL,
mtm) != 0) mode_tree_menu_callback, mtm) != 0)
menu_free(menu); menu_free(menu);
} }

View File

@ -240,11 +240,8 @@ paste_rename(const char *oldname, const char *newname, char **cause)
} }
pb_new = paste_get_name(newname); pb_new = paste_get_name(newname);
if (pb_new != NULL) { if (pb_new != NULL)
if (cause != NULL) paste_free(pb_new);
xasprintf(cause, "buffer %s already exists", newname);
return (-1);
}
RB_REMOVE(paste_name_tree, &paste_by_name, pb); RB_REMOVE(paste_name_tree, &paste_by_name, pb);

View File

@ -252,6 +252,7 @@ popup_draw_cb(struct client *c, void *data, struct screen_redraw_ctx *rctx)
tty_draw_line(tty, &s, 0, i, pd->sx, px, py + i, &defaults, tty_draw_line(tty, &s, 0, i, pd->sx, px, py + i, &defaults,
palette); palette);
} }
screen_free(&s);
if (pd->md != NULL) { if (pd->md != NULL) {
c->overlay_check = NULL; c->overlay_check = NULL;
c->overlay_data = NULL; c->overlay_data = NULL;
@ -573,7 +574,7 @@ menu:
x = m->x - (pd->menu->width + 4) / 2; x = m->x - (pd->menu->width + 4) / 2;
else else
x = 0; x = 0;
pd->md = menu_prepare(pd->menu, 0, NULL, x, m->y, c, NULL, pd->md = menu_prepare(pd->menu, 0, 0, NULL, x, m->y, c, NULL,
popup_menu_done, pd); popup_menu_done, pd);
c->flags |= CLIENT_REDRAWOVERLAY; c->flags |= CLIENT_REDRAWOVERLAY;

299
regress/input-keys.sh Normal file
View File

@ -0,0 +1,299 @@
#!/bin/sh
PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest"
$TMUX kill-server 2>/dev/null
$TMUX -f/dev/null new -x20 -y2 -d || exit 1
sleep 0.1
exit_status=0
assert_key () {
key=$1
expected_code=$2
$TMUX new-window -- sh -c 'stty raw -echo && cat -tv'
$TMUX send-keys "$key" $
actual_code=$($TMUX capturep -p | head -1 | sed -e 's/\$$//')
$TMUX kill-window
if [ "$actual_code" = "$expected_code" ]; then
if [ -n "$VERBOSE" ]; then
echo "[PASS] $key -> $actual_code"
fi
else
echo "[FAIL] $key -> $expected_code (Got: $actual_code)"
exit_status=1
fi
shift
shift
if [ "$1" = "--" ]; then
shift
assert_key "$@"
fi
}
assert_key 'C-Space' '^@'
assert_key 'C-a' '^A' -- 'M-C-a' '^[^A'
assert_key 'C-b' '^B' -- 'M-C-b' '^[^B'
assert_key 'C-c' '^C' -- 'M-C-c' '^[^C'
assert_key 'C-d' '^D' -- 'M-C-d' '^[^D'
assert_key 'C-e' '^E' -- 'M-C-e' '^[^E'
assert_key 'C-f' '^F' -- 'M-C-f' '^[^F'
assert_key 'C-g' '^G' -- 'M-C-g' '^[^G'
assert_key 'C-h' '^H' -- 'M-C-h' '^[^H'
assert_key 'C-i' '^I' -- 'M-C-i' '^[^I'
assert_key 'C-j' '' -- 'M-C-j' '^[' # NL
assert_key 'C-k' '^K' -- 'M-C-k' '^[^K'
assert_key 'C-l' '^L' -- 'M-C-l' '^[^L'
assert_key 'C-m' '^M' -- 'M-C-m' '^[^M'
assert_key 'C-n' '^N' -- 'M-C-n' '^[^N'
assert_key 'C-o' '^O' -- 'M-C-o' '^[^O'
assert_key 'C-p' '^P' -- 'M-C-p' '^[^P'
assert_key 'C-q' '^Q' -- 'M-C-q' '^[^Q'
assert_key 'C-r' '^R' -- 'M-C-r' '^[^R'
assert_key 'C-s' '^S' -- 'M-C-s' '^[^S'
assert_key 'C-t' '^T' -- 'M-C-t' '^[^T'
assert_key 'C-u' '^U' -- 'M-C-u' '^[^U'
assert_key 'C-v' '^V' -- 'M-C-v' '^[^V'
assert_key 'C-w' '^W' -- 'M-C-w' '^[^W'
assert_key 'C-x' '^X' -- 'M-C-x' '^[^X'
assert_key 'C-y' '^Y' -- 'M-C-y' '^[^Y'
assert_key 'C-z' '^Z' -- 'M-C-z' '^[^Z'
assert_key 'Escape' '^[' -- 'M-Escape' '^[^['
assert_key "C-\\" "^\\" -- "M-C-\\" "^[^\\"
assert_key 'C-]' '^]' -- 'M-C-]' '^[^]'
assert_key 'C-^' '^^' -- 'M-C-^' '^[^^'
assert_key 'C-_' '^_' -- 'M-C-_' '^[^_'
assert_key 'Space' ' ' -- 'M-Space' '^[ '
assert_key '!' '!' -- 'M-!' '^[!'
assert_key '"' '"' -- 'M-"' '^["'
assert_key '#' '#' -- 'M-#' '^[#'
assert_key '$' '$' -- 'M-$' '^[$'
assert_key '%' '%' -- 'M-%' '^[%'
assert_key '&' '&' -- 'M-&' '^[&'
assert_key "'" "'" -- "M-'" "^['"
assert_key '(' '(' -- 'M-(' '^[('
assert_key ')' ')' -- 'M-)' '^[)'
assert_key '*' '*' -- 'M-*' '^[*'
assert_key '+' '+' -- 'M-+' '^[+'
assert_key ',' ',' -- 'M-,' '^[,'
assert_key '-' '-' -- 'M--' '^[-'
assert_key '.' '.' -- 'M-.' '^[.'
assert_key '/' '/' -- 'M-/' '^[/'
assert_key '0' '0' -- 'M-0' '^[0'
assert_key '1' '1' -- 'M-1' '^[1'
assert_key '2' '2' -- 'M-2' '^[2'
assert_key '3' '3' -- 'M-3' '^[3'
assert_key '4' '4' -- 'M-4' '^[4'
assert_key '5' '5' -- 'M-5' '^[5'
assert_key '6' '6' -- 'M-6' '^[6'
assert_key '7' '7' -- 'M-7' '^[7'
assert_key '8' '8' -- 'M-8' '^[8'
assert_key '9' '9' -- 'M-9' '^[9'
assert_key ':' ':' -- 'M-:' '^[:'
assert_key '\;' ';' -- 'M-\;' '^[;'
assert_key '<' '<' -- 'M-<' '^[<'
assert_key '=' '=' -- 'M-=' '^[='
assert_key '>' '>' -- 'M->' '^[>'
assert_key '?' '?' -- 'M-?' '^[?'
assert_key '@' '@' -- 'M-@' '^[@'
assert_key 'A' 'A' -- 'M-A' '^[A'
assert_key 'B' 'B' -- 'M-B' '^[B'
assert_key 'C' 'C' -- 'M-C' '^[C'
assert_key 'D' 'D' -- 'M-D' '^[D'
assert_key 'E' 'E' -- 'M-E' '^[E'
assert_key 'F' 'F' -- 'M-F' '^[F'
assert_key 'G' 'G' -- 'M-G' '^[G'
assert_key 'H' 'H' -- 'M-H' '^[H'
assert_key 'I' 'I' -- 'M-I' '^[I'
assert_key 'J' 'J' -- 'M-J' '^[J'
assert_key 'K' 'K' -- 'M-K' '^[K'
assert_key 'L' 'L' -- 'M-L' '^[L'
assert_key 'M' 'M' -- 'M-M' '^[M'
assert_key 'N' 'N' -- 'M-N' '^[N'
assert_key 'O' 'O' -- 'M-O' '^[O'
assert_key 'P' 'P' -- 'M-P' '^[P'
assert_key 'Q' 'Q' -- 'M-Q' '^[Q'
assert_key 'R' 'R' -- 'M-R' '^[R'
assert_key 'S' 'S' -- 'M-S' '^[S'
assert_key 'T' 'T' -- 'M-T' '^[T'
assert_key 'U' 'U' -- 'M-U' '^[U'
assert_key 'V' 'V' -- 'M-V' '^[V'
assert_key 'W' 'W' -- 'M-W' '^[W'
assert_key 'X' 'X' -- 'M-X' '^[X'
assert_key 'Y' 'Y' -- 'M-Y' '^[Y'
assert_key 'Z' 'Z' -- 'M-Z' '^[Z'
assert_key '[' '[' -- 'M-[' '^[['
assert_key "\\" "\\" -- "M-\\" "^[\\"
assert_key ']' ']' -- 'M-]' '^[]'
assert_key '^' '^' -- 'M-^' '^[^'
assert_key '_' '_' -- 'M-_' '^[_'
assert_key '`' '`' -- 'M-`' '^[`'
assert_key 'a' 'a' -- 'M-a' '^[a'
assert_key 'b' 'b' -- 'M-b' '^[b'
assert_key 'c' 'c' -- 'M-c' '^[c'
assert_key 'd' 'd' -- 'M-d' '^[d'
assert_key 'e' 'e' -- 'M-e' '^[e'
assert_key 'f' 'f' -- 'M-f' '^[f'
assert_key 'g' 'g' -- 'M-g' '^[g'
assert_key 'h' 'h' -- 'M-h' '^[h'
assert_key 'i' 'i' -- 'M-i' '^[i'
assert_key 'j' 'j' -- 'M-j' '^[j'
assert_key 'k' 'k' -- 'M-k' '^[k'
assert_key 'l' 'l' -- 'M-l' '^[l'
assert_key 'm' 'm' -- 'M-m' '^[m'
assert_key 'n' 'n' -- 'M-n' '^[n'
assert_key 'o' 'o' -- 'M-o' '^[o'
assert_key 'p' 'p' -- 'M-p' '^[p'
assert_key 'q' 'q' -- 'M-q' '^[q'
assert_key 'r' 'r' -- 'M-r' '^[r'
assert_key 's' 's' -- 'M-s' '^[s'
assert_key 't' 't' -- 'M-t' '^[t'
assert_key 'u' 'u' -- 'M-u' '^[u'
assert_key 'v' 'v' -- 'M-v' '^[v'
assert_key 'w' 'w' -- 'M-w' '^[w'
assert_key 'x' 'x' -- 'M-x' '^[x'
assert_key 'y' 'y' -- 'M-y' '^[y'
assert_key 'z' 'z' -- 'M-z' '^[z'
assert_key '{' '{' -- 'M-{' '^[{'
assert_key '|' '|' -- 'M-|' '^[|'
assert_key '}' '}' -- 'M-}' '^[}'
assert_key '~' '~' -- 'M-~' '^[~'
assert_key 'Tab' '^I' -- 'M-Tab' '^[^I'
assert_key 'BSpace' '^?' -- 'M-BSpace' '^[^?'
## These cannot be sent, is that intentional?
## assert_key 'PasteStart' "^[[200~"
## assert_key 'PasteEnd' "^[[201~"
assert_key 'F1' "^[OP"
assert_key 'F2' "^[OQ"
assert_key 'F3' "^[OR"
assert_key 'F4' "^[OS"
assert_key 'F5' "^[[15~"
assert_key 'F6' "^[[17~"
assert_key 'F8' "^[[19~"
assert_key 'F9' "^[[20~"
assert_key 'F10' "^[[21~"
assert_key 'F11' "^[[23~"
assert_key 'F12' "^[[24~"
assert_key 'IC' '^[[2~'
assert_key 'Insert' '^[[2~'
assert_key 'DC' '^[[3~'
assert_key 'Delete' '^[[3~'
## Why do these differ from tty-keys?
assert_key 'Home' '^[[1~'
assert_key 'End' '^[[4~'
assert_key 'NPage' '^[[6~'
assert_key 'PageDown' '^[[6~'
assert_key 'PgDn' '^[[6~'
assert_key 'PPage' '^[[5~'
assert_key 'PageUp' '^[[5~'
assert_key 'PgUp' '^[[5~'
assert_key 'BTab' '^[[Z'
assert_key 'C-S-Tab' '^[[Z'
assert_key 'Up' '^[[A'
assert_key 'Down' '^[[B'
assert_key 'Right' '^[[C'
assert_key 'Left' '^[[D'
# assert_key 'KPEnter'
assert_key 'KP*' '*' -- 'M-KP*' '^[*'
assert_key 'KP+' '+' -- 'M-KP+' '^[+'
assert_key 'KP-' '-' -- 'M-KP-' '^[-'
assert_key 'KP.' '.' -- 'M-KP.' '^[.'
assert_key 'KP/' '/' -- 'M-KP/' '^[/'
assert_key 'KP0' '0' -- 'M-KP0' '^[0'
assert_key 'KP1' '1' -- 'M-KP1' '^[1'
assert_key 'KP2' '2' -- 'M-KP2' '^[2'
assert_key 'KP3' '3' -- 'M-KP3' '^[3'
assert_key 'KP4' '4' -- 'M-KP4' '^[4'
assert_key 'KP5' '5' -- 'M-KP5' '^[5'
assert_key 'KP6' '6' -- 'M-KP6' '^[6'
assert_key 'KP7' '7' -- 'M-KP7' '^[7'
assert_key 'KP8' '8' -- 'M-KP8' '^[8'
assert_key 'KP9' '9' -- 'M-KP9' '^[9'
# Extended keys
$TMUX set -g extended-keys always
assert_extended_key () {
extended_key=$1
expected_code_pattern=$2
expected_code=$(printf '%s' "$expected_code_pattern" | sed -e 's/;_/;2/')
assert_key "S-$extended_key" "$expected_code"
expected_code=$(printf '%s' "$expected_code_pattern" | sed -e 's/;_/;3/')
assert_key "M-$extended_key" "$expected_code"
expected_code=$(printf '%s' "$expected_code_pattern" | sed -e 's/;_/;4/')
assert_key "S-M-$extended_key" "$expected_code"
expected_code=$(printf '%s' "$expected_code_pattern" | sed -e 's/;_/;5/')
assert_key "C-$extended_key" "$expected_code"
expected_code=$(printf '%s' "$expected_code_pattern" | sed -e 's/;_/;6/')
assert_key "S-C-$extended_key" "$expected_code"
expected_code=$(printf '%s' "$expected_code_pattern" | sed -e 's/;_/;7/')
assert_key "C-M-$extended_key" "$expected_code"
expected_code=$(printf '%s' "$expected_code_pattern" | sed -e 's/;_/;8/')
assert_key "S-C-M-$extended_key" "$expected_code"
}
## Many of these pass without extended keys enabled -- are they extended keys?
assert_extended_key 'F1' '^[[1;_P'
assert_extended_key 'F2' "^[[1;_Q"
assert_extended_key 'F3' "^[[1;_R"
assert_extended_key 'F4' "^[[1;_S"
assert_extended_key 'F5' "^[[15;_~"
assert_extended_key 'F6' "^[[17;_~"
assert_extended_key 'F8' "^[[19;_~"
assert_extended_key 'F9' "^[[20;_~"
assert_extended_key 'F10' "^[[21;_~"
assert_extended_key 'F11' "^[[23;_~"
assert_extended_key 'F12' "^[[24;_~"
assert_extended_key 'Up' '^[[1;_A'
assert_extended_key 'Down' '^[[1;_B'
assert_extended_key 'Right' '^[[1;_C'
assert_extended_key 'Left' '^[[1;_D'
assert_extended_key 'Home' '^[[1;_H'
assert_extended_key 'End' '^[[1;_F'
assert_extended_key 'PPage' '^[[5;_~'
assert_extended_key 'PageUp' '^[[5;_~'
assert_extended_key 'PgUp' '^[[5;_~'
assert_extended_key 'NPage' '^[[6;_~'
assert_extended_key 'PageDown' '^[[6;_~'
assert_extended_key 'PgDn' '^[[6;_~'
assert_extended_key 'IC' '^[[2;_~'
assert_extended_key 'Insert' '^[[2;_~'
assert_extended_key 'DC' '^[[3;_~'
assert_extended_key 'Delete' '^[[3;_~'
assert_key 'C-Tab' "^[[9;5u"
assert_key 'C-S-Tab' "^[[1;5Z"
$TMUX kill-server 2>/dev/null
exit $exit_status

361
regress/tty-keys.sh Normal file
View File

@ -0,0 +1,361 @@
#!/bin/sh
PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest"
$TMUX kill-server 2>/dev/null
TMUX2="$TEST_TMUX -Ltest2"
$TMUX2 kill-server 2>/dev/null
TMP=$(mktemp)
trap "rm -f $TMP" 0 1 15
$TMUX2 -f/dev/null new -d || exit 1
$TMUX -f/dev/null new -d "$TMUX2 attach" || exit 1
sleep 0.1
exit_status=0
format_string () {
case $1 in
*\')
printf '"%%%%"'
;;
*)
printf "'%%%%'"
;;
esac
}
assert_key () {
keys=$1
expected_name=$2
format_string=$(format_string "$expected_name")
$TMUX2 command-prompt -k 'display-message -pl '"$format_string" > "$TMP" &
sleep 0.05
$TMUX send-keys $keys
wait
keys=$(printf '%s' "$keys" | sed -e 's/Escape/\\\\033/g' | tr -d '[:space:]')
actual_name=$(tr -d '[:space:]' < "$TMP")
if [ "$actual_name" = "$expected_name" ]; then
if [ -n "$VERBOSE" ]; then
echo "[PASS] $keys -> $actual_name"
fi
else
echo "[FAIL] $keys -> $expected_name (Got: '$actual_name')"
exit_status=1
fi
if [ "$3" = "--" ]; then
shift; shift; shift
assert_key "$@"
fi
}
assert_key 0x00 'C-Space' # -- 'Escape 0x00' 'M-C-Space'
assert_key 0x01 'C-a' -- 'Escape 0x01' 'M-C-a'
assert_key 0x02 'C-b' -- 'Escape 0x02' 'M-C-b'
assert_key 0x03 'C-c' -- 'Escape 0x03' 'M-C-c'
assert_key 0x04 'C-d' -- 'Escape 0x04' 'M-C-d'
assert_key 0x05 'C-e' -- 'Escape 0x05' 'M-C-e'
assert_key 0x06 'C-f' -- 'Escape 0x06' 'M-C-f'
assert_key 0x07 'C-g' -- 'Escape 0x07' 'M-C-g'
assert_key 0x08 'C-h' -- 'Escape 0x08' 'M-C-h'
assert_key 0x09 'Tab' -- 'Escape 0x09' 'M-Tab'
assert_key 0x0A 'C-j' -- 'Escape 0x0A' 'M-C-j'
assert_key 0x0B 'C-k' -- 'Escape 0x0B' 'M-C-k'
assert_key 0x0C 'C-l' -- 'Escape 0x0C' 'M-C-l'
assert_key 0x0D 'Enter' -- 'Escape 0x0D' 'M-Enter'
assert_key 0x0E 'C-n' -- 'Escape 0x0E' 'M-C-n'
assert_key 0x0F 'C-o' -- 'Escape 0x0F' 'M-C-o'
assert_key 0x10 'C-p' -- 'Escape 0x10' 'M-C-p'
assert_key 0x11 'C-q' -- 'Escape 0x11' 'M-C-q'
assert_key 0x12 'C-r' -- 'Escape 0x12' 'M-C-r'
assert_key 0x13 'C-s' -- 'Escape 0x13' 'M-C-s'
assert_key 0x14 'C-t' -- 'Escape 0x14' 'M-C-t'
assert_key 0x15 'C-u' -- 'Escape 0x15' 'M-C-u'
assert_key 0x16 'C-v' -- 'Escape 0x16' 'M-C-v'
assert_key 0x17 'C-w' -- 'Escape 0x17' 'M-C-w'
assert_key 0x18 'C-x' -- 'Escape 0x18' 'M-C-x'
assert_key 0x19 'C-y' -- 'Escape 0x19' 'M-C-y'
assert_key 0x1A 'C-z' -- 'Escape 0x1A' 'M-C-z'
assert_key 0x1B 'Escape' -- 'Escape 0x1B' 'M-Escape'
assert_key 0x1C "C-\\" -- 'Escape 0x1C' "M-C-\\"
assert_key 0x1D 'C-]' -- 'Escape 0x1D' 'M-C-]'
assert_key 0x1E 'C-^' -- 'Escape 0x1E' 'M-C-^'
assert_key 0x1F 'C-_' -- 'Escape 0x1F' 'M-C-_'
assert_key 0x20 'Space' -- 'Escape 0x20' 'M-Space'
assert_key 0x21 '!' -- 'Escape 0x21' 'M-!'
assert_key 0x22 '"' -- 'Escape 0x22' 'M-"'
assert_key 0x23 '#' -- 'Escape 0x23'= 'M-#'
assert_key 0x24 '$' -- 'Escape 0x24'= 'M-$'
assert_key 0x25 '%' -- 'Escape 0x25'= 'M-%'
assert_key 0x26 '&' -- 'Escape 0x26'= 'M-&'
assert_key 0x27 "'" -- 'Escape 0x27' "M-'"
assert_key 0x28 '(' -- 'Escape 0x28' 'M-('
assert_key 0x29 ')' -- 'Escape 0x29' 'M-)'
assert_key 0x2A '*' -- 'Escape 0x2A' 'M-*'
assert_key 0x2B '+' -- 'Escape 0x2B' 'M-+'
assert_key 0x2C ',' -- 'Escape 0x2C' 'M-,'
assert_key 0x2D '-' -- 'Escape 0x2D' 'M--'
assert_key 0x2E '.' -- 'Escape 0x2E' 'M-.'
assert_key 0x2F '/' -- 'Escape 0x2F' 'M-/'
assert_key 0x30 '0' -- 'Escape 0x30' 'M-0'
assert_key 0x31 '1' -- 'Escape 0x31' 'M-1'
assert_key 0x32 '2' -- 'Escape 0x32' 'M-2'
assert_key 0x33 '3' -- 'Escape 0x33' 'M-3'
assert_key 0x34 '4' -- 'Escape 0x34' 'M-4'
assert_key 0x35 '5' -- 'Escape 0x35' 'M-5'
assert_key 0x36 '6' -- 'Escape 0x36' 'M-6'
assert_key 0x37 '7' -- 'Escape 0x37' 'M-7'
assert_key 0x38 '8' -- 'Escape 0x38' 'M-8'
assert_key 0x39 '9' -- 'Escape 0x39' 'M-9'
assert_key 0x3A ':' -- 'Escape 0x3A' 'M-:'
assert_key 0x3B ';' -- 'Escape 0x3B' 'M-;'
assert_key 0x3C '<' -- 'Escape 0x3C' 'M-<'
assert_key 0x3D '=' -- 'Escape 0x3D' 'M-='
assert_key 0x3E '>' -- 'Escape 0x3E' 'M->'
assert_key 0x3F '?' -- 'Escape 0x3F' 'M-?'
assert_key 0x40 '@' -- 'Escape 0x40' 'M-@'
assert_key 0x41 'A' -- 'Escape 0x41' 'M-A'
assert_key 0x42 'B' -- 'Escape 0x42' 'M-B'
assert_key 0x43 'C' -- 'Escape 0x43' 'M-C'
assert_key 0x44 'D' -- 'Escape 0x44' 'M-D'
assert_key 0x45 'E' -- 'Escape 0x45' 'M-E'
assert_key 0x46 'F' -- 'Escape 0x46' 'M-F'
assert_key 0x47 'G' -- 'Escape 0x47' 'M-G'
assert_key 0x48 'H' -- 'Escape 0x48' 'M-H'
assert_key 0x49 'I' -- 'Escape 0x49' 'M-I'
assert_key 0x4A 'J' -- 'Escape 0x4A' 'M-J'
assert_key 0x4B 'K' -- 'Escape 0x4B' 'M-K'
assert_key 0x4C 'L' -- 'Escape 0x4C' 'M-L'
assert_key 0x4D 'M' -- 'Escape 0x4D' 'M-M'
assert_key 0x4E 'N' -- 'Escape 0x4E' 'M-N'
assert_key 0x4F 'O' -- 'Escape 0x4F' 'M-O'
assert_key 0x50 'P' -- 'Escape 0x50' 'M-P'
assert_key 0x51 'Q' -- 'Escape 0x51' 'M-Q'
assert_key 0x52 'R' -- 'Escape 0x52' 'M-R'
assert_key 0x53 'S' -- 'Escape 0x53' 'M-S'
assert_key 0x54 'T' -- 'Escape 0x54' 'M-T'
assert_key 0x55 'U' -- 'Escape 0x55' 'M-U'
assert_key 0x56 'V' -- 'Escape 0x56' 'M-V'
assert_key 0x57 'W' -- 'Escape 0x57' 'M-W'
assert_key 0x58 'X' -- 'Escape 0x58' 'M-X'
assert_key 0x59 'Y' -- 'Escape 0x59' 'M-Y'
assert_key 0x5A 'Z' -- 'Escape 0x5A' 'M-Z'
assert_key 0x5B '[' -- 'Escape 0x5B' 'M-['
assert_key 0x5C "\\" -- 'Escape 0x5C' "M-\\"
assert_key 0x5D ']' -- 'Escape 0x5D' 'M-]'
assert_key 0x5E '^' -- 'Escape 0x5E' 'M-^'
assert_key 0x5F '_' -- 'Escape 0x5F' 'M-_'
assert_key 0x60 '`' -- 'Escape 0x60' 'M-`'
assert_key 0x61 'a' -- 'Escape 0x61' 'M-a'
assert_key 0x62 'b' -- 'Escape 0x62' 'M-b'
assert_key 0x63 'c' -- 'Escape 0x63' 'M-c'
assert_key 0x64 'd' -- 'Escape 0x64' 'M-d'
assert_key 0x65 'e' -- 'Escape 0x65' 'M-e'
assert_key 0x66 'f' -- 'Escape 0x66' 'M-f'
assert_key 0x67 'g' -- 'Escape 0x67' 'M-g'
assert_key 0x68 'h' -- 'Escape 0x68' 'M-h'
assert_key 0x69 'i' -- 'Escape 0x69' 'M-i'
assert_key 0x6A 'j' -- 'Escape 0x6A' 'M-j'
assert_key 0x6B 'k' -- 'Escape 0x6B' 'M-k'
assert_key 0x6C 'l' -- 'Escape 0x6C' 'M-l'
assert_key 0x6D 'm' -- 'Escape 0x6D' 'M-m'
assert_key 0x6E 'n' -- 'Escape 0x6E' 'M-n'
assert_key 0x6F 'o' -- 'Escape 0x6F' 'M-o'
assert_key 0x70 'p' -- 'Escape 0x70' 'M-p'
assert_key 0x71 'q' -- 'Escape 0x71' 'M-q'
assert_key 0x72 'r' -- 'Escape 0x72' 'M-r'
assert_key 0x73 's' -- 'Escape 0x73' 'M-s'
assert_key 0x74 't' -- 'Escape 0x74' 'M-t'
assert_key 0x75 'u' -- 'Escape 0x75' 'M-u'
assert_key 0x76 'v' -- 'Escape 0x76' 'M-v'
assert_key 0x77 'w' -- 'Escape 0x77' 'M-w'
assert_key 0x78 'x' -- 'Escape 0x78' 'M-x'
assert_key 0x79 'y' -- 'Escape 0x79' 'M-y'
assert_key 0x7A 'z' -- 'Escape 0x7A' 'M-z'
assert_key 0x7B '{' -- 'Escape 0x7B' 'M-{'
assert_key 0x7C '|' -- 'Escape 0x7C' 'M-|'
assert_key 0x7D '}' -- 'Escape 0x7D' 'M-}'
assert_key 0x7E '~' -- 'Escape 0x7E' 'M-~'
assert_key 0x7F 'BSpace' -- 'Escape 0x7F' 'M-BSpace'
# Numeric keypad
assert_key 'Escape OM' 'KPEnter' -- 'Escape Escape OM' 'M-KPEnter'
assert_key 'Escape Oj' 'KP*' -- 'Escape Escape Oj' 'M-KP*'
assert_key 'Escape Ok' 'KP+' -- 'Escape Escape Ok' 'M-KP+'
assert_key 'Escape Om' 'KP-' -- 'Escape Escape Om' 'M-KP-'
assert_key 'Escape On' 'KP.' -- 'Escape Escape On' 'M-KP.'
assert_key 'Escape Oo' 'KP/' -- 'Escape Escape Oo' 'M-KP/'
assert_key 'Escape Op' 'KP0' -- 'Escape Escape Op' 'M-KP0'
assert_key 'Escape Oq' 'KP1' -- 'Escape Escape Oq' 'M-KP1'
assert_key 'Escape Or' 'KP2' -- 'Escape Escape Or' 'M-KP2'
assert_key 'Escape Os' 'KP3' -- 'Escape Escape Os' 'M-KP3'
assert_key 'Escape Ot' 'KP4' -- 'Escape Escape Ot' 'M-KP4'
assert_key 'Escape Ou' 'KP5' -- 'Escape Escape Ou' 'M-KP5'
assert_key 'Escape Ov' 'KP6' -- 'Escape Escape Ov' 'M-KP6'
assert_key 'Escape Ow' 'KP7' -- 'Escape Escape Ow' 'M-KP7'
assert_key 'Escape Ox' 'KP8' -- 'Escape Escape Ox' 'M-KP8'
assert_key 'Escape Oy' 'KP9' -- 'Escape Escape Oy' 'M-KP9'
# Arrow keys
assert_key 'Escape OA' 'Up' -- 'Escape Escape OA' 'M-Up'
assert_key 'Escape OB' 'Down' -- 'Escape Escape OB' 'M-Down'
assert_key 'Escape OC' 'Right' -- 'Escape Escape OC' 'M-Right'
assert_key 'Escape OD' 'Left' -- 'Escape Escape OD' 'M-Left'
assert_key 'Escape [A' 'Up' -- 'Escape Escape [A' 'M-Up'
assert_key 'Escape [B' 'Down' -- 'Escape Escape [B' 'M-Down'
assert_key 'Escape [C' 'Right' -- 'Escape Escape [C' 'M-Right'
assert_key 'Escape [D' 'Left' -- 'Escape Escape [D' 'M-Left'
# Other xterm keys
assert_key 'Escape OH' 'Home' -- 'Escape Escape OH' 'M-Home'
assert_key 'Escape OF' 'End' -- 'Escape Escape OF' 'M-End'
assert_key 'Escape [H' 'Home' -- 'Escape Escape [H' 'M-Home'
assert_key 'Escape [F' 'End' -- 'Escape Escape [F' 'M-End'
# rxvt arrow keys
assert_key 'Escape Oa' 'C-Up'
assert_key 'Escape Ob' 'C-Down'
assert_key 'Escape Oc' 'C-Right'
assert_key 'Escape Od' 'C-Left'
assert_key 'Escape [a' 'S-Up'
assert_key 'Escape [b' 'S-Down'
assert_key 'Escape [c' 'S-Right'
assert_key 'Escape [d' 'S-Left'
# rxvt function keys
assert_key 'Escape [11~' 'F1'
assert_key 'Escape [12~' 'F2'
assert_key 'Escape [13~' 'F3'
assert_key 'Escape [14~' 'F4'
assert_key 'Escape [15~' 'F5'
assert_key 'Escape [17~' 'F6'
assert_key 'Escape [18~' 'F7'
assert_key 'Escape [19~' 'F8'
assert_key 'Escape [20~' 'F9'
assert_key 'Escape [21~' 'F10'
assert_key 'Escape [23~' 'F11'
assert_key 'Escape [24~' 'F12'
# With TERM=screen, these will be seen as F11 and F12
# assert_key 'Escape [23~' 'S-F1'
# assert_key 'Escape [24~' 'S-F2'
assert_key 'Escape [25~' 'S-F3'
assert_key 'Escape [26~' 'S-F4'
assert_key 'Escape [28~' 'S-F5'
assert_key 'Escape [29~' 'S-F6'
assert_key 'Escape [31~' 'S-F7'
assert_key 'Escape [32~' 'S-F8'
assert_key 'Escape [33~' 'S-F9'
assert_key 'Escape [34~' 'S-F10'
assert_key 'Escape [23$' 'S-F11'
assert_key 'Escape [24$' 'S-F12'
assert_key 'Escape [11^' 'C-F1'
assert_key 'Escape [12^' 'C-F2'
assert_key 'Escape [13^' 'C-F3'
assert_key 'Escape [14^' 'C-F4'
assert_key 'Escape [15^' 'C-F5'
assert_key 'Escape [17^' 'C-F6'
assert_key 'Escape [18^' 'C-F7'
assert_key 'Escape [19^' 'C-F8'
assert_key 'Escape [20^' 'C-F9'
assert_key 'Escape [21^' 'C-F10'
assert_key 'Escape [23^' 'C-F11'
assert_key 'Escape [24^' 'C-F12'
assert_key 'Escape [11@' 'C-S-F1'
assert_key 'Escape [12@' 'C-S-F2'
assert_key 'Escape [13@' 'C-S-F3'
assert_key 'Escape [14@' 'C-S-F4'
assert_key 'Escape [15@' 'C-S-F5'
assert_key 'Escape [17@' 'C-S-F6'
assert_key 'Escape [18@' 'C-S-F7'
assert_key 'Escape [19@' 'C-S-F8'
assert_key 'Escape [20@' 'C-S-F9'
assert_key 'Escape [21@' 'C-S-F10'
assert_key 'Escape [23@' 'C-S-F11'
assert_key 'Escape [24@' 'C-S-F12'
# Focus tracking
assert_key 'Escape [I' 'FocusIn'
assert_key 'Escape [O' 'FocusOut'
# Paste keys
assert_key 'Escape [200~' 'PasteStart'
assert_key 'Escape [201~' 'PasteEnd'
assert_key 'Escape [Z' 'BTab'
assert_extended_key () {
code=$1
key_name=$2
assert_key "Escape [${code};5u" "C-$key_name"
assert_key "Escape [${code};7u" "M-C-$key_name"
}
# Extended keys
# assert_extended_key 65 'A'
# assert_extended_key 66 'B'
# assert_extended_key 67 'C'
# assert_extended_key 68 'D'
# assert_extended_key 69 'E'
# assert_extended_key 70 'F'
# assert_extended_key 71 'G'
# assert_extended_key 72 'H'
# assert_extended_key 73 'I'
# assert_extended_key 74 'J'
# assert_extended_key 75 'K'
# assert_extended_key 76 'L'
# assert_extended_key 77 'M'
# assert_extended_key 78 'N'
# assert_extended_key 79 'O'
# assert_extended_key 80 'P'
# assert_extended_key 81 'Q'
# assert_extended_key 82 'R'
# assert_extended_key 83 'S'
# assert_extended_key 84 'T'
# assert_extended_key 85 'U'
# assert_extended_key 86 'V'
# assert_extended_key 87 'W'
# assert_extended_key 88 'X'
# assert_extended_key 89 'Y'
# assert_extended_key 90 'Z'
# assert_extended_key 123 '{'
# assert_extended_key 124 '|'
# assert_extended_key 125 '}'
# assert_key 'Escape [105;5u' 'C-i'
# assert_key 'Escape [73;5u' 'C-I'
# assert_key 'Escape [109;5u' 'C-m'
# assert_key 'Escape [77;5u' 'C-M'
# assert_key 'Escape [91;5u' 'C-['
assert_key 'Escape [123;5u' 'C-{'
# assert_key 'Escape [64;5u' 'C-@'
assert_key 'Escape [32;2u' 'S-Space'
# assert_key 'Escape [32;6u' 'C-S-Space'
assert_key 'Escape [9;5u' 'C-Tab'
assert_key 'Escape [1;5Z' 'C-S-Tab'
$TMUX kill-server 2>/dev/null
$TMUX2 kill-server 2>/dev/null
exit $exit_status

View File

@ -34,7 +34,7 @@ static void screen_write_collect_flush(struct screen_write_ctx *, int,
static int screen_write_overwrite(struct screen_write_ctx *, static int screen_write_overwrite(struct screen_write_ctx *,
struct grid_cell *, u_int); struct grid_cell *, u_int);
static const struct grid_cell *screen_write_combine(struct screen_write_ctx *, static const struct grid_cell *screen_write_combine(struct screen_write_ctx *,
const struct utf8_data *, u_int *); const struct utf8_data *, u_int *, u_int *);
struct screen_write_citem { struct screen_write_citem {
u_int x; u_int x;
@ -1905,13 +1905,13 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
*/ */
if (ctx->flags & SCREEN_WRITE_ZWJ) { if (ctx->flags & SCREEN_WRITE_ZWJ) {
screen_write_collect_flush(ctx, 0, __func__); screen_write_collect_flush(ctx, 0, __func__);
screen_write_combine(ctx, &zwj, &xx); screen_write_combine(ctx, &zwj, &xx, &cx);
} }
if (width == 0 || (ctx->flags & SCREEN_WRITE_ZWJ)) { if (width == 0 || (ctx->flags & SCREEN_WRITE_ZWJ)) {
ctx->flags &= ~SCREEN_WRITE_ZWJ; ctx->flags &= ~SCREEN_WRITE_ZWJ;
screen_write_collect_flush(ctx, 0, __func__); screen_write_collect_flush(ctx, 0, __func__);
if ((gc = screen_write_combine(ctx, ud, &xx)) != NULL) { if ((gc = screen_write_combine(ctx, ud, &xx, &cx)) != NULL) {
cx = s->cx; cy = s->cy; cy = s->cy;
screen_write_set_cursor(ctx, xx, s->cy); screen_write_set_cursor(ctx, xx, s->cy);
screen_write_initctx(ctx, &ttyctx, 0); screen_write_initctx(ctx, &ttyctx, 0);
ttyctx.cell = gc; ttyctx.cell = gc;
@ -2038,16 +2038,19 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
/* Combine a UTF-8 zero-width character onto the previous. */ /* Combine a UTF-8 zero-width character onto the previous. */
static const struct grid_cell * static const struct grid_cell *
screen_write_combine(struct screen_write_ctx *ctx, const struct utf8_data *ud, screen_write_combine(struct screen_write_ctx *ctx, const struct utf8_data *ud,
u_int *xx) u_int *xx, u_int *cx)
{ {
struct screen *s = ctx->s; struct screen *s = ctx->s;
struct grid *gd = s->grid; struct grid *gd = s->grid;
static struct grid_cell gc; static struct grid_cell gc;
u_int n; u_int n, width;
/* Can't combine if at 0. */ /* Can't combine if at 0. */
if (s->cx == 0) if (s->cx == 0) {
*xx = 0;
return (NULL); return (NULL);
}
*xx = s->cx;
/* Empty data is out. */ /* Empty data is out. */
if (ud->size == 0) if (ud->size == 0)
@ -2061,22 +2064,35 @@ screen_write_combine(struct screen_write_ctx *ctx, const struct utf8_data *ud,
} }
if (n > s->cx) if (n > s->cx)
return (NULL); return (NULL);
*xx = s->cx - n;
/* Check there is enough space. */ /* Check there is enough space. */
if (gc.data.size + ud->size > sizeof gc.data.data) if (gc.data.size + ud->size > sizeof gc.data.data)
return (NULL); return (NULL);
(*xx) -= n;
log_debug("%s: %.*s onto %.*s at %u,%u", __func__, (int)ud->size, log_debug("%s: %.*s onto %.*s at %u,%u (width %u)", __func__,
ud->data, (int)gc.data.size, gc.data.data, *xx, s->cy); (int)ud->size, ud->data, (int)gc.data.size, gc.data.data, *xx,
s->cy, gc.data.width);
/* Append the data. */ /* Append the data. */
memcpy(gc.data.data + gc.data.size, ud->data, ud->size); memcpy(gc.data.data + gc.data.size, ud->data, ud->size);
gc.data.size += ud->size; gc.data.size += ud->size;
width = gc.data.width;
/* If this is U+FE0F VARIATION SELECTOR-16, force the width to 2. */
if (gc.data.width == 1 &&
ud->size == 3 &&
memcmp(ud->data, "\357\270\217", 3) == 0) {
grid_view_set_padding(gd, (*xx) + 1, s->cy);
gc.data.width = 2;
width += 2;
}
/* Set the new cell. */ /* Set the new cell. */
grid_view_set_cell(gd, *xx, s->cy, &gc); grid_view_set_cell(gd, *xx, s->cy, &gc);
*cx = (*xx) + width;
log_debug("%s: character at %u; cursor at %u", __func__, *xx, *cx);
return (&gc); return (&gc);
} }

View File

@ -42,6 +42,7 @@ static void server_client_check_modes(struct client *);
static void server_client_set_title(struct client *); static void server_client_set_title(struct client *);
static void server_client_set_path(struct client *); static void server_client_set_path(struct client *);
static void server_client_reset_state(struct client *); static void server_client_reset_state(struct client *);
static int server_client_is_bracket_pasting(struct client *, key_code);
static int server_client_assume_paste(struct session *); static int server_client_assume_paste(struct session *);
static void server_client_update_latest(struct client *); static void server_client_update_latest(struct client *);
@ -1754,6 +1755,25 @@ out:
return (key); return (key);
} }
/* Is this a bracket paste key? */
static int
server_client_is_bracket_pasting(struct client *c, key_code key)
{
if (key == KEYC_PASTE_START) {
c->flags |= CLIENT_BRACKETPASTING;
log_debug("%s: bracket paste on", c->name);
return (1);
}
if (key == KEYC_PASTE_END) {
c->flags &= ~CLIENT_BRACKETPASTING;
log_debug("%s: bracket paste off", c->name);
return (1);
}
return !!(c->flags & CLIENT_BRACKETPASTING);
}
/* Is this fast enough to probably be a paste? */ /* Is this fast enough to probably be a paste? */
static int static int
server_client_assume_paste(struct session *s) server_client_assume_paste(struct session *s)
@ -1862,8 +1882,14 @@ server_client_key_callback(struct cmdq_item *item, void *data)
if (KEYC_IS_MOUSE(key) && !options_get_number(s->options, "mouse")) if (KEYC_IS_MOUSE(key) && !options_get_number(s->options, "mouse"))
goto forward_key; goto forward_key;
/* Forward if bracket pasting. */
if (server_client_is_bracket_pasting(c, key))
goto forward_key;
/* Treat everything as a regular key when pasting is detected. */ /* Treat everything as a regular key when pasting is detected. */
if (!KEYC_IS_MOUSE(key) && server_client_assume_paste(s)) if (!KEYC_IS_MOUSE(key) &&
(~key & KEYC_SENT) &&
server_client_assume_paste(s))
goto forward_key; goto forward_key;
/* /*
@ -3214,3 +3240,69 @@ server_client_remove_pane(struct window_pane *wp)
} }
} }
} }
/* Print to a client. */
void
server_client_print(struct client *c, int parse, struct evbuffer *evb)
{
void *data = EVBUFFER_DATA(evb);
size_t size = EVBUFFER_LENGTH(evb);
struct window_pane *wp;
struct window_mode_entry *wme;
char *sanitized, *msg, *line;
if (!parse) {
utf8_stravisx(&msg, data, size,
VIS_OCTAL|VIS_CSTYLE|VIS_NOSLASH);
log_debug("%s: %s", __func__, msg);
} else {
msg = EVBUFFER_DATA(evb);
if (msg[size - 1] != '\0')
evbuffer_add(evb, "", 1);
}
if (c == NULL)
goto out;
if (c->session == NULL || (c->flags & CLIENT_CONTROL)) {
if (~c->flags & CLIENT_UTF8) {
sanitized = utf8_sanitize(msg);
if (c->flags & CLIENT_CONTROL)
control_write(c, "%s", sanitized);
else
file_print(c, "%s\n", sanitized);
free(sanitized);
} else {
if (c->flags & CLIENT_CONTROL)
control_write(c, "%s", msg);
else
file_print(c, "%s\n", msg);
}
goto out;
}
wp = server_client_get_pane(c);
wme = TAILQ_FIRST(&wp->modes);
if (wme == NULL || wme->mode != &window_view_mode)
window_pane_set_mode(wp, NULL, &window_view_mode, NULL, NULL);
if (parse) {
do {
line = evbuffer_readln(evb, NULL, EVBUFFER_EOL_LF);
if (line != NULL) {
window_copy_add(wp, 1, "%s", line);
free(line);
}
} while (line != NULL);
size = EVBUFFER_LENGTH(evb);
if (size != 0) {
line = EVBUFFER_DATA(evb);
window_copy_add(wp, 1, "%.*s", (int)size, line);
}
} else
window_copy_add(wp, 0, "%s", msg);
out:
if (!parse)
free(msg);
}

View File

@ -1766,7 +1766,7 @@ status_prompt_complete_list_menu(struct client *c, char **list, u_int size,
else else
offset = 0; offset = 0;
if (menu_display(menu, MENU_NOMOUSE|MENU_TAB, NULL, offset, if (menu_display(menu, MENU_NOMOUSE|MENU_TAB, 0, NULL, offset,
py, c, NULL, status_prompt_menu_callback, spm) != 0) { py, c, NULL, status_prompt_menu_callback, spm) != 0) {
menu_free(menu); menu_free(menu);
free(spm); free(spm);
@ -1859,7 +1859,7 @@ status_prompt_complete_window_menu(struct client *c, struct session *s,
else else
offset = 0; offset = 0;
if (menu_display(menu, MENU_NOMOUSE|MENU_TAB, NULL, offset, if (menu_display(menu, MENU_NOMOUSE|MENU_TAB, 0, NULL, offset,
py, c, NULL, status_prompt_menu_callback, spm) != 0) { py, c, NULL, status_prompt_menu_callback, spm) != 0) {
menu_free(menu); menu_free(menu);
free(spm); free(spm);

View File

@ -66,7 +66,8 @@ enum msgtype {
MSG_WRITE_OPEN, MSG_WRITE_OPEN,
MSG_WRITE, MSG_WRITE,
MSG_WRITE_READY, MSG_WRITE_READY,
MSG_WRITE_CLOSE MSG_WRITE_CLOSE,
MSG_READ_CANCEL
}; };
/* /*
@ -92,6 +93,10 @@ struct msg_read_done {
int error; int error;
}; };
struct msg_read_cancel {
int stream;
};
struct msg_write_open { struct msg_write_open {
int stream; int stream;
int fd; int fd;

120
tmux.1
View File

@ -469,7 +469,8 @@ and
.Ic confirm-before , .Ic confirm-before ,
parse their argument to create a new command which is inserted immediately parse their argument to create a new command which is inserted immediately
after themselves. after themselves.
This means that arguments can be parsed twice or more - once when the parent command (such as This means that arguments can be parsed twice or more - once when the parent
command (such as
.Ic if-shell ) .Ic if-shell )
is parsed and again when it parses and executes its command. is parsed and again when it parses and executes its command.
Commands like Commands like
@ -545,7 +546,7 @@ for example in these
.Xr sh 1 .Xr sh 1
commands: commands:
.Bd -literal -offset indent .Bd -literal -offset indent
$ tmux neww\e\e; splitw $ tmux neww\e; splitw
.Ed .Ed
.Pp .Pp
Or: Or:
@ -887,8 +888,8 @@ may consist entirely of the token
.Ql {mouse} .Ql {mouse}
(alternative form (alternative form
.Ql = ) .Ql = )
to specify the session, window or pane where the most recent mouse event occurred to specify the session, window or pane where the most recent mouse event
(see the occurred (see the
.Sx MOUSE SUPPORT .Sx MOUSE SUPPORT
section) section)
or or
@ -964,7 +965,7 @@ Will run
directly without invoking the shell. directly without invoking the shell.
.Pp .Pp
.Ar command .Ar command
.Op Ar arguments .Op Ar argument ...
refers to a refers to a
.Nm .Nm
command, either passed with the command and arguments separately, for example: command, either passed with the command and arguments separately, for example:
@ -1167,13 +1168,17 @@ session.
.Tg lsc .Tg lsc
.It Xo Ic list-clients .It Xo Ic list-clients
.Op Fl F Ar format .Op Fl F Ar format
.Op Fl f Ar filter
.Op Fl t Ar target-session .Op Fl t Ar target-session
.Xc .Xc
.D1 Pq alias: Ic lsc .D1 Pq alias: Ic lsc
List all clients attached to the server. List all clients attached to the server.
For the meaning of the
.Fl F .Fl F
flag, see the specifies the format of each line and
.Fl f
a filter.
Only clients for which the filter is true are shown.
See the
.Sx FORMATS .Sx FORMATS
section. section.
If If
@ -1460,7 +1465,7 @@ requests the clipboard from the client using the
.Xr xterm 1 .Xr xterm 1
escape sequence. escape sequence.
If If
Ar target-pane .Ar target-pane
is given, the clipboard is sent (in encoded form), otherwise it is stored in a is given, the clipboard is sent (in encoded form), otherwise it is stored in a
new paste buffer. new paste buffer.
.Pp .Pp
@ -1541,8 +1546,7 @@ show debugging information about jobs and terminals.
.Tg source .Tg source
.It Xo Ic source-file .It Xo Ic source-file
.Op Fl Fnqv .Op Fl Fnqv
.Ar path .Ar path ...
.Ar ...
.Xc .Xc
.D1 Pq alias: Ic source .D1 Pq alias: Ic source
Execute commands from one or more files specified by Execute commands from one or more files specified by
@ -1574,7 +1578,8 @@ server, if not already running, without creating any sessions.
.Pp .Pp
Note that as by default the Note that as by default the
.Nm .Nm
server will exit with no sessions, this is only useful if a session is created in server will exit with no sessions, this is only useful if a session is created
in
.Pa ~/.tmux.conf , .Pa ~/.tmux.conf ,
.Ic exit-empty .Ic exit-empty
is turned off, or another command is run as part of the same command sequence. is turned off, or another command is run as part of the same command sequence.
@ -1929,7 +1934,8 @@ bind PageUp copy-mode -eu
.Ed .Ed
.El .El
.Pp .Pp
A number of preset arrangements of panes are available, these are called layouts. A number of preset arrangements of panes are available, these are called
layouts.
These may be selected with the These may be selected with the
.Ic select-layout .Ic select-layout
command or cycled with command or cycled with
@ -1975,7 +1981,7 @@ For example:
$ tmux list-windows $ tmux list-windows
0: ksh [159x48] 0: ksh [159x48]
layout: bb62,159x48,0,0{79x48,0,0,79x48,80,0} layout: bb62,159x48,0,0{79x48,0,0,79x48,80,0}
$ tmux select-layout bb62,159x48,0,0{79x48,0,0,79x48,80,0} $ tmux select-layout 'bb62,159x48,0,0{79x48,0,0,79x48,80,0}'
.Ed .Ed
.Pp .Pp
.Nm .Nm
@ -3123,7 +3129,7 @@ Commands related to key bindings are as follows:
.Op Fl nr .Op Fl nr
.Op Fl N Ar note .Op Fl N Ar note
.Op Fl T Ar key-table .Op Fl T Ar key-table
.Ar key command Op Ar arguments .Ar key command Op Ar argument ...
.Xc .Xc
.D1 Pq alias: Ic bind .D1 Pq alias: Ic bind
Bind key Bind key
@ -3215,13 +3221,14 @@ lists only the first matching key.
lists the command for keys that do not have a note rather than skipping them. lists the command for keys that do not have a note rather than skipping them.
.Tg send .Tg send
.It Xo Ic send-keys .It Xo Ic send-keys
.Op Fl FHlMRX .Op Fl FHKlMRX
.Op Fl c Ar target-client
.Op Fl N Ar repeat-count .Op Fl N Ar repeat-count
.Op Fl t Ar target-pane .Op Fl t Ar target-pane
.Ar key Ar ... .Ar key ...
.Xc .Xc
.D1 Pq alias: Ic send .D1 Pq alias: Ic send
Send a key or keys to a window. Send a key or keys to a window or client.
Each argument Each argument
.Ar key .Ar key
is the name of the key (such as is the name of the key (such as
@ -3230,6 +3237,12 @@ or
.Ql NPage ) .Ql NPage )
to send; if the string is not recognised as a key, it is sent as a series of to send; if the string is not recognised as a key, it is sent as a series of
characters. characters.
If
.Fl K
is given, keys are sent to
.Ar target-client ,
so they are looked up in the client's key table, rather than to
.Ar target-pane .
All arguments are sent sequentially from first to last. All arguments are sent sequentially from first to last.
If no keys are given and the command is bound to a key, then that key is used. If no keys are given and the command is bound to a key, then that key is used.
.Pp .Pp
@ -3690,6 +3703,8 @@ Supports the overline SGR attribute.
Supports the DECFRA rectangle fill escape sequence. Supports the DECFRA rectangle fill escape sequence.
.It RGB .It RGB
Supports RGB colour with the SGR escape sequences. Supports RGB colour with the SGR escape sequences.
.It sixel
Supports SIXEL graphics.
.It strikethrough .It strikethrough
Supports the strikethrough SGR escape sequence. Supports the strikethrough SGR escape sequence.
.It sync .It sync
@ -4586,7 +4601,8 @@ hook and there are a number of hooks not associated with commands.
.Pp .Pp
Hooks are stored as array options, members of the array are executed in Hooks are stored as array options, members of the array are executed in
order when the hook is triggered. order when the hook is triggered.
Like options different hooks may be global or belong to a session, window or pane. Like options different hooks may be global or belong to a session, window or
pane.
Hooks may be configured with the Hooks may be configured with the
.Ic set-hook .Ic set-hook
or or
@ -4769,7 +4785,8 @@ or
.Ar target-pane .Ar target-pane
in commands bound to mouse key bindings. in commands bound to mouse key bindings.
It resolves to the window or pane over which the mouse event took place It resolves to the window or pane over which the mouse event took place
(for example, the window in the status line over which button 1 was released for a (for example, the window in the status line over which button 1 was released
for a
.Ql MouseUp1Status .Ql MouseUp1Status
binding, or the pane over which the wheel was scrolled for a binding, or the pane over which the wheel was scrolled for a
.Ql WheelDownPane .Ql WheelDownPane
@ -4909,13 +4926,16 @@ ignores case.
For example: For example:
.Ql #{C/r:^Start} .Ql #{C/r:^Start}
.Pp .Pp
Numeric operators may be performed by prefixing two comma-separated alternatives with an Numeric operators may be performed by prefixing two comma-separated alternatives
with an
.Ql e .Ql e
and an operator. and an operator.
An optional An optional
.Ql f .Ql f
flag may be given after the operator to use floating point numbers, otherwise integers are used. flag may be given after the operator to use floating point numbers, otherwise
This may be followed by a number giving the number of decimal places to use for the result. integers are used.
This may be followed by a number giving the number of decimal places to use for
the result.
The available operators are: The available operators are:
addition addition
.Ql + , .Ql + ,
@ -5045,10 +5065,11 @@ but also expands
.Xr strftime 3 .Xr strftime 3
specifiers. specifiers.
.Ql S:\& , .Ql S:\& ,
.Ql W:\& .Ql W:\& ,
or
.Ql P:\& .Ql P:\&
will loop over each session, window or pane and insert the format once or
.Ql L:\&
will loop over each session, window, pane or client and insert the format once
for each. for each.
For windows and panes, two comma-separated formats may be given: For windows and panes, two comma-separated formats may be given:
the second is used for the current window or active pane. the second is used for the current window or active pane.
@ -5075,7 +5096,8 @@ will substitute
with with
.Ql bar .Ql bar
throughout. throughout.
The first argument may be an extended regular expression and a final argument may be The first argument may be an extended regular expression and a final argument
may be
.Ql i .Ql i
to ignore case, for example to ignore case, for example
.Ql s/a(.)/\e1x/i:\& .Ql s/a(.)/\e1x/i:\&
@ -5083,6 +5105,15 @@ would change
.Ql abABab .Ql abABab
into into
.Ql bxBxbx . .Ql bxBxbx .
A different delimiter character may also be used, to avoid collisions with
literal slashes in the pattern.
For example,
.Ql s|foo/|bar/|:\&
will substitute
.Ql foo/
with
.Ql bar/
throughout.
.Pp .Pp
In addition, the last line of a shell command's output may be inserted using In addition, the last line of a shell command's output may be inserted using
.Ql #() . .Ql #() .
@ -5093,10 +5124,10 @@ When constructing formats,
.Nm .Nm
does not wait for does not wait for
.Ql #() .Ql #()
commands to finish; instead, the previous result from running the same command is used, commands to finish; instead, the previous result from running the same command
or a placeholder if the command has not been run before. is used, or a placeholder if the command has not been run before.
If the command hasn't exited, the most recent line of output will be used, but the status If the command hasn't exited, the most recent line of output will be used, but
line will not be updated more than once a second. the status line will not be updated more than once a second.
Commands are executed using Commands are executed using
.Pa /bin/sh .Pa /bin/sh
and with the and with the
@ -5397,8 +5428,8 @@ option:
.Ic list=on .Ic list=on
marks the start of the list; marks the start of the list;
.Ic list=focus .Ic list=focus
is the part of the list that should be kept in focus if the entire list won't fit is the part of the list that should be kept in focus if the entire list won't
in the available space (typically the current window); fit in the available space (typically the current window);
.Ic list=left-marker .Ic list=left-marker
and and
.Ic list=right-marker .Ic list=right-marker
@ -5801,13 +5832,13 @@ until it is dismissed.
.Op Fl O .Op Fl O
.Op Fl c Ar target-client .Op Fl c Ar target-client
.Op Fl t Ar target-pane .Op Fl t Ar target-pane
.Op Fl S Ar starting-choice
.Op Fl T Ar title .Op Fl T Ar title
.Op Fl x Ar position .Op Fl x Ar position
.Op Fl y Ar position .Op Fl y Ar position
.Ar name .Ar name
.Ar key .Ar key
.Ar command .Ar command Op Ar argument ...
.Ar ...
.Xc .Xc
.D1 Pq alias: Ic menu .D1 Pq alias: Ic menu
Display a menu on Display a menu on
@ -5831,6 +5862,9 @@ command should be omitted.
.Fl T .Fl T
is a format for the menu title (see is a format for the menu title (see
.Sx FORMATS ) . .Sx FORMATS ) .
.Fl S
sets the menu item selected by default, if the menu is not bound to a mouse key
binding.
.Pp .Pp
.Fl x .Fl x
and and
@ -6545,8 +6579,8 @@ and matching
.Em %end .Em %end
or or
.Em %error .Em %error
have three arguments: an integer time (as seconds from epoch), command number and have three arguments: an integer time (as seconds from epoch), command number
flags (currently not used). and flags (currently not used).
For example: For example:
.Bd -literal -offset indent .Bd -literal -offset indent
%begin 1363006971 2 1 %begin 1363006971 2 1
@ -6596,11 +6630,17 @@ sent when the
.Ar pause-after .Ar pause-after
flag is set. flag is set.
.Ar age .Ar age
is the time in milliseconds for which tmux had buffered the output before it was sent. is the time in milliseconds for which tmux had buffered the output before it
was sent.
Any subsequent arguments up until a single Any subsequent arguments up until a single
.Ql \&: .Ql \&:
are for future use and should be ignored. are for future use and should be ignored.
.It Ic %layout-change Ar window-id Ar window-layout Ar window-visible-layout Ar window-flags .It Xo Ic %layout-change
.Ar window-id
.Ar window-layout
.Ar window-visible-layout
.Ar window-flags
.Xc
The layout of a window with ID The layout of a window with ID
.Ar window-id .Ar window-id
changed. changed.
@ -6610,6 +6650,10 @@ The window's visible layout is
.Ar window-visible-layout .Ar window-visible-layout
and the window flags are and the window flags are
.Ar window-flags . .Ar window-flags .
.It Ic %message Ar message
A message sent with the
.Ic display-message
command.
.It Ic %output Ar pane-id Ar value .It Ic %output Ar pane-id Ar value
A window pane produced output. A window pane produced output.
.Ar value .Ar value

39
tmux.h
View File

@ -133,13 +133,14 @@ struct winlink;
#define KEYC_SHIFT 0x00400000000000ULL #define KEYC_SHIFT 0x00400000000000ULL
/* Key flag bits. */ /* Key flag bits. */
#define KEYC_LITERAL 0x01000000000000ULL #define KEYC_LITERAL 0x01000000000000ULL
#define KEYC_KEYPAD 0x02000000000000ULL #define KEYC_KEYPAD 0x02000000000000ULL
#define KEYC_CURSOR 0x04000000000000ULL #define KEYC_CURSOR 0x04000000000000ULL
#define KEYC_IMPLIED_META 0x08000000000000ULL #define KEYC_IMPLIED_META 0x08000000000000ULL
#define KEYC_BUILD_MODIFIERS 0x10000000000000ULL #define KEYC_BUILD_MODIFIERS 0x10000000000000ULL
#define KEYC_VI 0x20000000000000ULL #define KEYC_VI 0x20000000000000ULL
#define KEYC_EXTENDED 0x40000000000000ULL #define KEYC_EXTENDED 0x40000000000000ULL
#define KEYC_SENT 0x80000000000000ULL
/* Masks for key bits. */ /* Masks for key bits. */
#define KEYC_MASK_MODIFIERS 0x00f00000000000ULL #define KEYC_MASK_MODIFIERS 0x00f00000000000ULL
@ -1398,6 +1399,8 @@ struct tty {
u_int osy; u_int osy;
int mode; int mode;
int fg;
int bg;
u_int rlower; u_int rlower;
u_int rupper; u_int rupper;
@ -1425,9 +1428,14 @@ struct tty {
#define TTY_OPENED 0x20 #define TTY_OPENED 0x20
#define TTY_OSC52QUERY 0x40 #define TTY_OSC52QUERY 0x40
#define TTY_BLOCK 0x80 #define TTY_BLOCK 0x80
#define TTY_HAVEDA 0x100 #define TTY_HAVEDA 0x100 /* Primary DA. */
#define TTY_HAVEXDA 0x200 #define TTY_HAVEXDA 0x200
#define TTY_SYNCING 0x400 #define TTY_SYNCING 0x400
#define TTY_HAVEDA2 0x800 /* Secondary DA. */
#define TTY_HAVEFG 0x1000
#define TTY_HAVEBG 0x2000
#define TTY_ALL_REQUEST_FLAGS \
(TTY_HAVEDA|TTY_HAVEDA2|TTY_HAVEXDA|TTY_HAVEFG|TTY_HAVEBG)
int flags; int flags;
struct tty_term *term; struct tty_term *term;
@ -1823,6 +1831,7 @@ struct client {
#define CLIENT_CONTROL_WAITEXIT 0x200000000ULL #define CLIENT_CONTROL_WAITEXIT 0x200000000ULL
#define CLIENT_WINDOWSIZECHANGED 0x400000000ULL #define CLIENT_WINDOWSIZECHANGED 0x400000000ULL
#define CLIENT_CLIPBOARDBUFFER 0x800000000ULL #define CLIENT_CLIPBOARDBUFFER 0x800000000ULL
#define CLIENT_BRACKETPASTING 0x1000000000ULL
#define CLIENT_ALLREDRAWFLAGS \ #define CLIENT_ALLREDRAWFLAGS \
(CLIENT_REDRAWWINDOW| \ (CLIENT_REDRAWWINDOW| \
CLIENT_REDRAWSTATUS| \ CLIENT_REDRAWSTATUS| \
@ -2407,7 +2416,7 @@ void tty_keys_free(struct tty *);
int tty_keys_next(struct tty *); int tty_keys_next(struct tty *);
/* arguments.c */ /* arguments.c */
void args_set(struct args *, u_char, struct args_value *); void args_set(struct args *, u_char, struct args_value *, int);
struct args *args_create(void); struct args *args_create(void);
struct args *args_parse(const struct args_parse *, struct args_value *, struct args *args_parse(const struct args_parse *, struct args_value *,
u_int, char **); u_int, char **);
@ -2570,6 +2579,7 @@ u_int cmdq_next(struct client *);
struct cmdq_item *cmdq_running(struct client *); struct cmdq_item *cmdq_running(struct client *);
void cmdq_guard(struct cmdq_item *, const char *, int); void cmdq_guard(struct cmdq_item *, const char *, int);
void printflike(2, 3) cmdq_print(struct cmdq_item *, const char *, ...); void printflike(2, 3) cmdq_print(struct cmdq_item *, const char *, ...);
void cmdq_print_data(struct cmdq_item *, int, struct evbuffer *);
void printflike(2, 3) cmdq_error(struct cmdq_item *, const char *, ...); void printflike(2, 3) cmdq_error(struct cmdq_item *, const char *, ...);
/* cmd-wait-for.c */ /* cmd-wait-for.c */
@ -2624,7 +2634,9 @@ void file_print_buffer(struct client *, void *, size_t);
void printflike(2, 3) file_error(struct client *, const char *, ...); void printflike(2, 3) file_error(struct client *, const char *, ...);
void file_write(struct client *, const char *, int, const void *, size_t, void file_write(struct client *, const char *, int, const void *, size_t,
client_file_cb, void *); client_file_cb, void *);
void file_read(struct client *, const char *, client_file_cb, void *); struct client_file *file_read(struct client *, const char *, client_file_cb,
void *);
void file_cancel(struct client_file *);
void file_push(struct client_file *); void file_push(struct client_file *);
int file_write_left(struct client_files *); int file_write_left(struct client_files *);
void file_write_open(struct client_files *, struct tmuxpeer *, void file_write_open(struct client_files *, struct tmuxpeer *,
@ -2636,6 +2648,7 @@ void file_read_open(struct client_files *, struct tmuxpeer *, struct imsg *,
void file_write_ready(struct client_files *, struct imsg *); void file_write_ready(struct client_files *, struct imsg *);
void file_read_data(struct client_files *, struct imsg *); void file_read_data(struct client_files *, struct imsg *);
void file_read_done(struct client_files *, struct imsg *); void file_read_done(struct client_files *, struct imsg *);
void file_read_cancel(struct client_files *, struct imsg *);
/* server.c */ /* server.c */
extern struct tmuxproc *server_proc; extern struct tmuxproc *server_proc;
@ -2687,6 +2700,7 @@ struct client_window *server_client_add_client_window(struct client *, u_int);
struct window_pane *server_client_get_pane(struct client *); struct window_pane *server_client_get_pane(struct client *);
void server_client_set_pane(struct client *, struct window_pane *); void server_client_set_pane(struct client *, struct window_pane *);
void server_client_remove_pane(struct window_pane *); void server_client_remove_pane(struct window_pane *);
void server_client_print(struct client *, int, struct evbuffer *);
/* server-fn.c */ /* server-fn.c */
void server_redraw_client(struct client *); void server_redraw_client(struct client *);
@ -2779,6 +2793,7 @@ int colour_fromstring(const char *s);
int colour_256toRGB(int); int colour_256toRGB(int);
int colour_256to16(int); int colour_256to16(int);
int colour_byname(const char *); int colour_byname(const char *);
int colour_parseX11(const char *);
void colour_palette_init(struct colour_palette *); void colour_palette_init(struct colour_palette *);
void colour_palette_clear(struct colour_palette *); void colour_palette_clear(struct colour_palette *);
void colour_palette_free(struct colour_palette *); void colour_palette_free(struct colour_palette *);
@ -3303,11 +3318,11 @@ void menu_add_item(struct menu *, const struct menu_item *,
struct cmdq_item *, struct client *, struct cmdq_item *, struct client *,
struct cmd_find_state *); struct cmd_find_state *);
void menu_free(struct menu *); void menu_free(struct menu *);
struct menu_data *menu_prepare(struct menu *, int, struct cmdq_item *, u_int, struct menu_data *menu_prepare(struct menu *, int, int, struct cmdq_item *,
u_int, struct client *, struct cmd_find_state *, u_int, u_int, struct client *, struct cmd_find_state *,
menu_choice_cb, void *); menu_choice_cb, void *);
int menu_display(struct menu *, int, struct cmdq_item *, u_int, int menu_display(struct menu *, int, int, struct cmdq_item *,
u_int, struct client *, struct cmd_find_state *, u_int, u_int, struct client *, struct cmd_find_state *,
menu_choice_cb, void *); menu_choice_cb, void *);
struct screen *menu_mode_cb(struct client *, void *, u_int *, u_int *); struct screen *menu_mode_cb(struct client *, void *, u_int *, u_int *);
void menu_check_cb(struct client *, void *, u_int, u_int, u_int, void menu_check_cb(struct client *, void *, u_int, u_int, u_int,

View File

@ -345,6 +345,17 @@ static const struct tty_feature tty_feature_ignorefkeys = {
0 0
}; };
/* Terminal has sixel capability. */
static const char *const tty_feature_sixel_capabilities[] = {
"Sxl",
NULL
};
static const struct tty_feature tty_feature_sixel = {
"sixel",
tty_feature_sixel_capabilities,
TERM_SIXEL
};
/* Available terminal features. */ /* Available terminal features. */
static const struct tty_feature *const tty_features[] = { static const struct tty_feature *const tty_features[] = {
&tty_feature_256, &tty_feature_256,
@ -362,6 +373,7 @@ static const struct tty_feature *const tty_features[] = {
&tty_feature_overline, &tty_feature_overline,
&tty_feature_rectfill, &tty_feature_rectfill,
&tty_feature_rgb, &tty_feature_rgb,
&tty_feature_sixel,
&tty_feature_strikethrough, &tty_feature_strikethrough,
&tty_feature_sync, &tty_feature_sync,
&tty_feature_title, &tty_feature_title,

View File

@ -55,8 +55,11 @@ static int tty_keys_clipboard(struct tty *, const char *, size_t,
size_t *); size_t *);
static int tty_keys_device_attributes(struct tty *, const char *, size_t, static int tty_keys_device_attributes(struct tty *, const char *, size_t,
size_t *); size_t *);
static int tty_keys_device_attributes2(struct tty *, const char *, size_t,
size_t *);
static int tty_keys_extended_device_attributes(struct tty *, const char *, static int tty_keys_extended_device_attributes(struct tty *, const char *,
size_t, size_t *); size_t, size_t *);
static int tty_keys_colours(struct tty *, const char *, size_t, size_t *);
/* A key tree entry. */ /* A key tree entry. */
struct tty_key { struct tty_key {
@ -684,7 +687,7 @@ tty_keys_next(struct tty *tty)
goto partial_key; goto partial_key;
} }
/* Is this a device attributes response? */ /* Is this a primary device attributes response? */
switch (tty_keys_device_attributes(tty, buf, len, &size)) { switch (tty_keys_device_attributes(tty, buf, len, &size)) {
case 0: /* yes */ case 0: /* yes */
key = KEYC_UNKNOWN; key = KEYC_UNKNOWN;
@ -695,6 +698,17 @@ tty_keys_next(struct tty *tty)
goto partial_key; goto partial_key;
} }
/* Is this a secondary device attributes response? */
switch (tty_keys_device_attributes2(tty, buf, len, &size)) {
case 0: /* yes */
key = KEYC_UNKNOWN;
goto complete_key;
case -1: /* no, or not valid */
break;
case 1: /* partial */
goto partial_key;
}
/* Is this an extended device attributes response? */ /* Is this an extended device attributes response? */
switch (tty_keys_extended_device_attributes(tty, buf, len, &size)) { switch (tty_keys_extended_device_attributes(tty, buf, len, &size)) {
case 0: /* yes */ case 0: /* yes */
@ -706,6 +720,17 @@ tty_keys_next(struct tty *tty)
goto partial_key; goto partial_key;
} }
/* Is this a colours response? */
switch (tty_keys_colours(tty, buf, len, &size)) {
case 0: /* yes */
key = KEYC_UNKNOWN;
goto complete_key;
case -1: /* no, or not valid */
break;
case 1: /* partial */
goto partial_key;
}
/* Is this a mouse key press? */ /* Is this a mouse key press? */
switch (tty_keys_mouse(tty, buf, len, &size, &m)) { switch (tty_keys_mouse(tty, buf, len, &size, &m)) {
case 0: /* yes */ case 0: /* yes */
@ -1242,7 +1267,7 @@ tty_keys_clipboard(struct tty *tty, const char *buf, size_t len, size_t *size)
// } // }
/* /*
* Handle secondary device attributes input. Returns 0 for success, -1 for * Handle primary device attributes input. Returns 0 for success, -1 for
* failure, 1 for partial. * failure, 1 for partial.
*/ */
static int static int
@ -1250,17 +1275,15 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len,
size_t *size) size_t *size)
{ {
struct client *c = tty->client; struct client *c = tty->client;
int *features = &c->term_features;
u_int i, n = 0; u_int i, n = 0;
char tmp[64], *endptr, p[32] = { 0 }, *cp, *next; char tmp[128], *endptr, p[32] = { 0 }, *cp, *next;
*size = 0; *size = 0;
if (tty->flags & TTY_HAVEDA) if (tty->flags & TTY_HAVEDA)
return (-1); return (-1);
/* /* First three bytes are always \033[?. */
* First three bytes are always \033[>. Some older Terminal.app
* versions respond as for DA (\033[?) so accept and ignore that.
*/
if (buf[0] != '\033') if (buf[0] != '\033')
return (-1); return (-1);
if (len == 1) if (len == 1)
@ -1269,56 +1292,127 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len,
return (-1); return (-1);
if (len == 2) if (len == 2)
return (1); return (1);
if (buf[2] != '>' && buf[2] != '?') if (buf[2] != '?')
return (-1); return (-1);
if (len == 3) if (len == 3)
return (1); return (1);
/* Copy the rest up to a 'c'. */ /* Copy the rest up to a c. */
for (i = 0; i < (sizeof tmp) - 1; i++) { for (i = 0; i < (sizeof tmp); i++) {
if (3 + i == len) if (3 + i == len)
return (1); return (1);
if (buf[3 + i] == 'c') if (buf[3 + i] == 'c')
break; break;
tmp[i] = buf[3 + i]; tmp[i] = buf[3 + i];
} }
if (i == (sizeof tmp) - 1) if (i == (sizeof tmp))
return (-1); return (-1);
tmp[i] = '\0'; tmp[i] = '\0';
*size = 4 + i; *size = 4 + i;
/* Ignore DA response. */
if (buf[2] == '?')
return (0);
/* Convert all arguments to numbers. */ /* Convert all arguments to numbers. */
cp = tmp; cp = tmp;
while ((next = strsep(&cp, ";")) != NULL) { while ((next = strsep(&cp, ";")) != NULL) {
p[n] = strtoul(next, &endptr, 10); p[n] = strtoul(next, &endptr, 10);
if (*endptr != '\0') if (*endptr != '\0')
p[n] = 0; p[n] = 0;
n++; if (++n == nitems(p))
break;
}
/* Add terminal features. */
switch (p[0]) {
case 62: /* VT220 */
case 63: /* VT320 */
case 64: /* VT420 */
for (i = 1; i < n; i++) {
log_debug("%s: DA feature: %d", c->name, p[i]);
if (p[i] == 4)
tty_add_features(features, "sixel", ",");
}
break;
}
log_debug("%s: received primary DA %.*s", c->name, (int)*size, buf);
tty_update_features(tty);
tty->flags |= TTY_HAVEDA;
return (0);
}
/*
* Handle secondary device attributes input. Returns 0 for success, -1 for
* failure, 1 for partial.
*/
static int
tty_keys_device_attributes2(struct tty *tty, const char *buf, size_t len,
size_t *size)
{
struct client *c = tty->client;
int *features = &c->term_features;
u_int i, n = 0;
char tmp[128], *endptr, p[32] = { 0 }, *cp, *next;
*size = 0;
if (tty->flags & TTY_HAVEDA2)
return (-1);
/* First three bytes are always \033[>. */
if (buf[0] != '\033')
return (-1);
if (len == 1)
return (1);
if (buf[1] != '[')
return (-1);
if (len == 2)
return (1);
if (buf[2] != '>')
return (-1);
if (len == 3)
return (1);
/* Copy the rest up to a c. */
for (i = 0; i < (sizeof tmp); i++) {
if (3 + i == len)
return (1);
if (buf[3 + i] == 'c')
break;
tmp[i] = buf[3 + i];
}
if (i == (sizeof tmp))
return (-1);
tmp[i] = '\0';
*size = 4 + i;
/* Convert all arguments to numbers. */
cp = tmp;
while ((next = strsep(&cp, ";")) != NULL) {
p[n] = strtoul(next, &endptr, 10);
if (*endptr != '\0')
p[n] = 0;
if (++n == nitems(p))
break;
} }
/* Add terminal features. */ /* Add terminal features. */
switch (p[0]) { switch (p[0]) {
case 41: /* VT420 */ case 41: /* VT420 */
tty_add_features(&c->term_features, "margins,rectfill", ","); tty_add_features(features, "margins,rectfill", ",");
break; break;
case 'M': /* mintty */ case 'M': /* mintty */
tty_default_features(&c->term_features, "mintty", 0); tty_default_features(features, "mintty", 0);
break; break;
case 'T': /* tmux */ case 'T': /* tmux */
tty_default_features(&c->term_features, "tmux", 0); tty_default_features(features, "tmux", 0);
break; break;
case 'U': /* rxvt-unicode */ case 'U': /* rxvt-unicode */
tty_default_features(&c->term_features, "rxvt-unicode", 0); tty_default_features(features, "rxvt-unicode", 0);
break; break;
} }
log_debug("%s: received secondary DA %.*s", c->name, (int)*size, buf); log_debug("%s: received secondary DA %.*s", c->name, (int)*size, buf);
tty_update_features(tty); tty_update_features(tty);
tty->flags |= TTY_HAVEDA; tty->flags |= TTY_HAVEDA2;
return (0); return (0);
} }
@ -1332,6 +1426,7 @@ tty_keys_extended_device_attributes(struct tty *tty, const char *buf,
size_t len, size_t *size) size_t len, size_t *size)
{ {
struct client *c = tty->client; struct client *c = tty->client;
int *features = &c->term_features;
u_int i; u_int i;
char tmp[128]; char tmp[128];
@ -1357,7 +1452,7 @@ tty_keys_extended_device_attributes(struct tty *tty, const char *buf,
if (len == 4) if (len == 4)
return (1); return (1);
/* Copy the rest up to a '\033\\'. */ /* Copy the rest up to \033\. */
for (i = 0; i < (sizeof tmp) - 1; i++) { for (i = 0; i < (sizeof tmp) - 1; i++) {
if (4 + i == len) if (4 + i == len)
return (1); return (1);
@ -1372,13 +1467,13 @@ tty_keys_extended_device_attributes(struct tty *tty, const char *buf,
/* Add terminal features. */ /* Add terminal features. */
if (strncmp(tmp, "iTerm2 ", 7) == 0) if (strncmp(tmp, "iTerm2 ", 7) == 0)
tty_default_features(&c->term_features, "iTerm2", 0); tty_default_features(features, "iTerm2", 0);
else if (strncmp(tmp, "tmux ", 5) == 0) else if (strncmp(tmp, "tmux ", 5) == 0)
tty_default_features(&c->term_features, "tmux", 0); tty_default_features(features, "tmux", 0);
else if (strncmp(tmp, "XTerm(", 6) == 0) else if (strncmp(tmp, "XTerm(", 6) == 0)
tty_default_features(&c->term_features, "XTerm", 0); tty_default_features(features, "XTerm", 0);
else if (strncmp(tmp, "mintty ", 7) == 0) else if (strncmp(tmp, "mintty ", 7) == 0)
tty_default_features(&c->term_features, "mintty", 0); tty_default_features(features, "mintty", 0);
log_debug("%s: received extended DA %.*s", c->name, (int)*size, buf); log_debug("%s: received extended DA %.*s", c->name, (int)*size, buf);
free(c->term_type); free(c->term_type);
@ -1389,3 +1484,73 @@ tty_keys_extended_device_attributes(struct tty *tty, const char *buf,
return (0); return (0);
} }
/*
* Handle foreground or background input. Returns 0 for success, -1 for
* failure, 1 for partial.
*/
static int
tty_keys_colours(struct tty *tty, const char *buf, size_t len, size_t *size)
{
struct client *c = tty->client;
u_int i;
char tmp[128];
int n;
*size = 0;
if ((tty->flags & TTY_HAVEFG) && (tty->flags & TTY_HAVEBG))
return (-1);
/* First four bytes are always \033]1 and 0 or 1 and ;. */
if (buf[0] != '\033')
return (-1);
if (len == 1)
return (1);
if (buf[1] != ']')
return (-1);
if (len == 2)
return (1);
if (buf[2] != '1')
return (-1);
if (len == 3)
return (1);
if (buf[3] != '0' && buf[3] != '1')
return (-1);
if (len == 4)
return (1);
if (buf[4] != ';')
return (-1);
if (len == 5)
return (1);
/* Copy the rest up to \033\ or \007. */
for (i = 0; i < (sizeof tmp) - 1; i++) {
if (5 + i == len)
return (1);
if (buf[5 + i - 1] == '\033' && buf[5 + i] == '\\')
break;
if (buf[5 + i] == '\007')
break;
tmp[i] = buf[5 + i];
}
if (i == (sizeof tmp) - 1)
return (-1);
if (tmp[i - 1] == '\033')
tmp[i - 1] = '\0';
else
tmp[i] = '\0';
*size = 6 + i;
n = colour_parseX11(tmp);
if (n != -1 && buf[3] == '0') {
log_debug("%s: foreground is %s", c->name, colour_tostring(n));
tty->fg = n;
tty->flags |= TTY_HAVEFG;
} else if (n != -1) {
log_debug("%s: background is %s", c->name, colour_tostring(n));
tty->bg = n;
tty->flags |= TTY_HAVEBG;
}
return (0);
}

View File

@ -457,6 +457,9 @@ tty_term_apply_overrides(struct tty_term *term)
a = options_array_next(a); a = options_array_next(a);
} }
/* Log the SIXEL flag. */
log_debug("SIXEL flag is %d", !!(term->flags & TERM_SIXEL));
/* Update the RGB flag if the terminal has RGB colours. */ /* Update the RGB flag if the terminal has RGB colours. */
if (tty_term_has(term, TTYC_SETRGBF) && if (tty_term_has(term, TTYC_SETRGBF) &&
tty_term_has(term, TTYC_SETRGBB)) tty_term_has(term, TTYC_SETRGBB))

28
tty.c
View File

@ -108,6 +108,7 @@ tty_init(struct tty *tty, struct client *c)
tty->cstyle = SCREEN_CURSOR_DEFAULT; tty->cstyle = SCREEN_CURSOR_DEFAULT;
tty->ccolour = -1; tty->ccolour = -1;
tty->fg = tty->bg = -1;
if (tcgetattr(c->fd, &tty->tio) != 0) if (tcgetattr(c->fd, &tty->tio) != 0)
return (-1); return (-1);
@ -286,7 +287,6 @@ tty_open(struct tty *tty, char **cause)
evtimer_set(&tty->timer, tty_timer_callback, tty); evtimer_set(&tty->timer, tty_timer_callback, tty);
tty_start_tty(tty); tty_start_tty(tty);
tty_keys_build(tty); tty_keys_build(tty);
return (0); return (0);
@ -299,9 +299,9 @@ tty_start_timer_callback(__unused int fd, __unused short events, void *data)
struct client *c = tty->client; struct client *c = tty->client;
log_debug("%s: start timer fired", c->name); log_debug("%s: start timer fired", c->name);
if ((tty->flags & (TTY_HAVEDA|TTY_HAVEXDA)) == 0) if ((tty->flags & (TTY_HAVEDA|TTY_HAVEDA2|TTY_HAVEXDA)) == 0)
tty_update_features(tty); tty_update_features(tty);
tty->flags |= (TTY_HAVEDA|TTY_HAVEXDA); tty->flags |= TTY_ALL_REQUEST_FLAGS;
} }
void void
@ -341,6 +341,8 @@ tty_start_tty(struct tty *tty)
tty_puts(tty, "\033[?1000l\033[?1002l\033[?1003l"); tty_puts(tty, "\033[?1000l\033[?1002l\033[?1003l");
tty_puts(tty, "\033[?1006l\033[?1005l"); tty_puts(tty, "\033[?1006l\033[?1005l");
} }
if (tty_term_has(tty->term, TTYC_ENBP))
tty_putcode(tty, TTYC_ENBP);
evtimer_set(&tty->start_timer, tty_start_timer_callback, tty); evtimer_set(&tty->start_timer, tty_start_timer_callback, tty);
evtimer_add(&tty->start_timer, &tv); evtimer_add(&tty->start_timer, &tv);
@ -363,12 +365,18 @@ tty_send_requests(struct tty *tty)
return; return;
if (tty->term->flags & TERM_VT100LIKE) { if (tty->term->flags & TERM_VT100LIKE) {
if (~tty->flags & TTY_HAVEDA) if (~tty->term->flags & TTY_HAVEDA)
tty_puts(tty, "\033[c");
if (~tty->flags & TTY_HAVEDA2)
tty_puts(tty, "\033[>c"); tty_puts(tty, "\033[>c");
if (~tty->flags & TTY_HAVEXDA) if (~tty->flags & TTY_HAVEXDA)
tty_puts(tty, "\033[>q"); tty_puts(tty, "\033[>q");
if (~tty->flags & TTY_HAVEFG)
tty_puts(tty, "\033]10;?\033\\");
if (~tty->flags & TTY_HAVEBG)
tty_puts(tty, "\033]11;?\033\\");
} else } else
tty->flags |= (TTY_HAVEDA|TTY_HAVEXDA); tty->flags |= TTY_ALL_REQUEST_FLAGS;
} }
void void
@ -411,8 +419,6 @@ tty_stop_tty(struct tty *tty)
else if (tty_term_has(tty->term, TTYC_SS)) else if (tty_term_has(tty->term, TTYC_SS))
tty_raw(tty, tty_term_string1(tty->term, TTYC_SS, 0)); tty_raw(tty, tty_term_string1(tty->term, TTYC_SS, 0));
} }
if (tty->mode & MODE_BRACKETPASTE)
tty_raw(tty, tty_term_string(tty->term, TTYC_DSBP));
if (tty->ccolour != -1) if (tty->ccolour != -1)
tty_raw(tty, tty_term_string(tty->term, TTYC_CR)); tty_raw(tty, tty_term_string(tty->term, TTYC_CR));
@ -421,6 +427,8 @@ tty_stop_tty(struct tty *tty)
tty_raw(tty, "\033[?1000l\033[?1002l\033[?1003l"); tty_raw(tty, "\033[?1000l\033[?1002l\033[?1003l");
tty_raw(tty, "\033[?1006l\033[?1005l"); tty_raw(tty, "\033[?1006l\033[?1005l");
} }
if (tty_term_has(tty->term, TTYC_DSBP))
tty_raw(tty, tty_term_string(tty->term, TTYC_DSBP));
if (tty->term->flags & TERM_VT100LIKE) if (tty->term->flags & TERM_VT100LIKE)
tty_raw(tty, "\033[?7727l"); tty_raw(tty, "\033[?7727l");
@ -819,12 +827,6 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s)
else if (mode & MODE_MOUSE_STANDARD) else if (mode & MODE_MOUSE_STANDARD)
tty_puts(tty, "\033[?1000h"); tty_puts(tty, "\033[?1000h");
} }
if (changed & MODE_BRACKETPASTE) {
if (mode & MODE_BRACKETPASTE)
tty_putcode(tty, TTYC_ENBP);
else
tty_putcode(tty, TTYC_DSBP);
}
tty->mode = mode; tty->mode = mode;
} }

11
utf8.c
View File

@ -229,14 +229,23 @@ utf8_width(struct utf8_data *ud, int *width)
case 0: case 0:
return (UTF8_ERROR); return (UTF8_ERROR);
} }
log_debug("UTF-8 %.*s is %08X", (int)ud->size, ud->data, (u_int)wc);
#ifdef HAVE_UTF8PROC #ifdef HAVE_UTF8PROC
*width = utf8proc_wcwidth(wc); *width = utf8proc_wcwidth(wc);
log_debug("utf8proc_wcwidth(%08X) returned %d", (u_int)wc, *width);
#else #else
*width = wcwidth(wc); *width = wcwidth(wc);
log_debug("wcwidth(%08X) returned %d", (u_int)wc, *width);
if (*width < 0) {
/*
* C1 control characters are nonprintable, so they are always
* zero width.
*/
*width = (wc >= 0x80 && wc <= 0x9f) ? 0 : 1;
}
#endif #endif
if (*width >= 0 && *width <= 0xff) if (*width >= 0 && *width <= 0xff)
return (UTF8_DONE); return (UTF8_DONE);
log_debug("UTF-8 %.*s, wcwidth() %d", (int)ud->size, ud->data, *width);
return (UTF8_ERROR); return (UTF8_ERROR);
} }

View File

@ -64,6 +64,7 @@ static u_int next_active_point;
struct window_pane_input_data { struct window_pane_input_data {
struct cmdq_item *item; struct cmdq_item *item;
u_int wp; u_int wp;
struct client_file *file;
}; };
static struct window_pane *window_pane_create(struct window *, u_int, u_int, static struct window_pane *window_pane_create(struct window *, u_int, u_int,
@ -1543,18 +1544,18 @@ window_pane_input_callback(struct client *c, __unused const char *path,
size_t len = EVBUFFER_LENGTH(buffer); size_t len = EVBUFFER_LENGTH(buffer);
wp = window_pane_find_by_id(cdata->wp); wp = window_pane_find_by_id(cdata->wp);
if (wp == NULL || closed || error != 0 || (c->flags & CLIENT_DEAD)) { if (cdata->file != NULL && (wp == NULL || c->flags & CLIENT_DEAD)) {
if (wp == NULL) if (wp == NULL) {
c->retval = 1;
c->flags |= CLIENT_EXIT; c->flags |= CLIENT_EXIT;
}
evbuffer_drain(buffer, len); file_cancel(cdata->file);
} else if (cdata->file == NULL || closed || error != 0) {
cmdq_continue(cdata->item); cmdq_continue(cdata->item);
server_client_unref(c); server_client_unref(c);
free(cdata); free(cdata);
return; } else
} input_parse_buffer(wp, buf, len);
input_parse_buffer(wp, buf, len);
evbuffer_drain(buffer, len); evbuffer_drain(buffer, len);
} }
@ -1577,9 +1578,8 @@ window_pane_start_input(struct window_pane *wp, struct cmdq_item *item,
cdata = xmalloc(sizeof *cdata); cdata = xmalloc(sizeof *cdata);
cdata->item = item; cdata->item = item;
cdata->wp = wp->id; cdata->wp = wp->id;
cdata->file = file_read(c, "-", window_pane_input_callback, cdata);
c->references++; c->references++;
file_read(c, "-", window_pane_input_callback, cdata);
return (0); return (0);
} }