mirror of
https://github.com/tmux/tmux.git
synced 2026-10-13 06:04:34 +00:00
Parse terminal replies with keys and apply them only when finished, so
that a preceding Escape can be handled correctly. GitHub issue 5676 from Joan Fabrégat.
This commit is contained in:
12
tmux.h
12
tmux.h
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: tmux.h,v 1.1452 2026/10/06 10:55:55 nicm Exp $ */
|
/* $OpenBSD: tmux.h,v 1.1453 2026/10/06 17:49:45 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -402,6 +402,16 @@ enum {
|
|||||||
KEYC_REPORT_DARK_THEME,
|
KEYC_REPORT_DARK_THEME,
|
||||||
KEYC_REPORT_LIGHT_THEME,
|
KEYC_REPORT_LIGHT_THEME,
|
||||||
|
|
||||||
|
/* Terminal replies, handled in tty-keys.c and never fired. */
|
||||||
|
KEYC_REPORT_CLIPBOARD,
|
||||||
|
KEYC_REPORT_SYNC,
|
||||||
|
KEYC_REPORT_DA,
|
||||||
|
KEYC_REPORT_DA2,
|
||||||
|
KEYC_REPORT_XDA,
|
||||||
|
KEYC_REPORT_COLOURS,
|
||||||
|
KEYC_REPORT_PALETTE,
|
||||||
|
KEYC_REPORT_WINSZ,
|
||||||
|
|
||||||
/* Mouse state. */
|
/* Mouse state. */
|
||||||
KEYC_MOUSE, /* unclassified mouse event */
|
KEYC_MOUSE, /* unclassified mouse event */
|
||||||
KEYC_DRAGGING, /* dragging in progress */
|
KEYC_DRAGGING, /* dragging in progress */
|
||||||
|
|||||||
529
tty-keys.c
529
tty-keys.c
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: tty-keys.c,v 1.215 2026/09/25 10:37:05 nicm Exp $ */
|
/* $OpenBSD: tty-keys.c,v 1.216 2026/10/06 17:49:45 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -37,6 +37,20 @@
|
|||||||
* into a ternary tree.
|
* into a ternary tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A key or terminal reply parsed from the input buffer. A reply is parsed
|
||||||
|
* without side effects and applied only once it is complete and not part of
|
||||||
|
* a longer key.
|
||||||
|
*/
|
||||||
|
struct tty_key_parse {
|
||||||
|
key_code key;
|
||||||
|
size_t size;
|
||||||
|
struct mouse_event m;
|
||||||
|
|
||||||
|
int flags;
|
||||||
|
#define TTY_KEY_PARSE_NO_META 0x1
|
||||||
|
};
|
||||||
|
|
||||||
static void tty_keys_add1(struct tty_key **, const char *, key_code);
|
static void tty_keys_add1(struct tty_key **, const char *, key_code);
|
||||||
static void tty_keys_add(struct tty *, const char *, key_code);
|
static void tty_keys_add(struct tty *, const char *, key_code);
|
||||||
static void tty_keys_free1(struct tty_key *);
|
static void tty_keys_free1(struct tty_key *);
|
||||||
@@ -44,23 +58,44 @@ static struct tty_key *tty_keys_find1(struct tty_key *, const char *, size_t,
|
|||||||
size_t *);
|
size_t *);
|
||||||
static struct tty_key *tty_keys_find(struct tty *, const char *, size_t,
|
static struct tty_key *tty_keys_find(struct tty *, const char *, size_t,
|
||||||
size_t *);
|
size_t *);
|
||||||
static int tty_keys_next1(struct tty *, const char *, size_t, key_code *,
|
|
||||||
size_t *, int);
|
|
||||||
static void tty_keys_callback(int, short, void *);
|
static void tty_keys_callback(int, short, void *);
|
||||||
static int tty_keys_extended_key(struct tty *, const char *, size_t,
|
static int tty_keys_extended_key(struct tty *, const char *, size_t,
|
||||||
size_t *, key_code *);
|
size_t *, key_code *);
|
||||||
static int tty_keys_mouse(struct tty *, const char *, size_t, size_t *,
|
static int tty_keys_mouse(struct tty *, const char *, size_t, size_t *,
|
||||||
struct mouse_event *);
|
struct mouse_event *);
|
||||||
static int tty_keys_clipboard(struct tty *, const char *, size_t,
|
static int tty_keys_clipboard(struct tty *, const char *, size_t,
|
||||||
size_t *);
|
size_t *, int);
|
||||||
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 *, int);
|
||||||
static int tty_keys_device_attributes2(struct tty *, const char *, size_t,
|
static int tty_keys_device_attributes2(struct tty *, const char *, size_t,
|
||||||
size_t *);
|
size_t *, int);
|
||||||
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 *, int);
|
||||||
static int tty_keys_sync(struct tty *, const char *, size_t, size_t *);
|
static int tty_keys_sync(struct tty *, const char *, size_t, size_t *,
|
||||||
static int tty_keys_palette(struct tty *, const char *, size_t, size_t *);
|
int);
|
||||||
|
static int tty_keys_colours1(struct tty *, const char *, size_t, size_t *,
|
||||||
|
int);
|
||||||
|
static int tty_keys_palette(struct tty *, const char *, size_t, size_t *,
|
||||||
|
int);
|
||||||
|
static int tty_keys_winsz(struct tty *, const char *, size_t, size_t *,
|
||||||
|
int);
|
||||||
|
static int tty_keys_next1(struct tty *, const char *, size_t,
|
||||||
|
struct tty_key_parse *, int);
|
||||||
|
|
||||||
|
/* Terminal replies, in the order they are checked. */
|
||||||
|
static const struct {
|
||||||
|
key_code key;
|
||||||
|
int (*fn)(struct tty *, const char *, size_t, size_t *, int);
|
||||||
|
} tty_keys_replies[] = {
|
||||||
|
{ KEYC_REPORT_CLIPBOARD, tty_keys_clipboard },
|
||||||
|
{ KEYC_REPORT_SYNC, tty_keys_sync },
|
||||||
|
{ KEYC_REPORT_DA, tty_keys_device_attributes },
|
||||||
|
{ KEYC_REPORT_DA2, tty_keys_device_attributes2 },
|
||||||
|
{ KEYC_REPORT_XDA, tty_keys_extended_device_attributes },
|
||||||
|
{ KEYC_REPORT_COLOURS, tty_keys_colours1 },
|
||||||
|
{ KEYC_REPORT_PALETTE, tty_keys_palette },
|
||||||
|
{ KEYC_REPORT_WINSZ, tty_keys_winsz }
|
||||||
|
};
|
||||||
|
|
||||||
/* A key tree entry. */
|
/* A key tree entry. */
|
||||||
struct tty_key {
|
struct tty_key {
|
||||||
@@ -75,8 +110,8 @@ struct tty_key {
|
|||||||
|
|
||||||
/* Default raw keys. */
|
/* Default raw keys. */
|
||||||
struct tty_default_key_raw {
|
struct tty_default_key_raw {
|
||||||
const char *string;
|
const char *string;
|
||||||
key_code key;
|
key_code key;
|
||||||
};
|
};
|
||||||
static const struct tty_default_key_raw tty_default_raw_keys[] = {
|
static const struct tty_default_key_raw tty_default_raw_keys[] = {
|
||||||
/* Application escape. */
|
/* Application escape. */
|
||||||
@@ -614,10 +649,45 @@ tty_keys_partial_paste_end(const char *buf, size_t len)
|
|||||||
return (memcmp(buf, paste_end, len) == 0);
|
return (memcmp(buf, paste_end, len) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Look for a terminal reply, mouse event or extended key. These sequences
|
||||||
|
* carry their own modifiers, so none can follow an Escape as a Meta prefix.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
tty_keys_reply(struct tty *tty, const char *buf, size_t len,
|
||||||
|
struct tty_key_parse *kp)
|
||||||
|
{
|
||||||
|
u_int i;
|
||||||
|
int n;
|
||||||
|
|
||||||
|
for (i = 0; i < nitems(tty_keys_replies); i++) {
|
||||||
|
n = tty_keys_replies[i].fn(tty, buf, len, &kp->size, 0);
|
||||||
|
if (n == 0)
|
||||||
|
kp->key = tty_keys_replies[i].key;
|
||||||
|
if (n != -1)
|
||||||
|
return (n);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (tty_keys_mouse(tty, buf, len, &kp->size, &kp->m)) {
|
||||||
|
case 0: /* yes */
|
||||||
|
kp->key = KEYC_MOUSE;
|
||||||
|
return (0);
|
||||||
|
case -1: /* no, or not valid */
|
||||||
|
break;
|
||||||
|
case -2: /* yes, but we don't care */
|
||||||
|
kp->key = KEYC_UNKNOWN;
|
||||||
|
return (0);
|
||||||
|
case 1: /* partial */
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (tty_keys_extended_key(tty, buf, len, &kp->size, &kp->key));
|
||||||
|
}
|
||||||
|
|
||||||
/* Look up part of the next key. */
|
/* Look up part of the next key. */
|
||||||
static int
|
static int
|
||||||
tty_keys_next1(struct tty *tty, const char *buf, size_t len, key_code *key,
|
tty_keys_next1(struct tty *tty, const char *buf, size_t len,
|
||||||
size_t *size, int expired)
|
struct tty_key_parse *kp, int expired)
|
||||||
{
|
{
|
||||||
struct client *c = tty->client;
|
struct client *c = tty->client;
|
||||||
struct tty_key *tk, *tk1;
|
struct tty_key *tk, *tk1;
|
||||||
@@ -625,12 +695,27 @@ tty_keys_next1(struct tty *tty, const char *buf, size_t len, key_code *key,
|
|||||||
enum utf8_state more;
|
enum utf8_state more;
|
||||||
utf8_char uc;
|
utf8_char uc;
|
||||||
u_int i;
|
u_int i;
|
||||||
|
key_code key;
|
||||||
|
|
||||||
log_debug("%s: next key is %zu (%.*s) (expired=%d)", c->name, len,
|
log_debug("%s: next key is %zu (%.*s) (expired=%d)", c->name, len,
|
||||||
(int)len, buf, expired);
|
(int)len, buf, expired);
|
||||||
|
memset(kp, 0, sizeof *kp);
|
||||||
|
|
||||||
|
/* Is this a terminal reply, mouse event or extended key? */
|
||||||
|
switch (tty_keys_reply(tty, buf, len, kp)) {
|
||||||
|
case 0: /* yes */
|
||||||
|
kp->flags |= TTY_KEY_PARSE_NO_META;
|
||||||
|
return (0);
|
||||||
|
case -1: /* no, or not valid */
|
||||||
|
break;
|
||||||
|
case 1: /* partial */
|
||||||
|
if (!expired)
|
||||||
|
return (1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
/* Is this a known key? */
|
/* Is this a known key? */
|
||||||
tk = tty_keys_find(tty, buf, len, size);
|
tk = tty_keys_find(tty, buf, len, &kp->size);
|
||||||
if (tk != NULL && tk->key != KEYC_UNKNOWN) {
|
if (tk != NULL && tk->key != KEYC_UNKNOWN) {
|
||||||
tk1 = tk;
|
tk1 = tk;
|
||||||
do
|
do
|
||||||
@@ -638,18 +723,29 @@ tty_keys_next1(struct tty *tty, const char *buf, size_t len, key_code *key,
|
|||||||
while ((tk1 = tk1->next) != NULL);
|
while ((tk1 = tk1->next) != NULL);
|
||||||
if (tk->next != NULL && !expired)
|
if (tk->next != NULL && !expired)
|
||||||
return (1);
|
return (1);
|
||||||
*key = tk->key;
|
key = kp->key = tk->key;
|
||||||
if ((*key & KEYC_MASK_KEY) == KEYC_PASTE_START)
|
|
||||||
tty->flags |= TTY_BRACKETPASTE;
|
/*
|
||||||
else if ((*key & KEYC_MASK_KEY) == KEYC_PASTE_END)
|
* A key with an implied Meta already includes its Escape, and
|
||||||
tty->flags &= ~TTY_BRACKETPASTE;
|
* focus and theme reports never have a Meta modifier.
|
||||||
|
*/
|
||||||
|
if ((key & KEYC_IMPLIED_META) ||
|
||||||
|
key == KEYC_FOCUS_IN ||
|
||||||
|
key == KEYC_FOCUS_OUT ||
|
||||||
|
key == KEYC_REPORT_DARK_THEME ||
|
||||||
|
key == KEYC_REPORT_LIGHT_THEME)
|
||||||
|
kp->flags |= TTY_KEY_PARSE_NO_META;
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Is this the start of a known key? */
|
||||||
|
if (tk != NULL && !expired)
|
||||||
|
return (1);
|
||||||
|
|
||||||
/* Is this valid UTF-8? */
|
/* Is this valid UTF-8? */
|
||||||
more = utf8_open(&ud, (u_char)*buf);
|
more = utf8_open(&ud, (u_char)*buf);
|
||||||
if (more == UTF8_MORE) {
|
if (more == UTF8_MORE) {
|
||||||
*size = ud.size;
|
kp->size = ud.size;
|
||||||
if (len < ud.size) {
|
if (len < ud.size) {
|
||||||
if (!expired)
|
if (!expired)
|
||||||
return (1);
|
return (1);
|
||||||
@@ -662,84 +758,38 @@ tty_keys_next1(struct tty *tty, const char *buf, size_t len, key_code *key,
|
|||||||
|
|
||||||
if (utf8_from_data(&ud, &uc) != UTF8_DONE)
|
if (utf8_from_data(&ud, &uc) != UTF8_DONE)
|
||||||
return (-1);
|
return (-1);
|
||||||
*key = uc;
|
kp->key = uc;
|
||||||
|
|
||||||
log_debug("%s: UTF-8 key %.*s %#llx", c->name, (int)ud.size,
|
log_debug("%s: UTF-8 key %.*s %#llx", c->name, (int)ud.size,
|
||||||
ud.data, *key);
|
ud.data, kp->key);
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Process window size change escape sequences. */
|
/* Apply a complete terminal reply. */
|
||||||
static int
|
static void
|
||||||
tty_keys_winsz(struct tty *tty, const char *buf, size_t len, size_t *size)
|
tty_keys_apply(struct tty *tty, const char *buf, struct tty_key_parse *kp)
|
||||||
{
|
{
|
||||||
struct client *c = tty->client;
|
size_t size;
|
||||||
size_t end;
|
u_int i;
|
||||||
char tmp[64];
|
|
||||||
u_int sx, sy, xpixel, ypixel, char_x, char_y;
|
|
||||||
|
|
||||||
*size = 0;
|
if (kp->key == KEYC_MOUSE) {
|
||||||
|
tty->mouse_last_x = kp->m.x;
|
||||||
/* If we did not request this, ignore it. */
|
tty->mouse_last_y = kp->m.y;
|
||||||
if (!(tty->flags & TTY_WINSIZEQUERY))
|
tty->mouse_last_b = kp->m.b;
|
||||||
return (-1);
|
return;
|
||||||
|
|
||||||
/* First two 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);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Stop at either 't' or anything that isn't a
|
|
||||||
* number or ';'.
|
|
||||||
*/
|
|
||||||
for (end = 2; end < len && end != sizeof tmp; end++) {
|
|
||||||
if (buf[end] == 't')
|
|
||||||
break;
|
|
||||||
if (!isdigit((u_char)buf[end]) && buf[end] != ';')
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
if (end == len)
|
for (i = 0; i < nitems(tty_keys_replies); i++) {
|
||||||
return (1);
|
if (tty_keys_replies[i].key == kp->key) {
|
||||||
if (end == sizeof tmp || buf[end] != 't')
|
tty_keys_replies[i].fn(tty, buf, kp->size, &size, 1);
|
||||||
return (-1);
|
kp->key = KEYC_UNKNOWN;
|
||||||
|
return;
|
||||||
/* Copy to the buffer. */
|
}
|
||||||
memcpy(tmp, buf + 2, end - 2);
|
|
||||||
tmp[end - 2] = '\0';
|
|
||||||
|
|
||||||
/* Try to parse the window size sequence. */
|
|
||||||
if (sscanf(tmp, "8;%u;%u", &sy, &sx) == 2) {
|
|
||||||
/* Window size in characters. */
|
|
||||||
tty_set_size(tty, sx, sy, tty->xpixel, tty->ypixel);
|
|
||||||
|
|
||||||
*size = end + 1;
|
|
||||||
return (0);
|
|
||||||
} else if (sscanf(tmp, "4;%u;%u", &ypixel, &xpixel) == 2) {
|
|
||||||
/* Window size in pixels. */
|
|
||||||
char_x = (xpixel && tty->sx) ? xpixel / tty->sx : 0;
|
|
||||||
char_y = (ypixel && tty->sy) ? ypixel / tty->sy : 0;
|
|
||||||
tty_set_size(tty, tty->sx, tty->sy, char_x, char_y);
|
|
||||||
tty_invalidate(tty);
|
|
||||||
|
|
||||||
tty->flags &= ~TTY_WINSIZEQUERY;
|
|
||||||
*size = end + 1;
|
|
||||||
return (0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log_debug("%s: unrecognized window size sequence: %s", c->name, tmp);
|
|
||||||
return (-1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Process at least one key in the buffer. Return 0 if no keys present. */
|
/* Process at least one key in the buffer. Return 0 if no keys present. */
|
||||||
int
|
int
|
||||||
tty_keys_next(struct tty *tty)
|
tty_keys_next(struct tty *tty)
|
||||||
@@ -749,9 +799,9 @@ tty_keys_next(struct tty *tty)
|
|||||||
const char *buf;
|
const char *buf;
|
||||||
size_t len, size;
|
size_t len, size;
|
||||||
cc_t bspace;
|
cc_t bspace;
|
||||||
int delay, expired = 0, n, bg = tty->bg;
|
int delay, expired = 0, n;
|
||||||
key_code key, onlykey;
|
key_code key, onlykey;
|
||||||
struct mouse_event m = { 0 };
|
struct tty_key_parse kp;
|
||||||
struct key_event *event;
|
struct key_event *event;
|
||||||
|
|
||||||
/* Get key buffer. */
|
/* Get key buffer. */
|
||||||
@@ -771,127 +821,9 @@ tty_keys_next(struct tty *tty)
|
|||||||
return (1);
|
return (1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Is this a clipboard response? */
|
|
||||||
switch (tty_keys_clipboard(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 synchronized update mode response? */
|
|
||||||
switch (tty_keys_sync(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 primary device attributes response? */
|
|
||||||
switch (tty_keys_device_attributes(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 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? */
|
|
||||||
switch (tty_keys_extended_device_attributes(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 colours response? */
|
|
||||||
switch (tty_keys_colours(tty, buf, len, &size, &tty->fg, &tty->bg)) {
|
|
||||||
case 0: /* yes */
|
|
||||||
key = KEYC_UNKNOWN;
|
|
||||||
if (tty->bg != bg)
|
|
||||||
server_client_update_theme_colours(c);
|
|
||||||
session_theme_changed(c->session);
|
|
||||||
goto complete_key;
|
|
||||||
case -1: /* no, or not valid */
|
|
||||||
break;
|
|
||||||
case 1: /* partial */
|
|
||||||
if (tty->bg != bg)
|
|
||||||
server_client_update_theme_colours(c);
|
|
||||||
session_theme_changed(c->session);
|
|
||||||
goto partial_key;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Is this a palette response? */
|
|
||||||
switch (tty_keys_palette(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? */
|
|
||||||
switch (tty_keys_mouse(tty, buf, len, &size, &m)) {
|
|
||||||
case 0: /* yes */
|
|
||||||
key = KEYC_MOUSE;
|
|
||||||
goto complete_key;
|
|
||||||
case -1: /* no, or not valid */
|
|
||||||
break;
|
|
||||||
case -2: /* yes, but we don't care. */
|
|
||||||
key = KEYC_MOUSE;
|
|
||||||
goto discard_key;
|
|
||||||
case 1: /* partial */
|
|
||||||
goto partial_key;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Is this an extended key press? */
|
|
||||||
switch (tty_keys_extended_key(tty, buf, len, &size, &key)) {
|
|
||||||
case 0: /* yes */
|
|
||||||
goto complete_key;
|
|
||||||
case -1: /* no, or not valid */
|
|
||||||
break;
|
|
||||||
case 1: /* partial */
|
|
||||||
goto partial_key;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check for window size query */
|
|
||||||
switch (tty_keys_winsz(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;
|
|
||||||
}
|
|
||||||
|
|
||||||
first_key:
|
first_key:
|
||||||
/* Try to lookup complete key. */
|
/* Try to lookup complete key. */
|
||||||
n = tty_keys_next1(tty, buf, len, &key, &size, expired);
|
n = tty_keys_next1(tty, buf, len, &kp, expired);
|
||||||
if (n == 0) /* found */
|
if (n == 0) /* found */
|
||||||
goto complete_key;
|
goto complete_key;
|
||||||
if (n == 1)
|
if (n == 1)
|
||||||
@@ -903,22 +835,25 @@ first_key:
|
|||||||
*/
|
*/
|
||||||
if (*buf == '\033' && len > 1) {
|
if (*buf == '\033' && len > 1) {
|
||||||
/* Look for a key without the escape. */
|
/* Look for a key without the escape. */
|
||||||
n = tty_keys_next1(tty, buf + 1, len - 1, &key, &size, expired);
|
n = tty_keys_next1(tty, buf + 1, len - 1, &kp, expired);
|
||||||
if (n == 0) { /* found */
|
if (n == 0) { /* found */
|
||||||
if (key & KEYC_IMPLIED_META) {
|
if (kp.flags & TTY_KEY_PARSE_NO_META) {
|
||||||
/*
|
/*
|
||||||
* We want the escape key as well as the xterm
|
* We want the escape key as well as the xterm
|
||||||
* key, because the xterm sequence implicitly
|
* key, because the xterm sequence implicitly
|
||||||
* includes the escape (so if we see
|
* includes the escape (so if we see
|
||||||
* \033\033[1;3D we know it is an Escape
|
* \033\033[1;3D we know it is an Escape
|
||||||
* followed by M-Left, not just M-Left).
|
* followed by M-Left, not just M-Left). The
|
||||||
|
* same applies to terminal replies, mouse
|
||||||
|
* events and extended keys.
|
||||||
*/
|
*/
|
||||||
key = '\033';
|
memset(&kp, 0, sizeof kp);
|
||||||
size = 1;
|
kp.key = '\033';
|
||||||
|
kp.size = 1;
|
||||||
goto complete_key;
|
goto complete_key;
|
||||||
}
|
}
|
||||||
key |= KEYC_META;
|
kp.key |= KEYC_META;
|
||||||
size++;
|
kp.size++;
|
||||||
goto complete_key;
|
goto complete_key;
|
||||||
}
|
}
|
||||||
if (n == 1) /* partial */
|
if (n == 1) /* partial */
|
||||||
@@ -974,6 +909,9 @@ first_key:
|
|||||||
key = onlykey | KEYC_CTRL | (key & KEYC_META);
|
key = onlykey | KEYC_CTRL | (key & KEYC_META);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
memset(&kp, 0, sizeof kp);
|
||||||
|
kp.key = key;
|
||||||
|
kp.size = size;
|
||||||
goto complete_key;
|
goto complete_key;
|
||||||
|
|
||||||
partial_key:
|
partial_key:
|
||||||
@@ -1020,32 +958,42 @@ partial_key:
|
|||||||
return (0);
|
return (0);
|
||||||
|
|
||||||
complete_key:
|
complete_key:
|
||||||
log_debug("%s: complete key %.*s %#llx", c->name, (int)size, buf, key);
|
log_debug("%s: complete key %.*s %#llx", c->name, (int)kp.size, buf,
|
||||||
|
kp.key);
|
||||||
|
|
||||||
/* Remove key timer. */
|
/* Remove key timer. */
|
||||||
if (event_initialized(&tty->key_timer))
|
if (event_initialized(&tty->key_timer))
|
||||||
evtimer_del(&tty->key_timer);
|
evtimer_del(&tty->key_timer);
|
||||||
tty->flags &= ~TTY_TIMER;
|
tty->flags &= ~TTY_TIMER;
|
||||||
|
|
||||||
|
/* Apply terminal replies and mouse state. */
|
||||||
|
tty_keys_apply(tty, buf, &kp);
|
||||||
|
|
||||||
|
/* Check for bracketed paste. */
|
||||||
|
if ((kp.key & KEYC_MASK_KEY) == KEYC_PASTE_START)
|
||||||
|
tty->flags |= TTY_BRACKETPASTE;
|
||||||
|
else if ((kp.key & KEYC_MASK_KEY) == KEYC_PASTE_END)
|
||||||
|
tty->flags &= ~TTY_BRACKETPASTE;
|
||||||
|
|
||||||
/* Check for focus events. */
|
/* Check for focus events. */
|
||||||
if (key == KEYC_FOCUS_OUT) {
|
if (kp.key == KEYC_FOCUS_OUT) {
|
||||||
c->flags &= ~CLIENT_FOCUSED;
|
c->flags &= ~CLIENT_FOCUSED;
|
||||||
window_update_focus(c->session->curw->window);
|
window_update_focus(c->session->curw->window);
|
||||||
events_fire_client("client-focus-out", c);
|
events_fire_client("client-focus-out", c);
|
||||||
} else if (key == KEYC_FOCUS_IN) {
|
} else if (kp.key == KEYC_FOCUS_IN) {
|
||||||
c->flags |= CLIENT_FOCUSED;
|
c->flags |= CLIENT_FOCUSED;
|
||||||
events_fire_client("client-focus-in", c);
|
events_fire_client("client-focus-in", c);
|
||||||
window_update_focus(c->session->curw->window);
|
window_update_focus(c->session->curw->window);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fire the key. */
|
/* Fire the key. */
|
||||||
if (key != KEYC_UNKNOWN) {
|
if (kp.key != KEYC_UNKNOWN) {
|
||||||
event = xcalloc(1, sizeof *event);
|
event = xcalloc(1, sizeof *event);
|
||||||
event->key = key;
|
event->key = kp.key;
|
||||||
memcpy(&event->m, &m, sizeof event->m);
|
memcpy(&event->m, &kp.m, sizeof event->m);
|
||||||
|
|
||||||
event->buf = xmalloc(size);
|
event->buf = xmalloc(kp.size);
|
||||||
event->len = size;
|
event->len = kp.size;
|
||||||
memcpy (event->buf, buf, event->len);
|
memcpy (event->buf, buf, event->len);
|
||||||
|
|
||||||
if (!server_client_handle_key(c, event)) {
|
if (!server_client_handle_key(c, event)) {
|
||||||
@@ -1055,15 +1003,7 @@ complete_key:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Remove data from buffer. */
|
/* Remove data from buffer. */
|
||||||
evbuffer_drain(tty->in, size);
|
evbuffer_drain(tty->in, kp.size);
|
||||||
|
|
||||||
return (1);
|
|
||||||
|
|
||||||
discard_key:
|
|
||||||
log_debug("%s: discard key %.*s %#llx", c->name, (int)size, buf, key);
|
|
||||||
|
|
||||||
/* Remove data from buffer. */
|
|
||||||
evbuffer_drain(tty->in, size);
|
|
||||||
|
|
||||||
return (1);
|
return (1);
|
||||||
}
|
}
|
||||||
@@ -1328,7 +1268,7 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size,
|
|||||||
} else
|
} else
|
||||||
return (-1);
|
return (-1);
|
||||||
|
|
||||||
/* Fill mouse event. */
|
/* Fill mouse event. The last mouse state is updated when applied. */
|
||||||
m->lx = tty->mouse_last_x;
|
m->lx = tty->mouse_last_x;
|
||||||
m->x = x;
|
m->x = x;
|
||||||
m->ly = tty->mouse_last_y;
|
m->ly = tty->mouse_last_y;
|
||||||
@@ -1338,11 +1278,6 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size,
|
|||||||
m->sgr_type = sgr_type;
|
m->sgr_type = sgr_type;
|
||||||
m->sgr_b = sgr_b;
|
m->sgr_b = sgr_b;
|
||||||
|
|
||||||
/* Update last mouse state. */
|
|
||||||
tty->mouse_last_x = x;
|
|
||||||
tty->mouse_last_y = y;
|
|
||||||
tty->mouse_last_b = b;
|
|
||||||
|
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1351,7 +1286,8 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size,
|
|||||||
* partial.
|
* partial.
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
tty_keys_clipboard(struct tty *tty, const char *buf, size_t len, size_t *size)
|
tty_keys_clipboard(struct tty *tty, const char *buf, size_t len, size_t *size,
|
||||||
|
int apply)
|
||||||
{
|
{
|
||||||
struct client *c = tty->client;
|
struct client *c = tty->client;
|
||||||
size_t end, terminator = 0, needed;
|
size_t end, terminator = 0, needed;
|
||||||
@@ -1397,6 +1333,8 @@ tty_keys_clipboard(struct tty *tty, const char *buf, size_t len, size_t *size)
|
|||||||
if (end == len)
|
if (end == len)
|
||||||
return (1);
|
return (1);
|
||||||
*size = end + 1;
|
*size = end + 1;
|
||||||
|
if (!apply)
|
||||||
|
return (0);
|
||||||
|
|
||||||
/* Skip the initial part. */
|
/* Skip the initial part. */
|
||||||
buf += 5;
|
buf += 5;
|
||||||
@@ -1466,7 +1404,7 @@ tty_keys_clipboard(struct tty *tty, const char *buf, size_t len, size_t *size)
|
|||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len,
|
tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len,
|
||||||
size_t *size)
|
size_t *size, int apply)
|
||||||
{
|
{
|
||||||
struct client *c = tty->client;
|
struct client *c = tty->client;
|
||||||
u_int i, n = 0;
|
u_int i, n = 0;
|
||||||
@@ -1504,6 +1442,8 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len,
|
|||||||
return (-1);
|
return (-1);
|
||||||
tmp[i] = '\0';
|
tmp[i] = '\0';
|
||||||
*size = 4 + i;
|
*size = 4 + i;
|
||||||
|
if (!apply)
|
||||||
|
return (0);
|
||||||
|
|
||||||
/* Convert all arguments to numbers. */
|
/* Convert all arguments to numbers. */
|
||||||
cp = tmp;
|
cp = tmp;
|
||||||
@@ -1548,7 +1488,8 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len,
|
|||||||
* failure, 1 for partial.
|
* failure, 1 for partial.
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
tty_keys_sync(struct tty *tty, const char *buf, size_t len, size_t *size)
|
tty_keys_sync(struct tty *tty, const char *buf, size_t len, size_t *size,
|
||||||
|
int apply)
|
||||||
{
|
{
|
||||||
struct client *c = tty->client;
|
struct client *c = tty->client;
|
||||||
static const char prefix[] = "\033[?2026;";
|
static const char prefix[] = "\033[?2026;";
|
||||||
@@ -1580,6 +1521,8 @@ tty_keys_sync(struct tty *tty, const char *buf, size_t len, size_t *size)
|
|||||||
if (buf[i++] != 'y')
|
if (buf[i++] != 'y')
|
||||||
return (-1);
|
return (-1);
|
||||||
*size = i;
|
*size = i;
|
||||||
|
if (!apply)
|
||||||
|
return (0);
|
||||||
|
|
||||||
if (status == 1 || status == 2 || status == 3) {
|
if (status == 1 || status == 2 || status == 3) {
|
||||||
tty_parse_client_features(c, "sync", ",");
|
tty_parse_client_features(c, "sync", ",");
|
||||||
@@ -1597,7 +1540,7 @@ tty_keys_sync(struct tty *tty, const char *buf, size_t len, size_t *size)
|
|||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
tty_keys_device_attributes2(struct tty *tty, const char *buf, size_t len,
|
tty_keys_device_attributes2(struct tty *tty, const char *buf, size_t len,
|
||||||
size_t *size)
|
size_t *size, int apply)
|
||||||
{
|
{
|
||||||
struct client *c = tty->client;
|
struct client *c = tty->client;
|
||||||
u_int i, n = 0;
|
u_int i, n = 0;
|
||||||
@@ -1635,6 +1578,8 @@ tty_keys_device_attributes2(struct tty *tty, const char *buf, size_t len,
|
|||||||
return (-1);
|
return (-1);
|
||||||
tmp[i] = '\0';
|
tmp[i] = '\0';
|
||||||
*size = 4 + i;
|
*size = 4 + i;
|
||||||
|
if (!apply)
|
||||||
|
return (0);
|
||||||
|
|
||||||
/* Convert all arguments to numbers. */
|
/* Convert all arguments to numbers. */
|
||||||
cp = tmp;
|
cp = tmp;
|
||||||
@@ -1676,7 +1621,7 @@ tty_keys_device_attributes2(struct tty *tty, const char *buf, size_t len,
|
|||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
tty_keys_extended_device_attributes(struct tty *tty, const char *buf,
|
tty_keys_extended_device_attributes(struct tty *tty, const char *buf,
|
||||||
size_t len, size_t *size)
|
size_t len, size_t *size, int apply)
|
||||||
{
|
{
|
||||||
struct client *c = tty->client;
|
struct client *c = tty->client;
|
||||||
u_int i;
|
u_int i;
|
||||||
@@ -1715,7 +1660,7 @@ tty_keys_extended_device_attributes(struct tty *tty, const char *buf,
|
|||||||
if (i == (sizeof tmp) - 1)
|
if (i == (sizeof tmp) - 1)
|
||||||
return (-1);
|
return (-1);
|
||||||
*size = 5 + i;
|
*size = 5 + i;
|
||||||
if (i == 0)
|
if (i == 0 || !apply)
|
||||||
return (0);
|
return (0);
|
||||||
tmp[i - 1] = '\0';
|
tmp[i - 1] = '\0';
|
||||||
|
|
||||||
@@ -1749,7 +1694,7 @@ tty_keys_extended_device_attributes(struct tty *tty, const char *buf,
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Handle foreground or background input. Returns 0 for success, -1 for
|
* Handle foreground or background input. Returns 0 for success, -1 for
|
||||||
* failure, 1 for partial.
|
* failure, 1 for partial. If fg is NULL, only check the reply.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
tty_keys_colours(struct tty *tty, const char *buf, size_t len, size_t *size,
|
tty_keys_colours(struct tty *tty, const char *buf, size_t len, size_t *size,
|
||||||
@@ -1797,7 +1742,7 @@ tty_keys_colours(struct tty *tty, const char *buf, size_t len, size_t *size,
|
|||||||
if (i == (sizeof tmp) - 1)
|
if (i == (sizeof tmp) - 1)
|
||||||
return (-1);
|
return (-1);
|
||||||
*size = 6 + i;
|
*size = 6 + i;
|
||||||
if (i == 0)
|
if (i == 0 || fg == NULL)
|
||||||
return (0);
|
return (0);
|
||||||
if (tmp[i - 1] == '\033')
|
if (tmp[i - 1] == '\033')
|
||||||
tmp[i - 1] = '\0';
|
tmp[i - 1] = '\0';
|
||||||
@@ -1825,9 +1770,30 @@ tty_keys_colours(struct tty *tty, const char *buf, size_t len, size_t *size,
|
|||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Handle foreground or background input from the terminal. */
|
||||||
|
static int
|
||||||
|
tty_keys_colours1(struct tty *tty, const char *buf, size_t len, size_t *size,
|
||||||
|
int apply)
|
||||||
|
{
|
||||||
|
struct client *c = tty->client;
|
||||||
|
int bg = tty->bg, n;
|
||||||
|
|
||||||
|
if (!apply)
|
||||||
|
return (tty_keys_colours(tty, buf, len, size, NULL, NULL));
|
||||||
|
|
||||||
|
n = tty_keys_colours(tty, buf, len, size, &tty->fg, &tty->bg);
|
||||||
|
if (n == 0) {
|
||||||
|
if (tty->bg != bg)
|
||||||
|
server_client_update_theme_colours(c);
|
||||||
|
session_theme_changed(c->session);
|
||||||
|
}
|
||||||
|
return (n);
|
||||||
|
}
|
||||||
|
|
||||||
/* Handle OSC 4 palette colour responses. */
|
/* Handle OSC 4 palette colour responses. */
|
||||||
static int
|
static int
|
||||||
tty_keys_palette(struct tty *tty, const char *buf, size_t len, size_t *size)
|
tty_keys_palette(struct tty *tty, const char *buf, size_t len, size_t *size,
|
||||||
|
int apply)
|
||||||
{
|
{
|
||||||
struct client *c = tty->client;
|
struct client *c = tty->client;
|
||||||
u_int i;
|
u_int i;
|
||||||
@@ -1881,6 +1847,8 @@ tty_keys_palette(struct tty *tty, const char *buf, size_t len, size_t *size)
|
|||||||
return (-1);
|
return (-1);
|
||||||
if (idx < 0 || idx > 255)
|
if (idx < 0 || idx > 255)
|
||||||
return (-1);
|
return (-1);
|
||||||
|
if (!apply)
|
||||||
|
return (0);
|
||||||
|
|
||||||
/* Work out the colour. */
|
/* Work out the colour. */
|
||||||
pd.c = colour_parseX11(endptr + 1);
|
pd.c = colour_parseX11(endptr + 1);
|
||||||
@@ -1891,3 +1859,76 @@ tty_keys_palette(struct tty *tty, const char *buf, size_t len, size_t *size)
|
|||||||
|
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Process window size change escape sequences. */
|
||||||
|
static int
|
||||||
|
tty_keys_winsz(struct tty *tty, const char *buf, size_t len, size_t *size,
|
||||||
|
int apply)
|
||||||
|
{
|
||||||
|
struct client *c = tty->client;
|
||||||
|
size_t end;
|
||||||
|
char tmp[64];
|
||||||
|
u_int sx, sy, xpixel, ypixel, char_x, char_y;
|
||||||
|
|
||||||
|
*size = 0;
|
||||||
|
|
||||||
|
/* If we did not request this, ignore it. */
|
||||||
|
if (!(tty->flags & TTY_WINSIZEQUERY))
|
||||||
|
return (-1);
|
||||||
|
|
||||||
|
/* First two 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);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Stop at either 't' or anything that isn't a
|
||||||
|
* number or ';'.
|
||||||
|
*/
|
||||||
|
for (end = 2; end < len && end != sizeof tmp; end++) {
|
||||||
|
if (buf[end] == 't')
|
||||||
|
break;
|
||||||
|
if (!isdigit((u_char)buf[end]) && buf[end] != ';')
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (end == len)
|
||||||
|
return (1);
|
||||||
|
if (end == sizeof tmp || buf[end] != 't')
|
||||||
|
return (-1);
|
||||||
|
|
||||||
|
/* Copy to the buffer. */
|
||||||
|
memcpy(tmp, buf + 2, end - 2);
|
||||||
|
tmp[end - 2] = '\0';
|
||||||
|
|
||||||
|
/* Try to parse the window size sequence. */
|
||||||
|
if (sscanf(tmp, "8;%u;%u", &sy, &sx) == 2) {
|
||||||
|
*size = end + 1;
|
||||||
|
if (!apply)
|
||||||
|
return (0);
|
||||||
|
|
||||||
|
/* Window size in characters. */
|
||||||
|
tty_set_size(tty, sx, sy, tty->xpixel, tty->ypixel);
|
||||||
|
return (0);
|
||||||
|
} else if (sscanf(tmp, "4;%u;%u", &ypixel, &xpixel) == 2) {
|
||||||
|
*size = end + 1;
|
||||||
|
if (!apply)
|
||||||
|
return (0);
|
||||||
|
|
||||||
|
/* Window size in pixels. */
|
||||||
|
char_x = (xpixel && tty->sx) ? xpixel / tty->sx : 0;
|
||||||
|
char_y = (ypixel && tty->sy) ? ypixel / tty->sy : 0;
|
||||||
|
tty_set_size(tty, tty->sx, tty->sy, char_x, char_y);
|
||||||
|
tty_invalidate(tty);
|
||||||
|
|
||||||
|
tty->flags &= ~TTY_WINSIZEQUERY;
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
log_debug("%s: unrecognized window size sequence: %s", c->name, tmp);
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user