Adding 24-bit TrueColor RGB escape sequences to tmux

Suraj N. Kurapati

Demonstration of 24-bit colors in tmux under st 0.6

  1. Story
    1. Usage
      1. Patch

        Story

        Several months ago, I embarked on a journey to revive Koichi Shiraishi’s patch, which derives from Christian Hopps’s patch, which in turn derives from Arnis Lapsa’s patch. This patchwork added support for 24-bit RGB color escape sequences in tmux but remained unofficial and diverged over time because it wasn’t contributed upstream.

        With Nicholas Marriott’s invaluable assistance and testing/encouragement from fellow tmux enthusiasts, I rebased the once obsolete patchwork atop tmux’s master branch, enhanced it further, and contributed it back as a pull request. The result of all this effort is illustrated by the smooth bands of 24-bit color gradients permeating the lower half of the screenshot above.

        Nicholas Marriott finally merged the pull request into tmux’s master branch yesterday, making it an official part of tmux and bringing my months-long journey to a happy end: it feels victorious to have helped improve tmux again because, in addition to being one of my favorite tools, tmux is indispensable to hordes of programmers today and, given the terminal environment’s triumphant 50-year resilience to digital obsolescence, perhaps even so in decades to come!

        Usage

        Enable the Tc terminal capability for the outer terminal (to which tmux is attached) by setting the terminal-overrides option in tmux and subsequently detach and reattach tmux, as the following example (wherein $TERM is st-256color and % is a shell prompt) illustrates:

        1. Outside tmux:

          % echo $TERM
          st-256color
          
          % tmux attach
          
        2. Inside tmux:

          % tmux set-option -ga terminal-overrides ",st-256color:Tc"
          
          % tmux detach
          
        3. Outside tmux:

          % tmux attach
          

        Afterwards, you can run the following command to check whether the Tc terminal capability has been enabled properly for the outer terminal:

        tmux info | grep Tc
        

        If the command reports Tc: [missing], then support for 24-bit colors has not been enabled properly because the terminal-overrides option may have specified the outer terminal’s $TERM value incorrectly or because tmux may have been reattached to an entirely different outer terminal altogether.

        Patch

        This patch (available as a downloadable Git patch file) adds support for 24-bit RGB color escape sequences in tmux, as requested in tmux issue #34, by adjusting Koichi Shiraishi’s patch, which derives from Christian Hopps’s patch, which in turn derives from Arnis Lapsa’s patch, with Nicholas Marriott’s assistance.

        ---
         grid.c     |  11 +++-
         input.c    |  35 +++++++-----
         tmux.1     |  12 ++--
         tmux.h     |  20 ++++++-
         tty-term.c |   1 +
         tty.c      | 185 ++++++++++++++++++++++++++++++++++++++++++++++++++-----------
         6 files changed, 209 insertions(+), 55 deletions(-)
        
        diff --git a/grid.c b/grid.c
        index c24e6c6..782032f 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 1772a4c..ae20502 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 6a4b561..fcf388a 100644
        --- a/tmux.1
        +++ b/tmux.1
        @@ -2925,7 +2925,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
        @@ -4029,7 +4029,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
        @@ -4053,10 +4053,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 1ca6ef3..4bdc8af 100644
        --- a/tmux.h
        +++ b/tmux.h
        @@ -387,6 +387,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 */
        @@ -643,16 +644,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 4d41fd8..a3a2636 100644
        --- a/tty-term.c
        +++ b/tty-term.c
        @@ -254,6 +254,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 52521be..c6fc221 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)
         {
        --
        2.7.0