From 4743798400e3eeae2902c4540da905a565f05c47 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Thu, 5 Oct 2017 20:43:02 +0100 Subject: [PATCH] Support OSC 4 terminal colour-palette queries. Markus Gans points out that some applications which (not at all unreasonably) don't trust $TERM to tell them the full capabilities of their terminal will use the sequence "OSC 4 ; nn ; ? BEL" to ask for the colour-palette value in position nn, and they may not particularly care _what_ the results are but they will use them to decide whether the right number of colour palette entries even exist. --- fuzzterm.c | 1 + putty.h | 1 + terminal.c | 66 +++++++++++++++++++++++++++++++++++------------- unix/gtkwin.c | 11 ++++++++ windows/window.c | 52 +++++++++++++++++++++++++------------- 5 files changed, 96 insertions(+), 35 deletions(-) diff --git a/fuzzterm.c b/fuzzterm.c index 480dca37..c57412c4 100644 --- a/fuzzterm.c +++ b/fuzzterm.c @@ -81,6 +81,7 @@ Context get_ctx(void *frontend) { void free_ctx(Context ctx) { } void palette_set(void *frontend, int a, int b, int c, int d) { } void palette_reset(void *frontend) { } +int palette_get(void *frontend, int n, int *r, int *g, int *b) {return FALSE;} void write_clip(void *frontend, wchar_t *a, int *b, int c, int d) { } void get_clip(void *frontend, wchar_t **w, int *i) { } void set_raw_mouse_mode(void *frontend, int m) { } diff --git a/putty.h b/putty.h index 8a8b5425..f89d0709 100644 --- a/putty.h +++ b/putty.h @@ -633,6 +633,7 @@ Context get_ctx(void *frontend); void free_ctx(Context); void palette_set(void *frontend, int, int, int, int); void palette_reset(void *frontend); +int palette_get(void *frontend, int n, int *r, int *g, int *b); void write_aclip(void *frontend, char *, int, int); void write_clip(void *frontend, wchar_t *, int *, int, int); void get_clip(void *frontend, wchar_t **, int *); diff --git a/terminal.c b/terminal.c index 5102db12..0fa52e17 100644 --- a/terminal.c +++ b/terminal.c @@ -2727,6 +2727,22 @@ static void do_osc(Terminal *term) if (!term->no_remote_wintitle) set_title(term->frontend, term->osc_string); break; + case 4: + if (term->ldisc && !strcmp(term->osc_string, "?")) { + int r, g, b; + if (palette_get(term->frontend, toint(term->esc_args[1]), + &r, &g, &b)) { + char *reply_buf = dupprintf( + "\033]4;%u;rgb:%04x/%04x/%04x\007", + term->esc_args[1], + (unsigned)r * 0x0101, + (unsigned)g * 0x0101, + (unsigned)b * 0x0101); + ldisc_send(term->ldisc, reply_buf, strlen(reply_buf), 0); + sfree(reply_buf); + } + } + break; } } } @@ -3365,6 +3381,7 @@ static void term_out(Terminal *term) compatibility(OTHER); term->termstate = SEEN_OSC; term->esc_args[0] = 0; + term->esc_nargs = 1; break; case '7': /* DECSC: save cursor */ compatibility(VT100); @@ -4470,25 +4487,40 @@ static void term_out(Terminal *term) case '7': case '8': case '9': - if (term->esc_args[0] <= UINT_MAX / 10 && - term->esc_args[0] * 10 <= UINT_MAX - c - '0') - term->esc_args[0] = 10 * term->esc_args[0] + c - '0'; + if (term->esc_args[term->esc_nargs-1] <= UINT_MAX / 10 && + term->esc_args[term->esc_nargs-1] * 10 <= UINT_MAX - c - '0') + term->esc_args[term->esc_nargs-1] = + 10 * term->esc_args[term->esc_nargs-1] + c - '0'; else - term->esc_args[0] = UINT_MAX; + term->esc_args[term->esc_nargs-1] = UINT_MAX; break; - case 'L': - /* - * Grotty hack to support xterm and DECterm title - * sequences concurrently. - */ - if (term->esc_args[0] == 2) { - term->esc_args[0] = 1; - break; - } - /* else fall through */ - default: - term->termstate = OSC_STRING; - term->osc_strlen = 0; + default: + /* + * _Most_ other characters here terminate the + * immediate parsing of the OSC sequence and go + * into OSC_STRING state, but we deal with a + * couple of exceptions first. + */ + if (c == 'L' && term->esc_args[0] == 2) { + /* + * Grotty hack to support xterm and DECterm title + * sequences concurrently. + */ + term->esc_args[0] = 1; + } else if (c == ';' && term->esc_nargs == 1 && + term->esc_args[0] == 4) { + /* + * xterm's OSC 4 sequence to query the current + * RGB value of a colour takes a second + * numeric argument which is easiest to parse + * using the existing system rather than in + * do_osc. + */ + term->esc_args[term->esc_nargs++] = 0; + } else { + term->termstate = OSC_STRING; + term->osc_strlen = 0; + } } break; case OSC_STRING: diff --git a/unix/gtkwin.c b/unix/gtkwin.c index dcff614f..969c42ff 100644 --- a/unix/gtkwin.c +++ b/unix/gtkwin.c @@ -2222,6 +2222,17 @@ void palette_set(void *frontend, int n, int r, int g, int b) } } +int palette_get(void *frontend, int n, int *r, int *g, int *b) +{ + struct gui_data *inst = (struct gui_data *)frontend; + if (n < 0 || n >= NALLCOLOURS) + return FALSE; + *r = inst->cols[n].red >> 8; + *g = inst->cols[n].green >> 8; + *b = inst->cols[n].blue >> 8; + return TRUE; +} + void palette_reset(void *frontend) { struct gui_data *inst = (struct gui_data *)frontend; diff --git a/windows/window.c b/windows/window.c index 97c4d462..aa96a578 100644 --- a/windows/window.c +++ b/windows/window.c @@ -198,6 +198,9 @@ static int descent; #define NEXTCOLOURS 240 #define NALLCOLOURS (NCFGCOLOURS + NEXTCOLOURS) static COLORREF colours[NALLCOLOURS]; +struct rgb { + int r, g, b; +} colours_rgb[NALLCOLOURS]; static HPALETTE pal; static LPLOGPALETTE logpal; static RGBTRIPLE defpal[NALLCOLOURS]; @@ -1264,6 +1267,19 @@ static void systopalette(void) } } +static void internal_set_colour(int i, int r, int g, int b) +{ + assert(i >= 0); + assert(i < NALLCOLOURS); + if (pal) + colours[i] = PALETTERGB(r, g, b); + else + colours[i] = RGB(r, g, b); + colours_rgb[i].r = r; + colours_rgb[i].g = g; + colours_rgb[i].b = b; +} + /* * Set up the colour palette. */ @@ -1298,15 +1314,9 @@ static void init_palette(void) } ReleaseDC(hwnd, hdc); } - if (pal) - for (i = 0; i < NALLCOLOURS; i++) - colours[i] = PALETTERGB(defpal[i].rgbtRed, - defpal[i].rgbtGreen, - defpal[i].rgbtBlue); - else - for (i = 0; i < NALLCOLOURS; i++) - colours[i] = RGB(defpal[i].rgbtRed, - defpal[i].rgbtGreen, defpal[i].rgbtBlue); + for (i = 0; i < NALLCOLOURS; i++) + internal_set_colour(i, defpal[i].rgbtRed, + defpal[i].rgbtGreen, defpal[i].rgbtBlue); } /* @@ -4865,15 +4875,24 @@ void free_ctx(Context ctx) static void real_palette_set(int n, int r, int g, int b) { + internal_set_colour(n, r, g, b); if (pal) { logpal->palPalEntry[n].peRed = r; logpal->palPalEntry[n].peGreen = g; logpal->palPalEntry[n].peBlue = b; logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE; - colours[n] = PALETTERGB(r, g, b); SetPaletteEntries(pal, 0, NALLCOLOURS, logpal->palPalEntry); - } else - colours[n] = RGB(r, g, b); + } +} + +int palette_get(void *frontend, int n, int *r, int *g, int *b) +{ + if (n < 0 || n >= NALLCOLOURS) + return FALSE; + *r = colours_rgb[n].r; + *g = colours_rgb[n].g; + *b = colours_rgb[n].b; + return TRUE; } void palette_set(void *frontend, int n, int r, int g, int b) @@ -4903,17 +4922,14 @@ void palette_reset(void *frontend) /* And this */ for (i = 0; i < NALLCOLOURS; i++) { + internal_set_colour(i, defpal[i].rgbtRed, + defpal[i].rgbtGreen, defpal[i].rgbtBlue); if (pal) { logpal->palPalEntry[i].peRed = defpal[i].rgbtRed; logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen; logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue; logpal->palPalEntry[i].peFlags = 0; - colours[i] = PALETTERGB(defpal[i].rgbtRed, - defpal[i].rgbtGreen, - defpal[i].rgbtBlue); - } else - colours[i] = RGB(defpal[i].rgbtRed, - defpal[i].rgbtGreen, defpal[i].rgbtBlue); + } } if (pal) {