[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH] Support 24-bit terminal colors.
From: |
Rami Ylimäki |
Subject: |
[PATCH] Support 24-bit terminal colors. |
Date: |
Tue, 30 Aug 2016 23:11:46 +0300 |
Based on previous work by Rüdiger Sonderfeld, Christian Hopps and
Charles Strahan.
Currently it's not possible to detect whether terminal supports 24-bit
colors. Therefore following methods can be used to force 24-bit mode on:
emacs -nw --color=rgb
emacsclient -nw -F "((tty-color-mode . rgb))"
* lisp/server.el (server-process-filter): Pass 'tty-color-mode frame
parameter from command line to server-create-tty-frame.
(server-create-tty-frame): Pass 'tty-color-mode frame parameter to
make-frame.
* lisp/term/tty-colors.el (tty-color-mode-alist): Add 'rgb entry to
support 24-bit terminals. This enables the --color=rgb option as well.
* lisp/term/xterm.el (xterm-register-default-colors): Define all named
tty colors. Add xterm-rgb-support-b to check whether 24-bit mode has
been forced on.
* src/dispextern.h: Define bit FACE_TTY_NONINDEXED_COLOR to separate
indexed and rgb component based tty color encodings. The rgb component
based color "index" is simply represented by a 24-bit rgb triplet with
FACE_TTY_NONINDEXED_COLOR set.
* src/termchar.h (tty_display_info): Remove TN_max_pairs field
describing maximum number of color pairs. The field is unused and
would also be inconvenient with 24-bit colors. Add new tty control
functions TS_set_rgb_foreground and TS_set_rgb_background for
outputting rgb component based tty colors.
* src/term.c (turn_on_face): Output indexed or rgb component based tty
colors by detecting whether FACE_TTY_NONINDEXED_COLOR has been set.
(tty_default_color_capabilities): Add TS_set_rgb_foreground and
TS_set_rgb_background.
(tty_setup_colors): Initialize tty_display_info for 24-bit terminals.
* src/xfaces.c (tty_lookup_color): Use rgb component based tty color
encoding on 24-bit terminals.
(map_tty_color): Use rgb component based tty color encoding for face
foreground and background on 24-bit terminals.
---
doc/man/emacs.1.in | 2 +-
lisp/server.el | 14 ++++++--
lisp/term/tty-colors.el | 3 +-
lisp/term/xterm.el | 16 +++++++++
src/dispextern.h | 3 ++
src/term.c | 89 +++++++++++++++++++++++++++++++++++--------------
src/termchar.h | 7 ++--
src/xfaces.c | 51 ++++++++++++++++++++++++++--
8 files changed, 149 insertions(+), 36 deletions(-)
diff --git a/doc/man/emacs.1.in b/doc/man/emacs.1.in
index 3b1566f..79d48fb 100644
--- a/doc/man/emacs.1.in
+++ b/doc/man/emacs.1.in
@@ -252,7 +252,7 @@ Set additional X resources.
Override color mode for character terminals;
.I mode
defaults to "auto", and can also be "never", "auto", "always",
-or a mode name like "ansi8".
+or a mode name like "ansi8". Use "rgb" for 24-bit colors.
.TP
.BI \-bw " pixels\fR,\fP " \-\-border\-width " pixels"
Set the
diff --git a/lisp/server.el b/lisp/server.el
index 5300984..5ecd483 100644
--- a/lisp/server.el
+++ b/lisp/server.el
@@ -792,11 +792,13 @@ This handles splitting the command if it would be bigger
than
(setq prefix "-print-nonl "))
(server-send-string proc (concat prefix qtext "\n"))))
-(defun server-create-tty-frame (tty type proc)
+(defun server-create-tty-frame (tty type color-mode proc)
(unless tty
(error "Invalid terminal device"))
(unless type
(error "Invalid terminal type"))
+ (unless color-mode
+ (error "Invalid terminal color mode"))
(add-to-list 'frame-inherited-parameters 'client)
(let ((frame
(server-with-environment
@@ -812,6 +814,7 @@ This handles splitting the command if it would be bigger
than
(make-frame `((window-system . nil)
(tty . ,tty)
(tty-type . ,type)
+ (tty-color-mode . ,color-mode)
;; Ignore nowait here; we always need to
;; clean up opened ttys when the client dies.
(client . ,proc)
@@ -1055,6 +1058,8 @@ The following commands are accepted by the client:
(coding-system (and (default-value 'enable-multibyte-characters)
(or file-name-coding-system
default-file-name-coding-system)))
+ (tty-color-mode 'default)
+
nowait ; t if emacsclient does not want to wait for us.
frame ; Frame opened for the client (if any).
display ; Open frame on this display.
@@ -1089,7 +1094,10 @@ The following commands are accepted by the client:
(let ((alist (pop args-left)))
(if coding-system
(setq alist (decode-coding-string alist coding-system)))
- (setq frame-parameters (car (read-from-string alist)))))
+ (setq frame-parameters (car (read-from-string alist)))
+ (let ((color-mode (cdr (assoc 'tty-color-mode
frame-parameters))))
+ (if (assoc color-mode tty-color-mode-alist)
+ (setq tty-color-mode color-mode)))))
;; -display DISPLAY:
;; Open X frames on the given display instead of the default.
@@ -1241,7 +1249,7 @@ The following commands are accepted by the client:
frame-parameters))
;; When resuming on a tty, tty-name is nil.
(tty-name
- (server-create-tty-frame tty-name tty-type proc))))
+ (server-create-tty-frame tty-name tty-type tty-color-mode
proc))))
(process-put
proc 'continuation
diff --git a/lisp/term/tty-colors.el b/lisp/term/tty-colors.el
index a886950..b9b69cd 100644
--- a/lisp/term/tty-colors.el
+++ b/lisp/term/tty-colors.el
@@ -764,7 +764,8 @@
(auto . 0)
(ansi8 . 8)
(always . 8)
- (yes . 8))
+ (yes . 8)
+ (rgb . 16777216))
"An alist of supported standard tty color modes and their aliases.")
(defun tty-color-alist (&optional _frame)
diff --git a/lisp/term/xterm.el b/lisp/term/xterm.el
index 01c0113..e0f3eb5 100644
--- a/lisp/term/xterm.el
+++ b/lisp/term/xterm.el
@@ -905,6 +905,14 @@ hitting screen's max DCS length."
"Convert an 8-bit primary color value PRIM to a corresponding 16-bit value."
(logior prim (lsh prim 8)))
+(defun xterm-rgb-support-p ()
+ "Check whether 24-bit colors have been forced on."
+ (or
+ ;; emacs -nw --color=rgb
+ (eql (cdr (assoc 'tty-color-mode default-frame-alist)) 'rgb)
+ ;; emacsclient -nw -F "((tty-color-mode . rgb))"
+ (eql (cdr (assoc 'tty-color-mode (frame-parameters))) 'rgb)))
+
(defun xterm-register-default-colors (colors)
"Register the default set of colors for xterm or compatible emulator.
@@ -930,6 +938,14 @@ versions of xterm."
;; are more colors to support, compute them now.
(when (> ncolors 0)
(cond
+ ((xterm-rgb-support-p) ; 24-bit xterm
+ ;; all named tty colors
+ (let ((idx (length xterm-standard-colors)))
+ (mapc (lambda (color)
+ (unless (assoc (car color) xterm-standard-colors)
+ (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))
diff --git a/src/dispextern.h b/src/dispextern.h
index f2c42de..35d2106 100644
--- a/src/dispextern.h
+++ b/src/dispextern.h
@@ -1751,6 +1751,9 @@ struct face
#define FACE_TTY_DEFAULT_BG_COLOR ((unsigned long) -3)
+/* Color index bit indicating absence of palette. */
+#define FACE_TTY_NONINDEXED_COLOR ((unsigned long) (1 << 24))
+
/* True if COLOR is a specified (i.e., nondefault) foreground or
background color for a tty face. */
diff --git a/src/term.c b/src/term.c
index d54ff11..dc6028d 100644
--- a/src/term.c
+++ b/src/term.c
@@ -1891,6 +1891,43 @@ produce_glyphless_glyph (struct it *it, Lisp_Object
acronym)
? (tty->TN_no_color_video & (ATTR)) == 0 \
: 1)
+static char *
+tparam_color (struct tty_display_info *tty, unsigned long color, bool
is_foreground)
+{
+ const char *ts = is_foreground
+ ? tty->TS_set_foreground
+ : tty->TS_set_background;
+ return ts ? tparam (ts, NULL, 0, color, 0, 0, 0) : NULL;
+}
+
+static char *
+tparam_rgb (struct tty_display_info *tty, unsigned long color, bool
is_foreground)
+{
+ const char *ts = is_foreground
+ ? tty->TS_set_rgb_foreground
+ : tty->TS_set_rgb_background;
+ const int red = (color >> 16) & 0xFF;
+ const int green = (color >> 8) & 0xFF;
+ const int blue = color & 0xFF;
+ return ts ? tparam (ts, NULL, 0, red, green, blue, 0) : NULL;
+}
+
+static void
+turn_on_color (struct tty_display_info *tty, unsigned long color, bool
is_foreground)
+{
+ if (face_tty_specified_color (color))
+ {
+ char *p = color & FACE_TTY_NONINDEXED_COLOR
+ ? tparam_rgb (tty, color, is_foreground)
+ : tparam_color (tty, color, is_foreground);
+ if (p)
+ {
+ OUTPUT (tty, p);
+ xfree (p);
+ }
+ }
+}
+
/* Turn appearances of face FACE_ID on tty frame F on.
FACE_ID is a realized face ID number, in the face cache. */
@@ -1930,24 +1967,8 @@ turn_on_face (struct frame *f, int face_id)
if (tty->TN_max_colors > 0)
{
- 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);
- }
+ turn_on_color (tty, fg, !tty->standout_mode);
+ turn_on_color (tty, bg, tty->standout_mode);
}
}
@@ -2050,11 +2071,12 @@ TERMINAL does not refer to a text terminal. */)
to work around an HPUX compiler bug (?). See
http://lists.gnu.org/archive/html/emacs-devel/2007-08/msg00410.html */
static int default_max_colors;
-static int default_max_pairs;
static int default_no_color_video;
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. */
@@ -2067,8 +2089,9 @@ tty_default_color_capabilities (struct tty_display_info
*tty, bool save)
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;
}
else
@@ -2076,8 +2099,9 @@ tty_default_color_capabilities (struct tty_display_info
*tty, bool save)
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;
}
}
@@ -2097,9 +2121,10 @@ tty_setup_colors (struct tty_display_info *tty, int mode)
{
case -1: /* no colors at all */
tty->TN_max_colors = 0;
- 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_orig_pair = NULL;
+ tty->TS_set_foreground = tty->TS_set_background = NULL;
+ tty->TS_set_rgb_foreground = tty->TS_set_rgb_background = NULL;
break;
case 0: /* default colors, if any */
default:
@@ -2107,6 +2132,7 @@ tty_setup_colors (struct tty_display_info *tty, int mode)
break;
case 8: /* 8 standard ANSI colors */
tty->TS_orig_pair = "\033[0m";
+ tty->TS_set_rgb_foreground = tty->TS_set_rgb_background = NULL;
#ifdef TERMINFO
tty->TS_set_foreground = "\033[3%p1%dm";
tty->TS_set_background = "\033[4%p1%dm";
@@ -2115,9 +2141,21 @@ tty_setup_colors (struct tty_display_info *tty, int mode)
tty->TS_set_background = "\033[4%dm";
#endif
tty->TN_max_colors = 8;
- tty->TN_max_pairs = 64;
tty->TN_no_color_video = 0;
break;
+ case 16777216:
+ tty->TS_orig_pair = "\033[0m";
+ tty->TS_set_foreground = tty->TS_set_background = NULL;
+#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
+ tty->TN_max_colors = 16777216;
+ tty->TN_no_color_video = 0;
+ break;
}
}
@@ -4137,7 +4175,6 @@ use the Bourne shell command 'TERM=...; export TERM'
(C-shell:\n\
}
tty->TN_max_colors = tgetnum ("Co");
- tty->TN_max_pairs = tgetnum ("pa");
tty->TN_no_color_video = tgetnum ("NC");
if (tty->TN_no_color_video == -1)
@@ -4514,6 +4551,8 @@ bigger, or it may make it blink, or it may do nothing at
all. */);
default_orig_pair = NULL;
default_set_foreground = NULL;
default_set_background = NULL;
+ default_set_rgb_foreground = NULL;
+ default_set_rgb_background = NULL;
#endif /* !DOS_NT */
encode_terminal_src = NULL;
diff --git a/src/termchar.h b/src/termchar.h
index 35b30fb..81fe96e 100644
--- a/src/termchar.h
+++ b/src/termchar.h
@@ -149,10 +149,6 @@ struct tty_display_info
int TN_max_colors; /* "Co" -- number of colors. */
- /* "pa" -- max. number of color pairs on screen. Not handled yet.
- Could be a problem if not equal to TN_max_colors * TN_max_colors. */
- int TN_max_pairs;
-
/* "op" -- SVr4 set default pair to its original value. */
const char *TS_orig_pair;
@@ -160,6 +156,9 @@ struct tty_display_info
1 param, the color index. */
const char *TS_set_foreground;
const char *TS_set_background;
+ /* 3 params: red, green and blue. */
+ 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. */
diff --git a/src/xfaces.c b/src/xfaces.c
index 0a1315d..e4650fd 100644
--- a/src/xfaces.c
+++ b/src/xfaces.c
@@ -832,6 +832,45 @@ parse_rgb_list (Lisp_Object rgb_list, XColor *color)
return true;
}
+static bool
+parse_and_encode_rgb_list (Lisp_Object rgb_list, XColor *color)
+{
+ if (!parse_rgb_list (rgb_list, color))
+ return false;
+
+ color->pixel = FACE_TTY_NONINDEXED_COLOR
+ | (color->red / 256) << 16
+ | (color->green / 256) << 8
+ | (color->blue / 256);
+ return true;
+}
+
+static bool
+tty_supports_rgb (struct frame *f)
+{
+ return f->output_method == output_termcap
+ && f->output_data.tty->display_info->TS_set_rgb_foreground
+ && f->output_data.tty->display_info->TS_set_rgb_background;
+}
+
+static bool
+tty_lookup_rgb (Lisp_Object color, XColor *tty_color, XColor *std_color)
+{
+ if (NILP (Ffboundp (Qtty_color_standard_values)))
+ return false;
+
+ if (!NILP (Ffboundp (Qtty_color_canonicalize)))
+ color = call1 (Qtty_color_canonicalize, color);
+
+ color = call1 (Qtty_color_standard_values, color);
+ if (!parse_and_encode_rgb_list (color, tty_color))
+ return false;
+
+ if (std_color)
+ *std_color = *tty_color;
+
+ return true;
+}
/* Lookup on frame F the color described by the lisp string COLOR.
The resulting tty color is returned in TTY_COLOR; if STD_COLOR is
@@ -844,6 +883,9 @@ tty_lookup_color (struct frame *f, Lisp_Object color,
XColor *tty_color,
{
Lisp_Object frame, color_desc;
+ if (tty_supports_rgb (f))
+ return tty_lookup_rgb (color, tty_color, std_color);
+
if (!STRINGP (color) || NILP (Ffboundp (Qtty_color_desc)))
return false;
@@ -5707,8 +5749,12 @@ map_tty_color (struct frame *f, struct face *face,
CONSP (def)))
{
/* 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)));
+ (NAME INDEX R G B). We need the (R G B) or INDEX part. */
+ XColor tty_color;
+ if (tty_supports_rgb (f) && parse_and_encode_rgb_list (XCDR (XCDR
(def)), &tty_color))
+ pixel = tty_color.pixel;
+ else
+ pixel = XINT (XCAR (XCDR (def)));
}
if (pixel == default_pixel && STRINGP (color))
@@ -6395,6 +6441,7 @@ syms_of_xfaces (void)
DEFSYM (Qwindow_divider_first_pixel, "window-divider-first-pixel");
DEFSYM (Qwindow_divider_last_pixel, "window-divider-last-pixel");
DEFSYM (Qtty_color_desc, "tty-color-desc");
+ DEFSYM (Qtty_color_canonicalize, "tty-color-canonicalize");
DEFSYM (Qtty_color_standard_values, "tty-color-standard-values");
DEFSYM (Qtty_color_by_index, "tty-color-by-index");
--
2.7.4