Add send-keys -K to handle keys directly as if typed (so look up in key

table). GitHub issue 3361.
This commit is contained in:
nicm 2022-12-16 08:13:40 +00:00
parent 7e497c7f23
commit 3b3f42053a
5 changed files with 163 additions and 92 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;
}; };
@ -122,6 +126,101 @@ 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->string[0] == '-')
argument = NULL;
}
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 != ':') {
log_debug("%s: -%c", __func__, flag);
args_set(args, flag, NULL, 0);
continue;
}
optional_argument = (*found == ':');
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 +230,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) {
@ -323,13 +357,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 +521,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 +529,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 +542,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 +559,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 +632,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 +641,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

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

@ -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,
.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,18 @@ 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')) {
event = xmalloc(sizeof *event);
event->key = key;
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 +112,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 +137,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 +206,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 +220,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);
} }

11
tmux.1
View File

@ -3212,13 +3212,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 Ar ...
.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
@ -3227,6 +3228,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

2
tmux.h
View File

@ -2387,7 +2387,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 **);