Skip to content

Instantly share code, notes, and snippets.

@choppsv1
Last active March 14, 2017 10:06
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save choppsv1/3364fd6658ef498b5e06151217187017 to your computer and use it in GitHub Desktop.
Save choppsv1/3364fd6658ef498b5e06151217187017 to your computer and use it in GitHub Desktop.
True color (24-bit) terminal support for Emacs 25.1
This diff is a modified version of a diff written by Rüdiger Sonderfeld.
The original diff can be found here:
http://emacs.1067599.n5.nabble.com/RFC-Add-tty-True-Color-support-tt299962.html
To enable the feature one must set one of 2 environment variables either
ITERM_24BIT or KONSOLE_DBUS_SESSION. The former will enable ITU T.416 mode, the
latter will use semi-colon seperators used by other terminals (and also
supported by iterm development branch).
I've added ITERM_24BIT as an environment variable to set that will cause emacs
to use the : seperated values as specified by ITU T.416. These are apparently
better to use and supported by iterm development branch.
Other chagnes:
- modified map_tty_color to set the color into the pixel field.
- modified the colors defined to be the standard X11 list.
- modified tty-color-desc to add missing colors on demand.
Diffing ./lisp/term/tty-colors.el
*** ./lisp/term/tty-colors.el.orig 2016-06-29 05:49:20.000000000 -0400
--- ./lisp/term/tty-colors.el 2017-03-14 02:03:06.485300018 -0400
***************
*** 1009,1019 ****
Value is a list of the form (NAME INDEX R G B). The returned NAME or
RGB value may not be the same as the argument COLOR, because the latter
might need to be approximated if it is not supported directly."
(and (stringp color)
(let ((color (tty-color-canonicalize color)))
! (or (assoc color (tty-color-alist frame))
! (let ((rgb (tty-color-standard-values color)))
! (and rgb (tty-color-approximate rgb frame)))))))
(defun tty-color-gray-shades (&optional display)
"Return the number of gray colors supported by DISPLAY's terminal.
--- 1009,1029 ----
Value is a list of the form (NAME INDEX R G B). The returned NAME or
RGB value may not be the same as the argument COLOR, because the latter
might need to be approximated if it is not supported directly."
+ ;; Here we are expeting to return (name idx (values)), be nice if we
+ ;; could just return the rgb, there is one use in
+ ;; read-face-attribute that uses the name unforutnately. To
+ ;; accomidate we just create entries with RGB values.
(and (stringp color)
(let ((color (tty-color-canonicalize color)))
! (or (assoc color (tty-color-alist frame))
! (let ((rgb (tty-color-standard-values color)) elt len)
! (if rgb
! (if (< (display-color-cells frame) 16777216)
! (tty-color-approximate rgb frame)
! ;; Add this value to tty-color-alist
! (setq elt (append (list color (length tty-defined-color-alist)) rgb))
! (tty-modify-color-alist elt frame)
! elt)))))))
(defun tty-color-gray-shades (&optional display)
"Return the number of gray colors supported by DISPLAY's terminal.
Diffing ./lisp/term/xterm.el
*** ./lisp/term/xterm.el.orig 2016-06-29 05:49:20.000000000 -0400
--- ./lisp/term/xterm.el 2017-03-14 02:03:06.485300018 -0400
***************
*** 917,922 ****
--- 917,931 ----
;; are more colors to support, compute them now.
(when (> ncolors 0)
(cond
+ ((= (display-color-cells (selected-frame)) 16777216) ; 24-bit xterm
+ (let ((idx (length xterm-standard-colors)))
+ ;; Insert standard X colors after the standard xterm ones
+ (mapc (lambda (color)
+ (if (not (assoc (car color) xterm-standard-colors))
+ (progn
+ (tty-color-define (car color) idx (cdr color))
+ (setq idx (1+ idx)))))
+ color-name-rgb-alist)))
((= ncolors 240) ; 256-color xterm
;; 216 non-gray colors first
(let ((r 0) (g 0) (b 0))
Diffing ./src/dispextern.h
*** ./src/dispextern.h.orig 2016-07-25 03:10:59.000000000 -0400
--- ./src/dispextern.h 2017-03-14 02:03:06.488633268 -0400
***************
*** 1756,1764 ****
INLINE bool
face_tty_specified_color (unsigned long color)
{
! return color < FACE_TTY_DEFAULT_BG_COLOR;
}
/* Non-zero if FACE was realized for unibyte use. */
#define FACE_UNIBYTE_P(FACE) ((FACE)->charset < 0)
--- 1756,1770 ----
INLINE bool
face_tty_specified_color (unsigned long color)
{
! return (color < FACE_TTY_DEFAULT_BG_COLOR);
}
+ INLINE bool
+ face_tty_specified_24_bit_color (unsigned long color)
+ {
+ /* 24 bit colors have 24th but not 25th bit set */
+ return ((color & (0x03 << 24)) == (0x01 << 24));
+ }
/* Non-zero if FACE was realized for unibyte use. */
#define FACE_UNIBYTE_P(FACE) ((FACE)->charset < 0)
Diffing ./src/term.c
*** ./src/term.c.orig 2016-06-29 05:49:23.000000000 -0400
--- ./src/term.c 2017-03-14 06:03:58.790759007 -0400
***************
*** 1927,1944 ****
const char *ts;
char *p;
! ts = tty->standout_mode ? tty->TS_set_background : tty->TS_set_foreground;
if (face_tty_specified_color (fg) && ts)
{
! p = tparam (ts, NULL, 0, fg, 0, 0, 0);
OUTPUT (tty, p);
xfree (p);
}
! ts = tty->standout_mode ? tty->TS_set_foreground : tty->TS_set_background;
if (face_tty_specified_color (bg) && ts)
{
! p = tparam (ts, NULL, 0, bg, 0, 0, 0);
OUTPUT (tty, p);
xfree (p);
}
--- 1927,1966 ----
const char *ts;
char *p;
! if (face_tty_specified_24_bit_color(fg))
! ts = tty->standout_mode ? tty->TS_set_rgb_background : tty->TS_set_rgb_foreground;
! else
! ts = tty->standout_mode ? tty->TS_set_background : tty->TS_set_foreground;
if (face_tty_specified_color (fg) && ts)
{
! if (!face_tty_specified_24_bit_color(fg))
! p = tparam (ts, NULL, 0, fg, 0, 0, 0);
! else
! {
! const unsigned char r = (fg >> 16) & 0xFF,
! g = (fg >> 8) & 0xFF,
! b = fg & 0xFF;
! p = tparam (ts, NULL, 0, (int)r, (int)g, (int)b, 0);
! }
OUTPUT (tty, p);
xfree (p);
}
! if (face_tty_specified_24_bit_color(bg))
! ts = tty->standout_mode ? tty->TS_set_rgb_foreground : tty->TS_set_rgb_background;
! else
! ts = tty->standout_mode ? tty->TS_set_foreground : tty->TS_set_background;
if (face_tty_specified_color (bg) && ts)
{
! if (!face_tty_specified_24_bit_color(bg))
! p = tparam (ts, NULL, 0, bg, 0, 0, 0);
! else
! {
! const unsigned char r = (bg >> 16) & 0xFF,
! g = (bg >> 8) & 0xFF,
! b = bg & 0xFF;
! p = tparam (ts, NULL, 0, (int)r, (int)g, (int)b, 0);
! }
OUTPUT (tty, p);
xfree (p);
}
***************
*** 2037,2043 ****
{
struct terminal *t = decode_tty_terminal (terminal);
! return make_number (t ? t->display_info.tty->TN_max_colors : 0);
}
#ifndef DOS_NT
--- 2059,2070 ----
{
struct terminal *t = decode_tty_terminal (terminal);
! if (!t)
! return make_number (0);
! else if (t->display_info.tty->TS_set_rgb_foreground)
! return make_number (16777216); /* 24 bit True Color */
! else
! return make_number (t->display_info.tty->TN_max_colors);
}
#ifndef DOS_NT
***************
*** 2051,2056 ****
--- 2078,2085 ----
static char *default_orig_pair;
static char *default_set_foreground;
static char *default_set_background;
+ static char *default_set_rgb_foreground;
+ static char *default_set_rgb_background;
/* Save or restore the default color-related capabilities of this
terminal. */
***************
*** 2063,2068 ****
--- 2092,2099 ----
dupstring (&default_orig_pair, tty->TS_orig_pair);
dupstring (&default_set_foreground, tty->TS_set_foreground);
dupstring (&default_set_background, tty->TS_set_background);
+ dupstring (&default_set_rgb_foreground, tty->TS_set_rgb_foreground);
+ dupstring (&default_set_rgb_background, tty->TS_set_rgb_background);
default_max_colors = tty->TN_max_colors;
default_max_pairs = tty->TN_max_pairs;
default_no_color_video = tty->TN_no_color_video;
***************
*** 2072,2077 ****
--- 2103,2110 ----
tty->TS_orig_pair = default_orig_pair;
tty->TS_set_foreground = default_set_foreground;
tty->TS_set_background = default_set_background;
+ tty->TS_set_rgb_foreground = default_set_rgb_foreground;
+ tty->TS_set_rgb_background = default_set_rgb_background;
tty->TN_max_colors = default_max_colors;
tty->TN_max_pairs = default_max_pairs;
tty->TN_no_color_video = default_no_color_video;
***************
*** 2096,2101 ****
--- 2129,2135 ----
tty->TN_max_pairs = 0;
tty->TN_no_color_video = 0;
tty->TS_set_foreground = tty->TS_set_background = tty->TS_orig_pair = NULL;
+ tty->TS_set_rgb_foreground = tty->TS_set_rgb_background = NULL;
break;
case 0: /* default colors, if any */
default:
***************
*** 2110,2119 ****
--- 2144,2172 ----
tty->TS_set_foreground = "\033[3%dm";
tty->TS_set_background = "\033[4%dm";
#endif
+ tty->TS_set_rgb_foreground = NULL;
+ tty->TS_set_rgb_background = NULL;
tty->TN_max_colors = 8;
tty->TN_max_pairs = 64;
tty->TN_no_color_video = 0;
break;
+ case 16777216: /* RGB colors */
+ tty->TS_orig_pair = "\033[0m";
+ #ifdef TERMINFO
+ tty->TS_set_foreground = "\033[3%p1%dm";
+ tty->TS_set_background = "\033[4%p1%dm";
+ tty->TS_set_rgb_foreground = "\033[38;2;%p1%d;%p2%d;%p3%dm";
+ tty->TS_set_rgb_background = "\033[48;2;%p1%d;%p2%d;%p3%dm";
+ #else
+ tty->TS_set_foreground = "\033[3%dm";
+ tty->TS_set_background = "\033[4%dm";
+ tty->TS_set_rgb_foreground = "\033[38;2;%d;%d;%dm";
+ tty->TS_set_rgb_background = "\033[48;2;%d;%d;%dm";
+ #endif
+ tty->TN_max_colors = 16777216;
+ /*tty->TN_max_pairs = 64; TODO */
+ tty->TN_no_color_video = 0;
+ break;
}
}
***************
*** 4134,4139 ****
--- 4187,4213 ----
tty->TN_no_color_video = tgetnum ("NC");
if (tty->TN_no_color_video == -1)
tty->TN_no_color_video = 0;
+
+ /* TODO Reliable way to detect: Konsole, iTerm2, st */
+ if (tgetflag("Tc") || \
+ (getenv("COLORTERM") && !strcmp("truecolor", getenv("COLORTERM"))) || \
+ getenv ("KONSOLE_DBUS_SESSION") || \
+ getenv("ITERM_24BIT"))
+ {
+ /* TODO This should be extracted from terminfo/termcap. */
+ #ifdef TERMINFO
+ tty->TS_set_rgb_foreground = "\033[38;2;%p1%d;%p2%d;%p3%dm";
+ tty->TS_set_rgb_background = "\033[48;2;%p1%d;%p2%d;%p3%dm";
+ #else
+ tty->TS_set_rgb_foreground = "\033[38;2;%d;%d;%dm";
+ tty->TS_set_rgb_background = "\033[48;2;%d;%d;%dm";
+ #endif
+ }
+ else
+ {
+ tty->TS_set_rgb_foreground = NULL;
+ tty->TS_set_rgb_background = NULL;
+ }
}
tty_default_color_capabilities (tty, 1);
Diffing ./src/termchar.h
*** ./src/termchar.h.orig 2016-06-29 05:49:23.000000000 -0400
--- ./src/termchar.h 2017-03-14 02:03:06.488633268 -0400
***************
*** 161,166 ****
--- 161,170 ----
const char *TS_set_foreground;
const char *TS_set_background;
+ /* Support for 24bit RGB color terminals. */
+ const char *TS_set_rgb_foreground;
+ const char *TS_set_rgb_background;
+
int TF_hazeltine; /* termcap hz flag. */
int TF_insmode_motion; /* termcap mi flag: can move while in insert mode. */
int TF_standout_motion; /* termcap mi flag: can move while in standout mode. */
Diffing ./src/xfaces.c
*** ./src/xfaces.c.orig 2016-07-25 03:11:02.000000000 -0400
--- ./src/xfaces.c 2017-03-14 02:34:38.746452983 -0400
***************
*** 848,901 ****
if (!STRINGP (color) || NILP (Ffboundp (Qtty_color_desc)))
return false;
! XSETFRAME (frame, f);
! color_desc = call2 (Qtty_color_desc, color, frame);
! if (CONSP (color_desc) && CONSP (XCDR (color_desc)))
{
! Lisp_Object rgb;
! if (! INTEGERP (XCAR (XCDR (color_desc))))
! return false;
! tty_color->pixel = XINT (XCAR (XCDR (color_desc)));
! rgb = XCDR (XCDR (color_desc));
! if (! parse_rgb_list (rgb, tty_color))
! return false;
! /* Should we fill in STD_COLOR too? */
! if (std_color)
! {
! /* Default STD_COLOR to the same as TTY_COLOR. */
! *std_color = *tty_color;
! /* Do a quick check to see if the returned descriptor is
! actually _exactly_ equal to COLOR, otherwise we have to
! lookup STD_COLOR separately. If it's impossible to lookup
! a standard color, we just give up and use TTY_COLOR. */
! if ((!STRINGP (XCAR (color_desc))
! || NILP (Fstring_equal (color, XCAR (color_desc))))
! && !NILP (Ffboundp (Qtty_color_standard_values)))
! {
! /* Look up STD_COLOR separately. */
! rgb = call1 (Qtty_color_standard_values, color);
! if (! parse_rgb_list (rgb, std_color))
! return false;
! }
! }
! return true;
}
- else if (NILP (Fsymbol_value (intern ("tty-defined-color-alist"))))
- /* We were called early during startup, and the colors are not
- yet set up in tty-defined-color-alist. Don't return a failure
- indication, since this produces the annoying "Unable to
- load color" messages in the *Messages* buffer. */
- return true;
- else
- /* tty-color-desc seems to have returned a bad value. */
- return false;
}
/* A version of defined_color for non-X frames. */
--- 848,927 ----
if (!STRINGP (color) || NILP (Ffboundp (Qtty_color_desc)))
return false;
! if (f->output_method == output_termcap
! && f->output_data.tty->display_info->TS_set_rgb_foreground
! && !NILP (Ffboundp (Qtty_color_standard_values)))
! {
! /* Terminal supports 3 byte RGB colors. */
! if (!NILP (Ffboundp (Qtty_color_canonicalize)))
! color = call1(Qtty_color_canonicalize, color);
!
! color_desc = call1 (Qtty_color_standard_values, color);
! if (! parse_rgb_list (color_desc, tty_color))
! return false;
!
! /* Map XColor to 3 byte values. */
! tty_color->pixel = 1 << 24 /* Set bit 24 to mark RGB values. */
! | (tty_color->red / 256) << 16
! | (tty_color->green / 256) << 8
! | (tty_color->blue / 256);
!
! if (std_color)
! *std_color = *tty_color;
! return true;
! }
! else
{
! XSETFRAME (frame, f);
! color_desc = call2 (Qtty_color_desc, color, frame);
! if (CONSP (color_desc) && CONSP (XCDR (color_desc)))
! {
! Lisp_Object rgb;
! if (! INTEGERP (XCAR (XCDR (color_desc))))
! return false;
! tty_color->pixel = XINT (XCAR (XCDR (color_desc)));
! rgb = XCDR (XCDR (color_desc));
! if (! parse_rgb_list (rgb, tty_color))
! return false;
! /* Should we fill in STD_COLOR too? */
! if (std_color)
! {
! /* Default STD_COLOR to the same as TTY_COLOR. */
! *std_color = *tty_color;
! /* Do a quick check to see if the returned descriptor is
! actually _exactly_ equal to COLOR, otherwise we have to
! lookup STD_COLOR separately. If it's impossible to lookup
! a standard color, we just give up and use TTY_COLOR. */
! if ((!STRINGP (XCAR (color_desc))
! || NILP (Fstring_equal (color, XCAR (color_desc))))
! && !NILP (Ffboundp (Qtty_color_standard_values)))
! {
! /* Look up STD_COLOR separately. */
! rgb = call1 (Qtty_color_standard_values, color);
! if (! parse_rgb_list (rgb, std_color))
! return false;
! }
! }
!
! return true;
! }
! else if (NILP (Fsymbol_value (intern ("tty-defined-color-alist"))))
! /* We were called early during startup, and the colors are not
! yet set up in tty-defined-color-alist. Don't return a failure
! indication, since this produces the annoying "Unable to
! load color" messages in the *Messages* buffer. */
! return true;
! else
! /* tty-color-desc seems to have returned a bad value. */
! return false;
}
}
/* A version of defined_color for non-X frames. */
***************
*** 5700,5705 ****
--- 5726,5732 ----
unsigned long default_pixel =
foreground_p ? FACE_TTY_DEFAULT_FG_COLOR : FACE_TTY_DEFAULT_BG_COLOR;
unsigned long pixel = default_pixel;
+ XColor true_color;
#ifdef MSDOS
unsigned long default_other_pixel =
foreground_p ? FACE_TTY_DEFAULT_BG_COLOR : FACE_TTY_DEFAULT_FG_COLOR;
***************
*** 5718,5724 ****
{
/* Associations in tty-defined-color-alist are of the form
(NAME INDEX R G B). We need the INDEX part. */
! pixel = XINT (XCAR (XCDR (def)));
}
if (pixel == default_pixel && STRINGP (color))
--- 5745,5762 ----
{
/* Associations in tty-defined-color-alist are of the form
(NAME INDEX R G B). We need the INDEX part. */
! if (f->output_method == output_termcap
! && f->output_data.tty->display_info->TS_set_rgb_foreground
! && parse_rgb_list (XCDR (XCDR(def)), &true_color))
! {
! /* Map XColor to 3 byte values. */
! pixel = 1 << 24 /* Set bit 24 to mark RGB values. */
! | (true_color.red / 256) << 16
! | (true_color.green / 256) << 8
! | (true_color.blue / 256);
! }
! else
! pixel = XINT (XCAR (XCDR (def)));
}
if (pixel == default_pixel && STRINGP (color))
***************
*** 6405,6410 ****
--- 6443,6449 ----
DEFSYM (Qwindow_divider, "window-divider");
DEFSYM (Qwindow_divider_first_pixel, "window-divider-first-pixel");
DEFSYM (Qwindow_divider_last_pixel, "window-divider-last-pixel");
+ DEFSYM (Qtty_color_canonicalize, "tty-color-canonicalize");
DEFSYM (Qtty_color_desc, "tty-color-desc");
DEFSYM (Qtty_color_standard_values, "tty-color-standard-values");
DEFSYM (Qtty_color_by_index, "tty-color-by-index");
@kangjianbin
Copy link

Hi I tried this patch, but the color was incorrect for region:
24
If I changed foreground color to #fdf6e2, it worked.
color #fdf6e3 and #ffffff existed in 'list-color-display'.

@dsadinoff
Copy link

I find that these emacs patches (for 25.1 and 24.5) only work when using upper-case hex numbers. Any ideas?

@choppsv1
Copy link
Author

So I ported the patch to 25.1 but it doesn't appear to be working correctly... Any help triaging would be appreciated

@bsuh
Copy link

bsuh commented Nov 9, 2016

@choppsv1 I took a stab at porting it when 25.1-rc1 came out. It seems like you're missing the changes to map_tty_color. Here's my diff that seems to work https://gist.github.com/bsuh/bab7eeb8bda2421ac01c760c90668100

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