From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!.POSTED!not-for-mail From: =?UTF-8?q?Rami=20Ylim=C3=A4ki?= Newsgroups: gmane.emacs.devel Subject: [PATCH] Support 24-bit terminal colors. Date: Tue, 30 Aug 2016 23:55:48 +0300 Message-ID: <1472590548-30876-1-git-send-email-rami.ylimaki@vincit.fi> NNTP-Posting-Host: blaine.gmane.org Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Trace: blaine.gmane.org 1472590597 7397 195.159.176.226 (30 Aug 2016 20:56:37 GMT) X-Complaints-To: usenet@blaine.gmane.org NNTP-Posting-Date: Tue, 30 Aug 2016 20:56:37 +0000 (UTC) To: emacs-devel@gnu.org Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Tue Aug 30 22:56:30 2016 Return-path: Envelope-to: ged-emacs-devel@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by blaine.gmane.org with esmtp (Exim 4.84_2) (envelope-from ) id 1beq50-0000gE-7e for ged-emacs-devel@m.gmane.org; Tue, 30 Aug 2016 22:56:22 +0200 Original-Received: from localhost ([::1]:51249 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1beq4x-00038j-UD for ged-emacs-devel@m.gmane.org; Tue, 30 Aug 2016 16:56:19 -0400 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:48461) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1beq4p-00033m-Lk for emacs-devel@gnu.org; Tue, 30 Aug 2016 16:56:14 -0400 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1beq4l-0003J9-CT for emacs-devel@gnu.org; Tue, 30 Aug 2016 16:56:10 -0400 Original-Received: from mail.kapsi.fi ([217.30.184.167]:39062) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1beq4l-0003IX-0z for emacs-devel@gnu.org; Tue, 30 Aug 2016 16:56:07 -0400 Original-Received: from 91-158-222-159.elisa-laajakaista.fi ([91.158.222.159] helo=nopsakone.home) by mail.kapsi.fi with esmtpsa (TLS1.2:RSA_AES_128_CBC_SHA256:128) (Exim 4.80) (envelope-from ) id 1beq4h-0000q7-IL for emacs-devel@gnu.org; Tue, 30 Aug 2016 23:56:03 +0300 X-Mailer: git-send-email 2.7.4 X-SA-Exim-Connect-IP: 91.158.222.159 X-SA-Exim-Mail-From: rami.ylimaki@vincit.fi X-SA-Exim-Scanned: No (on mail.kapsi.fi); SAEximRunCond expanded to false X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 217.30.184.167 X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Original-Sender: "Emacs-devel" Xref: news.gmane.org gmane.emacs.devel:207005 Archived-At: From: Rami Ylimäki 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