]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
Add an option with a color type.
authorAndrei Pikas <gdb@mail.api.win>
Sat, 5 Oct 2024 19:27:44 +0000 (22:27 +0300)
committerTom Tromey <tom@tromey.com>
Sun, 12 Jan 2025 20:30:43 +0000 (13:30 -0700)
Colors can be specified as "none" for terminal's default color, as a name of
one of the eight standard colors of ISO/IEC 6429 "black", "red", "green", etc.,
as an RGB hexadecimal tripplet #RRGGBB for 24-bit TrueColor, or as an
integer from 0 to 255.  Integers 0 to 7 are the synonyms for the standard
colors.  Integers 8-15 are used for the so-called bright colors from the
aixterm extended 16-color palette.  Integers 16-255 are the indexes into xterm
extended 256-color palette (usually 6x6x6 cube plus gray ramp).  In
general, 256-color palette is terminal dependent and sometimes can be
changed with OSC 4 sequences, e.g. "\033]4;1;rgb:00/FF/00\033\\".

It is the responsibility of the user to verify that the terminal supports
the specified colors.

PATCH v5 changes: documentation fixed.
PATCH v6 changes: documentation fixed.
PATCH v7 changes: rebase onto master and fixes after review.
PATCH v8 changes: fixes after review.

34 files changed:
gdb/Makefile.in
gdb/NEWS
gdb/cli/cli-cmds.c
gdb/cli/cli-decode.c
gdb/cli/cli-decode.h
gdb/cli/cli-option.c
gdb/cli/cli-option.h
gdb/cli/cli-setshow.c
gdb/cli/cli-style.c
gdb/cli/cli-style.h
gdb/command.h
gdb/doc/gdb.texinfo
gdb/doc/guile.texi
gdb/doc/python.texi
gdb/guile/guile-internal.h
gdb/guile/guile.c
gdb/guile/scm-color.c [new file with mode: 0644]
gdb/guile/scm-param.c
gdb/python/py-color.c [new file with mode: 0644]
gdb/python/py-color.h [new file with mode: 0644]
gdb/python/py-param.c
gdb/python/python.c
gdb/testsuite/gdb.base/default.exp
gdb/testsuite/gdb.base/style.exp
gdb/testsuite/gdb.guile/scm-color.exp [new file with mode: 0644]
gdb/testsuite/gdb.guile/scm-parameter.exp
gdb/testsuite/gdb.python/py-color.exp [new file with mode: 0644]
gdb/testsuite/gdb.python/py-parameter.exp
gdb/testsuite/lib/gdb-utils.exp
gdb/testsuite/lib/gdb.exp
gdb/top.c
gdb/ui-style.c
gdb/ui-style.h
gdb/unittests/style-selftests.c

index 6d627b61fe70e3e929d9a5fa07df89f61ffffd8c..b726b1dc612ac01272072f62a690712ac948f043 100644 (file)
@@ -359,6 +359,7 @@ SUBDIR_GUILE_SRCS = \
        guile/scm-block.c \
        guile/scm-breakpoint.c \
        guile/scm-cmd.c \
+       guile/scm-color.c \
        guile/scm-disasm.c \
        guile/scm-exception.c \
        guile/scm-frame.c \
@@ -395,6 +396,7 @@ SUBDIR_PYTHON_SRCS = \
        python/py-bpevent.c \
        python/py-breakpoint.c \
        python/py-cmd.c \
+       python/py-color.c \
        python/py-connection.c \
        python/py-continueevent.c \
        python/py-dap.c \
index 1b218c40a23e620f1b5e8ecea5b4e7efdbedd057..f454846646eec2491acaa61beed57f5eda75e494 100644 (file)
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -3,6 +3,25 @@
 
 *** Changes since GDB 16
 
+* "set style" commands now supports numeric format for basic colors
+  from 0 to 255 and #RRGGBB format for TrueColor.
+
+* New built-in convenience variable $_colorsupport provides comma-separated
+  list of color space names supported by terminal.  Each color space name is one
+  of monochrome, ansi_8color, aixterm_16color, xterm_256color or rgb_24bit.
+  It is handy for conditionally using styling colors based on terminal features.
+  For example:
+
+  (gdb) if $_regex ($_colorsupport, ".*(^|,)rgb_24bit($|,).*")
+   >set style filename background #FACADE
+   >else
+   >if $_regex ($_colorsupport, ".*(^|,)xterm_256color($|,).*")
+    >set style filename background 224
+    >else
+    >set style filename background red
+    >end
+   >end
+
 * New commands
 
 maintenance check psymtabs
@@ -11,6 +30,20 @@ maintenance check psymtabs
 maintenance check symtabs
   Renamed from maintenance check-symtabs
 
+* Python API
+
+  ** New class gdb.Color for dealing with colors.
+
+  ** New constant gdb.PARAM_COLOR represents color type of a
+     gdb.Parameter.value.  Parameter's value is gdb.Color instance.
+
+* Guile API
+
+  ** New type <gdb:color> for dealing with colors.
+
+  ** New constant PARAM_COLOR represents color type of a value
+     of a <gdb:parameter> object.  Parameter's value is <gdb::color> instance.
+
 *** Changes in GDB 16
 
 * Support for Nios II targets has been removed as this architecture
index 0140c717ca2bf2afef83325492eb8e779b6ae919..69a7e8be51748fea08ac10d05d66aca2c0b9f6e4 100644 (file)
@@ -2398,6 +2398,11 @@ value_from_setting (const setting &var, struct gdbarch *gdbarch)
 
        return current_language->value_string (gdbarch, value, len);
       }
+    case var_color:
+      {
+       std::string s = var.get<ui_file_style::color> ().to_string ();
+       return current_language->value_string (gdbarch, s.c_str (), s.size ());
+      }
     default:
       gdb_assert_not_reached ("bad var_type");
     }
@@ -2445,6 +2450,7 @@ str_value_from_setting (const setting &var, struct gdbarch *gdbarch)
     case var_pinteger:
     case var_boolean:
     case var_auto_boolean:
+    case var_color:
       {
        std::string cmd_val = get_setshow_command_value_string (var);
 
index 4fbbfcb6aeb9fd8709d6c235d103d7d23c30af8e..2c5292286ccc63d6de880fcbd33de9110e584e2f 100644 (file)
@@ -23,6 +23,7 @@
 #include "cli/cli-cmds.h"
 #include "cli/cli-decode.h"
 #include "cli/cli-style.h"
+#include "cli/cli-utils.h"
 #include <optional>
 
 /* Prototypes for local functions.  */
@@ -739,6 +740,87 @@ add_setshow_enum_cmd (const char *name, command_class theclass,
   return cmds;
 }
 
+/* See cli-decode.h.  */
+
+void
+complete_on_color (completion_tracker &tracker,
+                  const char *text, const char *word)
+{
+  complete_on_enum (tracker, ui_file_style::basic_color_enums.data (),
+                   text, word);
+  if (*text == '\0')
+    {
+      /* Convenience to let the user know what the option
+        can accept.  Note there's no common prefix between
+        the strings on purpose, so that complete_on_enum doesn't do
+        a partial match.  */
+      tracker.add_completion (make_unique_xstrdup ("NUMBER"));
+      tracker.add_completion (make_unique_xstrdup ("#RRGGBB"));
+    }
+}
+
+/* Completer used in color commands.  */
+
+static void
+color_completer (struct cmd_list_element *ignore,
+                completion_tracker &tracker,
+                const char *text, const char *word)
+{
+  complete_on_color (tracker, text, word);
+}
+
+
+/* Add element named NAME to command list LIST (the list for set or
+   some sublist thereof).  CLASS is as in add_cmd.  VAR is address
+   of the variable which will contain the color.  */
+
+set_show_commands
+add_setshow_color_cmd (const char *name,
+                      enum command_class theclass,
+                      ui_file_style::color *var,
+                      const char *set_doc,
+                      const char *show_doc,
+                      const char *help_doc,
+                      cmd_func_ftype *set_func,
+                      show_value_ftype *show_func,
+                      struct cmd_list_element **set_list,
+                      struct cmd_list_element **show_list)
+{
+  set_show_commands commands = add_setshow_cmd_full<ui_file_style::color>
+    (name, theclass, var_color, var,
+     set_doc, show_doc, help_doc,
+     nullptr, nullptr, set_func, show_func,
+     set_list, show_list);
+
+  set_cmd_completer (commands.set, color_completer);
+
+  return commands;
+}
+
+/* Same as above but using a getter and a setter function instead of a pointer
+   to a global storage buffer.  */
+
+set_show_commands
+add_setshow_color_cmd (const char *name, command_class theclass,
+                      const char *set_doc, const char *show_doc,
+                      const char *help_doc,
+                      setting_func_types<ui_file_style::color>::set set_func,
+                      setting_func_types<ui_file_style::color>::get get_func,
+                      show_value_ftype *show_func,
+                      cmd_list_element **set_list,
+                      cmd_list_element **show_list)
+{
+  auto cmds = add_setshow_cmd_full<ui_file_style::color>
+    (name, theclass, var_color, nullptr,
+     set_doc, show_doc, help_doc,
+     set_func, get_func, nullptr, show_func,
+     set_list, show_list);
+
+  set_cmd_completer (cmds.set, color_completer);
+
+  return cmds;
+}
+
 /* See cli-decode.h.  */
 const char * const auto_boolean_enums[] = { "on", "off", "auto", NULL };
 
@@ -2756,3 +2838,95 @@ cli_user_command_p (struct cmd_list_element *cmd)
 {
   return cmd->theclass == class_user && cmd->func == do_simple_func;
 }
+
+/* See cli-decode.h.  */
+
+ui_file_style::color
+parse_cli_var_color (const char **args)
+{
+  /* Do a "set" command.  ARG is nullptr if no argument, or the
+     text of the argument.  */
+
+  if (args == nullptr || *args == nullptr || **args == '\0')
+    {
+      std::string msg;
+
+      for (size_t i = 0; ui_file_style::basic_color_enums[i]; ++i)
+       {
+         msg.append ("\"");
+         msg.append (ui_file_style::basic_color_enums[i]);
+         msg.append ("\", ");
+       }
+
+      error (_("Requires an argument. Valid arguments are %sinteger from -1 "
+            "to 255 or an RGB hex triplet in a format #RRGGBB"),
+            msg.c_str ());
+    }
+
+  const char *p = skip_to_space (*args);
+  size_t len = p - *args;
+
+  int nmatches = 0;
+  ui_file_style::basic_color match = ui_file_style::NONE;
+  for (int i = 0; ui_file_style::basic_color_enums[i]; ++i)
+    if (strncmp (*args, ui_file_style::basic_color_enums[i], len) == 0)
+      {
+       match = static_cast<ui_file_style::basic_color> (i - 1);
+       if (ui_file_style::basic_color_enums[i][len] == '\0')
+         {
+           nmatches = 1;
+           break; /* Exact match.  */
+         }
+       else
+         nmatches++;
+      }
+
+  if (nmatches == 1)
+    {
+      *args += len;
+      return ui_file_style::color (match);
+    }
+
+  if (nmatches > 1)
+    error (_("Ambiguous item \"%.*s\"."), (int) len, *args);
+
+  if (**args != '#')
+    {
+      ULONGEST num = get_ulongest (args);
+      if (num > 255)
+       error (_("integer %s out of range"), pulongest (num));
+      return ui_file_style::color (color_space::XTERM_256COLOR,
+                                  static_cast<int> (num));
+    }
+
+  /* Try to parse #RRGGBB string.  */
+  if (len != 7)
+    error_no_arg (_("invalid RGB hex triplet format"));
+
+  uint8_t r, g, b;
+  int scanned_chars = 0;
+  int parsed_args = sscanf (*args, "#%2" SCNx8 "%2" SCNx8 "%2" SCNx8 "%n",
+                           &r, &g, &b, &scanned_chars);
+
+  if (parsed_args != 3 || scanned_chars != 7)
+    error_no_arg (_("invalid RGB hex triplet format"));
+
+  *args += len;
+  return ui_file_style::color (r, g, b);
+}
+
+/* See cli-decode.h.  */
+
+ui_file_style::color
+parse_var_color (const char *arg)
+{
+  const char *end_arg = arg;
+  ui_file_style::color color = parse_cli_var_color (&end_arg);
+
+  int len = end_arg - arg;
+  const char *after = skip_spaces (end_arg);
+  if (*after != '\0')
+    error (_("Junk after item \"%.*s\": %s"), len, arg, after);
+
+  return color;
+}
index ec73c11d17248ffae810b05a592965de8295b371..18ea05d29af2b0b75a96276383c83fd01c63e383 100644 (file)
@@ -309,6 +309,27 @@ extern const char * const boolean_enums[];
 /* The enums of auto-boolean commands.  */
 extern const char * const auto_boolean_enums[];
 
+/* Add the different possible completions of TEXT with color.
+
+   WORD points in the same buffer as TEXT, and completions should be
+   returned relative to this position.  For example, suppose TEXT is "foo"
+   and we want to complete to "foobar".  If WORD is "oo", return
+   "oobar"; if WORD is "baz/foo", return "baz/foobar".  */
+
+extern void complete_on_color (completion_tracker &tracker,
+                              const char *text, const char *word);
+
+/* Parse ARGS, an option to a var_color variable.
+ *
+   Either returns the parsed value on success or throws an error.  ARGS may be
+   one of strings {none, black, red, green, yellow, blue, magenta,
+   cyan, white}, or color number from 0 to 255, or RGB hex triplet #RRGGBB.
+   */
+extern ui_file_style::color parse_cli_var_color (const char **args);
+
+/* Same as above but additionally check that there is no junk in the end.  */
+extern ui_file_style::color parse_var_color (const char *arg);
+
 /* Verify whether a given cmd_list_element is a user-defined command.
    Return 1 if it is user-defined.  Return 0 otherwise.  */
 
index fa00b913c99d81608220ab853244989fec511078..10a59449f7cbf9809fda6ffcbc74286dec5e743c 100644 (file)
@@ -45,6 +45,9 @@ union option_value
 
   /* For var_string and var_filename options.  This is allocated with new.  */
   std::string *string;
+
+  /* For var_color options.  */
+  ui_file_style::color color = ui_file_style::NONE;
 };
 
 /* Holds an options definition and its value.  */
@@ -433,6 +436,35 @@ parse_option (gdb::array_view<const option_def_group> options_group,
        val.enumeration = parse_cli_var_enum (args, match->enums);
        return option_def_and_value {*match, match_ctx, val};
       }
+    case var_color:
+      {
+       if (completion != nullptr)
+         {
+           const char *after_arg = skip_to_space (*args);
+           if (*after_arg == '\0')
+             {
+               complete_on_color (completion->tracker, *args, *args);
+
+               if (completion->tracker.have_completions ())
+                 return {};
+             }
+         }
+
+       if (check_for_argument (args, "--"))
+         {
+           /* Treat e.g., "backtrace -entry-values --" as if there
+              was no argument after "-entry-values".  This makes
+              parse_cli_var_color throw an error with a suggestion of
+              what are the valid options.  */
+           args = nullptr;
+         }
+
+       option_value val;
+       ui_file_style::color color = parse_cli_var_color (args);
+       ui_file_style::color approx_color = color.approximate (colorsupport ());
+       val.color = approx_color;
+       return option_def_and_value {*match, match_ctx, val};
+      }
     case var_string:
       {
        if (check_for_argument (args, "--"))
@@ -683,6 +715,10 @@ save_option_value_in_ctx (std::optional<option_def_and_value> &ov)
       *ov->option.var_address.enumeration (ov->option, ov->ctx)
        = ov->value->enumeration;
       break;
+    case var_color:
+      *ov->option.var_address.color (ov->option, ov->ctx)
+       = ov->value->color;
+      break;
     case var_string:
     case var_filename:
       *ov->option.var_address.string (ov->option, ov->ctx)
@@ -789,6 +825,12 @@ append_val_type_str (std::string &help, const option_def &opt,
          }
       }
       break;
+    case var_color:
+      help += ' ';
+      for (size_t i = 0; ui_file_style::basic_color_enums[i]; ++i)
+       help.append (ui_file_style::basic_color_enums[i]).append ("|");
+      help += "NUMBER|#RRGGBB";
+      break;
     case var_string:
       help += "STRING";
       break;
index dd3db8b721b7fbcc6c6e0ea47498175a73ac970d..ac614c326d4dc375af35e075e906a5c673224732 100644 (file)
@@ -91,6 +91,7 @@ public:
       int *(*integer) (const option_def &, void *ctx);
       const char **(*enumeration) (const option_def &, void *ctx);
       std::string *(*string) (const option_def &, void *ctx);
+      ui_file_style::color *(*color) (const option_def &, void *ctx);
     }
   var_address;
 
@@ -328,6 +329,26 @@ struct filename_option_def : option_def
   }
 };
 
+/* A var_color command line option.  */
+
+template<typename Context>
+struct color_option_def : option_def
+{
+  color_option_def (const char *long_option_,
+                   ui_file_style::color *(*get_var_address_cb_) (Context *),
+                   show_value_ftype *show_cmd_cb_,
+                   const char *set_doc_,
+                   const char *show_doc_ = nullptr,
+                   const char *help_doc_ = nullptr)
+    : option_def (long_option_, var_color,
+                 (erased_get_var_address_ftype *) get_var_address_cb_,
+                 show_cmd_cb_,
+                 set_doc_, show_doc_, help_doc_)
+  {
+    var_address.color = detail::get_var_address<ui_file_style::color, Context>;
+  }
+};
+
 /* A group of options that all share the same context pointer to pass
    to the options' get-current-value callbacks.  */
 struct option_def_group
index bcc793b3148035ae825696f31c42bb3811656eb7..a8f22c9bde6fcd8d267ded14be4c7b0f190ddd1b 100644 (file)
@@ -443,6 +443,13 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c)
        option_changed = c->var->set<const char *> (match);
       }
       break;
+    case var_color:
+      {
+       ui_file_style::color color = parse_var_color (arg);
+       ui_file_style::color approx_color = color.approximate (colorsupport ());
+       option_changed = c->var->set<ui_file_style::color> (approx_color);
+      }
+      break;
     default:
       error (_("gdb internal error: bad var_type in do_setshow_command"));
     }
@@ -520,6 +527,14 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c)
          interps_notify_param_changed
            (name, c->var->get<const char *> ());
          break;
+       case var_color:
+         {
+           const ui_file_style::color &color
+             = c->var->get<ui_file_style::color> ();
+           interps_notify_param_changed
+             (name, color.to_string ().c_str ());
+         }
+         break;
        case var_boolean:
          {
            const char *opt = c->var->get<bool> () ? "on" : "off";
@@ -585,6 +600,12 @@ get_setshow_command_value_string (const setting &var)
          stb.puts (value);
       }
       break;
+    case var_color:
+      {
+       const ui_file_style::color &value = var.get<ui_file_style::color> ();
+       stb.puts (value.to_string ().c_str ());
+      }
+      break;
     case var_boolean:
       stb.puts (var.get<bool> () ? "on" : "off");
       break;
index fa582a7d4d0fdda18be9b8cefb14aebdab0e3f10..41510828ae3ee169cb3c6d1fd4cd549a6afab165 100644 (file)
@@ -42,20 +42,6 @@ bool source_styling = true;
 
 bool disassembler_styling = true;
 
-/* Name of colors; must correspond to ui_file_style::basic_color.  */
-static const char * const cli_colors[] = {
-  "none",
-  "black",
-  "red",
-  "green",
-  "yellow",
-  "blue",
-  "magenta",
-  "cyan",
-  "white",
-  nullptr
-};
-
 /* Names of intensities; must correspond to
    ui_file_style::intensity.  */
 static const char * const cli_intensities[] = {
@@ -139,8 +125,8 @@ cli_style_option::cli_style_option (const char *name,
                                    ui_file_style::intensity intensity)
   : changed (name),
     m_name (name),
-    m_foreground (cli_colors[fg - ui_file_style::NONE]),
-    m_background (cli_colors[0]),
+    m_foreground (fg),
+    m_background (ui_file_style::NONE),
     m_intensity (cli_intensities[intensity])
 {
 }
@@ -151,32 +137,17 @@ cli_style_option::cli_style_option (const char *name,
                                    ui_file_style::intensity i)
   : changed (name),
     m_name (name),
-    m_foreground (cli_colors[0]),
-    m_background (cli_colors[0]),
+    m_foreground (ui_file_style::NONE),
+    m_background (ui_file_style::NONE),
     m_intensity (cli_intensities[i])
 {
 }
 
-/* Return the color number corresponding to COLOR.  */
-
-static int
-color_number (const char *color)
-{
-  for (int i = 0; i < ARRAY_SIZE (cli_colors); ++i)
-    {
-      if (color == cli_colors[i])
-       return i - 1;
-    }
-  gdb_assert_not_reached ("color not found");
-}
-
 /* See cli-style.h.  */
 
 ui_file_style
 cli_style_option::style () const
 {
-  int fg = color_number (m_foreground);
-  int bg = color_number (m_background);
   ui_file_style::intensity intensity = ui_file_style::NORMAL;
 
   for (int i = 0; i < ARRAY_SIZE (cli_intensities); ++i)
@@ -188,7 +159,7 @@ cli_style_option::style () const
        }
     }
 
-  return ui_file_style (fg, bg, intensity);
+  return ui_file_style (m_foreground, m_background, intensity);
 }
 
 /* See cli-style.h.  */
@@ -261,9 +232,8 @@ cli_style_option::add_setshow_commands (enum command_class theclass,
 
   set_show_commands commands;
 
-  commands = add_setshow_enum_cmd
-    ("foreground", theclass, cli_colors,
-     &m_foreground,
+  commands = add_setshow_color_cmd
+    ("foreground", theclass, &m_foreground,
      _("Set the foreground color for this property."),
      _("Show the foreground color for this property."),
      nullptr,
@@ -273,9 +243,8 @@ cli_style_option::add_setshow_commands (enum command_class theclass,
   commands.set->set_context (this);
   commands.show->set_context (this);
 
-  commands = add_setshow_enum_cmd
-    ("background", theclass, cli_colors,
-     &m_background,
+  commands = add_setshow_color_cmd
+    ("background", theclass, &m_background,
      _("Set the background color for this property."),
      _("Show the background color for this property."),
      nullptr,
index 171ec714b45b1c58d3babc9fe819a396f5af2c53..a1e538b04b3832408ae85c99d1beaf8242a4ee4f 100644 (file)
@@ -67,9 +67,9 @@ private:
   const char *m_name;
 
   /* The foreground.  */
-  const char *m_foreground;
+  ui_file_style::color m_foreground;
   /* The background.  */
-  const char *m_background;
+  ui_file_style::color m_background;
   /* The intensity.  */
   const char *m_intensity;
 
index 308614bcf7569652325a81d12e28c0af4b9378b9..37c7ddf4725f442202ca17bf75d90ba6a72d592c 100644 (file)
@@ -110,7 +110,9 @@ enum var_types
     /* Enumerated type.  Can only have one of the specified values.
        *VAR is a char pointer to the name of the element that we
        find.  */
-    var_enum
+    var_enum,
+    /* Color type.  *VAR is a ui_file_style::color structure.  */
+    var_color
   };
 
 /* A structure describing an extra literal accepted and shown in place
@@ -184,6 +186,14 @@ inline bool var_type_uses<const char *> (var_types t)
   return t == var_enum;
 }
 
+/* Return true if a setting of type T is backed by an ui_file_style::color
+   variable.  */
+template<>
+inline bool var_type_uses<ui_file_style::color> (var_types t)
+{
+  return t == var_color;
+}
+
 template<bool is_scalar, typename T> struct setting_func_types_1;
 
 template<typename T>
@@ -680,6 +690,20 @@ extern set_show_commands add_setshow_enum_cmd
    setting_func_types<const char *>::get get_func, show_value_ftype *show_func,
    cmd_list_element **set_list, cmd_list_element **show_list);
 
+extern set_show_commands add_setshow_color_cmd
+  (const char *name, command_class theclass, ui_file_style::color *var,
+   const char *set_doc, const char *show_doc, const char *help_doc,
+   cmd_func_ftype *set_func, show_value_ftype *show_func,
+   cmd_list_element **set_list, cmd_list_element **show_list);
+
+extern set_show_commands add_setshow_color_cmd
+  (const char *name, command_class theclass,
+   const char *set_doc, const char *show_doc, const char *help_doc,
+   setting_func_types<ui_file_style::color>::set set_func,
+   setting_func_types<ui_file_style::color>::get get_func,
+   show_value_ftype *show_func, cmd_list_element **set_list,
+   cmd_list_element **show_list);
+
 extern set_show_commands add_setshow_auto_boolean_cmd
   (const char *name, command_class theclass, auto_boolean *var,
    const char *set_doc, const char *show_doc, const char *help_doc,
index 0d55875ba387a5fd794db3b0441081cf2159203b..726e7cea479f0db4912631ea420a888ccbc510a4 100644 (file)
@@ -13009,6 +13009,20 @@ and @code{$_shell_exitsignal} according to the exit status of the last
 launched command.  These variables are set and used similarly to
 the variables @code{$_exitcode} and @code{$_exitsignal}.
 
+@item $_colorsupport
+@vindex $_colorsupport@r{, convenience variable}
+@cindex color space
+Comma-separated list of @dfn{color space} names supported by terminal.  Names
+could be any of @samp{monochrome}, @samp{ansi_8color}, @samp{aixterm_16color},
+@samp{xterm_256color}, @samp{rgb_24bit}.  E.g., for plain linux terminal the
+value could be @samp{monochrome,ansi_8color} and for terminal with truecolor
+support it could be
+@samp{monochrome,ansi_8color,aixterm_16color,xterm_256color,rgb_24bit}.
+@samp{rgb_24bit} availability is determined by the @env{COLORTERM} environment
+variable which may be @samp{truecolor} or @samp{24bit}. Other color spaces are
+determined by the "Co" termcap which in turn depends on the @env{TERM}
+environment variable.
+
 @end table
 
 @node Convenience Funs
@@ -27871,16 +27885,32 @@ For example, the style of file names can be controlled using the
 
 @table @code
 @item set style filename background @var{color}
-Set the background to @var{color}.  Valid colors are @samp{none}
-(meaning the terminal's default color), @samp{black}, @samp{red},
-@samp{green}, @samp{yellow}, @samp{blue}, @samp{magenta}, @samp{cyan},
-and@samp{white}.
+Set the background to @var{color}.  @var{color} can be @samp{none}
+(meaning the terminal's default color), a name of one of the eight standard
+colors of ISO/IEC 6429, index from 0 to 255 into terminal's color
+palette or a hexadecimal RGB triplet in @samp{#RRGGBB} format for
+24-bit TrueColor.
+
+Valid color names are @samp{black}, @samp{red}, @samp{green},
+@samp{yellow}, @samp{blue}, @samp{magenta}, @samp{cyan}, and
+@samp{white}.
+
+Integers 0 to 7 are the synonyms for the standard colors.  Integers 8-15 are
+used for the so-called bright colors from the aixterm extended 16-color
+palette.  Integers 16-255 are the indexes into xterm extended 256-color palette
+(usually 6x6x6 cube plus gray ramp).  In general, 256-color palette is terminal
+dependent and sometimes can be changed with OSC 4 sequences, e.g.
+"\033]4;1;rgb:00/FF/00\033\\". A hexadecimal 24-bit TrueColor is specified in
+the format @samp{#RRGGBB} where RR, GG and BB are the 2-digit hexadecimal
+integers specifiing the intensity of the red, green and blue color components,
+respectively.
+
+It is the responsibility of the user to verify that the terminal supports
+the specified colors.
 
 @item set style filename foreground @var{color}
-Set the foreground to @var{color}.  Valid colors are @samp{none}
-(meaning the terminal's default color), @samp{black}, @samp{red},
-@samp{green}, @samp{yellow}, @samp{blue}, @samp{magenta}, @samp{cyan},
-and@samp{white}.
+Set the foreground to @var{color}.  @var{color} can be given in the same ways
+as for the background.
 
 @item set style filename intensity @var{value}
 Set the intensity to @var{value}.  Valid intensities are @samp{normal}
index bd1262ea0cba88911776a0882b5c19f84a5bf980..f1b638ee7e34dfaec52b8d2251a4511f827e7209 100644 (file)
@@ -155,6 +155,7 @@ from the Guile interactive prompt.
 * I/O Ports in Guile::       GDB I/O ports
 * Memory Ports in Guile::    Accessing memory through ports and bytevectors
 * Iterators In Guile::       Basic iterator support
+* Colors In Guile::          Colorize output with Guile
 @end menu
 
 @node Basic Guile
@@ -408,6 +409,9 @@ as a symbol.
 
 @item <gdb:value>
 @xref{Values From Inferior In Guile}.
+
+@item <gdb:color>
+@xref{Colors In Guile}.
 @end table
 
 The following @value{GDBN} objects are managed internally so that the
@@ -2184,6 +2188,14 @@ The value is a filename.  This is just like
 @item PARAM_ENUM
 The value is a string, which must be one of a collection of string
 constants provided when the parameter is created.
+
+@item PARAM_COLOR
+The value is either a string or an unsigned integer.  Integer from 0 to 255
+means index into terminal's color palette.  String can be a hex RGB triplet in
+@samp{#RRGGBB} format or one of the following color names:
+@samp{none} (meaning the terminal's default color), @samp{black}, @samp{red},
+@samp{green}, @samp{yellow}, @samp{blue}, @samp{magenta}, @samp{cyan},
+or @samp{white}.
 @end vtable
 
 @node Progspaces In Guile
@@ -3820,6 +3832,98 @@ Run @var{iterator} until the result of @code{(pred element)} is true
 and return that as the result.  Otherwise return @code{#f}.
 @end deffn
 
+@node Colors In Guile
+@subsubsection Colors In Guile
+
+@cindex guile colors
+@tindex <gdb:color>
+You can assign instance of @code{<gdb:color>} to the value of
+a @code{<gdb:parameter>} object created with @code{PARAM_COLOR}.
+
+@code{<gdb:color>} may refer to an index from color palette or contain
+components of a color from some colorspace.
+
+@deffn {Scheme Procedure} make-color @w{@r{[}value} @
+    @w{@r{[}#:color-space color-space@r{]}@r{]}}
+
+@var{value} is an integer index of a color in palette, tuple with color
+components or a string.  String can be a hex RGB triplet in @samp{#RRGGBB}
+format or one of the following color names:
+@samp{none} (meaning the terminal's default color), @samp{black}, @samp{red},
+@samp{green}, @samp{yellow}, @samp{blue}, @samp{magenta}, @samp{cyan},
+or @samp{white}.
+
+@var{color-space} should be one of the @samp{COLORSPACE_} constants.  This
+argument tells @value{GDBN} which color space @var{value} belongs.
+@end deffn
+
+@deffn {Scheme Procedure} color? object
+Return @code{#t} if @var{object} is a @code{<gdb:color>} object.
+Otherwise return @code{#f}.
+@end deffn
+
+@deffn {Scheme Procedure} color-none? color
+Return @code{#t} if @var{color} is terminal's default.
+Otherwise return @code{#f}.
+@end deffn
+
+@deffn {Scheme Procedure} color-indexed? color
+Return @code{#t} if @var{color} is indexed, i.e. belongs to some palette.
+Otherwise return @code{#f}.
+@end deffn
+
+@deffn {Scheme Procedure} color-direct? color
+Return @code{#t} if @var{color} is direct in the sense of ISO/IEC 8613-6.
+Otherwise return @code{#f}.
+@end deffn
+
+@deffn {Scheme Procedure} color-string color
+Return the textual representation of a @code{<gdb:color>} object.
+@end deffn
+
+@deffn {Scheme Procedure} color-colorspace color
+Return the color space of a @code{<gdb:color>} object.
+@end deffn
+
+@deffn {Scheme Procedure} color-index color
+Return index of the color of a @code{<gdb:color>} object in a palette.
+@end deffn
+
+@deffn {Scheme Procedure} color-components color
+Return components of the direct @code{<gdb:color>} object.
+@end deffn
+
+@deffn {Scheme Procedure} color-escape-sequence color is_foreground
+Return string to change terminal's color to this.
+
+If @var{is_foreground} is @code{#t}, then the returned sequence will change
+foreground color.  Otherwise, the returned sequence will change background
+color.
+@end deffn
+
+When color is initialized, its color space must be specified.  The
+available color spaces are represented by constants defined in the @code{gdb}
+module:
+
+@vtable @code
+@item COLORSPACE_MONOCHROME
+Palette with only terminal's default color.
+
+@item COLORSPACE_ANSI_8COLOR
+Palette with eight standard colors of ISO/IEC 6429 "black", "red", "green", etc.
+
+@item COLORSPACE_AIXTERM_16COLOR
+Palette with 16 colors.  First eight are standard colors of ISO/IEC 6429
+"black", "red", "green", etc.  Next eight are their bright version.
+
+@item COLORSPACE_XTERM_256COLOR
+Palette with 256 colors.  First 16 are from COLORSPACE_AIXTERM_16COLOR.  Next
+216 colors are 6x6x6 RGB cube.  And last 24 colors form grayscale ramp.
+
+@item COLORSPACE_RGB_24BIT
+Direct 24-bit RGB colors.
+@end vtable
+
 @node Guile Auto-loading
 @subsection Guile Auto-loading
 @cindex guile auto-loading
index 2e10930076a8562720b0f84df947e0f932b94a90..d66cae6f997bf631c362505a25f7d8304f5e1495 100644 (file)
@@ -225,6 +225,7 @@ optional arguments while skipping others.  Example:
 * Finish Breakpoints in Python:: Setting Breakpoints on function return
                                 using Python.
 * Lazy Strings In Python::      Python representation of lazy strings.
+* Colors In Python::            Python representation of colors.
 * Architectures In Python::     Python representation of architectures.
 * Registers In Python::         Python representation of registers.
 * Connections In Python::       Python representation of connections.
@@ -5242,6 +5243,11 @@ except the special value -1 is returned for the setting of ``unlimited''.
 @item gdb.PARAM_ENUM
 The value is a string, which must be one of a collection string
 constants provided when the parameter is created.
+
+@findex PARAM_COLOR
+@findex gdb.PARAM_COLOR
+@item gdb.PARAM_COLOR
+The value is @code{gdb.Color} instance.
 @end table
 
 @node Functions In Python
@@ -7020,6 +7026,98 @@ resolve this to the lazy string's character type, use the type's
 writable.
 @end defvar
 
+@node Colors In Python
+@subsubsection Python representation of colors
+
+@cindex colors in python
+@tindex gdb.Color
+You can assign instance of @code{Color} to the @code{value} of
+a @code{Parameter} instance created with @code{PARAM_COLOR}.
+
+@code{Color} may refer to an index from color palette or contain components
+of a color from some colorspace.
+
+@defun Color.__init__ (@r{[}@var{value} @r{[}, @var{color-space}@r{]}@r{]})
+
+@var{value} is @code{None} (meaning the terminal's default color),
+an integer index of a color in palette, tuple with color components
+or a string.  String can be a hex RGB triplet in @samp{#RRGGBB} format
+or one of the following color names:
+@samp{none} (meaning the terminal's default color), @samp{black}, @samp{red},
+@samp{green}, @samp{yellow}, @samp{blue}, @samp{magenta}, @samp{cyan},
+or @samp{white}.
+
+@var{color-space} should be one of the @samp{COLORSPACE_} constants.  This
+argument tells @value{GDBN} which color space @var{value} belongs.
+@end defun
+
+@defvar Color.is_none
+This atribute is boolean.  If its value is @code{True} then color is terminal's
+default.
+@end defvar
+
+@defvar Color.is_indexed
+This atribute is boolean.  If its value is @code{True} then color is indexed,
+i.e. belongs to some palette.
+@end defvar
+
+@defvar Color.is_direct
+This atribute is boolean.  If its value is @code{True} then this object
+describes direct colour in the sense of ISO/IEC 8613-6.
+@end defvar
+
+@defvar Color.index
+This attribute exist if @code{is_indexed} is @code{True}.  Its integer value is
+index of a color in a palette.
+@end defvar
+
+@defvar Color.components
+This attribute exist if @code{is_direct} is @code{True}.  Its value is tuple
+with integer components of a color.
+@end defvar
+
+@defun Color.escape_sequence (@var{self}, @var{is_foreground})
+Returns string to change terminal's color to this.
+
+If @var{is_foreground} is @code{True}, then the returned sequence will change
+foreground color.  Otherwise, the returned sequence will change background
+color.
+@end defun
+
+When color is initialized, its color space must be specified.  The
+available color spaces are represented by constants defined in the @code{gdb}
+module:
+
+@table @code
+@findex COLORSPACE_MONOCHROME
+@findex gdb.COLORSPACE_MONOCHROME
+@item gdb.COLORSPACE_MONOCHROME
+Palette with only terminal's default color.
+
+@findex COLORSPACE_ANSI_8COLOR
+@findex gdb.COLORSPACE_ANSI_8COLOR
+@item gdb.COLORSPACE_ANSI_8COLOR
+Palette with eight standard colors of ISO/IEC 6429 "black", "red", "green", etc.
+
+@findex COLORSPACE_AIXTERM_16COLOR
+@findex gdb.COLORSPACE_AIXTERM_16COLOR
+@item gdb.COLORSPACE_AIXTERM_16COLOR
+Palette with 16 colors.  First eight are standard colors of ISO/IEC 6429
+"black", "red", "green", etc.  Next eight are their bright version.
+
+@findex COLORSPACE_XTERM_256COLOR
+@findex gdb.COLORSPACE_XTERM_256COLOR
+@item gdb.COLORSPACE_XTERM_256COLOR
+Palette with 256 colors.  First 16 are from COLORSPACE_AIXTERM_16COLOR.  Next
+216 colors are 6x6x6 RGB cube.  And last 24 colors form grayscale ramp.
+
+@findex COLORSPACE_RGB_24BIT
+@findex gdb.COLORSPACE_RGB_24BIT
+@item gdb.COLORSPACE_RGB_24BIT
+Direct 24-bit RGB colors.
+
+@end table
+
 @node Architectures In Python
 @subsubsection Python representation of architectures
 @cindex Python architectures
index 1057b83324593875d692978261e5f0420002743b..ef7a1d3ba751648e6ecfa956f26970ceb760d09a 100644 (file)
@@ -452,6 +452,14 @@ extern int gdbscm_valid_command_class_p (int command_class);
 extern char *gdbscm_canonicalize_command_name (const char *name,
                                               int want_trailing_space);
 
+/* scm-color.c */
+
+extern SCM coscm_scm_from_color (const ui_file_style::color &color);
+
+extern int coscm_is_color (SCM scm);
+
+extern const ui_file_style::color & coscm_get_color (SCM color_scm);
+
 /* scm-frame.c */
 
 struct frame_smob;
@@ -630,6 +638,7 @@ extern void gdbscm_initialize_arches (void);
 extern void gdbscm_initialize_auto_load (void);
 extern void gdbscm_initialize_blocks (void);
 extern void gdbscm_initialize_breakpoints (void);
+extern void gdbscm_initialize_colors (void);
 extern void gdbscm_initialize_commands (void);
 extern void gdbscm_initialize_disasm (void);
 extern void gdbscm_initialize_exceptions (void);
index 432093b6aea28acc9113b011784ee5278a8cca70..5358b8330a3ae5b7dca9836aa906301a2d6cc185 100644 (file)
@@ -594,6 +594,7 @@ initialize_gdb_module (void *data)
   gdbscm_initialize_auto_load ();
   gdbscm_initialize_blocks ();
   gdbscm_initialize_breakpoints ();
+  gdbscm_initialize_colors ();
   gdbscm_initialize_commands ();
   gdbscm_initialize_disasm ();
   gdbscm_initialize_frames ();
diff --git a/gdb/guile/scm-color.c b/gdb/guile/scm-color.c
new file mode 100644 (file)
index 0000000..6ebe252
--- /dev/null
@@ -0,0 +1,427 @@
+/* GDB parameters implemented in Guile.
+
+   Copyright (C) 2008-2024 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "value.h"
+#include "charset.h"
+#include "cli/cli-decode.h"
+#include "completer.h"
+#include "language.h"
+#include "arch-utils.h"
+#include "guile-internal.h"
+
+/* A GDB color.  */
+
+struct color_smob
+{
+  /* This always appears first.  */
+  gdb_smob base;
+
+  /* Underlying value.  */
+  ui_file_style::color color;
+};
+
+static const char color_smob_name[] = "gdb:color";
+
+/* The tag Guile knows the color smob by.  */
+static scm_t_bits color_smob_tag;
+
+/* Keywords used by make-color.  */
+static SCM colorspace_keyword;
+
+static const char *coscm_colorspace_name (color_space colorspace);
+
+/* Administrivia for color smobs.  */
+
+static int
+coscm_print_color_smob (SCM self, SCM port, scm_print_state *pstate)
+{
+  const ui_file_style::color &color = coscm_get_color (self);
+
+  gdbscm_printf (port, "#<%s", color_smob_name);
+
+  gdbscm_printf (port, " %s", color.to_string ().c_str ());
+  gdbscm_printf (port, " %s", coscm_colorspace_name (color.colorspace ()));
+  scm_puts (">", port);
+
+  scm_remember_upto_here_1 (self);
+
+  /* Non-zero means success.  */
+  return 1;
+}
+
+/* Create an empty (uninitialized) color.  */
+
+static SCM
+coscm_make_color_smob (void)
+{
+  color_smob *c_smob = (color_smob *)
+    scm_gc_calloc (sizeof (color_smob), color_smob_name);
+  SCM c_scm;
+
+  c_smob->color = ui_file_style::color (ui_file_style::NONE);
+  c_scm = scm_new_smob (color_smob_tag, (scm_t_bits) c_smob);
+  gdbscm_init_gsmob (&c_smob->base);
+
+  return c_scm;
+}
+
+/* Return the <gdb:color> object that encapsulates COLOR.  */
+
+SCM
+coscm_scm_from_color (const ui_file_style::color &color)
+{
+  SCM c_scm = coscm_make_color_smob ();
+  color_smob *c_smob = (color_smob *) SCM_SMOB_DATA (c_scm);
+  c_smob->color = color;
+  return c_scm;
+}
+
+/* Return the color field of color_smob.  */
+
+const ui_file_style::color &
+coscm_get_color (SCM color_scm)
+{
+  SCM_ASSERT_TYPE (coscm_is_color (color_scm), color_scm, SCM_ARG1, FUNC_NAME,
+                  _("<gdb:color>"));
+
+  color_smob *c_smob = (color_smob *) SCM_SMOB_DATA (color_scm);
+  return c_smob->color;
+
+}
+
+/* Returns non-zero if SCM is a <gdb:color> object.  */
+
+int
+coscm_is_color (SCM scm)
+{
+  return SCM_SMOB_PREDICATE (color_smob_tag, scm);
+}
+
+/* (gdb:color? scm) -> boolean */
+
+static SCM
+gdbscm_color_p (SCM scm)
+{
+  return scm_from_bool (coscm_is_color (scm));
+}
+
+static const scheme_integer_constant colorspaces[] =
+{
+  { "COLORSPACE_MONOCHROME", (int) color_space::MONOCHROME },
+  { "COLORSPACE_ANSI_8COLOR", (int) color_space::ANSI_8COLOR },
+  { "COLORSPACE_AIXTERM_16COLOR", (int) color_space::AIXTERM_16COLOR },
+  { "COLORSPACE_XTERM_256COLOR", (int) color_space::XTERM_256COLOR },
+  { "COLORSPACE_RGB_24BIT", (int) color_space::RGB_24BIT },
+
+  END_INTEGER_CONSTANTS
+};
+
+/* Return COLORSPACE as a string.  */
+
+static const char *
+coscm_colorspace_name (color_space colorspace)
+{
+  for (int i = 0; colorspaces[i].name != nullptr; ++i)
+    {
+      if (colorspaces[i].value == static_cast<int> (colorspace))
+       return colorspaces[i].name;
+    }
+
+  gdb_assert_not_reached ("bad color space");
+}
+
+/* Free function for a color_smob.  */
+static size_t
+coscm_free_color_smob (SCM self)
+{
+  (void) self;
+  return 0;
+}
+
+/* Color Scheme functions.  */
+
+/* (make-color [value
+     [#:color-space colorspace]]) -> <gdb:color>
+
+   VALUE is the value of the color.  It may be SCM_UNDEFINED, string, number
+   or list.
+
+   COLORSPACE is the color space of the VALUE.  It should be one of the
+   COLORSPACE_* constants defined in the gdb module.
+
+   The result is the <gdb:color> Scheme object.  */
+
+static SCM
+gdbscm_make_color (SCM value_scm, SCM rest)
+{
+  SCM colorspace_arg = SCM_UNDEFINED;
+  color_space colorspace = color_space::MONOCHROME;
+
+  scm_c_bind_keyword_arguments (FUNC_NAME, rest,
+                               static_cast<scm_t_keyword_arguments_flags> (0),
+                               colorspace_keyword, &colorspace_arg,
+                               SCM_UNDEFINED);
+
+  if (!SCM_UNBNDP (colorspace_arg))
+    {
+      SCM_ASSERT_TYPE (scm_is_integer (colorspace_arg), colorspace_arg,
+                      SCM_ARG2, FUNC_NAME, _("int"));
+      int colorspace_int = scm_to_int (colorspace_arg);
+      if (!color_space_safe_cast (&colorspace, colorspace_int))
+       gdbscm_out_of_range_error (FUNC_NAME, SCM_ARG2,
+                                  scm_from_int (colorspace_int),
+                                  _("invalid colorspace argument"));
+    }
+
+  ui_file_style::color color = ui_file_style::NONE;
+  gdbscm_gdb_exception exc {};
+
+  try
+    {
+      if (SCM_UNBNDP (value_scm) || scm_is_integer (value_scm))
+       {
+         int i = -1;
+         if (scm_is_integer (value_scm))
+           {
+             i = scm_to_int (value_scm);
+             if (i < 0)
+               gdbscm_out_of_range_error (FUNC_NAME, SCM_ARG1, value_scm,
+                                          _("negative color index"));
+           }
+
+         if (SCM_UNBNDP (colorspace_arg))
+           color = ui_file_style::color (i);
+         else
+           color = ui_file_style::color (colorspace, i);
+       }
+      else if (gdbscm_is_true (scm_list_p (value_scm)))
+       {
+         if (SCM_UNBNDP (colorspace_arg)
+             || colorspace != color_space::RGB_24BIT)
+           error (_("colorspace must be COLORSPACE_RGB_24BIT with "
+                  "value of list type."));
+
+         if (scm_ilength (value_scm) != 3)
+           error (_("List value with RGB must be of size 3."));
+
+         uint8_t rgb[3] = {};
+         int i = 0;
+         for (; i < 3 && !scm_is_eq (value_scm, SCM_EOL); ++i)
+           {
+             SCM item = scm_car (value_scm);
+
+             SCM_ASSERT_TYPE (scm_is_integer (item), item, SCM_ARG1, FUNC_NAME,
+                              _("int"));
+             int component = scm_to_int (item);
+             if (component < 0 || component > UINT8_MAX)
+               gdbscm_out_of_range_error (FUNC_NAME, SCM_ARG1, item,
+                                          _("invalid rgb component"));
+             rgb[i] = static_cast<uint8_t> (component);
+
+             value_scm = scm_cdr (value_scm);
+           }
+
+         gdb_assert (i == 3);
+
+         color = ui_file_style::color (rgb[0], rgb[1], rgb[2]);
+       }
+      else if (scm_is_string (value_scm))
+       {
+         SCM exception;
+
+         gdb::unique_xmalloc_ptr<char> string
+           = gdbscm_scm_to_host_string (value_scm, nullptr, &exception);
+         if (string == nullptr)
+           gdbscm_throw (exception);
+
+         color = parse_var_color (string.get ());
+
+         if (!SCM_UNBNDP (colorspace_arg) && colorspace != color.colorspace ())
+           error (_("colorspace doesn't match to the value."));
+
+       }
+      else
+       scm_wrong_type_arg_msg (FUNC_NAME, SCM_ARG1, value_scm,
+                               "integer, string or list");
+    }
+  catch (const gdb_exception &except)
+    {
+      exc = unpack (except);
+    }
+
+  GDBSCM_HANDLE_GDB_EXCEPTION (exc);
+
+  return coscm_scm_from_color (color);
+}
+
+/* (color-string <gdb:color>) -> value */
+
+static SCM
+gdbscm_color_string (SCM self)
+{
+  const ui_file_style::color &color = coscm_get_color (self);
+  std::string s = color.to_string ();
+  return gdbscm_scm_from_host_string (s.c_str (), s.size ());
+}
+
+/* (color-colorspace <gdb:color>) -> value */
+
+static SCM
+gdbscm_color_colorspace (SCM self)
+{
+  const ui_file_style::color &color = coscm_get_color (self);
+  return scm_from_int (static_cast<int> (color.colorspace ()));
+}
+
+/* (color-none? scm) -> boolean */
+
+static SCM
+gdbscm_color_none_p (SCM self)
+{
+  const ui_file_style::color &color = coscm_get_color (self);
+  return scm_from_bool (color.is_none ());
+}
+
+/* (color-indexed? scm) -> boolean */
+
+static SCM
+gdbscm_color_indexed_p (SCM self)
+{
+  const ui_file_style::color &color = coscm_get_color (self);
+  return scm_from_bool (color.is_indexed ());
+}
+
+/* (color-direct? scm) -> boolean */
+
+static SCM
+gdbscm_color_direct_p (SCM self)
+{
+  const ui_file_style::color &color = coscm_get_color (self);
+  return scm_from_bool (color.is_direct ());
+}
+
+/* (color-index <gdb:color>) -> value */
+
+static SCM
+gdbscm_color_index (SCM self)
+{
+  const ui_file_style::color &color = coscm_get_color (self);
+
+  if (!color.is_indexed ())
+    gdbscm_misc_error (FUNC_NAME, SCM_ARG1, self, "color is not indexed");
+  return scm_from_int (color.get_value ());
+}
+
+/* (color-components <gdb:color>) -> value */
+
+static SCM
+gdbscm_color_components (SCM self)
+{
+  const ui_file_style::color &color = coscm_get_color (self);
+
+  if (!color.is_direct ())
+    gdbscm_misc_error (FUNC_NAME, SCM_ARG1, self, "color is not direct");
+
+  uint8_t rgb[3] = {};
+  color.get_rgb (rgb);
+  SCM red = scm_from_uint8 (rgb[0]);
+  SCM green = scm_from_uint8 (rgb[1]);
+  SCM blue = scm_from_uint8 (rgb[2]);
+  return scm_list_3 (red, green, blue);
+}
+
+/* (color-escape-sequence <gdb:color> is_fg) -> value */
+
+static SCM
+gdbscm_color_escape_sequence (SCM self, SCM is_fg_scm)
+{
+  const ui_file_style::color &color = coscm_get_color (self);
+  SCM_ASSERT_TYPE (gdbscm_is_bool (is_fg_scm), is_fg_scm, SCM_ARG2, FUNC_NAME,
+                  _("boolean"));
+  bool is_fg = gdbscm_is_true (is_fg_scm);
+  std::string s = color.to_ansi (is_fg);
+  return gdbscm_scm_from_host_string (s.c_str (), s.size ());
+}
+
+/* Initialize the Scheme color support.  */
+
+static const scheme_function color_functions[] =
+{
+  { "make-color", 0, 1, 1, as_a_scm_t_subr (gdbscm_make_color),
+    "\
+Make a GDB color object.\n\
+\n\
+  Arguments: [value\n\
+      [#:color-space <colorspace>]]\n\
+    value: The name of the color.  It may be string, number with color index\n\
+      or list with RGB components.\n\
+    colorspace: The color space of the color, one of COLORSPACE_*." },
+
+  { "color?", 1, 0, 0, as_a_scm_t_subr (gdbscm_color_p),
+    "\
+Return #t if the object is a <gdb:color> object." },
+
+  { "color-none?", 1, 0, 0, as_a_scm_t_subr (gdbscm_color_none_p),
+    "\
+Return #t if the <gdb:color> object has default color." },
+
+  { "color-indexed?", 1, 0, 0, as_a_scm_t_subr (gdbscm_color_indexed_p),
+    "\
+Return #t if the <gdb:color> object is from indexed color space." },
+
+  { "color-direct?", 1, 0, 0, as_a_scm_t_subr (gdbscm_color_direct_p),
+    "\
+Return #t if the <gdb:color> object has direct color (e.g. RGB, CMY, CMYK)." },
+
+  { "color-string", 1, 0, 0, as_a_scm_t_subr (gdbscm_color_string),
+    "\
+Return the textual representation of a <gdb:color> object." },
+
+  { "color-colorspace", 1, 0, 0, as_a_scm_t_subr (gdbscm_color_colorspace),
+    "\
+Return the color space of a <gdb:color> object." },
+
+  { "color-index", 1, 0, 0, as_a_scm_t_subr (gdbscm_color_index),
+    "\
+Return index of the color of a <gdb:color> object in a palette." },
+
+  { "color-components", 1, 0, 0, as_a_scm_t_subr (gdbscm_color_components),
+    "\
+Return components of the direct <gdb:color> object." },
+
+  { "color-escape-sequence", 2, 0, 0,
+    as_a_scm_t_subr (gdbscm_color_escape_sequence),
+    "\
+Return string to change terminal's color to this." },
+
+  END_FUNCTIONS
+};
+
+void
+gdbscm_initialize_colors (void)
+{
+  color_smob_tag = gdbscm_make_smob_type (color_smob_name, sizeof (color_smob));
+  scm_set_smob_free (color_smob_tag, coscm_free_color_smob);
+  scm_set_smob_print (color_smob_tag, coscm_print_color_smob);
+
+  gdbscm_define_integer_constants (colorspaces, 1);
+  gdbscm_define_functions (color_functions, 1);
+
+  colorspace_keyword = scm_from_latin1_keyword ("color-space");
+}
index 3a1e1583f5405245216af61911d31fd56324997b..749c5ea1f7d06de6baab814e3cf52c414c594239 100644 (file)
@@ -47,6 +47,9 @@ union pascm_variable
 
   /* Hold a string, for enums.  */
   const char *cstringval;
+
+  /* Hold a color.  */
+  ui_file_style::color color;
 };
 
 /* A GDB parameter.
@@ -130,6 +133,7 @@ enum scm_param_types
   param_optional_filename,
   param_filename,
   param_enum,
+  param_color,
 };
 
 /* Translation from Guile parameters to GDB variable types.  Keep in the
@@ -155,7 +159,8 @@ param_to_var[] =
   { var_string_noescape },
   { var_optional_filename },
   { var_filename },
-  { var_enum }
+  { var_enum },
+  { var_color }
 };
 
 /* Wraps a setting around an existing param_smob.  This abstraction
@@ -179,6 +184,8 @@ make_setting (param_smob *s)
     return setting (type, s->value.stringval);
   else if (var_type_uses<const char *> (type))
     return setting (type, &s->value.cstringval);
+  else if (var_type_uses<ui_file_style::color> (s->type))
+    return setting (s->type, &s->value.color);
   else
     gdb_assert_not_reached ("unhandled var type");
 }
@@ -239,10 +246,9 @@ static SCM
 pascm_make_param_smob (void)
 {
   param_smob *p_smob = (param_smob *)
-    scm_gc_malloc (sizeof (param_smob), param_smob_name);
+    scm_gc_calloc (sizeof (param_smob), param_smob_name);
   SCM p_scm;
 
-  memset (p_smob, 0, sizeof (*p_smob));
   p_smob->cmd_class = no_class;
   p_smob->type = var_boolean; /* ARI: var_boolean */
   p_smob->set_func = SCM_BOOL_F;
@@ -511,6 +517,13 @@ add_setshow_generic (enum var_types param_type,
                                       set_list, show_list);
       break;
 
+    case var_color:
+      commands = add_setshow_color_cmd (cmd_name, cmd_class, &self->value.color,
+                                       set_doc, show_doc, help_doc,
+                                       set_func, show_func,
+                                       set_list, show_list);
+      break;
+
     default:
       gdb_assert_not_reached ("bad param_type value");
     }
@@ -588,6 +601,7 @@ static const scheme_integer_constant parameter_types[] =
   { "PARAM_OPTIONAL_FILENAME", param_optional_filename },
   { "PARAM_FILENAME", param_filename },
   { "PARAM_ENUM", param_enum },
+  { "PARAM_COLOR", param_color },
 
   END_INTEGER_CONSTANTS
 };
@@ -650,6 +664,11 @@ pascm_param_value (const setting &var, int arg_pos, const char *func_name)
        return gdbscm_scm_from_host_string (str, strlen (str));
       }
 
+    case var_color:
+      {
+       return coscm_scm_from_color (var.get<ui_file_style::color> ());
+      }
+
     case var_boolean:
       {
        if (var.get<bool> ())
@@ -764,6 +783,12 @@ pascm_set_param_value_x (param_smob *p_smob,
        break;
       }
 
+    case var_color:
+      SCM_ASSERT_TYPE (coscm_is_color (value), value, arg_pos, func_name,
+                      _("<gdb:color>"));
+      var.set<ui_file_style::color> (coscm_get_color (value));
+      break;
+
     case var_boolean:
       SCM_ASSERT_TYPE (gdbscm_is_bool (value), value, arg_pos, func_name,
                       _("boolean"));
@@ -1050,6 +1075,8 @@ gdbscm_make_parameter (SCM name_scm, SCM rest)
   scm_set_smob_free (parameter_smob_tag, pascm_free_parameter_smob);
   if (var_type_uses<std::string> (p_smob->type))
     p_smob->value.stringval = new std::string;
+  else if (var_type_uses<ui_file_style::color> (p_smob->type))
+    p_smob->value.color = ui_file_style::NONE;
 
   if (initial_value_arg_pos > 0)
     {
diff --git a/gdb/python/py-color.c b/gdb/python/py-color.c
new file mode 100644 (file)
index 0000000..3a90b62
--- /dev/null
@@ -0,0 +1,336 @@
+/* Python interface to ui_file_style::color objects.
+
+   Copyright (C) 2008-2024 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+
+#include "python-internal.h"
+#include "py-color.h"
+#include "cli/cli-decode.h"
+
+/* Colorspace constants and their values.  */
+static struct {
+  const char *name;
+  color_space value;
+} colorspace_constants[] =
+{
+  { "COLORSPACE_MONOCHROME", color_space::MONOCHROME },
+  { "COLORSPACE_ANSI_8COLOR", color_space::ANSI_8COLOR },
+  { "COLORSPACE_AIXTERM_16COLOR", color_space::AIXTERM_16COLOR },
+  { "COLORSPACE_XTERM_256COLOR", color_space::XTERM_256COLOR },
+  { "COLORSPACE_RGB_24BIT", color_space::RGB_24BIT },
+};
+
+/* A color.  */
+struct colorpy_object
+{
+  PyObject_HEAD
+
+  /* Underlying value.  */
+  ui_file_style::color color;
+};
+
+extern PyTypeObject colorpy_object_type;
+
+/* See py-color.h.  */
+gdbpy_ref<>
+create_color_object (const ui_file_style::color &color)
+{
+  gdbpy_ref<colorpy_object> color_obj (PyObject_New (colorpy_object,
+                                                    &colorpy_object_type));
+
+  if (color_obj == nullptr)
+    return nullptr;
+
+  color_obj->color = color;
+  return gdbpy_ref<> ((PyObject *) color_obj.release ());
+}
+
+/* See py-color.h.  */
+bool
+gdbpy_is_color (PyObject *obj)
+{
+  return PyObject_IsInstance (obj, (PyObject *) &colorpy_object_type);
+}
+
+/* See py-color.h.  */
+const ui_file_style::color &
+gdbpy_get_color (PyObject *obj)
+{
+  gdb_assert (gdbpy_is_color (obj));
+  colorpy_object *self = (colorpy_object *) obj;
+  return self->color;
+}
+
+/* Get an attribute.  */
+static PyObject *
+get_attr (PyObject *obj, PyObject *attr_name)
+{
+  if (! PyUnicode_Check (attr_name))
+    return PyObject_GenericGetAttr (obj, attr_name);
+
+  colorpy_object *self = (colorpy_object *) obj;
+  const ui_file_style::color &color = self->color;
+
+  if (! PyUnicode_CompareWithASCIIString (attr_name, "colorspace"))
+    {
+      int value = static_cast<int> (color.colorspace ());
+      return gdb_py_object_from_longest (value).release ();
+    }
+
+  if (! PyUnicode_CompareWithASCIIString (attr_name, "is_none"))
+    return PyBool_FromLong (color.is_none ());
+
+  if (! PyUnicode_CompareWithASCIIString (attr_name, "is_indexed"))
+    return PyBool_FromLong (color.is_indexed ());
+
+  if (! PyUnicode_CompareWithASCIIString (attr_name, "is_direct"))
+    return PyBool_FromLong (color.is_direct ());
+
+  if (color.is_indexed ()
+      && ! PyUnicode_CompareWithASCIIString (attr_name, "index"))
+    return gdb_py_object_from_longest (color.get_value ()).release ();
+
+  if (color.is_direct ()
+      && ! PyUnicode_CompareWithASCIIString (attr_name, "components"))
+    {
+      uint8_t rgb[3];
+      color.get_rgb (rgb);
+
+      gdbpy_ref<> rgb_objects[3];
+      for (int i = 0; i < 3; ++i)
+       {
+         rgb_objects[i] = gdb_py_object_from_ulongest (rgb[i]);
+         if (rgb_objects[i] == nullptr)
+           return nullptr;
+       }
+
+      PyObject *comp = PyTuple_New (3);
+      if (comp == nullptr)
+       return nullptr;
+
+      for (int i = 0; i < 3; ++i)
+       PyTuple_SET_ITEM (comp, i, rgb_objects[i].release ());
+
+      return comp;
+    }
+
+  return PyObject_GenericGetAttr (obj, attr_name);
+}
+
+/* Implementation of Color.escape_sequence (self, is_fg) -> str.  */
+
+static PyObject *
+colorpy_escape_sequence (PyObject *self, PyObject *is_fg_obj)
+{
+  if (!gdbpy_is_color (self))
+    {
+      PyErr_SetString (PyExc_RuntimeError,
+                      _("Object is not gdb.Color."));
+      return nullptr;
+    }
+
+  if (! PyBool_Check (is_fg_obj))
+    {
+      PyErr_SetString (PyExc_RuntimeError,
+                      _("A boolean argument is required."));
+      return nullptr;
+    }
+
+  bool is_fg = is_fg_obj == Py_True;
+  std::string s = gdbpy_get_color (self).to_ansi (is_fg);
+
+  return host_string_to_python_string (s.c_str ()).release ();
+}
+
+/* Object initializer; fills color with value.
+
+   Use: __init__(VALUE = None, COLORSPACE = None)
+
+   VALUE is a string, integer, RGB-tuple or None.
+
+   COLORSPACE is the color space index.
+
+   Returns -1 on error, with a python exception set.  */
+
+static int
+colorpy_init (PyObject *self, PyObject *args, PyObject *kwds)
+{
+  colorpy_object *obj = (colorpy_object *) self;
+  PyObject *value_obj = nullptr;
+  PyObject *colorspace_obj = nullptr;
+  color_space colorspace = color_space::MONOCHROME;
+
+  if (! PyArg_ParseTuple (args, "|OO", &value_obj, &colorspace_obj))
+    return -1;
+
+  try
+    {
+      if (colorspace_obj)
+       {
+         if (PyLong_Check (colorspace_obj))
+           {
+             long colorspace_id = -1;
+             if (! gdb_py_int_as_long (colorspace_obj, &colorspace_id))
+               return -1;
+             if (!color_space_safe_cast (&colorspace, colorspace_id))
+               error (_("colorspace %ld is out of range."), colorspace_id);
+           }
+         else if (colorspace_obj == Py_None)
+           colorspace_obj = nullptr;
+         else
+           error (_("colorspace must be None or integer"));
+       }
+
+      if (value_obj == nullptr || value_obj == Py_None)
+         obj->color = ui_file_style::color (colorspace, -1);
+      else if (PyLong_Check (value_obj))
+       {
+         long value = -1;
+         if (! gdb_py_int_as_long (value_obj, &value))
+           return -1;
+         if (value < 0 || value > INT_MAX)
+           error (_("value %ld is out of range."), value);
+         if (colorspace_obj)
+           obj->color = ui_file_style::color (colorspace, value);
+         else
+           obj->color = ui_file_style::color (value);
+       }
+      else if (PyTuple_Check (value_obj))
+       {
+         if (colorspace_obj == nullptr || colorspace != color_space::RGB_24BIT)
+           error (_("colorspace must be gdb.COLORSPACE_RGB_24BIT with "
+                  "value of tuple type."));
+         Py_ssize_t tuple_size = PyTuple_Size (value_obj);
+         if (tuple_size < 0)
+           return -1;
+         if (tuple_size != 3)
+           error (_("Tuple value with RGB must be of size 3."));
+         uint8_t rgb[3];
+         for (int i = 0; i < 3; ++i)
+           {
+             PyObject *item = PyTuple_GetItem (value_obj, i);
+             if (!PyLong_Check (item))
+               error (_("Item %d of an RGB tuple must be integer."), i);
+             long item_value = -1;
+             if (!gdb_py_int_as_long (item, &item_value))
+               return -1;
+             if (item_value < 0 || item_value > UINT8_MAX)
+               error (_("RGB item %ld is out of byte range."), item_value);
+             rgb[i] = static_cast<uint8_t> (item_value);
+           }
+
+         obj->color = ui_file_style::color (rgb[0], rgb[1], rgb[2]);
+       }
+      else if (PyUnicode_Check (value_obj))
+       {
+         gdb::unique_xmalloc_ptr<char>
+           str (python_string_to_host_string (value_obj));
+         if (str == nullptr)
+           return -1;
+         obj->color = parse_var_color (str.get());
+
+         if (colorspace_obj != nullptr
+             && colorspace != obj->color.colorspace ())
+           error (_("colorspace doesn't match to the value."));
+       }
+      else
+       error (_("value must be one of None, integer, tuple or str."));
+    }
+  catch (const gdb_exception &except)
+    {
+      return gdbpy_handle_gdb_exception (-1, except);
+    }
+
+  Py_INCREF (self);
+  return 0;
+}
+
+static PyObject *
+colorpy_str (PyObject *self)
+{
+  colorpy_object *obj = reinterpret_cast<colorpy_object *> (self);
+
+  return PyUnicode_FromString (obj->color.to_string ().c_str ());
+}
+
+/* Initialize the 'color' module.  */
+static int
+gdbpy_initialize_color (void)
+{
+  for (auto & pair : colorspace_constants)
+      if (PyModule_AddIntConstant (gdb_module, pair.name,
+                                  static_cast<long> (pair.value)) < 0)
+       return -1;
+
+  colorpy_object_type.tp_new = PyType_GenericNew;
+  return gdbpy_type_ready (&colorpy_object_type, gdb_module);
+}
+
+/* Color methods.  */
+
+static PyMethodDef color_methods[] =
+{
+  { "escape_sequence", colorpy_escape_sequence, METH_O,
+    "escape_sequence (is_foreground) -> str.\n\
+Return the ANSI escape sequence for this color.\n\
+IS_FOREGROUND indicates whether this is a foreground or background color."},
+  {nullptr}
+};
+
+PyTypeObject colorpy_object_type =
+{
+  PyVarObject_HEAD_INIT (nullptr, 0)
+  "gdb.Color",                   /*tp_name*/
+  sizeof (colorpy_object),       /*tp_basicsize*/
+  0,                             /*tp_itemsize*/
+  0,                             /*tp_dealloc*/
+  0,                             /*tp_print*/
+  0,                             /*tp_getattr*/
+  0,                             /*tp_setattr*/
+  0,                             /*tp_compare*/
+  0,                             /*tp_repr*/
+  0,                             /*tp_as_number*/
+  0,                             /*tp_as_sequence*/
+  0,                             /*tp_as_mapping*/
+  0,                             /*tp_hash */
+  0,                             /*tp_call*/
+  colorpy_str,                   /*tp_str*/
+  get_attr,                      /*tp_getattro*/
+  0,                             /*tp_setattro*/
+  0,                             /*tp_as_buffer*/
+  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+  "GDB color object",            /* tp_doc */
+  0,                             /* tp_traverse */
+  0,                             /* tp_clear */
+  0,                             /* tp_richcompare */
+  0,                             /* tp_weaklistoffset */
+  0,                             /* tp_iter */
+  0,                             /* tp_iternext */
+  color_methods,                 /* tp_methods */
+  0,                             /* tp_members */
+  0,                             /* tp_getset */
+  0,                             /* tp_base */
+  0,                             /* tp_dict */
+  0,                             /* tp_descr_get */
+  0,                             /* tp_descr_set */
+  0,                             /* tp_dictoffset */
+  colorpy_init,                          /* tp_init */
+  0,                             /* tp_alloc */
+};
+
+GDBPY_INITIALIZE_FILE (gdbpy_initialize_color);
diff --git a/gdb/python/py-color.h b/gdb/python/py-color.h
new file mode 100644 (file)
index 0000000..a778d5b
--- /dev/null
@@ -0,0 +1,35 @@
+/* Python interface to ui_file_style::color objects.
+
+   Copyright (C) 2009-2024 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef PYTHON_PY_COLOR_H
+#define PYTHON_PY_COLOR_H
+
+#include "python-internal.h"
+#include "ui-style.h"
+
+/* Create a new gdb.Color object from COLOR.  */
+extern gdbpy_ref<> create_color_object (const ui_file_style::color &color);
+
+/* Check if OBJ is instance of a gdb.Color type.  */
+extern bool gdbpy_is_color (PyObject *obj);
+
+/* Extracts value from OBJ object of gdb.Color type.  */
+extern const ui_file_style::color &gdbpy_get_color (PyObject *obj);
+
+#endif /* PYTHON_PY_COLOR_H */
index 9741782d3f145d6d1ec8e46a1225e3e535602b97..0e79d2199f6f886a1c4c544df99609fc38b84376 100644 (file)
@@ -26,6 +26,7 @@
 #include "completer.h"
 #include "language.h"
 #include "arch-utils.h"
+#include "py-color.h"
 
 /* Python parameter types as in PARM_CONSTANTS below.  */
 
@@ -43,6 +44,7 @@ enum py_param_types
   param_zuinteger,
   param_zuinteger_unlimited,
   param_enum,
+  param_color,
 };
 
 /* Translation from Python parameters to GDB variable types.  Keep in the
@@ -69,7 +71,8 @@ param_to_var[] =
   { var_integer },
   { var_uinteger },
   { var_pinteger, pinteger_unlimited_literals },
-  { var_enum }
+  { var_enum },
+  { var_color }
 };
 
 /* Parameter constants and their values.  */
@@ -90,6 +93,7 @@ static struct {
   { "PARAM_ZUINTEGER", param_zuinteger },
   { "PARAM_ZUINTEGER_UNLIMITED", param_zuinteger_unlimited },
   { "PARAM_ENUM", param_enum },
+  { "PARAM_COLOR", param_color },
   { NULL, 0 }
 };
 
@@ -114,6 +118,9 @@ union parmpy_variable
 
   /* Hold a string, for enums.  */
   const char *cstringval;
+
+  /* Hold a color.  */
+  ui_file_style::color color;
 };
 
 /* A GDB parameter.  */
@@ -157,6 +164,8 @@ make_setting (parmpy_object *s)
     return setting (type, s->value.stringval);
   else if (var_type_uses<const char *> (type))
     return setting (type, &s->value.cstringval);
+  else if (var_type_uses<ui_file_style::color> (s->type))
+    return setting (s->type, &s->value.color);
   else
     gdb_assert_not_reached ("unhandled var type");
 }
@@ -248,6 +257,19 @@ set_parameter_value (parmpy_object *self, PyObject *value)
        break;
       }
 
+    case var_color:
+      {
+       if (gdbpy_is_color (value))
+         self->value.color = gdbpy_get_color (value);
+       else
+         {
+           PyErr_SetString (PyExc_RuntimeError,
+                            _("color argument must be a gdb.Color object."));
+           return -1;
+         }
+      }
+      break;
+
     case var_boolean:
       if (! PyBool_Check (value))
        {
@@ -707,6 +729,15 @@ add_setshow_generic (enum var_types type, const literal_def *extra_literals,
                                       get_show_value, set_list, show_list);
       break;
 
+    case var_color:
+      /* Initialize the value, just in case.  */
+      self->value.color = ui_file_style::NONE;
+      commands = add_setshow_color_cmd (cmd_name.get (), cmdclass,
+                                       &self->value.color, set_doc,
+                                       show_doc, help_doc, get_set_value,
+                                       get_show_value, set_list, show_list);
+      break;
+
     default:
       gdb_assert_not_reached ("Unhandled parameter class.");
     }
@@ -830,7 +861,8 @@ parmpy_init (PyObject *self, PyObject *args, PyObject *kwds)
       && parmclass != param_string && parmclass != param_string_noescape
       && parmclass != param_optional_filename && parmclass != param_filename
       && parmclass != param_zinteger && parmclass != param_zuinteger
-      && parmclass != param_zuinteger_unlimited && parmclass != param_enum)
+      && parmclass != param_zuinteger_unlimited && parmclass != param_enum
+      && parmclass != param_color)
     {
       PyErr_SetString (PyExc_RuntimeError,
                       _("Invalid parameter class argument."));
@@ -854,7 +886,7 @@ parmpy_init (PyObject *self, PyObject *args, PyObject *kwds)
   extra_literals = param_to_var[parmclass].extra_literals;
   obj->type = type;
   obj->extra_literals = extra_literals;
-  memset (&obj->value, 0, sizeof (obj->value));
+  obj->value = {}; /* zeros initialization */
 
   if (var_type_uses<std::string> (obj->type))
     obj->value.stringval = new std::string;
@@ -900,6 +932,8 @@ parmpy_dealloc (PyObject *obj)
 
   if (var_type_uses<std::string> (parm_obj->type))
     delete parm_obj->value.stringval;
+  else if (var_type_uses<ui_file_style::color> (parm_obj->type))
+    parm_obj->value.color.~color();
 }
 
 /* Initialize the 'parameters' module.  */
index acd80e5515cf44949c86d7e3579d81b44c35a285..15bb91297399fc0a1ec65c65cf949c1b03b0b8f4 100644 (file)
@@ -36,6 +36,7 @@
 #include "run-on-main-thread.h"
 #include "observable.h"
 #include "build-id.h"
+#include "py-color.h"
 
 #if GDB_SELF_TEST
 #include "gdbsupport/selftest.h"
@@ -511,6 +512,12 @@ gdbpy_parameter_value (const setting &var)
        return host_string_to_python_string (str).release ();
       }
 
+    case var_color:
+      {
+       const ui_file_style::color &color = var.get<ui_file_style::color> ();
+       return create_color_object (color).release ();
+      }
+
     case var_boolean:
       {
        if (var.get<bool> ())
index bbc95aa0539b86463d9b42c8a7748a6e215161a3..f36def7cfee0ff50437869be480e96dd6354bea9 100644 (file)
 
 # Start with a fresh gdb
 
-gdb_exit
-gdb_start
+with_ansi_styling_terminal {
+    setenv TERM dumb
+    gdb_exit
+    gdb_start
+}
 
 set prev_timeout $timeout
 set timeout 60
@@ -684,6 +687,7 @@ set show_conv_list \
        {$_probe_arg10 = <error: No frame selected>} \
        {$_probe_arg11 = <error: No frame selected>} \
        {$_cimag = <internal function _cimag>} \
+       {$_colorsupport = "monochrome"} \
        {$_creal = <internal function _creal>} \
        {$_isvoid = <internal function _isvoid>} \
        {$_shell = <internal function _shell>} \
index 628c6b80570882ed5333fe0bab4d117f161f92d0..b027ab7174b82d6467b293d41f8779df58c97731 100644 (file)
@@ -70,6 +70,9 @@ proc run_style_tests { } {
     global currently_disabled_style decimal hex
 
     with_ansi_styling_terminal {
+       setenv TERM xterm-256color
+       setenv COLORTERM truecolor
+
        # Restart GDB with the correct TERM variable setting, this
        # means that GDB will enable styling.
        clean_restart_and_disable "restart 1" ${binfile}
@@ -311,6 +314,21 @@ proc run_style_tests { } {
        set url [limited_style "http:.*html" file]
        gdb_test "show version" "${vers}.*<$url>.*" \
            "'show version' is styled"
+
+       if { $currently_disabled_style != "version" } {
+           # Check that colors in styling can be set as integer and as RGB hex
+           # triplet.  Check that the version string is styled in the output of
+           # 'show version' according to the set colors.
+           gdb_test_no_output "set style version intensity normal"
+           gdb_test_no_output "set style version background 255"
+           gdb_test_no_output "set style version foreground #FED210"
+           gdb_test "show style version background" \
+               "The \033\\\[38;2;254;210;16;48;5;255;22;27m.*\".*version.*\".*style.*\033\\\[m background color is: 255" \
+               "Version's 256-color background style"
+           gdb_test "show style version foreground" \
+               "The \033\\\[38;2;254;210;16;48;5;255;22;27m.*\".*version.*\".*style.*\033\\\[m foreground color is: #FED210" \
+               "Version's TrueColor foreground style"
+       }
     }
 }
 
@@ -487,6 +505,179 @@ proc test_startup_version_string { } {
     gdb_test "" "${vers}.*" "version is styled at startup"
 }
 
+# Color support is disabled when TERM=dumb and COLORTERM="".
+# All colors are approximated to none when set.
+proc test_colorsupport_monochrome { } {
+    with_test_prefix "colorsupport_monochrome" {
+       with_ansi_styling_terminal {
+           setenv TERM dumb
+           gdb_exit
+           gdb_start
+           gdb_test "print \$_colorsupport" \
+               "\\\$1 = \"monochrome\"" \
+               "color support is monochrome"
+           gdb_test_no_output "set style enabled off"
+           gdb_test_no_output "set style filename foreground none"
+           gdb_test "show style filename foreground" \
+             "The \"filename\" style foreground color is: none" \
+             "none without approximation"
+           gdb_test_no_output "set style filename foreground blue"
+           gdb_test "show style filename foreground" \
+             "The \"filename\" style foreground color is: none" \
+             "blue approximated to none"
+       }
+    }
+}
+
+# Color support is limited by 8 colors when TERM=ansi and COLORTERM="".
+# All colors are approximated to basic colors when set.
+proc test_colorsupport_8color { } {
+    with_test_prefix "colorsupport_8color" {
+       with_ansi_styling_terminal {
+           gdb_exit
+           gdb_start
+           gdb_test "print \$_colorsupport" \
+               "\033\\\[.*m\\\$1\033\\\[m = \"monochrome,ansi_8color\"" \
+               "color support is 8 color"
+           gdb_test_no_output "set style enabled off"
+           gdb_test_no_output "set style filename foreground none"
+           gdb_test "show style filename foreground" \
+             "The \"filename\" style foreground color is: none" \
+             "none without approximation"
+           gdb_test_no_output "set style filename foreground yellow"
+           gdb_test "show style filename foreground" \
+             "The \"filename\" style foreground color is: yellow" \
+             "yellow without approximation"
+           gdb_test_no_output "set style filename foreground 9"
+           gdb_test "show style filename foreground" \
+             "The \"filename\" style foreground color is: red" \
+             "9 approximated to red"
+           gdb_test_no_output "set style filename foreground 118"
+           gdb_test "show style filename foreground" \
+             "The \"filename\" style foreground color is: green" \
+             "118 approximated to green"
+           gdb_test_no_output "set style filename foreground #000ABC"
+           gdb_test "show style filename foreground" \
+             "The \"filename\" style foreground color is: blue" \
+             "#000ABC approximated to blue"
+       }
+    }
+}
+
+# Color support is limited by 256 colors when TERM=xterm-256color and
+# COLORTERM="".  All colors are approximated by 256 color palette when set.
+proc test_colorsupport_256color { } {
+    with_test_prefix "colorsupport_256color" {
+       with_ansi_styling_terminal {
+           setenv TERM xterm-256color
+           gdb_exit
+           gdb_start
+           gdb_test "print \$_colorsupport" \
+               "\033\\\[.*m\\\$1\033\\\[m = \"monochrome,ansi_8color,aixterm_16color,xterm_256color\"" \
+               "color support is 256 color"
+           gdb_test_no_output "set style enabled off"
+           gdb_test_no_output "set style filename foreground none"
+           gdb_test "show style filename foreground" \
+             "The \"filename\" style foreground color is: none" \
+             "none without approximation"
+           gdb_test_no_output "set style filename foreground red"
+           gdb_test "show style filename foreground" \
+             "The \"filename\" style foreground color is: red" \
+             "red without approximation"
+           gdb_test_no_output "set style filename foreground 9"
+           gdb_test "show style filename foreground" \
+             "The \"filename\" style foreground color is: 9" \
+             "9 without approximation"
+           gdb_test_no_output "set style filename foreground 118"
+           gdb_test "show style filename foreground" \
+             "The \"filename\" style foreground color is: 118" \
+             "118 without approximation"
+           gdb_test_no_output "set style filename foreground #CD00CD"
+           gdb_test "show style filename foreground" \
+             "The \"filename\" style foreground color is: 5" \
+             "#CD00CD approximated to 5"
+           gdb_test_no_output "set style filename foreground #FFAF12"
+           gdb_test "show style filename foreground" \
+             "The \"filename\" style foreground color is: 214" \
+             "#FFAF12 approximated to 214"
+       }
+    }
+}
+
+# Color support is limited by 16777216 colors when TERM=xterm-256color and
+# COLORTERM="truecolor".  No approximation needed.
+proc test_colorsupport_truecolor { } {
+    with_test_prefix "colorsupport_truecolor" {
+       with_ansi_styling_terminal {
+           setenv TERM xterm-256color
+           setenv COLORTERM truecolor
+           gdb_exit
+           gdb_start
+           gdb_test "print \$_colorsupport" \
+               "\033\\\[.*m\\\$1\033\\\[m = \"monochrome,ansi_8color,aixterm_16color,xterm_256color,rgb_24bit\"" \
+               "color support is truecolor"
+           gdb_test_no_output "set style enabled off"
+           gdb_test_no_output "set style filename foreground none"
+           gdb_test "show style filename foreground" \
+             "The \"filename\" style foreground color is: none" \
+             "none without approximation"
+           gdb_test_no_output "set style filename foreground red"
+           gdb_test "show style filename foreground" \
+             "The \"filename\" style foreground color is: red" \
+             "red without approximation"
+           gdb_test_no_output "set style filename foreground 9"
+           gdb_test "show style filename foreground" \
+             "The \"filename\" style foreground color is: 9" \
+             "9 without approximation"
+           gdb_test_no_output "set style filename foreground 118"
+           gdb_test "show style filename foreground" \
+             "The \"filename\" style foreground color is: 118" \
+             "118 without approximation"
+           gdb_test_no_output "set style filename foreground #CD00CD"
+           gdb_test "show style filename foreground" \
+             "The \"filename\" style foreground color is: #CD00CD" \
+             "#CD00CD without approximation"
+       }
+    }
+}
+
+# Color support is limited by 16777216 colors when TERM=dumb and
+# COLORTERM=24bit.  No approximation needed.  Basic colors replaced with RGB.
+proc test_colorsupport_truecolor_only { } {
+    with_test_prefix "colorsupport_truecolor_only" {
+       with_ansi_styling_terminal {
+           setenv TERM dumb
+           setenv COLORTERM truecolor
+           gdb_exit
+           gdb_start
+           gdb_test "print \$_colorsupport" \
+               "\\\$1 = \"monochrome,rgb_24bit\"" \
+               "color support is truecolor only"
+           gdb_test_no_output "set style enabled off"
+           gdb_test_no_output "set style filename foreground none"
+           gdb_test "show style filename foreground" \
+             "The \"filename\" style foreground color is: none" \
+             "none without approximation"
+           gdb_test_no_output "set style filename foreground red"
+           gdb_test "show style filename foreground" \
+             "The \"filename\" style foreground color is: #DE382B" \
+             "red replaced by #DE382B"
+           gdb_test_no_output "set style filename foreground 9"
+           gdb_test "show style filename foreground" \
+             "The \"filename\" style foreground color is: #FF0000" \
+             "9 replaced by #FF0000"
+           gdb_test_no_output "set style filename foreground 118"
+           gdb_test "show style filename foreground" \
+             "The \"filename\" style foreground color is: #87FF00" \
+             "118 replaced by #87FF00"
+           gdb_test_no_output "set style filename foreground #CD00CD"
+           gdb_test "show style filename foreground" \
+             "The \"filename\" style foreground color is: #CD00CD" \
+             "#CD00CD without approximation"
+       }
+    }
+}
+
 # Check to see if the Python styling of disassembler output is
 # expected or not, this styling requires Python support in GDB, and
 # the Python pygments module to be available.
@@ -521,3 +712,9 @@ test_disassembler_error_handling
 
 # Finally, check the styling of the version string during startup.
 test_startup_version_string
+
+test_colorsupport_monochrome
+test_colorsupport_8color
+test_colorsupport_256color
+test_colorsupport_truecolor
+test_colorsupport_truecolor_only
diff --git a/gdb/testsuite/gdb.guile/scm-color.exp b/gdb/testsuite/gdb.guile/scm-color.exp
new file mode 100644 (file)
index 0000000..9fd7f38
--- /dev/null
@@ -0,0 +1,110 @@
+# Copyright (C) 2010-2024 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# This file is part of the GDB testsuite.
+# It tests GDB parameter support in Guile.
+
+load_lib gdb-guile.exp
+
+require allow_guile_tests
+
+clean_restart
+
+gdb_install_guile_utils
+gdb_install_guile_module
+
+gdb_test_no_output [concat "guile (define (print_color_attrs c) " \
+    "(display (color-string c)) (display \" \") " \
+    "(display (color-colorspace c)) (display \" \") " \
+    "(display (color-none? c)) (display \" \") " \
+    "(display (color-indexed? c)) (display \" \") " \
+    "(display (color-direct? c)) (newline))"] \
+    "print_color_attrs helper"
+
+gdb_test "guile (print_color_attrs (make-color))" \
+    "none 0 #t #f #f" \
+    "print attrs of a color without params"
+
+gdb_test_no_output "guile (define c (make-color \"green\"))" \
+    "create color from basic name string"
+gdb_test "guile (print_color_attrs c)" "green 1 #f #t #f" \
+    "print attrs of a basic color name"
+gdb_test "guile (print (color-index c))" "2" \
+    "print index of a basic color name"
+
+gdb_test_no_output "guile (define c (make-color 2))" \
+    "create color from basic index"
+gdb_test "guile (print_color_attrs c)" "green 1 #f #t #f" \
+    "print attrs of a basic color"
+gdb_test "guile (print (color-index c))" "2" \
+    "print index of a basic color"
+
+gdb_test_no_output "guile (define c (make-color 14))" \
+    "create color from integer 14"
+gdb_test "guile (print_color_attrs c)" "14 2 #f #t #f" \
+    "print attrs of an color 14"
+gdb_test "guile (print (color-index c))" "14" \
+    "print index of color 14"
+
+gdb_test_no_output "guile (define c (make-color 2 #:color-space COLORSPACE_ANSI_8COLOR))" \
+    "create color from basic index and ansi colorspace"
+gdb_test "guile (print_color_attrs c)" "green 1 #f #t #f" \
+    "print attrs of a basic color with ansi colorspace"
+gdb_test "guile (print (color-index c))" "2" \
+    "print index of a basic color with ansi colorspace"
+
+gdb_test_no_output "guile (define c (make-color 2 #:color-space COLORSPACE_XTERM_256COLOR))" \
+    "create color from basic index and xterm256 colorspace"
+gdb_test "guile (print_color_attrs c)" "2 3 #f #t #f" \
+    "print attrs of a basic color with xterm256 colorspace"
+gdb_test "guile (print (color-index c))" "2" \
+    "print index of a basic color with xterm256 colorspace"
+
+gdb_test_no_output "guile (define c (make-color '(171 205 239) #:color-space COLORSPACE_RGB_24BIT))" \
+    "create color from rgb components"
+gdb_test "guile (print_color_attrs c)" "#ABCDEF 4 #f #f #t" \
+    "print attrs of an RGB color"
+gdb_test "guile (print (color-components c))" "\\(171 205 239\\)" \
+    "print components of an RGB color"
+
+gdb_test_no_output "guile (define c (make-color \"none\"))" \
+    "create color from string none"
+gdb_test "guile (print_color_attrs c)" "none 0 #t #f #f" \
+    "print attrs of a color none"
+
+gdb_test_no_output "guile (define c (make-color \"254\"))" \
+    "create color from string 254"
+gdb_test "guile (print_color_attrs c)" "254 3 #f #t #f" \
+    "print attrs of an color 254"
+gdb_test "guile (print (color-index c))" "254" \
+    "print index of color 254"
+
+gdb_test_no_output "guile (define c_none (make-color \"none\"))" \
+    "save default color"
+gdb_test_no_output "guile (define c_red (make-color \"red\"))" \
+    "save blue color"
+gdb_test_no_output "guile (define c_green (make-color \"green\"))" \
+    "save yellow color"
+gdb_test [concat "guile " \
+    "(display (color-escape-sequence c_red #t)) " \
+    "(display (color-escape-sequence c_green #f)) " \
+    "(display \"red on green\") " \
+    "(display (color-escape-sequence c_none #f)) " \
+    "(display \" red on default\") " \
+    "(display (color-escape-sequence c_none #t)) " \
+    "(newline)"] \
+    "\033\\\[31m\033\\\[42mred on green\033\\\[49m red on default\033\\\[39m" \
+    "escape sequences"
+
index d10e9d2406594a2dee28652871b4bff9e83969bb..eabd17980e76bc35bc0c114285a2a6460f33b2d5 100644 (file)
@@ -389,3 +389,50 @@ with_test_prefix "previously-ambiguous" {
 }
 
 rename scm_param_test_maybe_no_output ""
+
+# Test a color parameter.
+
+with_ansi_styling_terminal {
+    # This enables 256 colors support and disables colors approximation.
+    setenv TERM xterm-256color
+    setenv COLORTERM truecolor
+
+    # Start with a fresh gdb.
+    gdb_exit
+    gdb_start
+    gdb_reinitialize_dir $srcdir/$subdir
+
+    gdb_install_guile_utils
+    gdb_install_guile_module
+
+    # We use "." here instead of ":" so that this works on win32 too.
+    set escaped_directory [string_to_regexp "$srcdir/$subdir"]
+
+    gdb_test_multiline "color gdb parameter" \
+       "guile" "" \
+       "(define test-color-param" "" \
+       "  (make-parameter \"print test-color-param\"" "" \
+       "   #:command-class COMMAND_DATA" "" \
+       "   #:parameter-type PARAM_COLOR" "" \
+       "   #:doc \"When set, test param does something useful. When disabled, does nothing.\"" "" \
+       "   #:show-doc \"Show the state of the test-color-param.\"" "" \
+       "   #:set-doc \"Set the state of the test-color-param.\"" "" \
+       "   #:show-func (lambda (self value)" "" \
+       "      (format #f \"The state of the test-color-param is ~a.\" value))" "" \
+       "   #:initial-value (make-color \"green\")))" "" \
+       "(register-parameter! test-color-param)" "" \
+       "end"
+
+    with_test_prefix "test-color-param" {
+       with_test_prefix "initial-value" {
+           gdb_test "guile (print (parameter-value test-color-param))" "= #<gdb:color green COLORSPACE_ANSI_8COLOR>" "color parameter value (green)"
+           gdb_test "show print test-color-param" "The state of the test-color-param is green." "show initial value"
+           gdb_test_no_output "set print test-color-param 255"
+       }
+       with_test_prefix "new-value" {
+           gdb_test "show print test-color-param" "The state of the test-color-param is 255." "show new value"
+           gdb_test "guile (print (parameter-value test-color-param))" "= #<gdb:color 255 COLORSPACE_XTERM_256COLOR>" "color parameter value (255)"
+           gdb_test "set print test-color-param 256" "integer 256 out of range.*" "set invalid color parameter"
+       }
+    }
+}
diff --git a/gdb/testsuite/gdb.python/py-color.exp b/gdb/testsuite/gdb.python/py-color.exp
new file mode 100644 (file)
index 0000000..eb62d7f
--- /dev/null
@@ -0,0 +1,100 @@
+# Copyright (C) 2010-2024 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# This file is part of the GDB testsuite.
+# It tests gdb.parameter and gdb.Parameter.
+
+load_lib gdb-python.exp
+
+require allow_python_tests
+
+# Start with a fresh gdb.
+clean_restart
+
+gdb_test_no_output "python print_color_attrs = lambda c: print (c, c.colorspace, c.is_none, c.is_indexed, c.is_direct)" \
+    "print_color_attrs helper"
+
+gdb_test_no_output "python c = gdb.Color ()" \
+    "create color without params"
+gdb_test "python print_color_attrs (c)" "none 0 True False False" \
+    "print attrs of a color without params"
+
+gdb_test_no_output "python c = gdb.Color ('green')" \
+    "create color from basic name string"
+gdb_test "python print_color_attrs (c)" "green 1 False True False" \
+    "print attrs of a basic color name"
+gdb_test "python print (c.index)" "2" \
+    "print index of a basic color name"
+
+gdb_test_no_output "python c = gdb.Color (2)" \
+    "create color from basic index"
+gdb_test "python print_color_attrs (c)" "green 1 False True False" \
+    "print attrs of a basic color"
+gdb_test "python print (c.index)" "2" \
+    "print index of a basic color"
+
+gdb_test_no_output "python c = gdb.Color (14)" \
+    "create color from integer 14"
+gdb_test "python print_color_attrs (c)" "14 2 False True False" \
+    "print attrs of an color 14"
+gdb_test "python print (c.index)" "14" \
+    "print index of color 14"
+
+gdb_test_no_output "python c = gdb.Color (2, gdb.COLORSPACE_ANSI_8COLOR)" \
+    "create color from basic index and ansi colorspace"
+gdb_test "python print_color_attrs (c)" "green 1 False True False" \
+    "print attrs of a basic color with ansi colorspace"
+gdb_test "python print (c.index)" "2" \
+    "print index of a basic color with ansi colorspace"
+
+gdb_test_no_output "python c = gdb.Color (2, gdb.COLORSPACE_XTERM_256COLOR)" \
+    "create color from basic index and xterm256 colorspace"
+gdb_test "python print_color_attrs (c)" "2 3 False True False" \
+    "print attrs of a basic color with xterm256 colorspace"
+gdb_test "python print (c.index)" "2" \
+    "print index of a basic color with xterm256 colorspace"
+
+gdb_test_no_output "python c = gdb.Color ((171, 205, 239), gdb.COLORSPACE_RGB_24BIT)" \
+    "create color from rgb components"
+gdb_test "python print_color_attrs (c)" "#ABCDEF 4 False False True" \
+    "print attrs of an RGB color"
+gdb_test "python print (c.components)" "\\(171, 205, 239\\)" \
+    "print components of an RGB color"
+
+gdb_test_no_output "python c = gdb.Color ('none')" \
+    "create color from string none"
+gdb_test "python print_color_attrs (c)" "none 0 True False False" \
+    "print attrs of a color none"
+
+gdb_test_no_output "python c = gdb.Color ('254')" \
+    "create color from string 254"
+gdb_test "python print_color_attrs (c)" "254 3 False True False" \
+    "print attrs of an color 254"
+gdb_test "python print (c.index)" "254" \
+    "print index of color 254"
+
+gdb_test_no_output "python c_none = gdb.Color ('none')" \
+    "save default color"
+gdb_test_no_output "python c_red = gdb.Color ('red')" \
+    "save blue color"
+gdb_test_no_output "python c_green = gdb.Color ('green')" \
+    "save yellow color"
+gdb_test [concat "python print (c_red.escape_sequence (True) + " \
+    "c_green.escape_sequence (False) + 'red on green' + " \
+    "c_none.escape_sequence (False) + ' red on default' + " \
+    "c_none.escape_sequence (True))"] \
+    "\033\\\[31m\033\\\[42mred on green\033\\\[49m red on default\033\\\[39m" \
+    "escape sequences"
+
index de524f49ad679e842fa6bec81c0359a6fe7b4a32..74e4178bf3513db3925e19d66c03642d5ca205a5 100644 (file)
@@ -185,6 +185,58 @@ proc_with_prefix test_enum_parameter { } {
        "Undefined item: \"three\".*" "set invalid enum parameter"
 }
 
+# Test an color parameter.
+proc_with_prefix test_color_parameter { } {
+    global env
+    with_ansi_styling_terminal {
+       # This enables 256 colors support and disables colors approximation.
+       setenv TERM xterm-256color
+       setenv COLORTERM truecolor
+
+       clean_restart
+
+       gdb_test_multiline "color gdb parameter" \
+           "python" "" \
+           "class TestColorParam (gdb.Parameter):" "" \
+           "   \"\"\"When set, test param does something useful. When disabled, does nothing.\"\"\"" "" \
+           "   show_doc = \"Show the state of the color\"" ""\
+           "   set_doc = \"Set the state of the color\"" "" \
+           "   def get_show_string (self, pvalue):" ""\
+           "      return \"The state of the color is \" + str(pvalue)" ""\
+           "   def get_set_string (self):" ""\
+           "      return \"The state of the color has been set to \" + str(self.value)" ""\
+           "   def __init__ (self, name):" "" \
+           "      super (TestColorParam, self).__init__ (name, gdb.COMMAND_DATA, gdb.PARAM_COLOR)" "" \
+           "      self.value = gdb.Color(\"green\")" "" \
+           "test_color_param = TestColorParam ('print test-color-param')" ""\
+           "end"
+
+       gdb_test "python print (test_color_param.value)" "green" \
+           "test color parameter value is green"
+       gdb_test "show print test-color-param" \
+           "The state of the color is green.*" \
+           "show parameter is initial value"
+       gdb_test "set print test-color-param 255" \
+           "The state of the color has been set to 255" "set color to 255"
+       gdb_test "show print test-color-param" \
+           "The state of the color is 255.*" "show parameter is new value"
+       gdb_test "python print (test_color_param.value)" "255" \
+           "test color parameter value is 255"
+       gdb_test_no_output "python test_color_param.value = gdb.Color(254)" \
+           "assign test_color_param.value to 254"
+       gdb_test "python print (test_color_param.value)" "254" \
+           "test color parameter value is integer"
+       gdb_test_no_output "python test_color_param.value =  gdb.Color('#FED210')" \
+           "assign test_color_param.value to #FED210"
+       gdb_test "python print (test_color_param.value.components)" "\\(254, 210, 16\\)" \
+           "test color parameter components from RGB hex tripple value"
+       gdb_test "set print test-color-param 256" \
+           "integer 256 out of range.*" "set invalid color parameter"
+       gdb_test "python test_color_param.value = gdb.Color(256)" \
+           ".*Error occurred in Python: Palette color index 256 is out of range.*" "set invalid color value"
+    }
+}
+
 # Test a file parameter.
 proc_with_prefix test_file_parameter { } {
     clean_restart
@@ -623,6 +675,7 @@ test_directories
 test_data_directory
 test_boolean_parameter
 test_enum_parameter
+test_color_parameter
 test_file_parameter
 test_undocumented_parameter
 test_really_undocumented_parameter
index 34223e654edf87dbf4ecc3b94ab598f818088063..b8ab30a43ff9234a4f760d99b00d988b146c0184 100644 (file)
@@ -67,20 +67,24 @@ proc string_list_to_regexp { args } {
 # "function", "variable", "address", etc.
 
 proc style {str style} {
+    set fg 39
+    set bg 49
+    set intensity 22
+    set reverse 27
     switch -exact -- $style {
-       title { set style 1 }
-       command { set style 1 }
-       file { set style 32 }
-       function { set style 33 }
-       highlight { set style 31 }
-       variable { set style 36 }
-       address { set style 34 }
-       metadata { set style 2 }
-       version { set style "35;1" }
-       line-number { set style 2 }
+       title { set intensity 1 }
+       command { set intensity 1 }
+       file { set fg 32 }
+       function { set fg 33 }
+       highlight { set fg 31 }
+       variable { set fg 36 }
+       address { set fg 34 }
+       metadata { set intensity 2 }
+       version { set fg 35; set intensity 1 }
+       line-number { set intensity 2 }
        none { return $str }
     }
-    return "\033\\\[${style}m${str}\033\\\[m"
+    return "\033\\\[${fg};${bg};${intensity};${reverse}m${str}\033\\\[m"
 }
 
 # gdb_get_bp_addr num
index 7ee2043f0f88602f64943e5083be2566b1f33692..85747b2beaa4ff8969e334ad0e20042c6df2a171 100644 (file)
@@ -10265,10 +10265,11 @@ proc with_override { name override body } {
 # Run BODY after setting the TERM environment variable to 'ansi', and
 # unsetting the NO_COLOR environment variable.
 proc with_ansi_styling_terminal { body } {
-    save_vars { ::env(TERM) ::env(NO_COLOR) } {
+    save_vars { ::env(TERM) ::env(NO_COLOR) ::env(COLORTERM) } {
        # Set environment variables to allow styling.
        setenv TERM ansi
        unset -nocomplain ::env(NO_COLOR)
+       unset -nocomplain ::env(COLORTERM)
 
        set code [catch {uplevel 1 $body} result]
     }
index f855afe3f15baa8dceeeab8d5f374e8e38561152..1b4740684d2ea6571c458cfa3e30dc84f31e3342 100644 (file)
--- a/gdb/top.c
+++ b/gdb/top.c
@@ -2129,6 +2129,17 @@ show_startup_quiet (struct ui_file *file, int from_tty,
              value);
 }
 
+static void
+init_colorsupport_var ()
+{
+  const std::vector<color_space> &cs = colorsupport ();
+  std::string s;
+  for (color_space c : cs)
+    s.append (s.empty () ? "" : ",").append (color_space_name (c));
+  struct internalvar *colorsupport_var = create_internalvar ("_colorsupport");
+  set_internalvar_string (colorsupport_var, s.c_str ());
+}
+
 static void
 init_main (void)
 {
@@ -2333,6 +2344,9 @@ gdb_init ()
      during startup.  */
   set_language (language_c);
   expected_language = current_language;        /* Don't warn about the change.  */
+
+  /* Create $_colorsupport convenience variable.  */
+  init_colorsupport_var ();
 }
 
 void _initialize_top ();
index 952102e30c3266e636194221ab4eedbf7cde4e26..f0a8e81d6371d3a1b3a532b6dd57b0affbbacbe5 100644 (file)
@@ -17,6 +17,7 @@
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 #include "ui-style.h"
+#include "gdb_curses.h"
 #include "gdbsupport/gdb_regex.h"
 
 /* A regular expression that is used for matching ANSI terminal escape
@@ -46,119 +47,250 @@ static const char ansi_regex_text[] =
 
 static regex_t ansi_regex;
 
-/* This maps bright colors to RGB triples.  The index is the bright
-   color index, starting with bright black.  The values come from
-   xterm.  */
-
-static const uint8_t bright_colors[][3] = {
-  { 127, 127, 127 },           /* Black.  */
-  { 255, 0, 0 },               /* Red.  */
-  { 0, 255, 0 },               /* Green.  */
-  { 255, 255, 0 },             /* Yellow.  */
-  { 92, 92, 255 },             /* Blue.  */
-  { 255, 0, 255 },             /* Magenta.  */
-  { 0, 255, 255 },             /* Cyan.  */
-  { 255, 255, 255 }            /* White.  */
+/* This maps 8-color palette to RGB triples.  The values come from
+   plain linux terminal.  */
+
+static const uint8_t palette_8colors[][3] = {
+  { 1, 1, 1 },                 /* Black.  */
+  { 222, 56, 43 },             /* Red.  */
+  { 57, 181, 74 },             /* Green.  */
+  { 255, 199, 6 },             /* Yellow.  */
+  { 0, 111, 184 },             /* Blue.  */
+  { 118, 38, 113 },            /* Magenta.  */
+  { 44, 181, 233 },            /* Cyan.  */
+  { 204, 204, 204 },           /* White.  */
+};
+
+/* This maps 16-color palette to RGB triples.  The values come from xterm.  */
+
+static const uint8_t palette_16colors[][3] = {
+  { 0, 0, 0 },                 /* Black.  */
+  { 205, 0, 0 },               /* Red.  */
+  { 0, 205, 0 },               /* Green.  */
+  { 205, 205, 0 },             /* Yellow.  */
+  { 0, 0, 238 },               /* Blue.  */
+  { 205, 0, 205 },             /* Magenta.  */
+  { 0, 205, 205 },             /* Cyan.  */
+  { 229, 229, 229 },           /* White.  */
+  { 127, 127, 127 },           /* Bright Black.  */
+  { 255, 0, 0 },               /* Bright Red.  */
+  { 0, 255, 0 },               /* Bright Green.  */
+  { 255, 255, 0 },             /* Bright Yellow.  */
+  { 92, 92, 255 },             /* Bright Blue.  */
+  { 255, 0, 255 },             /* Bright Magenta.  */
+  { 0, 255, 255 },             /* Bright Cyan.  */
+  { 255, 255, 255 }            /* Bright White.  */
 };
 
 /* See ui-style.h.  */
+/* Must correspond to ui_file_style::basic_color.  */
+const std::vector<const char *> ui_file_style::basic_color_enums = {
+  "none",
+  "black",
+  "red",
+  "green",
+  "yellow",
+  "blue",
+  "magenta",
+  "cyan",
+  "white",
+  nullptr
+};
 
-bool
+/* Returns text representation of a basic COLOR.  */
+
+static const char *
+basic_color_name (int color)
+{
+  int pos = color - ui_file_style::NONE;
+  if (0 <= pos && pos < ui_file_style::basic_color_enums.size ())
+    if (const char *s = ui_file_style::basic_color_enums[pos])
+      return s;
+  error (_("Basic color %d has no name."), color);
+}
+
+/* See ui-style.h.  */
+
+void
 ui_file_style::color::append_ansi (bool is_fg, std::string *str) const
 {
-  if (m_simple)
-    {
-      if (m_value >= BLACK && m_value <= WHITE)
-       str->append (std::to_string (m_value + (is_fg ? 30 : 40)));
-      else if (m_value > WHITE && m_value <= WHITE + 8)
-       str->append (std::to_string (m_value - WHITE + (is_fg ? 90 : 100)));
-      else if (m_value != -1)
-       {
-         str->append (is_fg ? "38;5;" : "48;5;");
-         str->append (std::to_string (m_value));
-       }
-      else
-       return false;
-    }
-  else
+  if (m_color_space == color_space::MONOCHROME)
+    str->append (is_fg ? "39" : "49");
+  else if (is_basic ())
+    str->append (std::to_string (m_value + (is_fg ? 30 : 40)));
+  else if (m_color_space == color_space::AIXTERM_16COLOR)
+    str->append (std::to_string (m_value - WHITE - 1 + (is_fg ? 90 : 100)));
+  else if (m_color_space == color_space::XTERM_256COLOR)
+    str->append (is_fg ? "38;5;" : "48;5;").append (std::to_string (m_value));
+  else if (m_color_space == color_space::RGB_24BIT)
     {
+      // See ISO/IEC 8613-6 (or ITU T.416) 13.1.8 Select Graphic Rendition (SGR)
       str->append (is_fg ? "38;2;" : "48;2;");
       str->append (std::to_string (m_red)
                   + ";" + std::to_string (m_green)
                   + ";" + std::to_string (m_blue));
     }
-  return true;
+  else
+    gdb_assert_not_reached ("no valid ansi representation of the color");
 }
 
 /* See ui-style.h.  */
+std::string
+ui_file_style::color::to_ansi (bool is_fg) const
+{
+  std::string s = "\033[";
+  append_ansi (is_fg, &s);
+  s.push_back ('m');
+  return s;
+}
 
-void
-ui_file_style::color::get_rgb (uint8_t *rgb) const
+/* See ui-style.h.  */
+
+std::string
+ui_file_style::color::to_string () const
 {
-  if (m_simple)
+  if (m_color_space == color_space::RGB_24BIT)
     {
-      /* Can't call this for a basic color or NONE -- those will end
-        up in the assert below.  */
-      if (m_value >= 8 && m_value <= 15)
-       memcpy (rgb, bright_colors[m_value - 8], 3 * sizeof (uint8_t));
-      else if (m_value >= 16 && m_value <= 231)
-       {
-         int value = m_value;
-         value -= 16;
-         /* This obscure formula seems to be what terminals actually
-            do.  */
-         int component = value / 36;
-         rgb[0] = component == 0 ? 0 : (55 + component * 40);
-         value %= 36;
-         component = value / 6;
-         rgb[1] = component == 0 ? 0 : (55 + component * 40);
-         value %= 6;
-         rgb[2] = value == 0 ? 0 : (55 + value * 40);
-       }
-      else if (m_value >= 232)
-       {
-         uint8_t v = (m_value - 232) * 10 + 8;
-         rgb[0] = v;
-         rgb[1] = v;
-         rgb[2] = v;
-       }
-      else
-       gdb_assert_not_reached ("get_rgb called on invalid color");
+      char s[64];
+      snprintf (s, sizeof s, "#%02X%02X%02X", m_red, m_green, m_blue);
+      return s;
     }
+  else if (is_none () || is_basic ())
+    return basic_color_name (m_value);
   else
+    return std::to_string (get_value ());
+}
+
+/* See ui-style.h.  */
+
+void
+ui_file_style::color::get_rgb (uint8_t *rgb) const
+{
+  if (m_color_space == color_space::RGB_24BIT)
     {
       rgb[0] = m_red;
       rgb[1] = m_green;
       rgb[2] = m_blue;
     }
+  else if (m_color_space == color_space::ANSI_8COLOR
+          && 0 <= m_value && m_value <= 7)
+    memcpy (rgb, palette_8colors[m_value], 3 * sizeof (uint8_t));
+  else if (m_color_space == color_space::AIXTERM_16COLOR
+          && 0 <= m_value && m_value <= 15)
+    memcpy (rgb, palette_16colors[m_value], 3 * sizeof (uint8_t));
+  else if (m_color_space != color_space::XTERM_256COLOR)
+    gdb_assert_not_reached ("get_rgb called on invalid color");
+  else if (0 <= m_value && m_value <= 15)
+    memcpy (rgb, palette_16colors[m_value], 3 * sizeof (uint8_t));
+  else if (m_value >= 16 && m_value <= 231)
+    {
+      int value = m_value;
+      value -= 16;
+      /* This obscure formula seems to be what terminals actually
+        do.  */
+      int component = value / 36;
+      rgb[0] = component == 0 ? 0 : (55 + component * 40);
+      value %= 36;
+      component = value / 6;
+      rgb[1] = component == 0 ? 0 : (55 + component * 40);
+      value %= 6;
+      rgb[2] = value == 0 ? 0 : (55 + value * 40);
+    }
+  else if (232 <= m_value && m_value <= 255)
+    {
+      uint8_t v = (m_value - 232) * 10 + 8;
+      rgb[0] = v;
+      rgb[1] = v;
+      rgb[2] = v;
+    }
+  else
+    gdb_assert_not_reached ("get_rgb called on invalid color");
 }
 
 /* See ui-style.h.  */
 
-std::string
-ui_file_style::to_ansi () const
+ui_file_style::color
+ui_file_style::color::approximate (const std::vector<color_space> &spaces) const
 {
-  std::string result ("\033[");
-  bool need_semi = m_foreground.append_ansi (true, &result);
-  if (!m_background.is_none ())
+  if (spaces.empty () || is_none ())
+    return NONE;
+
+  color_space target_space = color_space::MONOCHROME;
+  for (color_space sp : spaces)
+    if (sp == m_color_space)
+      return *this;
+    else if (sp > target_space)
+      target_space = sp;
+
+  if (target_space == color_space::RGB_24BIT)
     {
-      if (need_semi)
-       result.push_back (';');
-      m_background.append_ansi (false, &result);
-      need_semi = true;
+      uint8_t rgb[3];
+      get_rgb (rgb);
+      return color (rgb[0], rgb[1], rgb[2]);
+    }
+
+  int target_size = 0;
+  switch (target_space)
+    {
+    case color_space::ANSI_8COLOR:
+      target_size = 8;
+      break;
+    case color_space::AIXTERM_16COLOR:
+      target_size = 16;
+      break;
+    case color_space::XTERM_256COLOR:
+      target_size = 256;
+      break;
     }
-  if (m_intensity != NORMAL)
+
+  if (is_simple() && m_value < target_size)
+    return color (target_space, m_value);
+
+  color result = NONE;
+  int best_distance = std::numeric_limits<int>::max ();
+  uint8_t rgb[3];
+  get_rgb (rgb);
+
+  for (int i = 0; i < target_size; ++i)
     {
-      if (need_semi)
-       result.push_back (';');
-      result.append (std::to_string (m_intensity));
-      need_semi = true;
+      uint8_t c_rgb[3];
+      color c (target_space, i);
+      c.get_rgb (c_rgb);
+      int d_red = std::abs (rgb[0] - c_rgb[0]);
+      int d_green = std::abs (rgb[1] - c_rgb[1]);
+      int d_blue = std::abs (rgb[2] - c_rgb[2]);
+      int dist = d_red * d_red + d_green * d_green + d_blue * d_blue;
+      if (dist < best_distance)
+       {
+         best_distance = dist;
+         result = c;
+       }
     }
-  if (m_reverse)
+
+  return result;
+}
+
+/* See ui-style.h.  */
+
+std::string
+ui_file_style::to_ansi () const
+{
+  std::string result ("\033[");
+  if (!is_default ())
     {
-      if (need_semi)
-       result.push_back (';');
-      result.push_back ('7');
+      m_foreground.append_ansi (true, &result);
+      result.push_back (';');
+      m_background.append_ansi (false, &result);
+      result.push_back (';');
+      if (m_intensity == NORMAL)
+       result.append ("22");
+      else
+       result.append (std::to_string (m_intensity));
+      result.push_back (';');
+      if (m_reverse)
+       result.push_back ('7');
+      else
+       result.append ("27");
     }
   result.push_back ('m');
   return result;
@@ -315,9 +447,11 @@ ui_file_style::parse (const char *buf, size_t *n_read)
            case 35:
            case 36:
            case 37:
+             m_foreground = color (value - 30);
+             break;
              /* Note: not 38.  */
            case 39:
-             m_foreground = color (value - 30);
+             m_foreground = NONE;
              break;
 
            case 40:
@@ -328,9 +462,11 @@ ui_file_style::parse (const char *buf, size_t *n_read)
            case 45:
            case 46:
            case 47:
+             m_background = color (value - 40);
+             break;
              /* Note: not 48.  */
            case 49:
-             m_background = color (value - 40);
+             m_background = NONE;
              break;
 
            case 90:
@@ -412,3 +548,60 @@ _initialize_ui_style ()
      error.  */
   gdb_assert (code == 0);
 }
+
+/* See ui-style.h.  */
+
+const std::vector<color_space> &
+colorsupport ()
+{
+  static const std::vector<color_space> value = []
+    {
+      std::vector<color_space> result = {color_space::MONOCHROME};
+
+      int colors = tgetnum ("Co");
+      if (colors >= 8)
+       result.push_back (color_space::ANSI_8COLOR);
+      if (colors >= 16)
+       result.push_back (color_space::AIXTERM_16COLOR);
+      if (colors >= 256)
+       result.push_back (color_space::XTERM_256COLOR);
+
+      const char *colorterm = getenv ("COLORTERM");
+      if (colorterm != nullptr && (!strcmp (colorterm, "truecolor")
+         || !strcmp (colorterm, "24bit")))
+       result.push_back (color_space::RGB_24BIT);
+
+      return result;
+    } ();
+  return value;
+}
+
+const char *
+color_space_name (color_space c)
+{
+  switch (c)
+    {
+    case color_space::MONOCHROME: return "monochrome";
+    case color_space::ANSI_8COLOR: return "ansi_8color";
+    case color_space::AIXTERM_16COLOR: return "aixterm_16color";
+    case color_space::XTERM_256COLOR: return "xterm_256color";
+    case color_space::RGB_24BIT: return "rgb_24bit";
+    }
+  gdb_assert_not_reached ("color_space_name called on invalid color");
+}
+
+bool
+color_space_safe_cast (color_space *result, long c)
+{
+  switch (static_cast<color_space>(c))
+    {
+    case color_space::MONOCHROME:
+    case color_space::ANSI_8COLOR:
+    case color_space::AIXTERM_16COLOR:
+    case color_space::XTERM_256COLOR:
+    case color_space::RGB_24BIT:
+      *result = static_cast<color_space>(c);
+      return true;
+    }
+  return false;
+}
index 53ac2897c7879241639ed8c45afe9aa179fefc8a..d814588254143149e5b24a45f3a3cdd8a24c91da 100644 (file)
 #ifndef GDB_UI_STYLE_H
 #define GDB_UI_STYLE_H
 
+/* One of the color spaces that usually supported by terminals.  */
+enum class color_space
+{
+  /* one default terminal color  */
+  MONOCHROME,
+
+  /* foreground colors \e[30m ... \e[37m,
+     background colors \e[40m ... \e[47m  */
+  ANSI_8COLOR,
+
+  /* foreground colors \e[30m ... \e[37m, \e[90m ... \e[97m
+     background colors \e[40m ... \e[47m, \e[100m ... \e107m  */
+  AIXTERM_16COLOR,
+
+  /* foreground colors \e[38;5;0m ... \e[38;5;255m
+     background colors \e[48;5;0m ... \e[48;5;255m  */
+  XTERM_256COLOR,
+
+  /* foreground colors \e[38;2;0;0;0m ... \e[38;2;255;255;255m
+     background colors \e[48;2;0;0;0m ... \e[48;2;255;255;255m  */
+  RGB_24BIT
+};
+
+/* Color spaces supported by terminal.  */
+extern const std::vector<color_space> & colorsupport ();
+
+/* Textual representation of C.  */
+extern const char * color_space_name (color_space c);
+
+/* Cast C to RESULT and return true if it's value is valid; false otherwise.  */
+extern bool color_space_safe_cast (color_space *result, long c);
+
 /* Styles that can be applied to a ui_file.  */
 struct ui_file_style
 {
@@ -43,20 +75,61 @@ struct ui_file_style
   public:
 
     color (basic_color c)
-      : m_simple (true),
+      : m_color_space (c == NONE ? color_space::MONOCHROME
+                                : color_space::ANSI_8COLOR),
        m_value (c)
     {
     }
 
     color (int c)
-      : m_simple (true),
+      : m_value (c)
+    {
+      if (c < -1 || c > 255)
+       error (_("Palette color index %d is out of range."), c);
+      if (c == -1)
+       m_color_space = color_space::MONOCHROME;
+      else if (c <= 7)
+       m_color_space = color_space::ANSI_8COLOR;
+      else if (c <= 15)
+       m_color_space = color_space::AIXTERM_16COLOR;
+      else
+       m_color_space = color_space::XTERM_256COLOR;
+    }
+
+    color (color_space cs, int c)
+      : m_color_space (cs),
        m_value (c)
     {
-      gdb_assert (c >= -1 && c <= 255);
+      if (c < -1 || c > 255)
+       error (_("Palette color index %d is out of range."), c);
+
+      std::pair<int, int> range;
+      switch (cs)
+       {
+       case color_space::MONOCHROME:
+         range = {-1, -1};
+         break;
+       case color_space::ANSI_8COLOR:
+         range = {0, 7};
+         break;
+       case color_space::AIXTERM_16COLOR:
+         range = {0, 15};
+         break;
+       case color_space::XTERM_256COLOR:
+         range = {0, 255};
+         break;
+       default:
+         error (_("Color space %d is incompatible with indexed colors."),
+                static_cast<int> (cs));
+       }
+
+      if (c < range.first || c > range.second)
+       error (_("Color %d is out of range [%d, %d] of color space %d."),
+              c, range.first, range.second, static_cast<int> (cs));
     }
 
     color (uint8_t r, uint8_t g, uint8_t b)
-      : m_simple (false),
+      : m_color_space (color_space::RGB_24BIT),
        m_red (r),
        m_green (g),
        m_blue (b)
@@ -65,19 +138,24 @@ struct ui_file_style
 
     bool operator== (const color &other) const
     {
-      if (m_simple != other.m_simple)
+      if (m_color_space != other.m_color_space)
        return false;
-      if (m_simple)
+      if (is_simple ())
        return m_value == other.m_value;
       return (m_red == other.m_red && m_green == other.m_green
              && m_blue == other.m_blue);
     }
 
+    bool operator!= (const color &other) const
+    {
+      return ! (*this == other);
+    }
+
     bool operator< (const color &other) const
     {
-      if (m_simple != other.m_simple)
-       return m_simple < other.m_simple;
-      if (m_simple)
+      if (m_color_space != other.m_color_space)
+       return m_color_space < other.m_color_space;
+      if (is_simple ())
        return m_value < other.m_value;
       if (m_red < other.m_red)
        return true;
@@ -91,23 +169,54 @@ struct ui_file_style
       return false;
     }
 
+    color_space colorspace () const
+    {
+      return m_color_space;
+    }
+
     /* Return true if this is the "NONE" color, false otherwise.  */
     bool is_none () const
     {
-      return m_simple && m_value == NONE;
+      return m_color_space == color_space::MONOCHROME && m_value == NONE;
     }
 
     /* Return true if this is one of the basic colors, false
        otherwise.  */
     bool is_basic () const
     {
-      return m_simple && m_value >= BLACK && m_value <= WHITE;
+      if (m_color_space == color_space::ANSI_8COLOR
+         || m_color_space == color_space::AIXTERM_16COLOR)
+       return BLACK <= m_value && m_value <= WHITE;
+      else
+       return false;
+    }
+
+    /* Return true if this is one of the colors, stored as int, false
+       otherwise.  */
+    bool is_simple () const
+    {
+      return m_color_space != color_space::RGB_24BIT;
+    }
+
+    /* Return true if this is one of the indexed colors, false
+       otherwise.  */
+    bool is_indexed () const
+    {
+      return m_color_space != color_space::RGB_24BIT
+            && m_color_space != color_space::MONOCHROME;
+    }
+
+    /* Return true if this is one of the direct colors (RGB, CMY, CMYK), false
+       otherwise.  */
+    bool is_direct () const
+    {
+      return m_color_space == color_space::RGB_24BIT;
     }
 
-    /* Return the value of a basic color.  */
+    /* Return the value of a simple color.  */
     int get_value () const
     {
-      gdb_assert (is_basic ());
+      gdb_assert (is_simple ());
       return m_value;
     }
 
@@ -118,14 +227,23 @@ struct ui_file_style
 
     /* Append the ANSI terminal escape sequence for this color to STR.
        IS_FG indicates whether this is a foreground or background
-       color.  Returns true if any characters were written; returns
-       false otherwise (which can only happen for the "NONE"
-       color).  */
-    bool append_ansi (bool is_fg, std::string *str) const;
+       color.  */
+    void append_ansi (bool is_fg, std::string *str) const;
+
+    /* Return the ANSI escape sequence for this color.
+       IS_FG indicates whether this is a foreground or background color.  */
+    std::string to_ansi (bool is_fg) const;
+
+    /* Returns text representation of this object.
+       It is "none", name of a basic color, number or a #RRGGBB hex triplet.  */
+    std::string to_string () const;
+
+    /* Approximates THIS color by closest one from SPACES.  */
+    color approximate (const std::vector<color_space> &spaces) const;
 
   private:
 
-    bool m_simple;
+    color_space m_color_space;
     union
     {
       int m_value;
@@ -235,6 +353,9 @@ struct ui_file_style
     return this;
   }
 
+  /* nullptr-terminated list of names corresponding to enum basic_color.  */
+  static const std::vector<const char *> basic_color_enums;
+
 private:
 
   color m_foreground = NONE;
index 4dc3c842c4707bfcc6f31eecaee98b29f7ac03d5..72bc878582a7f439ba02b463a009628484b587ee 100644 (file)
@@ -58,7 +58,7 @@ run_tests ()
   SELF_CHECK (style.get_background ().is_none ());
   SELF_CHECK (style.get_intensity () == ui_file_style::NORMAL);
   SELF_CHECK (style.is_reverse ());
-  SELF_CHECK (style.to_ansi () == "\033[7m");
+  SELF_CHECK (style.to_ansi () == "\033[39;49;22;7m");
 
   style = ui_file_style ();
   SELF_CHECK (style.parse ("\033[32;1m", &n_read));
@@ -68,7 +68,7 @@ run_tests ()
   SELF_CHECK (style.get_background ().is_none ());
   SELF_CHECK (style.get_intensity () == ui_file_style::BOLD);
   SELF_CHECK (!style.is_reverse ());
-  SELF_CHECK (style.to_ansi () == "\033[32;1m");
+  SELF_CHECK (style.to_ansi () == "\033[32;49;1;27m");
 
   style = ui_file_style ();
   SELF_CHECK (style.parse ("\033[38;5;112;48;5;249m", &n_read));
@@ -81,7 +81,7 @@ run_tests ()
   CHECK_RGB (0xb2, 0xb2, 0xb2);
   SELF_CHECK (style.get_intensity () == ui_file_style::NORMAL);
   SELF_CHECK (!style.is_reverse ());
-  SELF_CHECK (style.to_ansi () == "\033[38;5;112;48;5;249m");
+  SELF_CHECK (style.to_ansi () == "\033[38;5;112;48;5;249;22;27m");
 
   style = ui_file_style ();
   SELF_CHECK (style.parse ("\033[38;2;83;84;85;48;2;0;1;254;2;7m", &n_read));