all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
* [PATCH] Support 24-bit terminal colors.
@ 2016-08-30 20:55 Rami Ylimäki
  2016-08-30 21:03 ` Daniel Colascione
  0 siblings, 1 reply; 27+ messages in thread
From: Rami Ylimäki @ 2016-08-30 20:55 UTC (permalink / raw)
  To: emacs-devel

From: Rami Ylimäki <rjy@iki.fi>

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




^ permalink raw reply related	[flat|nested] 27+ messages in thread
* Re: [PATCH] Support 24-bit terminal colors.
@ 2016-08-31 17:47 Rami Ylimäki
  2016-08-31 18:14 ` Eli Zaretskii
  0 siblings, 1 reply; 27+ messages in thread
From: Rami Ylimäki @ 2016-08-31 17:47 UTC (permalink / raw)
  To: Eli Zaretskii, emacs-devel

[-- Attachment #1: Type: text/plain, Size: 14511 bytes --]

2016-08-31 17:21 GMT+03:00 Eli Zaretskii <eliz@gnu.org>:

> > From: Rami Ylimäki <rjy@iki.fi>
> > Date: Tue, 30 Aug 2016 23:11:46 +0300
> >
> > Based on previous work by Rüdiger Sonderfeld, Christian Hopps and
> > Charles Strahan.
>
> Some of these people don't have copyright assignments on file, so how
> much of the code is actually taken from what they wrote?
>

The code is mostly based on the original patch [1] from Rüdiger.
[1] https://lists.gnu.org/archive/html/emacs-devel/2013-10/msg00461.html


> I also don't find your assignment, but maybe once you adjudicate the
> comments below, the number of changes will be small enough to accept
> without an assignment.  Nevertheless, I encourage you to start your
> legal paperwork rolling, so that we could continue receiving your
> contributions even if this one doesn't need it.
>

Sure, I can assign copyright. I believe you can send me instructions and
necessary documents to sign?


> > 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))"
>
> That is a screw.  Is there really no way of detecting true-color
> support?  Not even a way that's specific to each kind of terminal
> emulator that supports this mode?
>

I sent mail to bug-ncurses about this. I think 24-bit terminal support has
become more widespread and de facto standard since Rüdiger contacted them
so that they might be willing to standardize new terminal capabilities
regarding this.


> Asking the user to specify a command-line argument each time is so
> annoying that I'm tempted to say this feature is not yet ready for a
> complex multi-terminal program such as Emacs, and we should wait for
> some kind of standard to emerge before we dump this on our users.
>
> Can we devise some customizable data structure that stores names of
> terminals that support true-color?  Then users could set this list
> from their ~/.emacs init files, and have any frame open on those
> terminals automatically support 24-bit colors.  (If we go this way,
> the data structure will have to be consulted by startup.el at the
> right place, so that the initial frame is already set up correctly.)
>

Yeah, let's see first whether the terminfo guys are willing to add
xterm-24bit entry and related capabilities first.


> In any case, if we do agree to specify this via a command-line
> argument, the same command-line argument should IMO be supported by
> emacsclient; asking users to use Lisp is less user-friendly, IMO.
>

Ok.

> * 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.
>
> This part is IMO wrong: the true-color support is a property of the
> terminal, not of a frame.  So we should use terminal-parameters for
> communicating the support, and each frame on that terminal should
> thereafter automatically be set to use 24-bit colors.
>

Got it.

> 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.")
>
> I think 'rgb' is too general to be useful mnemonically.  How about
> '24bit' or 'true-color' instead?
>

24bit sounds good to me if we can't avoid the entry.


> > +(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)))
>
> Not sure why this function is needed.  Why not simply look at the
> number of supported colors, i.e. what display-color-cells returns?


With the changes, xterm-register-default-colors is called when the
auto-detected color count is still in effect. That function is just a dirty
workaround.


> >  (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)))
>
> Why don't you use the RGB spec of the color, encoded in an integer as
> the value of IDX here?  Won't that make your job easier in other
> places, and avoid special-casing the true-color terminal?
>

Good point.


> > +/* Color index bit indicating absence of palette.  */
> > +#define FACE_TTY_NONINDEXED_COLOR ((unsigned long) (1 << 24))
>
> Once again, not sure why this bit is needed, since you have the number
> of colors that can be used for the same purpose.


You are right, it's not needed.


> > +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);
> >      }
> >  }
>
> Is this refactoring really needed?  You now have two extra function
> call levels, in a function that is called _a_lot_ in the inner-most
> loops of the display engine.
>
> AFAIU, the only difference in the true-color case is that the 3 last
> arguments of tparam need to be computed by bitwise operations rather
> than set to zero.  Adding 2 extra levels of indirection just for that
> sounds excessive to me.  I'd keep that inline.
>

Ok.


> > @@ -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);
>
> Why do we need 2 new attributes here?  Can't we reuse
> TS_set_foreground and TS_set_background instead?  They seem to be
> unused in the 24-bit color case.
>

Yeah, they aren't needed.


> > -      default_max_pairs = tty->TN_max_pairs;
> [...]
> > @@ -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");
> [...]
> > -  /* "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;
> > -
>
> If deletion of this attribute is not necessary for the patch to work,
> it should be done in a separate commit.
>

It's not needed and is a separate change.


> > +      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;
>
> Do all true-color terminals support the same escape sequences for
> 24-bit colors?  Is there any standard documents we could rely upon for
> hard-coding them here?
>

AFAIK, there are no documents. However, it seems [2] that all or almost all
24-bit terminals support those sequences (semicolon separated rgb values in
range 0-255) in practice.
[2] https://gist.github.com/XVilka/8346728


>
> > +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;
> > +}
>
> Once again, why not just look at the number of supported colors?


Yeah.


> > +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_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)));
> >      }
>
> Sorry, I don't understand why this complexity is needed.  AFAIU, a
> true-color terminal can produce color directly from its RGB
> components, without the need to look up that RGB descriptor via
> tty-color-desc.  In addition, support for named colors, such as
> "burlywood1", is still required for these terminals, since most color
> specifications in Emacs are of that variety.  Is my understanding
> correct?
>
> If I'm right, then you could either (a) extend tty-color-desc, such
> that it would cons the (NAME INDEX R G B) list for a true-color
> terminal and hand it back to tty_lookup_color; or (b) if you wanted to
> be more efficient, generate the pixel value directly on the C level by
> parsing the color spec if it is of the form "#RRGGBB", and otherwise
> calling tty-color-desc as usual.  Assuming that tty-color-alist is set
> correctly for the true-color terminal, and stores the RGB values for
> each color, that will do the job, eliminating the need for all these
> support functions: parse_and_encode_rgb_list, tty_supports_rgb, and
> tty_lookup_rgb, and also save you 2 extra calls to Lisp.  Am I missing
> something?
>

Not missing anything. Thanks for detailed review and good advice.


> Thanks.
>

[-- Attachment #2: Type: text/html, Size: 20394 bytes --]

^ permalink raw reply	[flat|nested] 27+ messages in thread
* [PATCH] Support 24-bit terminal colors.
@ 2016-08-30 20:11 Rami Ylimäki
  2016-08-31 14:21 ` Eli Zaretskii
  0 siblings, 1 reply; 27+ messages in thread
From: Rami Ylimäki @ 2016-08-30 20:11 UTC (permalink / raw)
  To: emacs-devel

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




^ permalink raw reply related	[flat|nested] 27+ messages in thread

end of thread, other threads:[~2016-11-07  0:12 UTC | newest]

Thread overview: 27+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2016-08-30 20:55 [PATCH] Support 24-bit terminal colors Rami Ylimäki
2016-08-30 21:03 ` Daniel Colascione
2016-08-30 22:07   ` Clément Pit--Claudel
2016-08-30 22:37     ` Daniel Colascione
2016-08-30 22:47       ` Clément Pit--Claudel
2016-08-31 21:42       ` Richard Stallman
2016-09-01  2:39         ` Eli Zaretskii
2016-09-19  4:32           ` Charles Strahan
2016-09-19 22:28             ` Richard Stallman
2016-09-23 13:46               ` Clément Pit--Claudel
2016-09-25 17:16                 ` Richard Stallman
2016-09-25 19:45                   ` Clément Pit--Claudel
2016-09-26  0:10                   ` Chad Brown
2016-09-26 17:52                     ` Richard Stallman
2016-11-07  0:12                       ` Charles Strahan
2016-09-23 13:48       ` Clément Pit--Claudel
2016-09-24  9:05         ` Kalle Olavi Niemitalo
2016-08-30 22:08   ` Rami Ylimäki
  -- strict thread matches above, loose matches on Subject: below --
2016-08-31 17:47 Rami Ylimäki
2016-08-31 18:14 ` Eli Zaretskii
2016-08-30 20:11 Rami Ylimäki
2016-08-31 14:21 ` Eli Zaretskii
2016-09-04  5:45   ` David Caldwell
2016-09-04  8:11     ` Yuri Khan
2016-09-04 14:18       ` Eli Zaretskii
2016-09-04 16:44         ` Rami Ylimäki
2016-09-04 17:51           ` Eli Zaretskii

Code repositories for project(s) associated with this external index

	https://git.savannah.gnu.org/cgit/emacs.git
	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.