gbranden pushed a commit to branch master
in repository groff.

commit 9edae43657c222f630eef84e8ab38603071a033f
Author: Deri James <d...@chuzzlewit.myzen.co.uk>
AuthorDate: Sat May 24 00:08:28 2025 +0000

    [grotty]: Add SGR 38/48 high color depth support.
    
    * src/devices/grotty/tty.cpp: Add support for devices supporting
      ECMA-48/ISO 6429 SGR 38 and 48 escape sequences for specifying 24-bit
      color values in the RGB color space.  Add global Boolean variable
      `want_sgr_truecolor`.
    
      (class tty_glyph): Promote `back_color_idx` and `fore_color_idx`
      member variables from `schar` (signed char) to `long`.
    
      (class tty_printer): Promote `curr_fore_idx` and `curr_back_idx`
      member variables from `schar` (signed char) to `long`.  Promote return
      value of `color_to_idx()` member function from pointer-to-`schar` to
      pointer-to-`long`.  Promote fourth argument of `has_color()` member
      function from pointer-to-`schar` to pointer-to-`long`.  Promote first
      argument of `put_color()` member function from `schar` to `long`.
    
      (tty_printer::has_color): Promote `idx` argument from pointer-to-
      `schar` to pointer-to-`long`.  Guard existing color-definition logic
      behind test of `want_sgr_truecolor` for falsity; otherwise, compute
      `idx` using bitwise shifts and addition.
    
      (tty_printer::tty_printer): Promote `dummy` local variable from
      `schar` to `long`.
    
      (tty_printer::color_to_idx): Promote return value from pointer-to-
      `schar` to pointer-to-`long`.  Promote `idx` local variable from
      `schar` to `long`.
    
      (tty_printer::put_color): Promote `color_index` argument from `schar`
      to `long`.  Guard existing SGR color escape sequence emission logic
      (which uses SGR 30-37 and 40-47) behind test of `want_sgr_truecolor`
      for falsity; otherwise, emit SGR 38 and 48 sequences.
    
      (main): Recognize new `-t` option to select use of SGR 38 and 48
      escape sequences.  If specified, configure `want_sgr_truecolor` as
      true.
    
    * src/devices/grotty/grotty.1.man (Synopsis, Description, Options):
    * src/devices/grotty/tty.cpp (usage): Document new `-t` option.
    
    Fixes <https://savannah.gnu.org/bugs/?67153>.
---
 ChangeLog                       |  40 ++++++++++++++
 src/devices/grotty/grotty.1.man |  38 +++++++++++++-
 src/devices/grotty/tty.cpp      | 114 ++++++++++++++++++++++++----------------
 3 files changed, 145 insertions(+), 47 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index d72025bbc..79977bd49 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,43 @@
+2025-05-24  Deri James  <d...@chuzzlewit.myzen.co.uk>
+
+       * src/devices/grotty/tty.cpp: Add support for devices supporting
+       ECMA-48/ISO 6429 SGR 38 and 48 escape sequences for specifying
+       24-bit color values in the RGB color space.  Add global Boolean
+       variable `want_sgr_truecolor`.
+       (class tty_glyph): Promote `back_color_idx` and
+       `fore_color_idx` member variables from `schar` (signed char) to
+       `long`.
+       (class tty_printer): Promote `curr_fore_idx` and `curr_back_idx`
+       member variables from `schar` (signed char) to `long`.  Promote
+       return value of `color_to_idx()` member function from
+       pointer-to-`schar` to pointer-to-`long`.  Promote fourth
+       argument of `has_color()` member function from
+       pointer-to-`schar` to pointer-to-`long`.  Promote first argument
+       of `put_color()` member function from `schar` to `long`.
+       (tty_printer::has_color): Promote `idx` argument from
+       pointer-to-`schar` to pointer-to-`long`.  Guard existing
+       color-definition logic behind test of `want_sgr_truecolor` for
+       falsity; otherwise, compute `idx` using bitwise shifts and
+       addition.
+       (tty_printer::tty_printer): Promote `dummy` local variable
+       from `schar` to `long`.
+       (tty_printer::color_to_idx): Promote return value from
+       pointer-to-`schar` to pointer-to-`long`.  Promote `idx` local
+       variable from `schar` to `long`.
+       (tty_printer::put_color): Promote `color_index` argument from
+       `schar` to `long`.  Guard existing SGR color escape sequence
+       emission logic (which uses SGR 30-37 and 40-47) behind test of
+       `want_sgr_truecolor` for falsity; otherwise, emit SGR 38 and 48
+       sequences.
+       (main): Recognize new `-t` option to select use of SGR 38 and 48
+       escape sequences.  If specified, configure `want_sgr_truecolor`
+       as true.
+       * src/devices/grotty/grotty.1.man (Synopsis, Description)
+       (Options):
+       * src/devices/grotty/tty.cpp (usage): Document new `-t` option.
+
+       Fixes <https://savannah.gnu.org/bugs/?67153>.
+
 2025-06-03  G. Branden Robinson <g.branden.robin...@gmail.com>
 
        * src/devices/grotty/tty.cpp (class tty_printer): Rename member
diff --git a/src/devices/grotty/grotty.1.man b/src/devices/grotty/grotty.1.man
index eb043d193..edac3db65 100644
--- a/src/devices/grotty/grotty.1.man
+++ b/src/devices/grotty/grotty.1.man
@@ -52,7 +52,7 @@ output driver for typewriter-like (terminal) devices
 .\" ====================================================================
 .
 .SY grotty
-.RB [ \-dfho ]
+.RB [ \-dfhot ]
 .RB [ \-i \||\| \-r ]
 .RB [ \-F\~\c
 .IR font-directory ]
@@ -146,7 +146,7 @@ reverse video
 [\[lq]negative image\[rq]]
 and colors).
 .
-Devices supporting the appropriate sequences can view
+By default, devices supporting the appropriate sequences can view
 .I roff
 documents using eight different background and foreground colors.
 .
@@ -165,6 +165,37 @@ and cyan.
 Unrecognized colors are mapped to the default color,
 which is dependent on the settings of the terminal.
 .
+If
+.B -P-t
+is passed to groff (which sets
+.I grotty's
+.B -t
+flag - see below) it operates in TRUECOLOR mode and any
+RGB colour can be used as foreground and background.
+Many terminal emulators now support the TRUECOLOR extensions
+if your environment includes:-
+.P
+.RS
+COLORTERM="truecolor"
+.RE
+.P
+You specify the RGB colours using the usual:-
+.P
+.RS
+\&.defcolor mediumspringgreen rgb #00fa9a
+.P
+.RE
+NOTE: only rgb colour definitions are supported.
+.P
+And then use \[rs]m[...] and \[rs]M[...] to specify the foreground
+and background colours.
+The 8 legacy, 4 bit, colours (defined above) may look different when
+used with 24 bit colours. The reason is because although tty.tmac
+defines "yellow" as rgb #ffff00, it uses rgb #b26818 (or #e5cb3f for
+text when the bold font is selected), whereas for 24 bit colours the
+full #ffff00 is used, and using the bold font just bolds the text
+rather than alter the colour.
+.P
 OSC\~8 hyperlinks are produced for these devices.
 .
 .
@@ -587,6 +618,9 @@ is also specified.
 .
 .
 .TP
+.B \-t
+Use 24 bit "TRUECOLOR" extension rather than 4 bit legacy colours.
+.TP
 .B \-u
 Suppress the use of underlining for italic characters in legacy output
 format.
diff --git a/src/devices/grotty/tty.cpp b/src/devices/grotty/tty.cpp
index 92bd22296..c5f8b072a 100644
--- a/src/devices/grotty/tty.cpp
+++ b/src/devices/grotty/tty.cpp
@@ -68,7 +68,7 @@ static bool do_sgr_italics;
 static bool want_reverse_video_for_italics = false;
 static bool do_reverse_video;
 static bool use_overstriking_drawing_scheme = false;
-
+static bool want_sgr_truecolor = false;
 static void update_options();
 static void usage(FILE *stream);
 
@@ -169,8 +169,8 @@ public:
   int hpos;
   unsigned int code;
   unsigned char mode;
-  schar back_color_idx;
-  schar fore_color_idx;
+  long back_color_idx;
+  long fore_color_idx;
   inline int draw_mode() { return mode & (VDRAW_MODE|HDRAW_MODE); }
   inline int order() {
     return mode & (VDRAW_MODE|HDRAW_MODE|CU_MODE|COLOR_CHANGE); }
@@ -182,20 +182,20 @@ class tty_printer : public printer {
   int nlines;
   int cached_v;
   int cached_vpos;
-  schar curr_fore_idx;
-  schar curr_back_idx;
+  long curr_fore_idx;
+  long curr_back_idx;
   bool is_underlining;
   bool is_boldfacing;
   bool is_continuously_underlining;
   PTABLE(schar) tty_colors;
   void make_underline(int);
   void make_bold(output_character, int);
-  schar color_to_idx(color *);
+  long color_to_idx(color *);
   void add_char(output_character, int, int, int, color *, color *,
                unsigned char);
   void simple_add_char(const output_character, const environment *);
   char *make_rgb_string(unsigned int, unsigned int, unsigned int);
-  bool has_color(unsigned int, unsigned int, unsigned int, schar *,
+  bool has_color(unsigned int, unsigned int, unsigned int, long *,
                 schar = DEFAULT_COLOR_IDX);
   void line(int, int, int, int, color *, color *);
   void draw_line(int *, int, const environment *);
@@ -210,7 +210,7 @@ public:
   void change_color(const environment * const);
   void change_fill_color(const environment * const);
   void put_char(output_character);
-  void put_color(schar, int);
+  void put_color(long, int);
   void begin_page(int) { }
   void end_page(int);
   font *make_font(const char *);
@@ -240,19 +240,24 @@ char *tty_printer::make_rgb_string(unsigned int r,
 
 bool tty_printer::has_color(unsigned int r,
                            unsigned int g,
-                           unsigned int b, schar *idx, schar value)
+                           unsigned int b, long *idx, schar value)
 {
   bool is_known_color = true;
-  char *s = make_rgb_string(r, g, b);
-  schar *i = tty_colors.lookup(s);
-  if (0 /* nullptr */ == i) {
-    is_known_color = false;
-    i = new schar[1];
-    *i = value;
-    tty_colors.define(s, i);
+  if (!want_sgr_truecolor) {
+    char *s = make_rgb_string(r, g, b);
+    schar *i = tty_colors.lookup(s);
+    if (0 /* nullptr */ == i) {
+      is_known_color = false;
+      i = new schar[1];
+      *i = value;
+      tty_colors.define(s, i);
+    }
+    *idx = *i;
+    delete[] s;
+  }
+  else {
+    *idx=((r>>8)<<16) + ((g>>8)<<8) + (b>>8);
   }
-  *idx = *i;
-  delete[] s;
   return is_known_color;
 }
 
@@ -263,7 +268,7 @@ tty_printer::tty_printer() : cached_v(0)
     vline_char = 0x2502;
   }
   // TODO: Skip color setup if terminfo `colors` capability is "-1".
-  schar dummy;
+  long dummy;
   // Create the eight ANSI X3.64/ECMA-48/ISO 6429 standard colors.
   // black, white
   (void) has_color(0, 0, 0, &dummy, 0);
@@ -334,13 +339,13 @@ void tty_printer::make_bold(output_character c, int w)
   }
 }
 
-schar tty_printer::color_to_idx(color *col)
+long tty_printer::color_to_idx(color *col)
 {
   if (col->is_default())
     return DEFAULT_COLOR_IDX;
   unsigned int r, g, b;
   col->get_rgb(&r, &g, &b);
-  schar idx;
+  long idx;
   if (!has_color(r, g, b, &idx)) {
     char *s = col->print_color();
     error("unsupported color '%1' mapped to default", s);
@@ -702,33 +707,48 @@ void tty_printer::put_char(output_character wc)
     putchar(wc);
 }
 
-void tty_printer::put_color(schar color_index, int back)
+void tty_printer::put_color(long color_index, int back)
 {
-  if (color_index == DEFAULT_COLOR_IDX) {
-    putstring(SGR_DEFAULT);
-    // set bold and underline again
-    if (is_boldfacing)
-      putstring(SGR_BOLD);
-    if (is_underlining) {
-      if (do_sgr_italics)
-       putstring(SGR_ITALIC);
-      else if (do_reverse_video)
-       putstring(SGR_REVERSE);
+  if (!want_sgr_truecolor) {
+    if (color_index == DEFAULT_COLOR_IDX) {
+      putstring(SGR_DEFAULT);
+      // set bold and underline again
+      if (is_boldfacing)
+        putstring(SGR_BOLD);
+      if (is_underlining) {
+        if (do_sgr_italics)
+          putstring(SGR_ITALIC);
+        else if (do_reverse_video)
+          putstring(SGR_REVERSE);
+        else
+          putstring(SGR_UNDERLINE);
+      }
+      // set other color again
+      back = !back;
+      color_index = back ? curr_back_idx : curr_fore_idx;
+    }
+    if (color_index != DEFAULT_COLOR_IDX) {
+      putstring(CSI);
+      if (back)
+        putchar('4');
       else
-       putstring(SGR_UNDERLINE);
+        putchar('3');
+      putchar(color_index + '0');
+      putchar('m');
     }
-    // set other color again
-    back = !back;
-    color_index = back ? curr_back_idx : curr_fore_idx;
   }
-  if (color_index != DEFAULT_COLOR_IDX) {
+  else {
+    if (color_index == DEFAULT_COLOR_IDX) {
+      putstring(SGR_DEFAULT);
+      back = !back;
+      color_index = back ? curr_back_idx : curr_fore_idx;
+      if (color_index == DEFAULT_COLOR_IDX) {return;}
+    }
     putstring(CSI);
-    if (back)
-      putchar('4');
-    else
-      putchar('3');
-    putchar(color_index + '0');
-    putchar('m');
+    int fb = back ? 48 : 38;
+    static char buf[24];
+    sprintf(buf,"%d;2;%u;%u;%um",fb, color_index>>16, (color_index>>8) & 0xff, 
color_index & 0xff);
+    putstring(buf);
   }
 }
 
@@ -959,7 +979,7 @@ int main(int argc, char **argv)
     { "version", no_argument, 0, 'v' },
     { NULL, 0, 0, 0 }
   };
-  while ((c = getopt_long(argc, argv, ":bBcdfF:hiI:oruUv", long_options,
+  while ((c = getopt_long(argc, argv, ":bBcdfF:hiI:ortuUv", long_options,
          NULL)) != EOF)
     switch(c) {
     case 'v':
@@ -1015,6 +1035,10 @@ int main(int argc, char **argv)
       // Ignore \D commands.
       allow_drawing_commands = false;
       break;
+    case 't':
+      // TRUECOLOR
+      want_sgr_truecolor = true;
+      break;
     case CHAR_MAX + 1: // --help
       usage(stdout);
       break;
@@ -1045,7 +1069,7 @@ int main(int argc, char **argv)
 static void usage(FILE *stream)
 {
   fprintf(stream,
-"usage: %s [-dfho] [-i|-r] [-F font-directory] [file ...]\n"
+"usage: %s [-dfhot] [-i|-r] [-F font-directory] [file ...]\n"
 "usage: %s -c [-bBdfhouU] [-F font-directory] [file ...]\n"
 "usage: %s {-v | --version}\n"
 "usage: %s --help\n",

_______________________________________________
groff-commit mailing list
groff-commit@gnu.org
https://lists.gnu.org/mailman/listinfo/groff-commit

Reply via email to