diff --git a/grid.c b/grid.c index c24e6c6a..782032f4 100644 --- a/grid.c +++ b/grid.c @@ -37,7 +37,7 @@ /* Default grid cell data. */ const struct grid_cell grid_default_cell = { - 0, 0, 8, 8, { { ' ' }, 0, 1, 1 } + 0, 0, { .fg = 8 }, { .bg = 8 }, { { ' ' }, 0, 1, 1 } }; const struct grid_cell_entry grid_default_entry = { 0, { .data = { 0, 8, 8, ' ' } } @@ -284,6 +284,7 @@ grid_set_cell(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc) struct grid_line *gl; struct grid_cell_entry *gce; struct grid_cell *gcp; + int extended; if (grid_check_y(gd, py) != 0) return; @@ -293,8 +294,12 @@ grid_set_cell(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc) gl = &gd->linedata[py]; gce = &gl->celldata[px]; - if ((gce->flags & GRID_FLAG_EXTENDED) || gc->data.size != 1 || - gc->data.width != 1) { + extended = (gce->flags & GRID_FLAG_EXTENDED); + if (!extended && (gc->data.size != 1 || gc->data.width != 1)) + extended = 1; + if (!extended && (gc->flags & (GRID_FLAG_FGRGB|GRID_FLAG_BGRGB))) + extended = 1; + if (extended) { if (~gce->flags & GRID_FLAG_EXTENDED) { gl->extddata = xreallocarray(gl->extddata, gl->extdsize + 1, sizeof *gl->extddata); diff --git a/input.c b/input.c index 1772a4cd..ae205024 100644 --- a/input.c +++ b/input.c @@ -1629,18 +1629,20 @@ input_csi_dispatch_sgr_256(struct input_ctx *ictx, int fgbg, u_int *i) c = input_get(ictx, *i, 0, -1); if (c == -1) { if (fgbg == 38) { - gc->flags &= ~GRID_FLAG_FG256; + gc->flags &= ~(GRID_FLAG_FG256|GRID_FLAG_FGRGB); gc->fg = 8; } else if (fgbg == 48) { - gc->flags &= ~GRID_FLAG_BG256; + gc->flags &= ~(GRID_FLAG_BG256|GRID_FLAG_BGRGB); gc->bg = 8; } } else { if (fgbg == 38) { gc->flags |= GRID_FLAG_FG256; + gc->flags &= ~GRID_FLAG_FGRGB; gc->fg = c; } else if (fgbg == 48) { gc->flags |= GRID_FLAG_BG256; + gc->flags &= ~GRID_FLAG_BGRGB; gc->bg = c; } } @@ -1651,7 +1653,7 @@ void input_csi_dispatch_sgr_rgb(struct input_ctx *ictx, int fgbg, u_int *i) { struct grid_cell *gc = &ictx->cell.cell; - int c, r, g, b; + int r, g, b; (*i)++; r = input_get(ictx, *i, 0, -1); @@ -1666,13 +1668,18 @@ input_csi_dispatch_sgr_rgb(struct input_ctx *ictx, int fgbg, u_int *i) if (b == -1 || b > 255) return; - c = colour_find_rgb(r, g, b); if (fgbg == 38) { - gc->flags |= GRID_FLAG_FG256; - gc->fg = c; + gc->flags &= ~GRID_FLAG_FG256; + gc->flags |= GRID_FLAG_FGRGB; + gc->fg_rgb.r = r; + gc->fg_rgb.g = g; + gc->fg_rgb.b = b; } else if (fgbg == 48) { - gc->flags |= GRID_FLAG_BG256; - gc->bg = c; + gc->flags &= ~GRID_FLAG_BG256; + gc->flags |= GRID_FLAG_BGRGB; + gc->bg_rgb.r = r; + gc->bg_rgb.g = g; + gc->bg_rgb.b = b; } } @@ -1754,11 +1761,11 @@ input_csi_dispatch_sgr(struct input_ctx *ictx) case 35: case 36: case 37: - gc->flags &= ~GRID_FLAG_FG256; + gc->flags &= ~(GRID_FLAG_FG256|GRID_FLAG_FGRGB); gc->fg = n - 30; break; case 39: - gc->flags &= ~GRID_FLAG_FG256; + gc->flags &= ~(GRID_FLAG_FG256|GRID_FLAG_FGRGB); gc->fg = 8; break; case 40: @@ -1769,11 +1776,11 @@ input_csi_dispatch_sgr(struct input_ctx *ictx) case 45: case 46: case 47: - gc->flags &= ~GRID_FLAG_BG256; + gc->flags &= ~(GRID_FLAG_BG256|GRID_FLAG_BGRGB); gc->bg = n - 40; break; case 49: - gc->flags &= ~GRID_FLAG_BG256; + gc->flags &= ~(GRID_FLAG_BG256|GRID_FLAG_BGRGB); gc->bg = 8; break; case 90: @@ -1784,7 +1791,7 @@ input_csi_dispatch_sgr(struct input_ctx *ictx) case 95: case 96: case 97: - gc->flags &= ~GRID_FLAG_FG256; + gc->flags &= ~(GRID_FLAG_FG256|GRID_FLAG_FGRGB); gc->fg = n; break; case 100: @@ -1795,7 +1802,7 @@ input_csi_dispatch_sgr(struct input_ctx *ictx) case 105: case 106: case 107: - gc->flags &= ~GRID_FLAG_BG256; + gc->flags &= ~(GRID_FLAG_BG256|GRID_FLAG_BGRGB); gc->bg = n - 10; break; } diff --git a/tmux.1 b/tmux.1 index e41ba798..e70b07c3 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2921,7 +2921,7 @@ and poor for interactive programs such as shells. .Op Ic on | off .Xc Allow programs to change the window name using a terminal escape -sequence (\\033k...\\033\\\\). +sequence (\eek...\ee\e\e). The default is on. .Pp .It Xo Ic alternate-screen @@ -4024,7 +4024,7 @@ This command only works from outside .El .Sh TERMINFO EXTENSIONS .Nm -understands some extensions to +understands some unofficial extensions to .Xr terminfo 5 : .Bl -tag -width Ds .It Em Cs , Cr @@ -4048,10 +4048,12 @@ $ printf '\e033[4 q' If .Em Se is not set, \&Ss with argument 0 will be used to reset the cursor style instead. +.It Em \&Tc +Indicate that the terminal supports the +.Ql direct colour +RGB escape sequence (for example, \ee[38;2;255;255;255m). .It Em \&Ms -This sequence can be used by -.Nm -to store the current buffer in the host terminal's selection (clipboard). +Store the current buffer in the host terminal's selection (clipboard). See the .Em set-clipboard option above and the diff --git a/tmux.h b/tmux.h index eb3373da..8edb9209 100644 --- a/tmux.h +++ b/tmux.h @@ -385,6 +385,7 @@ enum tty_code_code { TTYC_SMSO, /* enter_standout_mode, so */ TTYC_SMUL, /* enter_underline_mode, us */ TTYC_SS, /* set cursor style, Ss */ + TTYC_TC, /* 24-bit "true" colour, Tc */ TTYC_TSL, /* to_status_line, tsl */ TTYC_VPA, /* row_address, cv */ TTYC_XENL, /* eat_newline_glitch, xn */ @@ -641,16 +642,31 @@ enum utf8_state { #define GRID_FLAG_BG256 0x2 #define GRID_FLAG_PADDING 0x4 #define GRID_FLAG_EXTENDED 0x8 +#define GRID_FLAG_FGRGB 0x10 +#define GRID_FLAG_BGRGB 0x20 /* Grid line flags. */ #define GRID_LINE_WRAPPED 0x1 +/* Grid cell RGB colours. */ +struct grid_cell_rgb { + u_char r; + u_char g; + u_char b; +}; + /* Grid cell data. */ struct grid_cell { u_char flags; u_char attr; - u_char fg; - u_char bg; + union { + u_char fg; + struct grid_cell_rgb fg_rgb; + }; + union { + u_char bg; + struct grid_cell_rgb bg_rgb; + }; struct utf8_data data; }; diff --git a/tty-term.c b/tty-term.c index 8716a1a9..05ef01e7 100644 --- a/tty-term.c +++ b/tty-term.c @@ -251,6 +251,7 @@ const struct tty_term_code_entry tty_term_codes[] = { [TTYC_SMSO] = { TTYCODE_STRING, "smso" }, [TTYC_SMUL] = { TTYCODE_STRING, "smul" }, [TTYC_SS] = { TTYCODE_STRING, "Ss" }, + [TTYC_TC] = { TTYCODE_FLAG, "Tc" }, [TTYC_TSL] = { TTYCODE_STRING, "tsl" }, [TTYC_VPA] = { TTYCODE_STRING, "vpa" }, [TTYC_XENL] = { TTYCODE_FLAG, "xenl" }, diff --git a/tty.c b/tty.c index 52521be9..c6fc2213 100644 --- a/tty.c +++ b/tty.c @@ -36,8 +36,15 @@ static int tty_log_fd = -1; void tty_read_callback(struct bufferevent *, void *); void tty_error_callback(struct bufferevent *, short, void *); +static int tty_same_fg(const struct grid_cell *, const struct grid_cell *); +static int tty_same_bg(const struct grid_cell *, const struct grid_cell *); +static int tty_same_colours(const struct grid_cell *, const struct grid_cell *); +static int tty_is_fg(const struct grid_cell *, int); +static int tty_is_bg(const struct grid_cell *, int); + void tty_set_italics(struct tty *); int tty_try_256(struct tty *, u_char, const char *); +int tty_try_rgb(struct tty *, const struct grid_cell_rgb *, const char *); void tty_colours(struct tty *, const struct grid_cell *); void tty_check_fg(struct tty *, struct grid_cell *); @@ -61,6 +68,74 @@ void tty_default_colours(struct grid_cell *, const struct window_pane *); #define tty_pane_full_width(tty, ctx) \ ((ctx)->xoff == 0 && screen_size_x((ctx)->wp->screen) >= (tty)->sx) +static int +tty_same_fg(const struct grid_cell *gc1, const struct grid_cell *gc2) +{ + int flags1, flags2; + + flags1 = (gc1->flags & (GRID_FLAG_FG256|GRID_FLAG_FGRGB)); + flags2 = (gc2->flags & (GRID_FLAG_FG256|GRID_FLAG_FGRGB)); + + if (flags1 != flags2) + return (0); + + if (flags1 & GRID_FLAG_FGRGB) { + if (gc1->fg_rgb.r != gc2->fg_rgb.r) + return (0); + if (gc1->fg_rgb.g != gc2->fg_rgb.g) + return (0); + if (gc1->fg_rgb.b != gc2->fg_rgb.b) + return (0); + return (1); + } + return (gc1->fg == gc2->fg); +} + +static int +tty_same_bg(const struct grid_cell *gc1, const struct grid_cell *gc2) +{ + int flags1, flags2; + + flags1 = (gc1->flags & (GRID_FLAG_BG256|GRID_FLAG_BGRGB)); + flags2 = (gc2->flags & (GRID_FLAG_BG256|GRID_FLAG_BGRGB)); + + if (flags1 != flags2) + return (0); + + if (flags1 & GRID_FLAG_BGRGB) { + if (gc1->bg_rgb.r != gc2->bg_rgb.r) + return (0); + if (gc1->bg_rgb.g != gc2->bg_rgb.g) + return (0); + if (gc1->bg_rgb.b != gc2->bg_rgb.b) + return (0); + return (1); + } + return (gc1->bg == gc2->bg); +} + +static int +tty_same_colours(const struct grid_cell *gc1, const struct grid_cell *gc2) +{ + return (tty_same_fg(gc1, gc2) && tty_same_bg(gc1, gc2)); +} + +static int +tty_is_fg(const struct grid_cell *gc, int c) +{ + if (gc->flags & (GRID_FLAG_FG256|GRID_FLAG_FGRGB)) + return (0); + return (gc->fg == c); +} + +static int +tty_is_bg(const struct grid_cell *gc, int c) +{ + if (gc->flags & (GRID_FLAG_BG256|GRID_FLAG_BGRGB)) + return (0); + return (gc->bg == c); +} + void tty_create_log(void) { @@ -1423,12 +1498,10 @@ void tty_colours(struct tty *tty, const struct grid_cell *gc) { struct grid_cell *tc = &tty->cell; - u_char fg = gc->fg, bg = gc->bg, flags = gc->flags; int have_ax, fg_default, bg_default; /* No changes? Nothing is necessary. */ - if (fg == tc->fg && bg == tc->bg && - ((flags ^ tc->flags) & (GRID_FLAG_FG256|GRID_FLAG_BG256)) == 0) + if (tty_same_colours(gc, tc)) return; /* @@ -1437,8 +1510,8 @@ tty_colours(struct tty *tty, const struct grid_cell *gc) * case if only one is default need to fall onward to set the other * colour. */ - fg_default = (fg == 8 && !(flags & GRID_FLAG_FG256)); - bg_default = (bg == 8 && !(flags & GRID_FLAG_BG256)); + fg_default = tty_is_fg(gc, 8); + bg_default = tty_is_bg(gc, 8); if (fg_default || bg_default) { /* * If don't have AX but do have op, send sgr0 (op can't @@ -1451,48 +1524,52 @@ tty_colours(struct tty *tty, const struct grid_cell *gc) if (!have_ax && tty_term_has(tty->term, TTYC_OP)) tty_reset(tty); else { - if (fg_default && - (tc->fg != 8 || tc->flags & GRID_FLAG_FG256)) { + if (fg_default && !tty_is_fg(tc, 8)) { if (have_ax) tty_puts(tty, "\033[39m"); - else if (tc->fg != 7 || - tc->flags & GRID_FLAG_FG256) + else if (!tty_is_fg(tc, 7)) tty_putcode1(tty, TTYC_SETAF, 7); tc->fg = 8; - tc->flags &= ~GRID_FLAG_FG256; + tc->flags &= ~(GRID_FLAG_FG256|GRID_FLAG_FGRGB); } - if (bg_default && - (tc->bg != 8 || tc->flags & GRID_FLAG_BG256)) { + if (bg_default && !tty_is_bg(tc, 8)) { if (have_ax) tty_puts(tty, "\033[49m"); - else if (tc->bg != 0 || - tc->flags & GRID_FLAG_BG256) + else if (!tty_is_bg(tc, 0)) tty_putcode1(tty, TTYC_SETAB, 0); tc->bg = 8; - tc->flags &= ~GRID_FLAG_BG256; + tc->flags &= ~(GRID_FLAG_BG256|GRID_FLAG_BGRGB); } } } /* Set the foreground colour. */ - if (!fg_default && (fg != tc->fg || - ((flags & GRID_FLAG_FG256) != (tc->flags & GRID_FLAG_FG256)))) + if (!fg_default && !tty_same_fg(gc, tc)) tty_colours_fg(tty, gc); /* * Set the background colour. This must come after the foreground as * tty_colour_fg() can call tty_reset(). */ - if (!bg_default && (bg != tc->bg || - ((flags & GRID_FLAG_BG256) != (tc->flags & GRID_FLAG_BG256)))) + if (!bg_default && !tty_same_bg(gc, tc)) tty_colours_bg(tty, gc); } void tty_check_fg(struct tty *tty, struct grid_cell *gc) { - u_int colours; + struct grid_cell_rgb *rgb = &gc->fg_rgb; + u_int colours; + /* Is this a 24-bit colour? */ + if (gc->flags & GRID_FLAG_FGRGB) { + /* Not a 24-bit terminal? Translate to 256-colour palette. */ + if (!tty_term_flag(tty->term, TTYC_TC)) { + gc->flags &= ~GRID_FLAG_FGRGB; + gc->flags |= GRID_FLAG_FG256; + gc->fg = colour_find_rgb(rgb->r, rgb->g, rgb->b); + } + } colours = tty_term_number(tty->term, TTYC_COLORS); /* Is this a 256-colour colour? */ @@ -1524,8 +1601,18 @@ tty_check_fg(struct tty *tty, struct grid_cell *gc) void tty_check_bg(struct tty *tty, struct grid_cell *gc) { - u_int colours; + struct grid_cell_rgb *rgb = &gc->bg_rgb; + u_int colours; + /* Is this a 24-bit colour? */ + if (gc->flags & GRID_FLAG_BGRGB) { + /* Not a 24-bit terminal? Translate to 256-colour palette. */ + if (!tty_term_flag(tty->term, TTYC_TC)) { + gc->flags &= ~GRID_FLAG_BGRGB; + gc->flags |= GRID_FLAG_BG256; + gc->bg = colour_find_rgb(rgb->r, rgb->g, rgb->b); + } + } colours = tty_term_number(tty->term, TTYC_COLORS); /* Is this a 256-colour colour? */ @@ -1560,12 +1647,21 @@ tty_colours_fg(struct tty *tty, const struct grid_cell *gc) u_char fg = gc->fg; char s[32]; + tc->flags &= ~(GRID_FLAG_FG256|GRID_FLAG_FGRGB); + + /* Is this a 24-bit colour? */ + if (gc->flags & GRID_FLAG_FGRGB) { + if (tty_try_rgb(tty, &gc->fg_rgb, "38") == 0) + goto save_fg; + /* Should not get here, already converted in tty_check_fg. */ + return; + } + /* Is this a 256-colour colour? */ if (gc->flags & GRID_FLAG_FG256) { - /* Try as 256 colours. */ if (tty_try_256(tty, fg, "38") == 0) goto save_fg; - /* Else already handled by tty_check_fg. */ + /* Should not get here, already converted in tty_check_fg. */ return; } @@ -1581,9 +1677,12 @@ tty_colours_fg(struct tty *tty, const struct grid_cell *gc) save_fg: /* Save the new values in the terminal current cell. */ - tc->fg = fg; - tc->flags &= ~GRID_FLAG_FG256; - tc->flags |= gc->flags & GRID_FLAG_FG256; + if (gc->flags & GRID_FLAG_FGRGB) + memcpy(&tc->fg_rgb, &gc->fg_rgb, sizeof tc->fg_rgb); + else + tc->fg = fg; + tc->flags &= ~(GRID_FLAG_FGRGB|GRID_FLAG_FG256); + tc->flags |= (gc->flags & (GRID_FLAG_FG256|GRID_FLAG_FGRGB)); } void @@ -1593,12 +1692,19 @@ tty_colours_bg(struct tty *tty, const struct grid_cell *gc) u_char bg = gc->bg; char s[32]; + /* Is this a 24-bit colour? */ + if (gc->flags & GRID_FLAG_BGRGB) { + if (tty_try_rgb(tty, &gc->bg_rgb, "48") == 0) + goto save_bg; + /* Should not get here, already converted in tty_check_bg. */ + return; + } + /* Is this a 256-colour colour? */ if (gc->flags & GRID_FLAG_BG256) { - /* Try as 256 colours. */ if (tty_try_256(tty, bg, "48") == 0) goto save_bg; - /* Else already handled by tty_check_bg. */ + /* Should not get here, already converted in tty_check_bg. */ return; } @@ -1614,9 +1720,12 @@ tty_colours_bg(struct tty *tty, const struct grid_cell *gc) save_bg: /* Save the new values in the terminal current cell. */ - tc->bg = bg; - tc->flags &= ~GRID_FLAG_BG256; - tc->flags |= gc->flags & GRID_FLAG_BG256; + if (gc->flags & GRID_FLAG_BGRGB) + memcpy(&tc->bg_rgb, &gc->bg_rgb, sizeof tc->bg_rgb); + else + tc->bg = bg; + tc->flags &= ~(GRID_FLAG_BGRGB|GRID_FLAG_BG256); + tc->flags |= (gc->flags & (GRID_FLAG_BG256|GRID_FLAG_BGRGB)); } int @@ -1656,6 +1765,20 @@ fallback: return (0); } +int +tty_try_rgb(struct tty *tty, const struct grid_cell_rgb *rgb, const char *type) +{ + char s[32]; + + if (!tty_term_flag(tty->term, TTYC_TC)) + return (-1); + + xsnprintf(s, sizeof s, "\033[%s;2;%hhu;%hhu;%hhum", type, rgb->r, + rgb->g, rgb->b); + tty_puts(tty, s); + return (0); +} + void tty_default_colours(struct grid_cell *gc, const struct window_pane *wp) {