Query the client terminal for foreground and background colours and if

OSC 10 or 11 is received but no colour has been set inside tmux, return
the colour from the first attached client (probably most people will
have all light or or all dark terminals).
This commit is contained in:
nicm 2023-01-03 11:43:24 +00:00
parent 3fe01ff09c
commit a41a927441
6 changed files with 199 additions and 61 deletions

View File

@ -834,7 +834,8 @@ cmdq_print_data(struct cmdq_item *item, int parse, struct evbuffer *evb)
char *sanitized, *msg, *line;
if (!parse) {
utf8_stravisx(&msg, data, size, VIS_OCTAL|VIS_CSTYLE);
utf8_stravisx(&msg, data, size,
VIS_OCTAL|VIS_CSTYLE|VIS_NOSLASH);
log_debug("%s: %s", __func__, msg);
} else {
msg = EVBUFFER_DATA(evb);

View File

@ -960,6 +960,47 @@ colour_byname(const char *name)
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. */
void
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);
}
}

115
input.c
View File

@ -1086,6 +1086,7 @@ input_reply(struct input_ctx *ictx, const char *fmt, ...)
xvasprintf(&reply, fmt, ap);
va_end(ap);
log_debug("%s: %s", __func__, reply);
bufferevent_write(bev, reply, strlen(reply));
free(reply);
}
@ -2456,47 +2457,6 @@ input_top_bit_set(struct input_ctx *ictx)
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. */
static void
input_osc_colour_reply(struct input_ctx *ictx, u_int n, int c)
@ -2545,7 +2505,7 @@ input_osc_4(struct input_ctx *ictx, const char *p)
input_osc_colour_reply(ictx, 4, c);
continue;
}
if ((c = input_osc_parse_colour(s)) == -1) {
if ((c = colour_parseX11(s)) == -1) {
s = next;
continue;
}
@ -2601,6 +2561,47 @@ bad:
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. */
static void
input_osc_10(struct input_ctx *ictx, const char *p)
@ -2610,14 +2611,18 @@ input_osc_10(struct input_ctx *ictx, const char *p)
int c;
if (strcmp(p, "?") == 0) {
if (wp != NULL) {
tty_default_colours(&defaults, wp);
input_osc_colour_reply(ictx, 10, defaults.fg);
}
if (wp == NULL)
return;
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;
}
if ((c = input_osc_parse_colour(p)) == -1) {
if ((c = colour_parseX11(p)) == -1) {
log_debug("bad OSC 10: %s", p);
return;
}
@ -2654,14 +2659,18 @@ input_osc_11(struct input_ctx *ictx, const char *p)
int c;
if (strcmp(p, "?") == 0) {
if (wp != NULL) {
tty_default_colours(&defaults, wp);
input_osc_colour_reply(ictx, 11, defaults.bg);
}
if (wp == NULL)
return;
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;
}
if ((c = input_osc_parse_colour(p)) == -1) {
if ((c = colour_parseX11(p)) == -1) {
log_debug("bad OSC 11: %s", p);
return;
}
@ -2706,7 +2715,7 @@ input_osc_12(struct input_ctx *ictx, const char *p)
return;
}
if ((c = input_osc_parse_colour(p)) == -1) {
if ((c = colour_parseX11(p)) == -1) {
log_debug("bad OSC 12: %s", p);
return;
}

7
tmux.h
View File

@ -1380,6 +1380,8 @@ struct tty {
u_int osy;
int mode;
int fg;
int bg;
u_int rlower;
u_int rupper;
@ -1411,6 +1413,10 @@ struct tty {
#define TTY_HAVEXDA 0x200
#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;
struct tty_term *term;
@ -2759,6 +2765,7 @@ int colour_fromstring(const char *s);
int colour_256toRGB(int);
int colour_256to16(int);
int colour_byname(const char *);
int colour_parseX11(const char *);
void colour_palette_init(struct colour_palette *);
void colour_palette_clear(struct colour_palette *);
void colour_palette_free(struct colour_palette *);

View File

@ -59,6 +59,7 @@ 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 *,
size_t, size_t *);
static int tty_keys_colours(struct tty *, const char *, size_t, size_t *);
/* A key tree entry. */
struct tty_key {
@ -719,6 +720,17 @@ tty_keys_next(struct tty *tty)
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? */
switch (tty_keys_mouse(tty, buf, len, &size, &m)) {
case 0: /* yes */
@ -1278,7 +1290,7 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len,
if (len == 3)
return (1);
/* Copy the rest up to a 'c'. */
/* Copy the rest up to a c. */
for (i = 0; i < (sizeof tmp); i++) {
if (3 + i == len)
return (1);
@ -1352,7 +1364,7 @@ tty_keys_device_attributes2(struct tty *tty, const char *buf, size_t len,
if (len == 3)
return (1);
/* Copy the rest up to a 'c'. */
/* Copy the rest up to a c. */
for (i = 0; i < (sizeof tmp); i++) {
if (3 + i == len)
return (1);
@ -1433,7 +1445,7 @@ tty_keys_extended_device_attributes(struct tty *tty, const char *buf,
if (len == 4)
return (1);
/* Copy the rest up to a '\033\\'. */
/* Copy the rest up to \033\. */
for (i = 0; i < (sizeof tmp) - 1; i++) {
if (4 + i == len)
return (1);
@ -1465,3 +1477,68 @@ tty_keys_extended_device_attributes(struct tty *tty, const char *buf,
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\. */
for (i = 0; i < (sizeof tmp) - 1; i++) {
if (5 + i == len)
return (1);
if (buf[5 + i - 1] == '\033' && buf[5 + i] == '\\')
break;
tmp[i] = buf[5 + i];
}
if (i == (sizeof tmp) - 1)
return (-1);
tmp[i - 1] = '\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);
}

10
tty.c
View File

@ -108,6 +108,7 @@ tty_init(struct tty *tty, struct client *c)
tty->cstyle = SCREEN_CURSOR_DEFAULT;
tty->ccolour = -1;
tty->fg = tty->bg = -1;
if (tcgetattr(c->fd, &tty->tio) != 0)
return (-1);
@ -286,7 +287,6 @@ tty_open(struct tty *tty, char **cause)
evtimer_set(&tty->timer, tty_timer_callback, tty);
tty_start_tty(tty);
tty_keys_build(tty);
return (0);
@ -301,7 +301,7 @@ tty_start_timer_callback(__unused int fd, __unused short events, void *data)
log_debug("%s: start timer fired", c->name);
if ((tty->flags & (TTY_HAVEDA|TTY_HAVEDA2|TTY_HAVEXDA)) == 0)
tty_update_features(tty);
tty->flags |= (TTY_HAVEDA|TTY_HAVEDA2|TTY_HAVEXDA);
tty->flags |= TTY_ALL_REQUEST_FLAGS;
}
void
@ -369,8 +369,12 @@ tty_send_requests(struct tty *tty)
tty_puts(tty, "\033[>c");
if (~tty->flags & TTY_HAVEXDA)
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
tty->flags |= (TTY_HAVEDA|TTY_HAVEDA2|TTY_HAVEXDA);
tty->flags |= TTY_ALL_REQUEST_FLAGS;
}
void