From d21788ce70be80e9c4ed0c52c149e01147c4a823 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 3 Jan 2023 11:43:24 +0000 Subject: [PATCH] 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). --- usr.bin/tmux/cmd-queue.c | 5 +- usr.bin/tmux/colour.c | 44 ++++++++++++++- usr.bin/tmux/input.c | 117 +++++++++++++++++++++------------------ usr.bin/tmux/tmux.h | 9 ++- usr.bin/tmux/tty-keys.c | 85 ++++++++++++++++++++++++++-- usr.bin/tmux/tty.c | 12 ++-- 6 files changed, 205 insertions(+), 67 deletions(-) diff --git a/usr.bin/tmux/cmd-queue.c b/usr.bin/tmux/cmd-queue.c index 2ca2eb81266..cfeb48c59c5 100644 --- a/usr.bin/tmux/cmd-queue.c +++ b/usr.bin/tmux/cmd-queue.c @@ -1,4 +1,4 @@ -/* $OpenBSD: cmd-queue.c,v 1.112 2022/12/16 08:22:05 nicm Exp $ */ +/* $OpenBSD: cmd-queue.c,v 1.113 2023/01/03 11:43:24 nicm Exp $ */ /* * Copyright (c) 2013 Nicholas Marriott @@ -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); diff --git a/usr.bin/tmux/colour.c b/usr.bin/tmux/colour.c index 16080ae3f50..4f7a9a08d55 100644 --- a/usr.bin/tmux/colour.c +++ b/usr.bin/tmux/colour.c @@ -1,4 +1,4 @@ -/* $OpenBSD: colour.c,v 1.25 2022/03/24 12:07:25 nicm Exp $ */ +/* $OpenBSD: colour.c,v 1.26 2023/01/03 11:43:24 nicm Exp $ */ /* * Copyright (c) 2008 Nicholas Marriott @@ -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); } - } diff --git a/usr.bin/tmux/input.c b/usr.bin/tmux/input.c index fd3ed0744b9..6ca2f08db38 100644 --- a/usr.bin/tmux/input.c +++ b/usr.bin/tmux/input.c @@ -1,4 +1,4 @@ -/* $OpenBSD: input.c,v 1.212 2022/11/11 08:37:55 nicm Exp $ */ +/* $OpenBSD: input.c,v 1.213 2023/01/03 11:43:24 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -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; } diff --git a/usr.bin/tmux/tmux.h b/usr.bin/tmux/tmux.h index 4c2544b7fa8..f3b57669f72 100644 --- a/usr.bin/tmux/tmux.h +++ b/usr.bin/tmux/tmux.h @@ -1,4 +1,4 @@ -/* $OpenBSD: tmux.h,v 1.1188 2022/12/26 19:16:03 jmc Exp $ */ +/* $OpenBSD: tmux.h,v 1.1189 2023/01/03 11:43:24 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -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 *); diff --git a/usr.bin/tmux/tty-keys.c b/usr.bin/tmux/tty-keys.c index 06ac22570a0..d0a423daf3c 100644 --- a/usr.bin/tmux/tty-keys.c +++ b/usr.bin/tmux/tty-keys.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tty-keys.c,v 1.162 2022/11/11 08:44:11 nicm Exp $ */ +/* $OpenBSD: tty-keys.c,v 1.163 2023/01/03 11:43:24 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -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); +} diff --git a/usr.bin/tmux/tty.c b/usr.bin/tmux/tty.c index d3c6f9645fb..a810aede094 100644 --- a/usr.bin/tmux/tty.c +++ b/usr.bin/tmux/tty.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tty.c,v 1.425 2022/11/11 08:37:55 nicm Exp $ */ +/* $OpenBSD: tty.c,v 1.426 2023/01/03 11:43:24 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -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 -- 2.20.1