Skip to content

Instantly share code, notes, and snippets.

@choppsv1
Created November 11, 2015 06:29
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save choppsv1/886322cb9701b759edbc to your computer and use it in GitHub Desktop.
Save choppsv1/886322cb9701b759edbc to your computer and use it in GitHub Desktop.
True color (24-bit) terminal support for tmux-2.1
This diff is a modified version of a diff written by Arnis Lapsa.
[ The original can be found here: https://gist.github.com/ArnisL/6156593 ]
This diff adds support to tmux for 24-bit color CSI SRG sequences. This
allows terminal based programs that take advantage of it (e.g., vim or
emacs with https://gist.github.com/choppsv1/73d51cedd3e8ec72e1c1 patch)
to display 16 million colors while running in tmux.
The primary changes I've made were to support ":" as a delimeter as well
as the older ";" delimeter, as well as adapting to 2.1. The ":" delimiter
is defined by ITU T.416 which is apparently the correct way to do this;
however, many early implementations of 24 bit support in terminal
applications used ";" so both are supported. Tmux 2.1 added parsing
support for the 24 bit SGR code, but not output support, instead it
translated the colors.
diff --git a/colour.c b/colour.c
index a56ddce..0b396b8 100644
--- a/colour.c
+++ b/colour.c
@@ -29,13 +29,6 @@
* of the 256 colour palette.
*/
-struct colour_rgb {
- u_char i;
- u_char r;
- u_char g;
- u_char b;
-};
-
const struct colour_rgb colour_from_256[] = {
{ 0, 0x00, 0x00, 0x00 }, { 1, 0x00, 0x00, 0x5f },
{ 2, 0x00, 0x00, 0x87 }, { 3, 0x00, 0x00, 0xaf },
diff --git a/input.c b/input.c
index 3a02b0c..77694f5 100644
--- a/input.c
+++ b/input.c
@@ -505,8 +505,7 @@ const struct input_transition input_state_csi_enter_table[] = {
{ 0x1c, 0x1f, input_c0_dispatch, NULL },
{ 0x20, 0x2f, input_intermediate, &input_state_csi_intermediate },
{ 0x30, 0x39, input_parameter, &input_state_csi_parameter },
- { 0x3a, 0x3a, NULL, &input_state_csi_ignore },
- { 0x3b, 0x3b, input_parameter, &input_state_csi_parameter },
+ { 0x3a, 0x3b, input_parameter, &input_state_csi_parameter },
{ 0x3c, 0x3f, input_intermediate, &input_state_csi_parameter },
{ 0x40, 0x7e, input_csi_dispatch, &input_state_ground },
{ 0x7f, 0xff, NULL, NULL },
@@ -523,8 +522,7 @@ const struct input_transition input_state_csi_parameter_table[] = {
{ 0x1c, 0x1f, input_c0_dispatch, NULL },
{ 0x20, 0x2f, input_intermediate, &input_state_csi_intermediate },
{ 0x30, 0x39, input_parameter, NULL },
- { 0x3a, 0x3a, NULL, &input_state_csi_ignore },
- { 0x3b, 0x3b, input_parameter, NULL },
+ { 0x3a, 0x3b, input_parameter, NULL },
{ 0x3c, 0x3f, NULL, &input_state_csi_ignore },
{ 0x40, 0x7e, input_csi_dispatch, &input_state_ground },
{ 0x7f, 0xff, NULL, NULL },
@@ -917,7 +915,7 @@ input_split(struct input_ctx *ictx)
return (0);
ptr = ictx->param_buf;
- while ((out = strsep(&ptr, ";")) != NULL) {
+ while ((out = strsep(&ptr, ":;")) != NULL) {
if (*out == '\0')
n = -1;
else {
@@ -1628,17 +1626,21 @@ input_csi_dispatch_sgr_256(struct input_ctx *ictx, int fgbg, u_int *i)
if (c == -1) {
if (fgbg == 38) {
gc->flags &= ~GRID_FLAG_FG256;
+ gc->flags &= ~GRID_FLAG_FG24;
gc->fg = 8;
} else if (fgbg == 48) {
gc->flags &= ~GRID_FLAG_BG256;
+ gc->flags &= ~GRID_FLAG_BG24;
gc->bg = 8;
}
} else {
if (fgbg == 38) {
gc->flags |= GRID_FLAG_FG256;
+ gc->flags &= ~GRID_FLAG_FG24;
gc->fg = c;
} else if (fgbg == 48) {
gc->flags |= GRID_FLAG_BG256;
+ gc->flags &= ~GRID_FLAG_BG24;
gc->bg = c;
}
}
@@ -1664,13 +1666,27 @@ 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;
- } else if (fgbg == 48) {
- gc->flags |= GRID_FLAG_BG256;
- gc->bg = c;
+ /* XXX chopps: This would be a good place to add a "translate" option */
+ if (0) {
+ c = colour_find_rgb(r, g, b);
+ if (fgbg == 38) {
+ gc->flags |= GRID_FLAG_FG256;
+ gc->fg = c;
+ } else if (fgbg == 48) {
+ gc->flags |= GRID_FLAG_BG256;
+ gc->bg = c;
+ }
+ } else {
+ struct colour_rgb rgb = {.i=0, .r=r, .g=g, .b=b};
+ if (fgbg == 38) {
+ gc->flags &= ~GRID_FLAG_FG256;
+ gc->flags |= GRID_FLAG_FG24;
+ gc->fg_rgb = rgb;
+ } else if (fgbg == 48) {
+ gc->flags &= ~GRID_FLAG_BG256;
+ gc->flags |= GRID_FLAG_BG24;
+ gc->bg_rgb = rgb;
+ }
}
}
@@ -1753,10 +1769,12 @@ input_csi_dispatch_sgr(struct input_ctx *ictx)
case 36:
case 37:
gc->flags &= ~GRID_FLAG_FG256;
+ gc->flags &= ~GRID_FLAG_FG24;
gc->fg = n - 30;
break;
case 39:
gc->flags &= ~GRID_FLAG_FG256;
+ gc->flags &= ~GRID_FLAG_FG24;
gc->fg = 8;
break;
case 40:
@@ -1768,10 +1786,12 @@ input_csi_dispatch_sgr(struct input_ctx *ictx)
case 46:
case 47:
gc->flags &= ~GRID_FLAG_BG256;
+ gc->flags &= ~GRID_FLAG_BG24;
gc->bg = n - 40;
break;
case 49:
gc->flags &= ~GRID_FLAG_BG256;
+ gc->flags &= ~GRID_FLAG_BG24;
gc->bg = 8;
break;
case 90:
@@ -1794,6 +1814,7 @@ input_csi_dispatch_sgr(struct input_ctx *ictx)
case 106:
case 107:
gc->flags &= ~GRID_FLAG_BG256;
+ gc->flags &= ~GRID_FLAG_BG24;
gc->bg = n - 10;
break;
}
diff --git a/tmux.h b/tmux.h
index b16b27f..95fbbf6 100644
--- a/tmux.h
+++ b/tmux.h
@@ -638,10 +638,20 @@ struct utf8_data {
#define GRID_FLAG_FG256 0x1
#define GRID_FLAG_BG256 0x2
#define GRID_FLAG_PADDING 0x4
+#define GRID_FLAG_FG24 0x8
+#define GRID_FLAG_BG24 0x10
/* Grid line flags. */
#define GRID_LINE_WRAPPED 0x1
+/* An RGB colour. */
+struct colour_rgb {
+ u_char i;
+ u_char r;
+ u_char g;
+ u_char b;
+};
+
/* Grid cell data. */
struct grid_cell {
u_char attr;
@@ -651,6 +661,8 @@ struct grid_cell {
u_char xstate; /* top 4 bits width, bottom 4 bits size */
u_char xdata[UTF8_SIZE];
+ struct colour_rgb fg_rgb;
+ struct colour_rgb bg_rgb;
} __packed;
/* Grid line. */
diff --git a/tty.c b/tty.c
index c0ae79b..cd1a84a 100644
--- a/tty.c
+++ b/tty.c
@@ -38,6 +38,7 @@ void tty_error_callback(struct bufferevent *, short, void *);
void tty_set_italics(struct tty *);
int tty_try_256(struct tty *, u_char, const char *);
+int tty_try_24(struct tty *, struct colour_rgb, const char *);
void tty_colours(struct tty *, const struct grid_cell *);
void tty_check_fg(struct tty *, struct grid_cell *);
@@ -454,11 +455,8 @@ tty_set_italics(struct tty *tty)
const char *s;
if (tty_term_has(tty->term, TTYC_SITM)) {
- s = options_get_string(global_options, "default-terminal");
- if (strcmp(s, "screen") != 0 && strncmp(s, "screen-", 7) != 0) {
- tty_putcode(tty, TTYC_SITM);
- return;
- }
+ tty_putcode(tty, TTYC_SITM);
+ return;
}
tty_putcode(tty, TTYC_SMSO);
}
@@ -1439,14 +1437,23 @@ tty_attributes(struct tty *tty, const struct grid_cell *gc,
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)
+ tc->fg_rgb.r == gc->fg_rgb.r &&
+ tc->fg_rgb.g == gc->fg_rgb.g &&
+ tc->fg_rgb.b == gc->fg_rgb.b &&
+
+ tc->bg_rgb.r == gc->bg_rgb.r &&
+ tc->bg_rgb.g == gc->bg_rgb.g &&
+ tc->bg_rgb.b == gc->bg_rgb.b &&
+ ((flags ^ tc->flags) & (GRID_FLAG_FG256|GRID_FLAG_BG256|GRID_FLAG_FG24|GRID_FLAG_BG24)) == 0
+
+ )
return;
/*
@@ -1455,8 +1462,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 = (fg == 8 && !(flags & GRID_FLAG_FG256) && !(flags & GRID_FLAG_FG24));
+ bg_default = (bg == 8 && !(flags & GRID_FLAG_BG256) && !(flags & GRID_FLAG_BG24));
if (fg_default || bg_default) {
/*
* If don't have AX but do have op, send sgr0 (op can't
@@ -1470,39 +1477,49 @@ tty_colours(struct tty *tty, const struct grid_cell *gc)
tty_reset(tty);
else {
if (fg_default &&
- (tc->fg != 8 || tc->flags & GRID_FLAG_FG256)) {
+ (tc->fg != 8 || tc->flags & GRID_FLAG_FG256 || tc->flags & GRID_FLAG_FG24)) {
if (have_ax)
tty_puts(tty, "\033[39m");
else if (tc->fg != 7 ||
- tc->flags & GRID_FLAG_FG256)
+ tc->flags & GRID_FLAG_FG256 ||
+ tc->flags & GRID_FLAG_FG24)
tty_putcode1(tty, TTYC_SETAF, 7);
tc->fg = 8;
tc->flags &= ~GRID_FLAG_FG256;
+ tc->flags &= ~GRID_FLAG_FG24;
}
if (bg_default &&
- (tc->bg != 8 || tc->flags & GRID_FLAG_BG256)) {
+ (tc->bg != 8 || tc->flags & GRID_FLAG_BG256 || tc->flags & GRID_FLAG_BG24)) {
if (have_ax)
tty_puts(tty, "\033[49m");
else if (tc->bg != 0 ||
- tc->flags & GRID_FLAG_BG256)
+ tc->flags & GRID_FLAG_BG256 ||
+ tc->flags & GRID_FLAG_BG24)
tty_putcode1(tty, TTYC_SETAB, 0);
tc->bg = 8;
tc->flags &= ~GRID_FLAG_BG256;
+ tc->flags &= ~GRID_FLAG_BG24;
}
}
}
/* Set the foreground colour. */
- if (!fg_default && (fg != tc->fg ||
- ((flags & GRID_FLAG_FG256) != (tc->flags & GRID_FLAG_FG256))))
+ if (!fg_default && (fg != tc->fg || ((flags & GRID_FLAG_FG256) != (tc->flags & GRID_FLAG_FG256)) ||
+ (
+ ( tc->fg_rgb.r!=gc->fg_rgb.r || tc->fg_rgb.g!=gc->fg_rgb.g || tc->fg_rgb.b!=gc->fg_rgb.b ) ||
+ ((flags & GRID_FLAG_FG24) != (tc->flags & GRID_FLAG_FG24))
+ )))
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 && (bg != tc->bg || ((flags & GRID_FLAG_BG256) != (tc->flags & GRID_FLAG_BG256)) ||
+ (
+ ( tc->bg_rgb.r!=gc->bg_rgb.r || tc->bg_rgb.g!=gc->bg_rgb.g || tc->bg_rgb.b!=gc->bg_rgb.b ) ||
+ ((flags & GRID_FLAG_BG24) != (tc->flags & GRID_FLAG_BG24))
+ )))
tty_colours_bg(tty, gc);
}
@@ -1514,7 +1531,7 @@ tty_check_fg(struct tty *tty, struct grid_cell *gc)
colours = tty_term_number(tty->term, TTYC_COLORS);
/* Is this a 256-colour colour? */
- if (gc->flags & GRID_FLAG_FG256) {
+ if (gc->flags & GRID_FLAG_FG256 && !(gc->flags & GRID_FLAG_BG24)) {
/* And not a 256 colour mode? */
if (!(tty->term->flags & TERM_256COLOURS) &&
!(tty->term_flags & TERM_256COLOURS)) {
@@ -1547,7 +1564,7 @@ tty_check_bg(struct tty *tty, struct grid_cell *gc)
colours = tty_term_number(tty->term, TTYC_COLORS);
/* Is this a 256-colour colour? */
- if (gc->flags & GRID_FLAG_BG256) {
+ if (gc->flags & GRID_FLAG_BG256 && !(gc->flags & GRID_FLAG_BG24)) {
/*
* And not a 256 colour mode? Translate to 16-colour
* palette. Bold background doesn't exist portably, so just
@@ -1575,15 +1592,29 @@ void
tty_colours_fg(struct tty *tty, const struct grid_cell *gc)
{
struct grid_cell *tc = &tty->cell;
+ struct colour_rgb rgb= gc->fg_rgb;
u_char fg = gc->fg;
char s[32];
+ tc->flags &= ~GRID_FLAG_FG256;
+ tc->flags &= ~GRID_FLAG_FG24;
+
+ /* Is this a 24-colour colour? */
+ if (gc->flags & GRID_FLAG_FG24) {
+//log_debug("trying to output 24bit fg");
+ if (tty_try_24(tty, rgb, "38") == 0){
+ tc->fg_rgb = rgb;
+ tc->flags |= gc->flags & GRID_FLAG_FG24;
+ }
+ 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. */
+ if (tty_try_256(tty, fg, "38") == 0){
+ tc->fg = fg;
+ tc->flags |= gc->flags & GRID_FLAG_FG256;
+ }
return;
}
@@ -1591,32 +1622,41 @@ tty_colours_fg(struct tty *tty, const struct grid_cell *gc)
if (fg >= 90 && fg <= 97) {
xsnprintf(s, sizeof s, "\033[%dm", fg);
tty_puts(tty, s);
- goto save_fg;
+ tc->fg = fg;
+ return;
}
/* Otherwise set the foreground colour. */
tty_putcode1(tty, TTYC_SETAF, fg);
-
-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;
}
void
tty_colours_bg(struct tty *tty, const struct grid_cell *gc)
{
struct grid_cell *tc = &tty->cell;
+ struct colour_rgb rgb= gc->bg_rgb;
u_char bg = gc->bg;
char s[32];
+ tc->flags &= ~GRID_FLAG_BG256;
+ tc->flags &= ~GRID_FLAG_BG24;
+
+ /* Is this a 24-colour colour? */
+ if (gc->flags & GRID_FLAG_BG24) {
+ if (tty_try_24(tty, rgb, "48") == 0){
+ tc->bg_rgb = rgb;
+ tc->flags |= gc->flags & GRID_FLAG_BG24;
+ }
+ 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. */
+ if (tty_try_256(tty, bg, "48") == 0){
+ tc->bg = bg;
+ tc->flags |= gc->flags & GRID_FLAG_BG256;
+ }
return;
}
@@ -1624,17 +1664,15 @@ tty_colours_bg(struct tty *tty, const struct grid_cell *gc)
if (bg >= 90 && bg <= 97) {
xsnprintf(s, sizeof s, "\033[%dm", bg + 10);
tty_puts(tty, s);
- goto save_bg;
+ tc->bg = bg;
+ return;
}
/* Otherwise set the background colour. */
tty_putcode1(tty, TTYC_SETAB, bg);
-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;
}
int
@@ -1674,6 +1712,23 @@ fallback:
return (0);
}
+
+int
+tty_try_24(struct tty *tty, struct colour_rgb rgb, const char *type)
+{
+ char s[32];
+
+ //if (!(tty->term->flags & TERM_256COLOURS) &&
+ // !(tty->term_flags & TERM_256COLOURS))
+ // return (-1);
+
+ //xsnprintf(s, sizeof s, "\033[%s;5;%hhum", type, colour);
+ xsnprintf(s, sizeof s, "\033[%s;2;%hhu;%hhu;%hhum", type, rgb.r, rgb.g, rgb.b);
+//log_debug("24bit output: %s",s);
+ tty_puts(tty, s);
+ return (0);
+}
+
void
tty_default_colours(struct grid_cell *gc, const struct window_pane *wp)
{
@biocyberman
Copy link

This commit says tmux has support for true color tmux/tmux@427b820 as discussed in this issue:
tmux/tmux#34

So, do I need to apply your patch to get true color?
I just came here because of poor color during emacs's ediff

update

I tried the patch anyway, but failed. Could you give the commit you used to make the patch?

➜  tmux git:(master) ✗ git checkout 2.1  
HEAD is now at 310f0a9... Update for 2.1 release.
➜  tmux git:(310f0a9) ✗ patch -p1 <tmux-2.1-24bit.diff                                                                                                                                                                                                                           [ 2:58PM]
patching file colour.c
patching file input.c
patching file tmux.h
Hunk #1 succeeded at 628 (offset -10 lines).
Hunk #2 succeeded at 651 (offset -10 lines).
patching file tty.c
Hunk #1 succeeded at 36 (offset -2 lines).
Hunk #2 FAILED at 455.
Hunk #3 succeeded at 1445 (offset 5 lines).
Hunk #4 succeeded at 1470 (offset 5 lines).
Hunk #5 succeeded at 1485 (offset 5 lines).
Hunk #6 succeeded at 1539 (offset 5 lines).
Hunk #7 succeeded at 1572 (offset 5 lines).
Hunk #8 succeeded at 1600 (offset 5 lines).
Hunk #9 succeeded at 1630 (offset 5 lines).
Hunk #10 succeeded at 1672 (offset 5 lines).
Hunk #11 succeeded at 1720 (offset 5 lines).
1 out of 11 hunks FAILED -- saving rejects to file tty.c.rej

Content of tty.c.rej

 --- tty.c
 +++ tty.c
 @@ -455,11 +456,8 @@ tty_set_italics(struct tty *tty)
     const char  *s;

     if (tty_term_has(tty->term, TTYC_SITM)) {
 -       s = options_get_string(global_options, "default-terminal");
 -       if (strcmp(s, "screen") != 0 && strncmp(s, "screen-", 7) != 0) {
 -           tty_putcode(tty, TTYC_SITM);
 -           return;
 -       }
 +       tty_putcode(tty, TTYC_SITM);
 +       return;
     }
     tty_putcode(tty, TTYC_SMSO);
  }

@choppsv1
Copy link
Author

Hi, sorry I didn't seem to get mail when you left this message. Do things work now with tmux 2.2?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment