Merge remote-tracking branch 'refs/remotes/tmux-openbsd/master'

* refs/remotes/tmux-openbsd/master:
  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.
  Use the .Cm macro for keywords used as arguments to commands and options, mostly replacing dubious use of .Ic, but a few others as well; nicm@ agrees with the general direction.
This commit is contained in:
tmux update bot
2026-10-06 21:00:17 +00:00
3 changed files with 510 additions and 453 deletions

422
tmux.1

File diff suppressed because it is too large Load Diff

12
tmux.h
View File

@@ -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>
@@ -411,6 +411,16 @@ enum {
KEYC_REPORT_DARK_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. */
KEYC_MOUSE, /* unclassified mouse event */
KEYC_DRAGGING, /* dragging in progress */

View File

@@ -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>
@@ -37,6 +37,20 @@
* 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_add(struct tty *, const char *, key_code);
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 *);
static struct tty_key *tty_keys_find(struct tty *, const char *, 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 int tty_keys_extended_key(struct tty *, const char *, size_t,
size_t *, key_code *);
static int tty_keys_mouse(struct tty *, const char *, size_t, size_t *,
struct mouse_event *);
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,
size_t *);
size_t *, int);
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 *,
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 *);
size_t, size_t *, int);
static int tty_keys_sync(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. */
struct tty_key {
@@ -75,8 +110,8 @@ struct tty_key {
/* Default raw keys. */
struct tty_default_key_raw {
const char *string;
key_code key;
const char *string;
key_code key;
};
static const struct tty_default_key_raw tty_default_raw_keys[] = {
/* Application escape. */
@@ -614,10 +649,45 @@ tty_keys_partial_paste_end(const char *buf, size_t len)
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. */
static int
tty_keys_next1(struct tty *tty, const char *buf, size_t len, key_code *key,
size_t *size, int expired)
tty_keys_next1(struct tty *tty, const char *buf, size_t len,
struct tty_key_parse *kp, int expired)
{
struct client *c = tty->client;
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;
utf8_char uc;
u_int i;
key_code key;
log_debug("%s: next key is %zu (%.*s) (expired=%d)", c->name, len,
(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? */
tk = tty_keys_find(tty, buf, len, size);
tk = tty_keys_find(tty, buf, len, &kp->size);
if (tk != NULL && tk->key != KEYC_UNKNOWN) {
tk1 = tk;
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);
if (tk->next != NULL && !expired)
return (1);
*key = tk->key;
if ((*key & KEYC_MASK_KEY) == KEYC_PASTE_START)
tty->flags |= TTY_BRACKETPASTE;
else if ((*key & KEYC_MASK_KEY) == KEYC_PASTE_END)
tty->flags &= ~TTY_BRACKETPASTE;
key = kp->key = tk->key;
/*
* A key with an implied Meta already includes its Escape, and
* 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);
}
/* Is this the start of a known key? */
if (tk != NULL && !expired)
return (1);
/* Is this valid UTF-8? */
more = utf8_open(&ud, (u_char)*buf);
if (more == UTF8_MORE) {
*size = ud.size;
kp->size = ud.size;
if (len < ud.size) {
if (!expired)
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)
return (-1);
*key = uc;
kp->key = uc;
log_debug("%s: UTF-8 key %.*s %#llx", c->name, (int)ud.size,
ud.data, *key);
ud.data, kp->key);
return (0);
}
return (-1);
}
/* Process window size change escape sequences. */
static int
tty_keys_winsz(struct tty *tty, const char *buf, size_t len, size_t *size)
/* Apply a complete terminal reply. */
static void
tty_keys_apply(struct tty *tty, const char *buf, struct tty_key_parse *kp)
{
struct client *c = tty->client;
size_t end;
char tmp[64];
u_int sx, sy, xpixel, ypixel, char_x, char_y;
size_t size;
u_int i;
*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 (kp->key == KEYC_MOUSE) {
tty->mouse_last_x = kp->m.x;
tty->mouse_last_y = kp->m.y;
tty->mouse_last_b = kp->m.b;
return;
}
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) {
/* 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);
for (i = 0; i < nitems(tty_keys_replies); i++) {
if (tty_keys_replies[i].key == kp->key) {
tty_keys_replies[i].fn(tty, buf, kp->size, &size, 1);
kp->key = KEYC_UNKNOWN;
return;
}
}
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. */
int
tty_keys_next(struct tty *tty)
@@ -749,9 +799,9 @@ tty_keys_next(struct tty *tty)
const char *buf;
size_t len, size;
cc_t bspace;
int delay, expired = 0, n, bg = tty->bg;
int delay, expired = 0, n;
key_code key, onlykey;
struct mouse_event m = { 0 };
struct tty_key_parse kp;
struct key_event *event;
/* Get key buffer. */
@@ -771,127 +821,9 @@ tty_keys_next(struct tty *tty)
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:
/* 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 */
goto complete_key;
if (n == 1)
@@ -903,22 +835,25 @@ first_key:
*/
if (*buf == '\033' && len > 1) {
/* 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 (key & KEYC_IMPLIED_META) {
if (kp.flags & TTY_KEY_PARSE_NO_META) {
/*
* We want the escape key as well as the xterm
* key, because the xterm sequence implicitly
* includes the escape (so if we see
* \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';
size = 1;
memset(&kp, 0, sizeof kp);
kp.key = '\033';
kp.size = 1;
goto complete_key;
}
key |= KEYC_META;
size++;
kp.key |= KEYC_META;
kp.size++;
goto complete_key;
}
if (n == 1) /* partial */
@@ -974,6 +909,9 @@ first_key:
key = onlykey | KEYC_CTRL | (key & KEYC_META);
}
memset(&kp, 0, sizeof kp);
kp.key = key;
kp.size = size;
goto complete_key;
partial_key:
@@ -1020,32 +958,42 @@ partial_key:
return (0);
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. */
if (event_initialized(&tty->key_timer))
evtimer_del(&tty->key_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. */
if (key == KEYC_FOCUS_OUT) {
if (kp.key == KEYC_FOCUS_OUT) {
c->flags &= ~CLIENT_FOCUSED;
window_update_focus(c->session->curw->window);
events_fire_client("client-focus-out", c);
} else if (key == KEYC_FOCUS_IN) {
} else if (kp.key == KEYC_FOCUS_IN) {
c->flags |= CLIENT_FOCUSED;
events_fire_client("client-focus-in", c);
window_update_focus(c->session->curw->window);
}
/* Fire the key. */
if (key != KEYC_UNKNOWN) {
if (kp.key != KEYC_UNKNOWN) {
event = xcalloc(1, sizeof *event);
event->key = key;
memcpy(&event->m, &m, sizeof event->m);
event->key = kp.key;
memcpy(&event->m, &kp.m, sizeof event->m);
event->buf = xmalloc(size);
event->len = size;
event->buf = xmalloc(kp.size);
event->len = kp.size;
memcpy (event->buf, buf, event->len);
if (!server_client_handle_key(c, event)) {
@@ -1055,15 +1003,7 @@ complete_key:
}
/* Remove data from buffer. */
evbuffer_drain(tty->in, 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);
evbuffer_drain(tty->in, kp.size);
return (1);
}
@@ -1328,7 +1268,7 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size,
} else
return (-1);
/* Fill mouse event. */
/* Fill mouse event. The last mouse state is updated when applied. */
m->lx = tty->mouse_last_x;
m->x = x;
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_b = sgr_b;
/* Update last mouse state. */
tty->mouse_last_x = x;
tty->mouse_last_y = y;
tty->mouse_last_b = b;
return (0);
}
@@ -1351,7 +1286,8 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size,
* partial.
*/
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;
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)
return (1);
*size = end + 1;
if (!apply)
return (0);
/* Skip the initial part. */
buf += 5;
@@ -1466,7 +1404,7 @@ tty_keys_clipboard(struct tty *tty, const char *buf, size_t len, size_t *size)
*/
static int
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;
u_int i, n = 0;
@@ -1504,6 +1442,8 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len,
return (-1);
tmp[i] = '\0';
*size = 4 + i;
if (!apply)
return (0);
/* Convert all arguments to numbers. */
cp = tmp;
@@ -1548,7 +1488,8 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len,
* failure, 1 for partial.
*/
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;
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')
return (-1);
*size = i;
if (!apply)
return (0);
if (status == 1 || status == 2 || status == 3) {
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
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;
u_int i, n = 0;
@@ -1635,6 +1578,8 @@ tty_keys_device_attributes2(struct tty *tty, const char *buf, size_t len,
return (-1);
tmp[i] = '\0';
*size = 4 + i;
if (!apply)
return (0);
/* Convert all arguments to numbers. */
cp = tmp;
@@ -1676,7 +1621,7 @@ tty_keys_device_attributes2(struct tty *tty, const char *buf, size_t len,
*/
static int
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;
u_int i;
@@ -1715,7 +1660,7 @@ tty_keys_extended_device_attributes(struct tty *tty, const char *buf,
if (i == (sizeof tmp) - 1)
return (-1);
*size = 5 + i;
if (i == 0)
if (i == 0 || !apply)
return (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
* failure, 1 for partial.
* failure, 1 for partial. If fg is NULL, only check the reply.
*/
int
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)
return (-1);
*size = 6 + i;
if (i == 0)
if (i == 0 || fg == NULL)
return (0);
if (tmp[i - 1] == '\033')
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);
}
/* 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. */
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;
u_int i;
@@ -1881,6 +1847,8 @@ tty_keys_palette(struct tty *tty, const char *buf, size_t len, size_t *size)
return (-1);
if (idx < 0 || idx > 255)
return (-1);
if (!apply)
return (0);
/* Work out the colour. */
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);
}
/* 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);
}