diff --git a/fuzzterm.c b/fuzzterm.c index 66099cb9..83858f7c 100644 --- a/fuzzterm.c +++ b/fuzzterm.c @@ -91,10 +91,9 @@ static void fuzz_set_minimised(TermWin *tw, bool minimised) {} static void fuzz_set_maximised(TermWin *tw, bool maximised) {} static void fuzz_move(TermWin *tw, int x, int y) {} static void fuzz_set_zorder(TermWin *tw, bool top) {} -static bool fuzz_palette_get(TermWin *tw, unsigned n, int *r, int *g, int *b) -{ return false; } -static void fuzz_palette_set(TermWin *tw, unsigned n, int r, int g, int b) {} -static void fuzz_palette_reset(TermWin *tw) {} +static void fuzz_palette_set(TermWin *tw, unsigned start, unsigned ncolours, + const rgb *colours) {} +static void fuzz_palette_get_overrides(TermWin *tw) {} static void fuzz_get_pos(TermWin *tw, int *x, int *y) { *x = *y = 0; } static void fuzz_get_pixels(TermWin *tw, int *x, int *y) { *x = *y = 0; } @@ -119,9 +118,8 @@ static const TermWinVtable fuzz_termwin_vt = { .set_maximised = fuzz_set_maximised, .move = fuzz_move, .set_zorder = fuzz_set_zorder, - .palette_get = fuzz_palette_get, .palette_set = fuzz_palette_set, - .palette_reset = fuzz_palette_reset, + .palette_get_overrides = fuzz_palette_get_overrides, .get_pos = fuzz_get_pos, .get_pixels = fuzz_get_pixels, }; diff --git a/putty.h b/putty.h index f9eaf451..55751943 100644 --- a/putty.h +++ b/putty.h @@ -63,9 +63,9 @@ * Since the OSC 4 encoding contains the full set of colours used in * the terminal display, that's the encoding used by front ends to * store any actual data associated with their palette entries. So the - * TermWin palette_{set,get} methods use that encoding, and so does - * the bitwise encoding of attribute words used in terminal redraw - * operations. + * TermWin palette_set and palette_get_overrides methods use that + * encoding, and so does the bitwise encoding of attribute words used + * in terminal redraw operations. * * The Conf encoding, of course, is used by config.c and settings.c. * @@ -1204,6 +1204,10 @@ bool console_set_trust_status(Seat *seat, bool trusted); int filexfer_get_userpass_input(Seat *seat, prompts_t *p, bufchain *input); bool cmdline_seat_verbose(Seat *seat); +typedef struct rgb { + uint8_t r, g, b; +} rgb; + /* * Data type 'TermWin', which is a vtable encapsulating all the * functionality that Terminal expects from its containing terminal @@ -1268,10 +1272,16 @@ struct TermWinVtable { void (*move)(TermWin *, int x, int y); void (*set_zorder)(TermWin *, bool top); - /* Palette-handling functions. Palette indices are in OSC 4 encoding. */ - bool (*palette_get)(TermWin *, unsigned n, int *r, int *g, int *b); - void (*palette_set)(TermWin *, unsigned n, int r, int g, int b); - void (*palette_reset)(TermWin *); + /* Set the colour palette that the TermWin will use to display + * text. One call to this function sets 'ncolours' consecutive + * colours in the OSC 4 sequence, starting at 'start'. */ + void (*palette_set)(TermWin *, unsigned start, unsigned ncolours, + const rgb *colours); + + /* Query the front end for any OS-local overrides to the default + * colours stored in Conf. The front end should set any it cares + * about by calling term_palette_override. */ + void (*palette_get_overrides)(TermWin *); void (*get_pos)(TermWin *, int *x, int *y); void (*get_pixels)(TermWin *, int *x, int *y); @@ -1325,12 +1335,11 @@ static inline void win_move(TermWin *win, int x, int y) { win->vt->move(win, x, y); } static inline void win_set_zorder(TermWin *win, bool top) { win->vt->set_zorder(win, top); } -static inline bool win_palette_get(TermWin *win, unsigned n, - int *r, int *g, int *b) { return win->vt->palette_get(win, n, r, g, b); } -static inline void win_palette_set(TermWin *win, unsigned n, - int r, int g, int b) { win->vt->palette_set(win, n, r, g, b); } -static inline void win_palette_reset(TermWin *win) -{ win->vt->palette_reset(win); } +static inline void win_palette_set( + TermWin *win, unsigned start, unsigned ncolours, const rgb *colours) +{ win->vt->palette_set(win, start, ncolours, colours); } +static inline void win_palette_get_overrides(TermWin *win) +{ win->vt->palette_get_overrides(win); } static inline void win_get_pos(TermWin *win, int *x, int *y) { win->vt->get_pos(win, x, y); } static inline void win_get_pixels(TermWin *win, int *x, int *y) @@ -1763,6 +1772,8 @@ void term_keyinputw(Terminal *, const wchar_t * widebuf, int len); void term_get_cursor_position(Terminal *term, int *x, int *y); void term_setup_window_titles(Terminal *term, const char *title_hostname); void term_notify_minimised(Terminal *term, bool minimised); +void term_notify_palette_overrides_changed(Terminal *term); +void term_palette_override(Terminal *term, unsigned osc4_index, rgb rgb); typedef enum SmallKeypadKey { SKK_HOME, SKK_END, SKK_INSERT, SKK_DELETE, SKK_PGUP, SKK_PGDN, diff --git a/terminal.c b/terminal.c index 10e094c7..3b37b497 100644 --- a/terminal.c +++ b/terminal.c @@ -1716,6 +1716,113 @@ void term_setup_window_titles(Terminal *term, const char *title_hostname) win_set_icon_title(term->win, term->icon_title); } +static void palette_rebuild(Terminal *term) +{ + unsigned min_changed = OSC4_NCOLOURS, max_changed = 0; + + for (unsigned i = 0; i < OSC4_NCOLOURS; i++) { + rgb new_value; + bool found = false; + + for (unsigned j = lenof(term->subpalettes); j-- > 0 ;) { + if (term->subpalettes[j].present[i]) { + new_value = term->subpalettes[j].values[i]; + found = true; + break; + } + } + + assert(found); /* we expect SUBPAL_CONF to always be set */ + + if (new_value.r != term->palette[i].r || + new_value.g != term->palette[i].g || + new_value.b != term->palette[i].b) { + term->palette[i] = new_value; + if (min_changed > i) + min_changed = i; + if (max_changed < i) + max_changed = i; + } + } + + if (min_changed <= max_changed) { + /* + * At least one colour changed, so pass the result back to the + * TermWin. This also requires invalidating the rest of the + * window, because usually all the text will need redrawing in + * the new colours. + */ + win_palette_set(term->win, min_changed, max_changed - min_changed + 1, + term->palette + min_changed); + term_invalidate(term); + term_schedule_update(term); + } +} + +static void palette_reset(Terminal *term, bool overrides_only) +{ + if (!overrides_only) { + for (unsigned i = 0; i < OSC4_NCOLOURS; i++) + term->subpalettes[SUBPAL_CONF].present[i] = true; + + /* + * Copy all the palette information out of the Conf. + */ + for (unsigned i = 0; i < CONF_NCOLOURS; i++) { + rgb *col = &term->subpalettes[SUBPAL_CONF].values[ + colour_indices_conf_to_osc4[i]]; + col->r = conf_get_int_int(term->conf, CONF_colours, i*3+0); + col->g = conf_get_int_int(term->conf, CONF_colours, i*3+1); + col->b = conf_get_int_int(term->conf, CONF_colours, i*3+2); + } + + /* + * Directly invent the rest of the xterm-256 colours. + */ + for (unsigned i = 0; i < 216; i++) { + rgb *col = &term->subpalettes[SUBPAL_CONF].values[i + 16]; + int r = i / 36, g = (i / 6) % 6, b = i % 6; + col->r = r ? r * 40 + 55 : 0; + col->g = g ? g * 40 + 55 : 0; + col->b = b ? b * 40 + 55 : 0; + } + for (unsigned i = 0; i < 24; i++) { + rgb *col = &term->subpalettes[SUBPAL_CONF].values[i + 232]; + int shade = i * 10 + 8; + col->r = col->g = col->b = shade; + } + + /* + * Get rid of all escape-sequence configuration. + */ + for (unsigned i = 0; i < OSC4_NCOLOURS; i++) + term->subpalettes[SUBPAL_SESSION].present[i] = false; + } + + /* + * Re-fetch any OS-local overrides. + */ + for (unsigned i = 0; i < OSC4_NCOLOURS; i++) + term->subpalettes[SUBPAL_PLATFORM].present[i] = false; + win_palette_get_overrides(term->win); + + /* + * Rebuild the composite palette. + */ + palette_rebuild(term); +} + +void term_palette_override(Terminal *term, unsigned osc4_index, rgb rgb) +{ + /* + * We never expect to be called except as re-entry from our own + * call to win_palette_get_overrides above, so we need not mess + * about calling palette_rebuild. + */ + term->subpalettes[SUBPAL_PLATFORM].present[osc4_index] = true; + term->subpalettes[SUBPAL_PLATFORM].values[osc4_index] = rgb; +} + /* * Initialise the terminal. */ @@ -1812,6 +1919,8 @@ Terminal *term_init(Conf *myconf, struct unicode_data *ucsdata, TermWin *win) term->icon_title = dupstr(""); term->minimised = false; + palette_reset(term, false); + return term; } @@ -2877,15 +2986,14 @@ static void do_osc(Terminal *term) break; case 4: if (term->ldisc && !strcmp(term->osc_string, "?")) { - int r, g, b; - if (win_palette_get(term->win, toint(term->esc_args[1]), - &r, &g, &b)) { + unsigned index = term->esc_args[1]; + if (index < OSC4_NCOLOURS) { + rgb colour = term->palette[index]; 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); + "\033]4;%u;rgb:%04x/%04x/%04x\007", index, + (unsigned)colour.r * 0x0101, + (unsigned)colour.g * 0x0101, + (unsigned)colour.b * 0x0101); ldisc_send(term->ldisc, reply_buf, strlen(reply_buf), false); sfree(reply_buf); @@ -4788,7 +4896,7 @@ static void term_out(Terminal *term) term->osc_strlen = 0; break; case 'R': /* Linux palette reset */ - win_palette_reset(term->win); + palette_reset(term, false); term_invalidate(term); term->termstate = TOPLEVEL; break; @@ -4890,12 +4998,16 @@ static void term_out(Terminal *term) unsigned osc4_index = colour_indices_oscp_to_osc4[oscp_index]; - win_palette_set( - term->win, osc4_index, - term->osc_string[1] * 16 + term->osc_string[2], - term->osc_string[3] * 16 + term->osc_string[4], - term->osc_string[5] * 16 + term->osc_string[6]); - term_invalidate(term); + rgb *value = &term->subpalettes[SUBPAL_SESSION].values[ + osc4_index]; + value->r = term->osc_string[1] * 16 + term->osc_string[2]; + value->g = term->osc_string[3] * 16 + term->osc_string[4]; + value->b = term->osc_string[5] * 16 + term->osc_string[6]; + term->subpalettes[SUBPAL_SESSION].present[ + osc4_index] = true; + + palette_rebuild(term); + term->termstate = TOPLEVEL; } break; @@ -7349,3 +7461,8 @@ void term_notify_minimised(Terminal *term, bool minimised) { term->minimised = minimised; } + +void term_notify_palette_overrides_changed(Terminal *term) +{ + palette_reset(term, true); +} diff --git a/terminal.h b/terminal.h index 40c31963..ed8a0da6 100644 --- a/terminal.h +++ b/terminal.h @@ -345,6 +345,21 @@ struct terminal_tag { char *window_title, *icon_title; bool minimised; + + /* Multi-layered colour palette. The colours from Conf (plus the + * default xterm-256 ones that don't have Conf ids at all) have + * lowest priority, followed by platform overrides if any, + * followed by escape-sequence overrides during the session. */ + struct term_subpalette { + rgb values[OSC4_NCOLOURS]; + bool present[OSC4_NCOLOURS]; + } subpalettes[3]; +#define SUBPAL_CONF 0 +#define SUBPAL_PLATFORM 1 +#define SUBPAL_SESSION 2 + + /* The composite palette that we make out of the above */ + rgb palette[OSC4_NCOLOURS]; }; static inline bool in_utf(Terminal *term) diff --git a/unix/gtkwin.c b/unix/gtkwin.c index 2143d094..012aad2f 100644 --- a/unix/gtkwin.c +++ b/unix/gtkwin.c @@ -2518,26 +2518,6 @@ static void gtkwin_request_resize(TermWin *tw, int w, int h) } -static void real_palette_set(GtkFrontend *inst, unsigned n, - int r, int g, int b) -{ - inst->cols[n].red = r * 0x0101; - inst->cols[n].green = g * 0x0101; - inst->cols[n].blue = b * 0x0101; - -#if !GTK_CHECK_VERSION(3,0,0) - { - gboolean success[1]; - gdk_colormap_free_colors(inst->colmap, inst->cols + n, 1); - gdk_colormap_alloc_colors(inst->colmap, inst->cols + n, 1, - false, true, success); - if (!success[0]) - g_error("%s: couldn't allocate colour %d (#%02x%02x%02x)\n", - appname, n, r, g, b); - } -#endif -} - #if GTK_CHECK_VERSION(3,0,0) char *colour_to_css(const GdkColor *col) { @@ -2581,36 +2561,13 @@ void set_window_background(GtkFrontend *inst) set_gtk_widget_background(GTK_WIDGET(inst->window), &inst->cols[258]); } -static void gtkwin_palette_set(TermWin *tw, unsigned n, int r, int g, int b) +static void gtkwin_palette_set(TermWin *tw, unsigned start, unsigned ncolours, + const rgb *colours) { GtkFrontend *inst = container_of(tw, GtkFrontend, termwin); - if (n >= OSC4_NCOLOURS) - return; - real_palette_set(inst, n, r, g, b); - if (n == OSC4_COLOUR_bg) { - /* Default Background changed. Ensure space between text area and - * window border is redrawn */ - set_window_background(inst); - draw_backing_rect(inst); - gtk_widget_queue_draw(inst->area); - } -} -static bool gtkwin_palette_get(TermWin *tw, unsigned n, int *r, int *g, int *b) -{ - GtkFrontend *inst = container_of(tw, GtkFrontend, termwin); - if (n >= OSC4_NCOLOURS) - return false; - *r = inst->cols[n].red >> 8; - *g = inst->cols[n].green >> 8; - *b = inst->cols[n].blue >> 8; - return true; -} - -static void gtkwin_palette_reset(TermWin *tw) -{ - GtkFrontend *inst = container_of(tw, GtkFrontend, termwin); - int i; + assert(start <= OSC4_NCOLOURS); + assert(ncolours <= OSC4_NCOLOURS - start); #if !GTK_CHECK_VERSION(3,0,0) if (!inst->colmap) { @@ -2620,37 +2577,24 @@ static void gtkwin_palette_reset(TermWin *tw) } #endif - for (i = 0; i < CONF_NCOLOURS; i++) { - int w = colour_indices_conf_to_osc4[i]; - inst->cols[w].red = - conf_get_int_int(inst->conf, CONF_colours, i*3+0) * 0x0101; - inst->cols[w].green = - conf_get_int_int(inst->conf, CONF_colours, i*3+1) * 0x0101; - inst->cols[w].blue = - conf_get_int_int(inst->conf, CONF_colours, i*3+2) * 0x0101; - } + for (unsigned i = 0; i < ncolours; i++) { + const rgb *in = &colours[i]; + GdkColor *out = &inst->cols[start + i]; - for (i = 0; i < 216; i++) { - int r = i / 36, g = (i / 6) % 6, b = i % 6; - inst->cols[i+16].red = r ? r * 0x2828 + 0x3737 : 0; - inst->cols[i+16].green = g ? g * 0x2828 + 0x3737 : 0; - inst->cols[i+16].blue = b ? b * 0x2828 + 0x3737 : 0; - } - for (i = 0; i < 24; i++) { - int shade = i * 0x0a0a + 0x0808; - inst->cols[i+232].red = inst->cols[i+232].green = - inst->cols[i+232].blue = shade; + out->red = in->r * 0x0101; + out->green = in->g * 0x0101; + out->blue = in->b * 0x0101; } #if !GTK_CHECK_VERSION(3,0,0) { gboolean success[OSC4_NCOLOURS]; - gdk_colormap_alloc_colors(inst->colmap, inst->cols, OSC4_NCOLOURS, - false, true, success); - for (i = 0; i < OSC4_NCOLOURS; i++) { + gdk_colormap_alloc_colors(inst->colmap + start, inst->cols + start, + ncolours, false, true, success); + for (unsigned i = 0; i < ncolours; i++) { if (!success[i]) g_error("%s: couldn't allocate colour %d (#%02x%02x%02x)\n", - appname, i, + appname, start + i, conf_get_int_int(inst->conf, CONF_colours, i*3+0), conf_get_int_int(inst->conf, CONF_colours, i*3+1), conf_get_int_int(inst->conf, CONF_colours, i*3+2)); @@ -2658,15 +2602,24 @@ static void gtkwin_palette_reset(TermWin *tw) } #endif - /* Since Default Background may have changed, ensure that space - * between text area and window border is refreshed. */ - set_window_background(inst); - if (inst->area && gtk_widget_get_window(inst->area)) { - draw_backing_rect(inst); - gtk_widget_queue_draw(inst->area); + if (start <= OSC4_COLOUR_bg && OSC4_COLOUR_bg < start + ncolours) { + /* Default Background has changed, so ensure that space between text + * area and window border is refreshed. */ + set_window_background(inst); + if (inst->area && gtk_widget_get_window(inst->area)) { + draw_backing_rect(inst); + gtk_widget_queue_draw(inst->area); + } } } +static void gtkwin_palette_get_overrides(TermWin *tw) +{ + /* GTK has no analogue of Windows's 'standard system colours', so GTK PuTTY + * has no config option to override the normally configured colours from + * it */ +} + static struct clipboard_state *clipboard_from_atom( GtkFrontend *inst, GdkAtom atom) { @@ -4616,7 +4569,6 @@ static void after_change_settings_dialog(void *vctx, int retval) *(struct after_change_settings_dialog_ctx *)vctx; GtkFrontend *inst = ctx.inst; Conf *oldconf = inst->conf, *newconf = ctx.newconf; - int i, j; bool need_size; sfree(vctx); /* we've copied this already */ @@ -4645,37 +4597,6 @@ static void after_change_settings_dialog(void *vctx, int retval) cache_conf_values(inst); - /* - * Just setting inst->conf is sufficient to cause colour - * setting changes to appear on the next ESC]R palette - * reset. But we should also check whether any colour - * settings have been changed, and revert the ones that have - * to the new default, on the assumption that the user is - * most likely to want an immediate update. - */ - for (i = 0; i < CONF_NCOLOURS; i++) { - for (j = 0; j < 3; j++) - if (conf_get_int_int(oldconf, CONF_colours, i*3+j) != - conf_get_int_int(newconf, CONF_colours, i*3+j)) - break; - if (j < 3) { - real_palette_set(inst, colour_indices_conf_to_osc4[i], - conf_get_int_int(newconf,CONF_colours,i*3+0), - conf_get_int_int(newconf,CONF_colours,i*3+1), - conf_get_int_int(newconf,CONF_colours,i*3+2)); - - /* - * If the default background has changed, we must - * repaint the space in between the window border - * and the text area. - */ - if (i == CONF_COLOUR_bg) { - set_window_background(inst); - draw_backing_rect(inst); - } - } - } - need_size = false; /* @@ -5130,9 +5051,8 @@ static const TermWinVtable gtk_termwin_vt = { .set_maximised = gtkwin_set_maximised, .move = gtkwin_move, .set_zorder = gtkwin_set_zorder, - .palette_get = gtkwin_palette_get, .palette_set = gtkwin_palette_set, - .palette_reset = gtkwin_palette_reset, + .palette_get_overrides = gtkwin_palette_get_overrides, .get_pos = gtkwin_get_pos, .get_pixels = gtkwin_get_pixels, }; @@ -5243,11 +5163,6 @@ void new_session_window(Conf *conf, const char *geometry_string) } } - /* - * Set up the colour map. - */ - win_palette_reset(&inst->termwin); - inst->width = conf_get_int(inst->conf, CONF_width); inst->height = conf_get_int(inst->conf, CONF_height); cache_conf_values(inst); diff --git a/windows/window.c b/windows/window.c index 94d7dc03..95473b1b 100644 --- a/windows/window.c +++ b/windows/window.c @@ -92,8 +92,6 @@ static void show_mouseptr(bool show); static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, unsigned char *output); -static void conftopalette(void); -static void systopalette(void); static void init_palette(void); static void init_fonts(int, int); static void another_font(int); @@ -190,12 +188,10 @@ static enum { static int descent, font_strikethrough_y; static COLORREF colours[OSC4_NCOLOURS]; -static struct rgb { - int r, g, b; -} colours_rgb[OSC4_NCOLOURS]; static HPALETTE pal; static LPLOGPALETTE logpal; -static RGBTRIPLE defpal[OSC4_NCOLOURS]; +bool tried_pal = false; +COLORREF colorref_modifier = 0; static HBITMAP caretbm; @@ -244,9 +240,8 @@ static void wintw_set_minimised(TermWin *, bool minimised); static void wintw_set_maximised(TermWin *, bool maximised); static void wintw_move(TermWin *, int x, int y); static void wintw_set_zorder(TermWin *, bool top); -static bool wintw_palette_get(TermWin *, unsigned n, int *r, int *g, int *b); -static void wintw_palette_set(TermWin *, unsigned n, int r, int g, int b); -static void wintw_palette_reset(TermWin *); +static void wintw_palette_set(TermWin *, unsigned, unsigned, const rgb *); +static void wintw_palette_get_overrides(TermWin *); static void wintw_get_pos(TermWin *, int *x, int *y); static void wintw_get_pixels(TermWin *, int *x, int *y); @@ -271,9 +266,8 @@ static const TermWinVtable windows_termwin_vt = { .set_maximised = wintw_set_maximised, .move = wintw_move, .set_zorder = wintw_set_zorder, - .palette_get = wintw_palette_get, .palette_set = wintw_palette_set, - .palette_reset = wintw_palette_reset, + .palette_get_overrides = wintw_palette_get_overrides, .get_pos = wintw_get_pos, .get_pixels = wintw_get_pixels, }; @@ -681,8 +675,6 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) conf_cache_data(); - conftopalette(); - /* * Guess some defaults for the window size. This all gets * updated later, so we don't really care too much. However, we @@ -735,6 +727,11 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) */ init_fonts(0,0); + /* + * Prepare a logical palette. + */ + init_palette(); + /* * Initialise the terminal. (We have to do this _after_ * creating the window, since the terminal is the first thing @@ -869,13 +866,6 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) ShowWindow(wgs.term_hwnd, show); SetForegroundWindow(wgs.term_hwnd); - /* - * Set the palette up. - */ - pal = NULL; - logpal = NULL; - init_palette(); - term_set_focus(term, GetForegroundWindow() == wgs.term_hwnd); UpdateWindow(wgs.term_hwnd); @@ -1198,115 +1188,36 @@ static void wm_netevent_callback(void *vctx) sfree(vctx); } -/* - * Copy the colour palette from the configuration data into defpal. - */ -static void conftopalette(void) +static inline rgb rgb_from_colorref(COLORREF cr) { - int i; - - for (i = 0; i < 22; i++) { - int w = colour_indices_conf_to_osc4[i]; - defpal[w].rgbtRed = conf_get_int_int(conf, CONF_colours, i*3+0); - defpal[w].rgbtGreen = conf_get_int_int(conf, CONF_colours, i*3+1); - defpal[w].rgbtBlue = conf_get_int_int(conf, CONF_colours, i*3+2); - } - for (i = 0; i < 216; i++) { - int r = i / 36, g = (i / 6) % 6, b = i % 6; - defpal[i+16].rgbtRed = r ? r * 40 + 55 : 0; - defpal[i+16].rgbtGreen = g ? g * 40 + 55 : 0; - defpal[i+16].rgbtBlue = b ? b * 40 + 55 : 0; - } - for (i = 0; i < 24; i++) { - int shade = i * 10 + 8; - defpal[i+232].rgbtRed = defpal[i+232].rgbtGreen = - defpal[i+232].rgbtBlue = shade; - } - - /* Override with system colours if appropriate */ - if (conf_get_bool(conf, CONF_system_colour)) - systopalette(); + rgb toret; + toret.r = GetRValue(cr); + toret.g = GetGValue(cr); + toret.b = GetBValue(cr); + return toret; } -/* - * Override bit of defpal with colours from the system. - * (NB that this takes a copy the system colours at the time this is called, - * so subsequent colour scheme changes don't take effect. To fix that we'd - * probably want to be using GetSysColorBrush() and the like.) - */ -static void systopalette(void) +static void wintw_palette_get_overrides(TermWin *tw) { - int i; - static const struct { int nIndex; int norm; int bold; } or[] = - { - { COLOR_WINDOWTEXT, OSC4_COLOUR_fg, OSC4_COLOUR_fg_bold }, - { COLOR_WINDOW, OSC4_COLOUR_bg, OSC4_COLOUR_bg_bold }, - { COLOR_HIGHLIGHTTEXT, OSC4_COLOUR_cursor_fg, OSC4_COLOUR_cursor_fg }, - { COLOR_HIGHLIGHT, OSC4_COLOUR_cursor_bg, OSC4_COLOUR_cursor_bg }, - }; + if (conf_get_bool(conf, CONF_system_colour)) { + rgb rgb; - for (i = 0; i < (sizeof(or)/sizeof(or[0])); i++) { - COLORREF colour = GetSysColor(or[i].nIndex); - defpal[or[i].norm].rgbtRed = - defpal[or[i].bold].rgbtRed = GetRValue(colour); - defpal[or[i].norm].rgbtGreen = - defpal[or[i].bold].rgbtGreen = GetGValue(colour); - defpal[or[i].norm].rgbtBlue = - defpal[or[i].bold].rgbtBlue = GetBValue(colour); + rgb = rgb_from_colorref(GetSysColor(COLOR_WINDOWTEXT)); + term_palette_override(term, OSC4_COLOUR_fg, rgb); + term_palette_override(term, OSC4_COLOUR_fg_bold, rgb); + + rgb = rgb_from_colorref(GetSysColor(COLOR_WINDOW)); + term_palette_override(term, OSC4_COLOUR_bg, rgb); + term_palette_override(term, OSC4_COLOUR_bg_bold, rgb); + + rgb = rgb_from_colorref(GetSysColor(COLOR_HIGHLIGHTTEXT)); + term_palette_override(term, OSC4_COLOUR_cursor_fg, rgb); + + rgb = rgb_from_colorref(GetSysColor(COLOR_HIGHLIGHT)); + term_palette_override(term, OSC4_COLOUR_cursor_bg, rgb); } } -static void internal_set_colour(unsigned i, int r, int g, int b) -{ - assert(i < OSC4_NCOLOURS); - 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. - */ -static void init_palette(void) -{ - int i; - HDC hdc = GetDC(wgs.term_hwnd); - if (hdc) { - if (conf_get_bool(conf, CONF_try_palette) && - GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) { - /* - * This is a genuine case where we must use smalloc - * because the snew macros can't cope. - */ - logpal = smalloc(sizeof(*logpal) - - sizeof(logpal->palPalEntry) - + OSC4_NCOLOURS * sizeof(PALETTEENTRY)); - logpal->palVersion = 0x300; - logpal->palNumEntries = OSC4_NCOLOURS; - for (i = 0; i < OSC4_NCOLOURS; i++) { - 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 = PC_NOCOLLAPSE; - } - pal = CreatePalette(logpal); - if (pal) { - SelectPalette(hdc, pal, false); - RealizePalette(hdc); - SelectPalette(hdc, GetStockObject(DEFAULT_PALETTE), false); - } - } - ReleaseDC(wgs.term_hwnd, hdc); - } - for (i = 0; i < OSC4_NCOLOURS; i++) - internal_set_colour(i, defpal[i].rgbtRed, - defpal[i].rgbtGreen, defpal[i].rgbtBlue); -} - /* * This is a wrapper to ExtTextOut() to force Windows to display * the precise glyphs we give it. Otherwise it would do its own @@ -2340,17 +2251,23 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, ldisc_configure(ldisc, conf); ldisc_echoedit_update(ldisc); } - if (pal) - DeleteObject(pal); - logpal = NULL; - pal = NULL; - conftopalette(); - init_palette(); + + if (conf_get_bool(conf, CONF_system_colour) != + conf_get_bool(prev_conf, CONF_system_colour)) + term_notify_palette_overrides_changed(term); /* Pass new config data to the terminal */ term_reconfig(term, conf); setup_clipboards(term, conf); + /* Reinitialise the colour palette, in case the terminal + * just read new settings out of Conf */ + if (pal) + DeleteObject(pal); + logpal = NULL; + pal = NULL; + init_palette(); + /* Pass new config data to the back end */ if (backend) backend_reconfig(backend, conf); @@ -3292,8 +3209,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, case WM_SYSCOLORCHANGE: if (conf_get_bool(conf, CONF_system_colour)) { /* Refresh palette from system colours. */ - /* XXX actually this zaps the entire palette. */ - systopalette(); + term_notify_palette_overrides_changed(term); init_palette(); /* Force a repaint of the terminal window. */ term_invalidate(term); @@ -4746,70 +4662,73 @@ static void wintw_free_draw_ctx(TermWin *tw) wintw_hdc = NULL; } -static void real_palette_set(unsigned n, int r, int g, int b) +/* + * Set up the colour palette. + */ +static void init_palette(void) { - 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; - SetPaletteEntries(pal, 0, OSC4_NCOLOURS, logpal->palPalEntry); + pal = NULL; + logpal = snew_plus(LOGPALETTE, (OSC4_NCOLOURS - 1) * sizeof(PALETTEENTRY)); + logpal->palVersion = 0x300; + logpal->palNumEntries = OSC4_NCOLOURS; + for (unsigned i = 0; i < OSC4_NCOLOURS; i++) + logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE; +} + +static void wintw_palette_set(TermWin *win, unsigned start, + unsigned ncolours, const rgb *colours_in) +{ + assert(start <= OSC4_NCOLOURS); + assert(ncolours <= OSC4_NCOLOURS - start); + + for (unsigned i = 0; i < ncolours; i++) { + const rgb *in = &colours_in[i]; + PALETTEENTRY *out = &logpal->palPalEntry[i + start]; + out->peRed = in->r; + out->peGreen = in->g; + out->peBlue = in->b; + colours[i + start] = RGB(in->r, in->g, in->b) ^ colorref_modifier; } -} -static bool wintw_palette_get(TermWin *tw, unsigned n, int *r, int *g, int *b) -{ - if (n >= OSC4_NCOLOURS) - return false; - *r = colours_rgb[n].r; - *g = colours_rgb[n].g; - *b = colours_rgb[n].b; - return true; -} + bool got_new_palette = false; + + if (!tried_pal && conf_get_bool(conf, CONF_try_palette)) { + HDC hdc = GetDC(wgs.term_hwnd); + if (GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) { + pal = CreatePalette(logpal); + if (pal) { + SelectPalette(hdc, pal, false); + RealizePalette(hdc); + SelectPalette(hdc, GetStockObject(DEFAULT_PALETTE), false); + + /* Convert all RGB() values in colours[] into PALETTERGB(), + * and ensure we stick to that later */ + colorref_modifier = PALETTERGB(0, 0, 0) ^ RGB(0, 0, 0); + for (unsigned i = 0; i < OSC4_NCOLOURS; i++) + colours[i] ^= colorref_modifier; + + /* Inhibit the SetPaletteEntries call below */ + got_new_palette = true; + } + } + ReleaseDC(wgs.term_hwnd, hdc); + tried_pal = true; + } + + if (pal && !got_new_palette) { + /* We already had a palette, so replace the changed colours in the + * existing one. */ + SetPaletteEntries(pal, start, ncolours, logpal->palPalEntry + start); -static void wintw_palette_set(TermWin *tw, unsigned n, int r, int g, int b) -{ - if (n >= OSC4_NCOLOURS) - return; - real_palette_set(n, r, g, b); - if (pal) { HDC hdc = make_hdc(); UnrealizeObject(pal); RealizePalette(hdc); free_hdc(hdc); - } else { - if (n == OSC4_COLOUR_bg) - /* If Default Background changes, we need to ensure any - * space between the text area and the window border is - * redrawn. */ - InvalidateRect(wgs.term_hwnd, NULL, true); - } -} - -static void wintw_palette_reset(TermWin *tw) -{ - /* And this */ - for (unsigned i = 0; i < OSC4_NCOLOURS; 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; - } } - if (pal) { - HDC hdc; - SetPaletteEntries(pal, 0, OSC4_NCOLOURS, logpal->palPalEntry); - hdc = make_hdc(); - RealizePalette(hdc); - free_hdc(hdc); - } else { - /* Default Background may have changed. Ensure any space between - * text area and window border is redrawn. */ + if (start <= OSC4_COLOUR_bg && OSC4_COLOUR_bg < start + ncolours) { + /* If Default Background changes, we need to ensure any space between + * the text area and the window border is redrawn. */ InvalidateRect(wgs.term_hwnd, NULL, true); } } @@ -5008,9 +4927,9 @@ static void wintw_clip_write( for (i = 0; i < OSC4_NCOLOURS; i++) { if (palette[i] != 0) { + const PALETTEENTRY *pe = &logpal->palPalEntry[i]; strbuf_catf(rtf, "\\red%d\\green%d\\blue%d;", - defpal[i].rgbtRed, defpal[i].rgbtGreen, - defpal[i].rgbtBlue); + pe->peRed, pe->peGreen, pe->peBlue); } } if (rgbtree) {