Add a -H flag to send-keys to send literal keys given as hex numbers

(needed for control clients to send mouse sequences). Also add some
format flags for UTF-8 and SGR mouse mode. Requested by Bradley Smith in
GitHub issues 1832 and 1833.
This commit is contained in:
nicm 2019-07-09 14:03:12 +00:00
parent ad11d49d64
commit fc2016dbb6
7 changed files with 94 additions and 40 deletions

View File

@ -38,6 +38,7 @@ TAILQ_HEAD(args_values, args_value);
struct args_entry {
u_char flag;
struct args_values values;
u_int count;
RB_ENTRY(args_entry) entry;
};
@ -174,6 +175,7 @@ args_print(struct args *args)
size_t len;
char *buf;
int i;
u_int j;
struct args_entry *entry;
struct args_value *value;
@ -187,7 +189,8 @@ args_print(struct args *args)
if (*buf == '\0')
args_print_add(&buf, &len, "-");
args_print_add(&buf, &len, "%c", entry->flag);
for (j = 0; j < entry->count; j++)
args_print_add(&buf, &len, "%c", entry->flag);
}
/* Then the flags with arguments. */
@ -244,7 +247,12 @@ args_escape(const char *s)
int
args_has(struct args *args, u_char ch)
{
return (args_find(args, ch) != NULL);
struct args_entry *entry;
entry = args_find(args, ch);
if (entry == NULL)
return (0);
return (entry->count);
}
/* Set argument value in the arguments tree. */
@ -258,9 +266,11 @@ args_set(struct args *args, u_char ch, const char *s)
if (entry == NULL) {
entry = xcalloc(1, sizeof *entry);
entry->flag = ch;
entry->count = 1;
TAILQ_INIT(&entry->values);
RB_INSERT(args_tree, &args->tree, entry);
}
} else
entry->count++;
if (s != NULL) {
value = xcalloc(1, sizeof *value);

View File

@ -33,8 +33,8 @@ const struct cmd_entry cmd_send_keys_entry = {
.name = "send-keys",
.alias = "send",
.args = { "lXRMN:t:", 0, -1 },
.usage = "[-lXRM] [-N repeat-count] " CMD_TARGET_PANE_USAGE " key ...",
.args = { "HlXRMN:t:", 0, -1 },
.usage = "[-HlXRM] [-N repeat-count] " CMD_TARGET_PANE_USAGE " key ...",
.target = { 't', CMD_FIND_PANE, 0 },
@ -56,9 +56,9 @@ const struct cmd_entry cmd_send_prefix_entry = {
};
static struct cmdq_item *
cmd_send_keys_inject(struct client *c, struct cmd_find_state *fs,
struct cmdq_item *item, key_code key)
cmd_send_keys_inject_key(struct client *c, struct cmdq_item *item, key_code key)
{
struct cmd_find_state *fs = &item->target;
struct window_mode_entry *wme;
struct key_table *table;
struct key_binding *bd;
@ -81,6 +81,44 @@ cmd_send_keys_inject(struct client *c, struct cmd_find_state *fs,
return (item);
}
static struct cmdq_item *
cmd_send_keys_inject_string(struct client *c, struct cmdq_item *item,
struct args *args, int i)
{
const char *s = args->argv[i];
struct utf8_data *ud, *uc;
wchar_t wc;
key_code key;
char *endptr;
long n;
int literal;
if (args_has(args, 'H')) {
n = strtol(s, &endptr, 16);
if (*s =='\0' || n < 0 || n > 0xff || *endptr != '\0')
return (item);
return (cmd_send_keys_inject_key(c, item, KEYC_LITERAL|n));
}
literal = args_has(args, 'l');
if (!literal) {
key = key_string_lookup_string(s);
if (key != KEYC_NONE && key != KEYC_UNKNOWN)
return (cmd_send_keys_inject_key(c, item, key));
literal = 1;
}
if (literal) {
ud = utf8_fromcstr(s);
for (uc = ud; uc->size != 0; uc++) {
if (utf8_combine(uc, &wc) != UTF8_DONE)
continue;
item = cmd_send_keys_inject_key(c, item, wc);
}
free(ud);
}
return (item);
}
static enum cmd_retval
cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item)
{
@ -90,11 +128,8 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item)
struct session *s = item->target.s;
struct winlink *wl = item->target.wl;
struct mouse_event *m = &item->shared->mouse;
struct cmd_find_state *fs = &item->target;
struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes);
struct utf8_data *ud, *uc;
wchar_t wc;
int i, literal;
int i;
key_code key;
u_int np = 1;
char *cause = NULL;
@ -141,7 +176,7 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item)
key = options_get_number(s->options, "prefix2");
else
key = options_get_number(s->options, "prefix");
cmd_send_keys_inject(c, fs, item, key);
cmd_send_keys_inject_key(c, item, key);
return (CMD_RETURN_NORMAL);
}
@ -151,28 +186,8 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item)
}
for (; np != 0; np--) {
for (i = 0; i < args->argc; i++) {
literal = args_has(args, 'l');
if (!literal) {
key = key_string_lookup_string(args->argv[i]);
if (key != KEYC_NONE && key != KEYC_UNKNOWN) {
item = cmd_send_keys_inject(c, fs, item,
key);
} else
literal = 1;
}
if (literal) {
ud = utf8_fromcstr(args->argv[i]);
for (uc = ud; uc->size != 0; uc++) {
if (utf8_combine(uc, &wc) != UTF8_DONE)
continue;
item = cmd_send_keys_inject(c, fs, item,
wc);
}
free(ud);
}
}
for (i = 0; i < args->argc; i++)
item = cmd_send_keys_inject_string(c, item, args, i);
}
return (CMD_RETURN_NORMAL);

View File

@ -2300,6 +2300,8 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp)
!!(wp->base.mode & MODE_KKEYPAD));
format_add(ft, "wrap_flag", "%d",
!!(wp->base.mode & MODE_WRAP));
format_add(ft, "origin_flag", "%d",
!!(wp->base.mode & MODE_ORIGIN));
format_add(ft, "mouse_any_flag", "%d",
!!(wp->base.mode & ALL_MOUSE_MODES));
@ -2309,6 +2311,10 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp)
!!(wp->base.mode & MODE_MOUSE_BUTTON));
format_add(ft, "mouse_all_flag", "%d",
!!(wp->base.mode & MODE_MOUSE_ALL));
format_add(ft, "mouse_utf8_flag", "%d",
!!(wp->base.mode & MODE_MOUSE_UTF8));
format_add(ft, "mouse_sgr_flag", "%d",
!!(wp->base.mode & MODE_MOUSE_SGR));
format_add_cb(ft, "pane_tabs", format_cb_pane_tabs);
}

View File

@ -173,6 +173,13 @@ input_key(struct window_pane *wp, key_code key, struct mouse_event *m)
return;
}
/* Literal keys go as themselves (can't be more than eight bits). */
if (key & KEYC_LITERAL) {
ud.data[0] = (u_char)key;
bufferevent_write(wp->event, &ud.data[0], 1);
return;
}
/*
* If this is a normal 7-bit key, just send it, with a leading escape
* if necessary. If it is a UTF-8 key, split it and send it.

View File

@ -284,6 +284,12 @@ key_string_lookup_key(key_code key)
return (out);
}
/* Literal keys are themselves. */
if (key & KEYC_LITERAL) {
snprintf(out, sizeof out, "%c", (int)(key & 0xff));
return (out);
}
/*
* Special case: display C-@ as C-Space. Could do this below in
* the (key >= 0 && key <= 32), but this way we let it be found

19
tmux.1
View File

@ -2622,7 +2622,7 @@ With
only
.Ar key-table .
.It Xo Ic send-keys
.Op Fl lMRX
.Op Fl HlMRX
.Op Fl N Ar repeat-count
.Op Fl t Ar target-pane
.Ar key Ar ...
@ -2637,10 +2637,16 @@ or
.Ql NPage )
to send; if the string is not recognised as a key, it is sent as a series of
characters.
All arguments are sent sequentially from first to last.
.Pp
The
.Fl l
flag disables key name lookup and sends the keys literally.
All arguments are sent sequentially from first to last.
flag disables key name lookup and processes the keys as literal UTF-8
characters.
The
.Fl H
flag expects each key to be a hexadecimal number for an ASCII character.
.Pp
The
.Fl R
flag causes the terminal state to be reset.
@ -4180,11 +4186,14 @@ The following variables are available, where appropriate:
.It Li "mouse_all_flag" Ta "" Ta "Pane mouse all flag"
.It Li "mouse_any_flag" Ta "" Ta "Pane mouse any flag"
.It Li "mouse_button_flag" Ta "" Ta "Pane mouse button flag"
.It Li "mouse_line" Ta "" Ta "Line under mouse, if any"
.It Li "mouse_sgr_flag" Ta "" Ta "Pane mouse SGR flag"
.It Li "mouse_standard_flag" Ta "" Ta "Pane mouse standard flag"
.It Li "mouse_utf8_flag" Ta "" Ta "Pane mouse UTF-8 flag"
.It Li "mouse_word" Ta "" Ta "Word under mouse, if any"
.It Li "mouse_x" Ta "" Ta "Mouse X position, if any"
.It Li "mouse_y" Ta "" Ta "Mouse Y position, if any"
.It Li "mouse_word" Ta "" Ta "Word under mouse, if any"
.It Li "mouse_line" Ta "" Ta "Line under mouse, if any"
.It Li "origin_flag" Ta "" Ta "Pane origin flag"
.It Li "pane_active" Ta "" Ta "1 if active pane"
.It Li "pane_at_bottom" Ta "" Ta "1 if pane is at the bottom of window"
.It Li "pane_at_left" Ta "" Ta "1 if pane is at the left of window"

3
tmux.h
View File

@ -111,9 +111,10 @@ struct winlink;
#define KEYC_CTRL 0x400000000000ULL
#define KEYC_SHIFT 0x800000000000ULL
#define KEYC_XTERM 0x1000000000000ULL
#define KEYC_LITERAL 0x2000000000000ULL
/* Mask to obtain key w/o modifiers. */
#define KEYC_MASK_MOD (KEYC_ESCAPE|KEYC_CTRL|KEYC_SHIFT|KEYC_XTERM)
#define KEYC_MASK_MOD (KEYC_ESCAPE|KEYC_CTRL|KEYC_SHIFT|KEYC_XTERM|KEYC_LITERAL)
#define KEYC_MASK_KEY (~KEYC_MASK_MOD)
/* Is this a mouse key? */