public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH] Add an option with a color type.
@ 2022-08-15 21:15 Andrei Pikas
  2022-08-17 17:40 ` Andrew Burgess
  2022-08-17 17:57 ` [PATCH] " Eli Zaretskii
  0 siblings, 2 replies; 13+ messages in thread
From: Andrei Pikas @ 2022-08-15 21:15 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrei Pikas

Colors can be specified as name of one of the basic colors "none", "black",
"white", etc., as a number up to 255, or as RGB hexadecimal tripplet #RRGGBB.
---
 gdb/NEWS                         |   3 +
 gdb/cli/cli-cmds.c               |   8 ++
 gdb/cli/cli-decode.c             | 200 +++++++++++++++++++++++++++++++
 gdb/cli/cli-decode.h             |  30 +++++
 gdb/cli/cli-option.c             |  42 +++++++
 gdb/cli/cli-option.h             |  21 ++++
 gdb/cli/cli-setshow.c            |  53 ++++++++
 gdb/cli/cli-setshow.h            |   6 +
 gdb/cli/cli-style.c              |  49 ++------
 gdb/cli/cli-style.h              |   4 +-
 gdb/command.h                    |  26 +++-
 gdb/guile/scm-param.c            |  69 ++++++++++-
 gdb/python/py-param.c            |  63 +++++++++-
 gdb/python/python.c              |  18 +++
 gdb/testsuite/gdb.base/style.exp |  15 +++
 gdb/ui-style.h                   |  16 ++-
 16 files changed, 575 insertions(+), 48 deletions(-)

diff --git a/gdb/NEWS b/gdb/NEWS
index d2efe2a0a58..5234d4f8501 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -55,6 +55,9 @@
   Python Pygments is still used.  For supported targets, libopcodes
   styling is used by default.
 
+  "set style" commands now supports numeric format for basic colors
+  from -1 to 255 and #RRGGBB format for truecolor.
+
 * New commands
 
 maintenance set ignore-prologue-end-flag on|off
diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c
index 18fb6e6d869..5039c665b16 100644
--- a/gdb/cli/cli-cmds.c
+++ b/gdb/cli/cli-cmds.c
@@ -2275,6 +2275,13 @@ value_from_setting (const setting &var, struct gdbarch *gdbarch)
 	  return value_cstring ("", 1,
 				builtin_type (gdbarch)->builtin_char);
       }
+    case var_color:
+      {
+	char s[64];
+	print_color_str (s, sizeof s, var.get<ui_file_style::color> ());
+	size_t len = strlen (s);
+	return value_cstring (s, len, builtin_type (gdbarch)->builtin_char);
+      }
     default:
       gdb_assert_not_reached ("bad var_type");
     }
@@ -2324,6 +2331,7 @@ str_value_from_setting (const setting &var, struct gdbarch *gdbarch)
     case var_auto_boolean:
     case var_uinteger:
     case var_zuinteger:
+    case var_color:
       {
 	std::string cmd_val = get_setshow_command_value_string (var);
 
diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
index fde554c7e6c..1e36ae84123 100644
--- a/gdb/cli/cli-decode.c
+++ b/gdb/cli/cli-decode.c
@@ -670,6 +670,112 @@ add_setshow_enum_cmd (const char *name, command_class theclass,
   return cmds;
 }
 
+/* See cli-decode.h.  */
+/* Must correspond to ui_file_style::basic_color.  */
+const std::vector<const char *> basic_color_enums = {
+  "none",
+  "black",
+  "red",
+  "green",
+  "yellow",
+  "blue",
+  "magenta",
+  "cyan",
+  "white",
+  nullptr
+};
+
+/* See cli-decode.h.  */
+
+const char * basic_color_name (int color)
+{
+  int pos = color - ui_file_style::NONE;
+  if (0 <= pos && pos < basic_color_enums.size ())
+    if (const char *s = basic_color_enums[pos])
+      return s;
+  error (_("Basic color %d has no name."), pos);
+}
+
+/* See cli-decode.h.  */
+
+void
+complete_on_color (completion_tracker &tracker,
+		   const char *text, const char *word)
+{
+  complete_on_enum (tracker, 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 readline 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 };
 
@@ -2524,3 +2630,97 @@ 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 NULL if no argument, or the
+     text of the argument. */
+
+  if (args == nullptr || *args == nullptr || **args == '\0')
+    {
+      std::string msg;
+
+      for (size_t i = 0; basic_color_enums[i]; ++i)
+	msg.append ("\"").append (basic_color_enums[i]).append ("\", ");
+
+      error (_("Requires an argument. Valid arguments are %s integer 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 (size_t i = 0; basic_color_enums[i]; ++i)
+    if (strncmp (*args, basic_color_enums[i], len) == 0)
+      {
+	if (basic_color_enums[i][len] == '\0')
+	  {
+	    match = static_cast<ui_file_style::basic_color> (i - 1);
+	    nmatches = 1;
+	    break; /* Exact match.  */
+	  }
+	else
+	  {
+	    match = static_cast<ui_file_style::basic_color> (i - 1);
+	    nmatches++;
+	  }
+      }
+
+  if (nmatches == 1)
+    {
+      *args += len;
+      return ui_file_style::color (match);
+    }
+
+  if (nmatches > 1)
+    error (_("Ambiguous item \"%.*s\"."), (int) len, *args);
+
+  if (**args != '#')
+    {
+      char *end = nullptr;
+      long num = strtol (*args, &end, 0);
+      if (end - *args != len)
+	error (_("Expected integer at: %s"), *args);
+      if (num < -1 || num > 255)
+	error (_("integer %s out of range"), plongest (num));
+
+      *args = end;
+      return ui_file_style::color (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;
+}
diff --git a/gdb/cli/cli-decode.h b/gdb/cli/cli-decode.h
index 18db8822af3..1f2ef80c7e9 100644
--- a/gdb/cli/cli-decode.h
+++ b/gdb/cli/cli-decode.h
@@ -306,6 +306,36 @@ extern const char * const boolean_enums[];
 /* The enums of auto-boolean commands.  */
 extern const char * const auto_boolean_enums[];
 
+/* The enums of color commands with names of the basic colors that
+   can be handled by ANSI terminals.
+   Corresponds to ui_file_style::basic_color and starts with name for
+   ui_file_style::NONE.  */
+extern const std::vector<const char *> basic_color_enums;
+
+/* Returns text representation of COLOR.  */
+extern const char * basic_color_name (int color);
+
+/* 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".  */
+
+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 -1 to 255, or RGB hex triplet #RRGGBB.
+   */
+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.  */
+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.  */
 
diff --git a/gdb/cli/cli-option.c b/gdb/cli/cli-option.c
index b1794ad4b17..bd6556312db 100644
--- a/gdb/cli/cli-option.c
+++ b/gdb/cli/cli-option.c
@@ -46,6 +46,9 @@ union option_value
 
   /* For var_string options.  This is malloc-allocated.  */
   std::string *string;
+
+  /* For var_color options.  */
+  ui_file_style::color color = ui_file_style::NONE;
 };
 
 /* Holds an options definition and its value.  */
@@ -424,6 +427,33 @@ 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;
+	val.color = parse_cli_var_color (args);
+	return option_def_and_value {*match, match_ctx, val};
+      }
     case var_string:
       {
 	if (check_for_argument (args, "--"))
@@ -601,6 +631,10 @@ save_option_value_in_ctx (gdb::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:
       *ov->option.var_address.string (ov->option, ov->ctx)
 	= std::move (*ov->value->string);
@@ -677,6 +711,14 @@ get_val_type_str (const option_def &opt, std::string &buffer)
 	  }
 	return buffer.c_str ();
       }
+    case var_color:
+      {
+	buffer = "";
+	for (size_t i = 0; basic_color_enums[i]; ++i)
+	  buffer.append (basic_color_enums[i]).append ("|");
+	buffer += "NUMBER|#RRGGBB";
+	return buffer.c_str ();
+      }
     case var_string:
       return "STRING";
     default:
diff --git a/gdb/cli/cli-option.h b/gdb/cli/cli-option.h
index 26a8da3a5a4..d30c7024324 100644
--- a/gdb/cli/cli-option.h
+++ b/gdb/cli/cli-option.h
@@ -87,6 +87,7 @@ struct option_def
       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;
 
@@ -282,6 +283,26 @@ struct string_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
diff --git a/gdb/cli/cli-setshow.c b/gdb/cli/cli-setshow.c
index 139ebaf8323..5e4d0355ec5 100644
--- a/gdb/cli/cli-setshow.c
+++ b/gdb/cli/cli-setshow.c
@@ -297,6 +297,34 @@ parse_cli_var_enum (const char **args, const char *const *enums)
   return match;
 }
 
+/* See cli-setshow.h.  */
+
+bool
+print_color_str (char *s, size_t buf_size, const ui_file_style::color &color)
+{
+  if (!buf_size)
+    return false;
+
+  int n;
+  if (!color.is_simple ())
+    {
+      uint8_t rgb[3];
+      color.get_rgb (rgb);
+      n = snprintf (s, buf_size, "#%02X%02X%02X", rgb[0], rgb[1], rgb[2]);
+    }
+  else if (color.is_none () || color.is_basic ())
+    n = snprintf (s, buf_size, "%s", basic_color_name (color.get_value()));
+  else
+    n = snprintf (s, buf_size, "%d", color.get_value ());
+
+  if (n >= 0 && n < buf_size)
+    return true;
+
+  n = std::clamp<ptrdiff_t> (n, 0, buf_size - 1);
+  s[n] = 0;
+  return true;
+}
+
 /* Do a "set" command.  ARG is NULL if no argument, or the
    text of the argument, and FROM_TTY is nonzero if this command is
    being entered directly by the user (i.e. these are just like any
@@ -454,6 +482,12 @@ 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);
+	option_changed = c->var->set<ui_file_style::color> (color);
+      }
+      break;
     case var_zuinteger_unlimited:
       option_changed = c->var->set<int>
 	(parse_cli_var_zuinteger_unlimited (&arg, true));
@@ -535,6 +569,17 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c)
 	  gdb::observers::command_param_changed.notify
 	    (name, c->var->get<const char *> ());
 	  break;
+	case var_color:
+	  {
+	    char s[64];
+	    const ui_file_style::color &color
+	      = c->var->get<ui_file_style::color> ();
+	    print_color_str (s, sizeof s, color);
+
+	    gdb::observers::command_param_changed.notify
+	      (name, s);
+	  }
+	  break;
 	case var_boolean:
 	  {
 	    const char *opt = c->var->get<bool> () ? "on" : "off";
@@ -602,6 +647,14 @@ get_setshow_command_value_string (const setting &var)
 	  stb.puts (value);
       }
       break;
+    case var_color:
+      {
+	char s[64];
+	const ui_file_style::color &value = var.get<ui_file_style::color> ();
+	print_color_str (s, sizeof s, value);
+	stb.puts (s);
+      }
+      break;
     case var_boolean:
       stb.puts (var.get<bool> () ? "on" : "off");
       break;
diff --git a/gdb/cli/cli-setshow.h b/gdb/cli/cli-setshow.h
index 3e5d105eab7..3f50cf25263 100644
--- a/gdb/cli/cli-setshow.h
+++ b/gdb/cli/cli-setshow.h
@@ -52,6 +52,12 @@ extern int parse_cli_var_zuinteger_unlimited (const char **arg,
 const char *parse_cli_var_enum (const char **args,
 				const char *const *enums);
 
+/* Prints text representation of COLOR into S up to BUF_SIZE characters.
+   Returns true if value have been written without truncation,
+   false otherwise.  */
+bool
+print_color_str (char *s, size_t buf_size, const ui_file_style::color &color);
+
 extern void do_set_command (const char *arg, int from_tty,
 			    struct cmd_list_element *c);
 extern void do_show_command (const char *arg, int from_tty,
diff --git a/gdb/cli/cli-style.c b/gdb/cli/cli-style.c
index abf685561fa..3123a391763 100644
--- a/gdb/cli/cli-style.c
+++ b/gdb/cli/cli-style.c
@@ -43,20 +43,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[] = {
@@ -132,8 +118,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])
 {
 }
@@ -144,32 +130,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)
@@ -181,7 +152,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.  */
@@ -254,9 +225,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,
@@ -266,9 +236,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,
diff --git a/gdb/cli/cli-style.h b/gdb/cli/cli-style.h
index 4090cf01333..4db86b00359 100644
--- a/gdb/cli/cli-style.h
+++ b/gdb/cli/cli-style.h
@@ -67,9 +67,9 @@ class cli_style_option
   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;
 
diff --git a/gdb/command.h b/gdb/command.h
index d901da3c8cb..691c3999d78 100644
--- a/gdb/command.h
+++ b/gdb/command.h
@@ -119,7 +119,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
   };
 
 /* Return true if a setting of type VAR_TYPE is backed with type T.
@@ -179,6 +181,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>
@@ -665,6 +675,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,
diff --git a/gdb/guile/scm-param.c b/gdb/guile/scm-param.c
index 54c8c27301a..c7ef3ddd03b 100644
--- a/gdb/guile/scm-param.c
+++ b/gdb/guile/scm-param.c
@@ -48,6 +48,9 @@ union pascm_variable
 
   /* Hold a string, for enums.  */
   const char *cstringval;
+
+  /* Hold a color.  */
+  ui_file_style::color color;
 };
 
 /* A GDB parameter.
@@ -193,7 +196,7 @@ pascm_make_param_smob (void)
     scm_gc_malloc (sizeof (param_smob), param_smob_name);
   SCM p_scm;
 
-  memset (p_smob, 0, sizeof (*p_smob));
+  memset (reinterpret_cast<void *> (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;
@@ -466,6 +469,13 @@ add_setshow_generic (enum var_types param_type, enum command_class cmd_class,
 				       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");
     }
@@ -545,6 +555,7 @@ static const scheme_integer_constant parameter_types[] =
   { "PARAM_OPTIONAL_FILENAME", var_optional_filename },
   { "PARAM_FILENAME", var_filename },
   { "PARAM_ENUM", var_enum },
+  { "PARAM_COLOR", var_color },
 
   END_INTEGER_CONSTANTS
 };
@@ -611,6 +622,24 @@ 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:
+      {
+	const ui_file_style::color &color = var.get<ui_file_style::color> ();
+	if (color.is_none () || color.is_basic ())
+	  {
+	    const char *str = basic_color_name (color.get_value ());
+	    return gdbscm_scm_from_host_string (str, strlen (str));
+	  }
+	else if (color.is_simple ())
+	  return scm_from_int (color.get_value ());
+
+	uint8_t rgb[3];
+	color.get_rgb (rgb);
+	char s[64];
+	int n = snprintf (s, sizeof s, "#%02X%02X%02X", rgb[0], rgb[1], rgb[2]);
+	return gdbscm_scm_from_host_string (s, n);
+      }
+
     case var_boolean:
       {
 	if (var.get<bool> ())
@@ -716,6 +745,44 @@ pascm_set_param_value_x (param_smob *p_smob,
 	break;
       }
 
+    case var_color:
+      SCM_ASSERT_TYPE (scm_is_string (value) || scm_is_integer (value),
+		       value, arg_pos, func_name,
+		       _("string or integer"));
+
+      if (scm_is_integer (value))
+	{
+	  int i = scm_to_int (value);
+	  if (i < -1 || i > 255)
+	    gdbscm_out_of_range_error (func_name, arg_pos, value,
+				       _("must be in range from -1 to 255"));
+	  var.set<ui_file_style::color> (i);
+	}
+      else
+	{
+	  SCM exception;
+
+	  gdb::unique_xmalloc_ptr<char> string
+	    = gdbscm_scm_to_host_string (value, nullptr, &exception);
+	  if (string == nullptr)
+	    gdbscm_throw (exception);
+
+	  gdbscm_gdb_exception exc {};
+	  try
+	    {
+	      ui_file_style::color color = parse_var_color (string.get ());
+	      var.set<ui_file_style::color> (color);
+	    }
+	  catch (const gdb_exception &except)
+	    {
+	      exc = unpack (except);
+	    }
+
+	  GDBSCM_HANDLE_GDB_EXCEPTION (exc);
+	}
+
+      break;
+
     case var_boolean:
       SCM_ASSERT_TYPE (gdbscm_is_bool (value), value, arg_pos, func_name,
 		       _("boolean"));
diff --git a/gdb/python/py-param.c b/gdb/python/py-param.c
index 5d509ba4658..03db582a3ee 100644
--- a/gdb/python/py-param.c
+++ b/gdb/python/py-param.c
@@ -46,6 +46,7 @@ static struct {
   { "PARAM_ZUINTEGER", var_zuinteger },
   { "PARAM_ZUINTEGER_UNLIMITED", var_zuinteger_unlimited },
   { "PARAM_ENUM", var_enum },
+  { "PARAM_COLOR", var_color },
   { NULL, 0 }
 };
 
@@ -70,6 +71,9 @@ union parmpy_variable
 
   /* Hold a string, for enums.  */
   const char *cstringval;
+
+  /* Hold a color.  */
+  ui_file_style::color color = ui_file_style::NONE;
 };
 
 /* A GDB parameter.  */
@@ -108,6 +112,8 @@ make_setting (parmpy_object *s)
     return setting (s->type, s->value.stringval);
   else if (var_type_uses<const char *> (s->type))
     return setting (s->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");
 }
@@ -199,6 +205,49 @@ set_parameter_value (parmpy_object *self, PyObject *value)
 	break;
       }
 
+    case var_color:
+      {
+	if (gdbpy_is_string (value))
+	  {
+	    gdb::unique_xmalloc_ptr<char>
+	      str (python_string_to_host_string (value));
+	    if (str == NULL)
+	      return -1;
+	    try
+	      {
+		self->value.color = parse_var_color (str.get());
+	      }
+	    catch (const gdb_exception &except)
+	      {
+		gdbpy_convert_exception (except);
+		return -1;
+	      }
+	  }
+	else if (PyLong_Check (value))
+	  {
+	    long l;
+	    if (! gdb_py_int_as_long (value, &l))
+	      return -1;
+	    if (l < -1 || l > 255)
+	      {
+		PyErr_SetString (PyExc_RuntimeError,
+				 _("Range exceeded."));
+		return -1;
+	      }
+	    self->value.color = ui_file_style::color (l);
+	  }
+	else if (value == Py_None)
+	  self->value.color = ui_file_style::NONE;
+	else
+	  {
+	    PyErr_SetString (PyExc_RuntimeError,
+			     _("color arguments must be a string, an integer "
+			       "or None."));
+	    return -1;
+	  }
+      }
+      break;
+
     case var_boolean:
       if (! PyBool_Check (value))
 	{
@@ -637,6 +686,15 @@ add_setshow_generic (int parmclass, enum command_class cmdclass,
 				       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.");
     }
@@ -758,7 +816,8 @@ parmpy_init (PyObject *self, PyObject *args, PyObject *kwds)
       && parmclass != var_string && parmclass != var_string_noescape
       && parmclass != var_optional_filename && parmclass != var_filename
       && parmclass != var_zinteger && parmclass != var_zuinteger
-      && parmclass != var_zuinteger_unlimited && parmclass != var_enum)
+      && parmclass != var_zuinteger_unlimited && parmclass != var_enum
+      && parmclass != var_color)
     {
       PyErr_SetString (PyExc_RuntimeError,
 		       _("Invalid parameter class argument."));
@@ -779,7 +838,7 @@ parmpy_init (PyObject *self, PyObject *args, PyObject *kwds)
   else
     obj->enumeration = NULL;
   obj->type = (enum var_types) parmclass;
-  memset (&obj->value, 0, sizeof (obj->value));
+  memset (reinterpret_cast<void *> (&obj->value), 0, sizeof (obj->value));
 
   if (var_type_uses<std::string> (obj->type))
     obj->value.stringval = new std::string;
diff --git a/gdb/python/python.c b/gdb/python/python.c
index c7d4157b70c..b1caea5665d 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -484,6 +484,24 @@ 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> ();
+	if (color.is_none () || color.is_basic ())
+	  return host_string_to_python_string
+	    (basic_color_name (color.get_value ())).release ();
+	else if (color.is_simple ())
+	  return gdb_py_object_from_longest (color.get_value ()).release ();
+	else
+	  {
+	    uint8_t rgb[3];
+	    color.get_rgb (rgb);
+	    char s[64];
+	    snprintf (s, sizeof s, "#%02X%02X%02X", rgb[0], rgb[1], rgb[2]);
+	    return host_string_to_python_string (s).release ();
+	  }
+      }
+
     case var_boolean:
       {
 	if (var.get<bool> ())
diff --git a/gdb/testsuite/gdb.base/style.exp b/gdb/testsuite/gdb.base/style.exp
index 2242c5bf743..f6e71c89109 100644
--- a/gdb/testsuite/gdb.base/style.exp
+++ b/gdb/testsuite/gdb.base/style.exp
@@ -298,6 +298,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
+	    # tripplet. 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;255m.*\".*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;255m.*\".*version.*\".*style.*\033\\\[m foreground color is: #FED210" \
+		"Version's TrueColor foreground style"
+	}
     }
 }
 
diff --git a/gdb/ui-style.h b/gdb/ui-style.h
index fe1b2af611d..a1ba0ff413d 100644
--- a/gdb/ui-style.h
+++ b/gdb/ui-style.h
@@ -73,6 +73,11 @@ struct ui_file_style
 	      && 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)
@@ -104,10 +109,17 @@ struct ui_file_style
       return m_simple && m_value >= BLACK && m_value <= WHITE;
     }
 
-    /* Return the value of a basic color.  */
+    /* Return true if this is one of the simple colors, false
+       otherwise.  */
+    bool is_simple () const
+    {
+      return m_simple;
+    }
+
+    /* Return the value of a simple color.  */
     int get_value () const
     {
-      gdb_assert (is_basic ());
+      gdb_assert (is_simple ());
       return m_value;
     }
 
-- 
2.34.1


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

* Re: [PATCH] Add an option with a color type.
  2022-08-15 21:15 [PATCH] Add an option with a color type Andrei Pikas
@ 2022-08-17 17:40 ` Andrew Burgess
  2022-08-17 18:46   ` Andrei Pikas
  2022-08-20 12:19   ` [PATCH v2] " Andrei Pikas
  2022-08-17 17:57 ` [PATCH] " Eli Zaretskii
  1 sibling, 2 replies; 13+ messages in thread
From: Andrew Burgess @ 2022-08-17 17:40 UTC (permalink / raw)
  To: Andrei Pikas, gdb-patches; +Cc: Andrei Pikas


Thanks for working on this.  I only had time for a quick skim through,
but I had a few thoughts.

Andrei Pikas <gdb@mail.api.win> writes:

> Colors can be specified as name of one of the basic colors "none", "black",
> "white", etc., as a number up to 255, or as RGB hexadecimal tripplet #RRGGBB.
> ---
>  gdb/NEWS                         |   3 +
>  gdb/cli/cli-cmds.c               |   8 ++
>  gdb/cli/cli-decode.c             | 200 +++++++++++++++++++++++++++++++
>  gdb/cli/cli-decode.h             |  30 +++++
>  gdb/cli/cli-option.c             |  42 +++++++
>  gdb/cli/cli-option.h             |  21 ++++
>  gdb/cli/cli-setshow.c            |  53 ++++++++
>  gdb/cli/cli-setshow.h            |   6 +
>  gdb/cli/cli-style.c              |  49 ++------
>  gdb/cli/cli-style.h              |   4 +-
>  gdb/command.h                    |  26 +++-
>  gdb/guile/scm-param.c            |  69 ++++++++++-
>  gdb/python/py-param.c            |  63 +++++++++-
>  gdb/python/python.c              |  18 +++
>  gdb/testsuite/gdb.base/style.exp |  15 +++
>  gdb/ui-style.h                   |  16 ++-

I notice there's no gdb/doc/* changes here, that'll definitely need
filling in.

>  16 files changed, 575 insertions(+), 48 deletions(-)
>
> diff --git a/gdb/NEWS b/gdb/NEWS
> index d2efe2a0a58..5234d4f8501 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -55,6 +55,9 @@
>    Python Pygments is still used.  For supported targets, libopcodes
>    styling is used by default.
>  
> +  "set style" commands now supports numeric format for basic colors
> +  from -1 to 255 and #RRGGBB format for truecolor.
> +

You should also mention the new Python and Guile API features as
separate entries in the 'Python API' list and (might need adding) the
'Guile API' list.

I am also not convinced the support for -1 as an option is needed.  If I
understand correctly, if a user does:

  set style filename foreground -1

this is the same as:

  set style filename foreground none

which is already supported.  So, I think we should just support 0->255
for the 256-color mode, and point users towards 'none' if they don't
want styling.

>  * New commands
>  
>  maintenance set ignore-prologue-end-flag on|off
> diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c
> index 18fb6e6d869..5039c665b16 100644
> --- a/gdb/cli/cli-cmds.c
> +++ b/gdb/cli/cli-cmds.c
> @@ -2275,6 +2275,13 @@ value_from_setting (const setting &var, struct gdbarch *gdbarch)
>  	  return value_cstring ("", 1,
>  				builtin_type (gdbarch)->builtin_char);
>        }
> +    case var_color:
> +      {
> +	char s[64];
> +	print_color_str (s, sizeof s, var.get<ui_file_style::color> ());
> +	size_t len = strlen (s);

I think we should follow the pattern we have for many other types, in
adding a ui_file_style::color::to_string () function that returns a
std::string.

> +	return value_cstring (s, len, builtin_type (gdbarch)->builtin_char);
> +      }
>      default:
>        gdb_assert_not_reached ("bad var_type");
>      }
> @@ -2324,6 +2331,7 @@ str_value_from_setting (const setting &var, struct gdbarch *gdbarch)
>      case var_auto_boolean:
>      case var_uinteger:
>      case var_zuinteger:
> +    case var_color:
>        {
>  	std::string cmd_val = get_setshow_command_value_string (var);
>  
> diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
> index fde554c7e6c..1e36ae84123 100644
> --- a/gdb/cli/cli-decode.c
> +++ b/gdb/cli/cli-decode.c
> @@ -670,6 +670,112 @@ add_setshow_enum_cmd (const char *name, command_class theclass,
>    return cmds;
>  }
>  
> +/* See cli-decode.h.  */
> +/* Must correspond to ui_file_style::basic_color.  */
> +const std::vector<const char *> basic_color_enums = {
> +  "none",
> +  "black",
> +  "red",
> +  "green",
> +  "yellow",
> +  "blue",
> +  "magenta",
> +  "cyan",
> +  "white",
> +  nullptr
> +};
> +
> +/* See cli-decode.h.  */
> +
> +const char * basic_color_name (int color)

The GNU style places the function name at the start of a line, like:

const char *
basic_color_name (int color)
{
  ...
}

> +{
> +  int pos = color - ui_file_style::NONE;
> +  if (0 <= pos && pos < basic_color_enums.size ())
> +    if (const char *s = basic_color_enums[pos])
> +      return s;
> +  error (_("Basic color %d has no name."), pos);
> +}
> +
> +/* See cli-decode.h.  */
> +
> +void
> +complete_on_color (completion_tracker &tracker,
> +		   const char *text, const char *word)
> +{
> +  complete_on_enum (tracker, 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 readline 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 };
>  
> @@ -2524,3 +2630,97 @@ 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 NULL if no argument, or the
> +     text of the argument. */
> +
> +  if (args == nullptr || *args == nullptr || **args == '\0')
> +    {
> +      std::string msg;
> +
> +      for (size_t i = 0; basic_color_enums[i]; ++i)
> +	msg.append ("\"").append (basic_color_enums[i]).append ("\", ");
> +
> +      error (_("Requires an argument. Valid arguments are %s integer 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 (size_t i = 0; basic_color_enums[i]; ++i)
> +    if (strncmp (*args, basic_color_enums[i], len) == 0)
> +      {
> +	if (basic_color_enums[i][len] == '\0')
> +	  {
> +	    match = static_cast<ui_file_style::basic_color> (i - 1);
> +	    nmatches = 1;
> +	    break; /* Exact match.  */
> +	  }
> +	else
> +	  {
> +	    match = static_cast<ui_file_style::basic_color> (i - 1);
> +	    nmatches++;
> +	  }
> +      }
> +
> +  if (nmatches == 1)
> +    {
> +      *args += len;
> +      return ui_file_style::color (match);
> +    }
> +
> +  if (nmatches > 1)
> +    error (_("Ambiguous item \"%.*s\"."), (int) len, *args);
> +
> +  if (**args != '#')
> +    {
> +      char *end = nullptr;
> +      long num = strtol (*args, &end, 0);
> +      if (end - *args != len)
> +	error (_("Expected integer at: %s"), *args);
> +      if (num < -1 || num > 255)
> +	error (_("integer %s out of range"), plongest (num));
> +
> +      *args = end;
> +      return ui_file_style::color (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)

Newline after ::color.

> +{
> +  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;
> +}
> diff --git a/gdb/cli/cli-decode.h b/gdb/cli/cli-decode.h
> index 18db8822af3..1f2ef80c7e9 100644
> --- a/gdb/cli/cli-decode.h
> +++ b/gdb/cli/cli-decode.h
> @@ -306,6 +306,36 @@ extern const char * const boolean_enums[];
>  /* The enums of auto-boolean commands.  */
>  extern const char * const auto_boolean_enums[];
>  
> +/* The enums of color commands with names of the basic colors that
> +   can be handled by ANSI terminals.
> +   Corresponds to ui_file_style::basic_color and starts with name for
> +   ui_file_style::NONE.  */
> +extern const std::vector<const char *> basic_color_enums;
> +
> +/* Returns text representation of COLOR.  */
> +extern const char * basic_color_name (int color);
> +
> +/* 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".  */
> +
> +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 -1 to 255, or RGB hex triplet #RRGGBB.
> +   */
> +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.  */
> +ui_file_style::color parse_var_color (const char *arg);

I think these should be marked extern.

> +
>  /* Verify whether a given cmd_list_element is a user-defined command.
>     Return 1 if it is user-defined.  Return 0 otherwise.  */
>  
> diff --git a/gdb/cli/cli-option.c b/gdb/cli/cli-option.c
> index b1794ad4b17..bd6556312db 100644
> --- a/gdb/cli/cli-option.c
> +++ b/gdb/cli/cli-option.c
> @@ -46,6 +46,9 @@ union option_value
>  
>    /* For var_string options.  This is malloc-allocated.  */
>    std::string *string;
> +
> +  /* For var_color options.  */
> +  ui_file_style::color color = ui_file_style::NONE;
>  };
>  
>  /* Holds an options definition and its value.  */
> @@ -424,6 +427,33 @@ 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;
> +	val.color = parse_cli_var_color (args);
> +	return option_def_and_value {*match, match_ctx, val};
> +      }
>      case var_string:
>        {
>  	if (check_for_argument (args, "--"))
> @@ -601,6 +631,10 @@ save_option_value_in_ctx (gdb::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:
>        *ov->option.var_address.string (ov->option, ov->ctx)
>  	= std::move (*ov->value->string);
> @@ -677,6 +711,14 @@ get_val_type_str (const option_def &opt, std::string &buffer)
>  	  }
>  	return buffer.c_str ();
>        }
> +    case var_color:
> +      {
> +	buffer = "";
> +	for (size_t i = 0; basic_color_enums[i]; ++i)
> +	  buffer.append (basic_color_enums[i]).append ("|");
> +	buffer += "NUMBER|#RRGGBB";
> +	return buffer.c_str ();
> +      }
>      case var_string:
>        return "STRING";
>      default:
> diff --git a/gdb/cli/cli-option.h b/gdb/cli/cli-option.h
> index 26a8da3a5a4..d30c7024324 100644
> --- a/gdb/cli/cli-option.h
> +++ b/gdb/cli/cli-option.h
> @@ -87,6 +87,7 @@ struct option_def
>        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;
>  
> @@ -282,6 +283,26 @@ struct string_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
> diff --git a/gdb/cli/cli-setshow.c b/gdb/cli/cli-setshow.c
> index 139ebaf8323..5e4d0355ec5 100644
> --- a/gdb/cli/cli-setshow.c
> +++ b/gdb/cli/cli-setshow.c
> @@ -297,6 +297,34 @@ parse_cli_var_enum (const char **args, const char *const *enums)
>    return match;
>  }
>  
> +/* See cli-setshow.h.  */
> +
> +bool
> +print_color_str (char *s, size_t buf_size, const ui_file_style::color &color)
> +{
> +  if (!buf_size)
> +    return false;
> +
> +  int n;
> +  if (!color.is_simple ())
> +    {
> +      uint8_t rgb[3];
> +      color.get_rgb (rgb);
> +      n = snprintf (s, buf_size, "#%02X%02X%02X", rgb[0], rgb[1], rgb[2]);
> +    }
> +  else if (color.is_none () || color.is_basic ())
> +    n = snprintf (s, buf_size, "%s", basic_color_name (color.get_value()));
> +  else
> +    n = snprintf (s, buf_size, "%d", color.get_value ());
> +
> +  if (n >= 0 && n < buf_size)
> +    return true;
> +
> +  n = std::clamp<ptrdiff_t> (n, 0, buf_size - 1);
> +  s[n] = 0;
> +  return true;
> +}
> +
>  /* Do a "set" command.  ARG is NULL if no argument, or the
>     text of the argument, and FROM_TTY is nonzero if this command is
>     being entered directly by the user (i.e. these are just like any
> @@ -454,6 +482,12 @@ 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);
> +	option_changed = c->var->set<ui_file_style::color> (color);
> +      }
> +      break;
>      case var_zuinteger_unlimited:
>        option_changed = c->var->set<int>
>  	(parse_cli_var_zuinteger_unlimited (&arg, true));
> @@ -535,6 +569,17 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c)
>  	  gdb::observers::command_param_changed.notify
>  	    (name, c->var->get<const char *> ());
>  	  break;
> +	case var_color:
> +	  {
> +	    char s[64];
> +	    const ui_file_style::color &color
> +	      = c->var->get<ui_file_style::color> ();
> +	    print_color_str (s, sizeof s, color);
> +
> +	    gdb::observers::command_param_changed.notify
> +	      (name, s);
> +	  }
> +	  break;
>  	case var_boolean:
>  	  {
>  	    const char *opt = c->var->get<bool> () ? "on" : "off";
> @@ -602,6 +647,14 @@ get_setshow_command_value_string (const setting &var)
>  	  stb.puts (value);
>        }
>        break;
> +    case var_color:
> +      {
> +	char s[64];
> +	const ui_file_style::color &value = var.get<ui_file_style::color> ();
> +	print_color_str (s, sizeof s, value);
> +	stb.puts (s);

Could make use of ui_file_style::color::to_string () here.

> +      }
> +      break;
>      case var_boolean:
>        stb.puts (var.get<bool> () ? "on" : "off");
>        break;
> diff --git a/gdb/cli/cli-setshow.h b/gdb/cli/cli-setshow.h
> index 3e5d105eab7..3f50cf25263 100644
> --- a/gdb/cli/cli-setshow.h
> +++ b/gdb/cli/cli-setshow.h
> @@ -52,6 +52,12 @@ extern int parse_cli_var_zuinteger_unlimited (const char **arg,
>  const char *parse_cli_var_enum (const char **args,
>  				const char *const *enums);
>  
> +/* Prints text representation of COLOR into S up to BUF_SIZE characters.
> +   Returns true if value have been written without truncation,
> +   false otherwise.  */
> +bool
> +print_color_str (char *s, size_t buf_size, const ui_file_style::color &color);
> +

This should be extern, and not place the function name on a new line.
But I think this should go completely and the body move into a new
to_string function as I mention above.

>  extern void do_set_command (const char *arg, int from_tty,
>  			    struct cmd_list_element *c);
>  extern void do_show_command (const char *arg, int from_tty,
> diff --git a/gdb/cli/cli-style.c b/gdb/cli/cli-style.c
> index abf685561fa..3123a391763 100644
> --- a/gdb/cli/cli-style.c
> +++ b/gdb/cli/cli-style.c
> @@ -43,20 +43,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[] = {
> @@ -132,8 +118,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])
>  {
>  }
> @@ -144,32 +130,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)
> @@ -181,7 +152,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.  */
> @@ -254,9 +225,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,
> @@ -266,9 +236,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,
> diff --git a/gdb/cli/cli-style.h b/gdb/cli/cli-style.h
> index 4090cf01333..4db86b00359 100644
> --- a/gdb/cli/cli-style.h
> +++ b/gdb/cli/cli-style.h
> @@ -67,9 +67,9 @@ class cli_style_option
>    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;
>  
> diff --git a/gdb/command.h b/gdb/command.h
> index d901da3c8cb..691c3999d78 100644
> --- a/gdb/command.h
> +++ b/gdb/command.h
> @@ -119,7 +119,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. */

Two spaces after a '.' in comments, including the final '.'.

> +    var_color
>    };
>  
>  /* Return true if a setting of type VAR_TYPE is backed with type T.
> @@ -179,6 +181,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>
> @@ -665,6 +675,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,
> diff --git a/gdb/guile/scm-param.c b/gdb/guile/scm-param.c
> index 54c8c27301a..c7ef3ddd03b 100644
> --- a/gdb/guile/scm-param.c
> +++ b/gdb/guile/scm-param.c
> @@ -48,6 +48,9 @@ union pascm_variable
>  
>    /* Hold a string, for enums.  */
>    const char *cstringval;
> +
> +  /* Hold a color.  */
> +  ui_file_style::color color;
>  };
>  
>  /* A GDB parameter.
> @@ -193,7 +196,7 @@ pascm_make_param_smob (void)
>      scm_gc_malloc (sizeof (param_smob), param_smob_name);
>    SCM p_scm;
>  
> -  memset (p_smob, 0, sizeof (*p_smob));
> +  memset (reinterpret_cast<void *> (p_smob), 0, sizeof (*p_smob));

This seems a bit random for this patch.  If this is needed then this
should probably be in a separate patch, with its own justification.

>    p_smob->cmd_class = no_class;
>    p_smob->type = var_boolean; /* ARI: var_boolean */
>    p_smob->set_func = SCM_BOOL_F;
> @@ -466,6 +469,13 @@ add_setshow_generic (enum var_types param_type, enum command_class cmd_class,
>  				       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");
>      }
> @@ -545,6 +555,7 @@ static const scheme_integer_constant parameter_types[] =
>    { "PARAM_OPTIONAL_FILENAME", var_optional_filename },
>    { "PARAM_FILENAME", var_filename },
>    { "PARAM_ENUM", var_enum },
> +  { "PARAM_COLOR", var_color },
>  
>    END_INTEGER_CONSTANTS
>  };
> @@ -611,6 +622,24 @@ 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:
> +      {
> +	const ui_file_style::color &color = var.get<ui_file_style::color> ();
> +	if (color.is_none () || color.is_basic ())
> +	  {
> +	    const char *str = basic_color_name (color.get_value ());
> +	    return gdbscm_scm_from_host_string (str, strlen (str));
> +	  }
> +	else if (color.is_simple ())
> +	  return scm_from_int (color.get_value ());
> +
> +	uint8_t rgb[3];
> +	color.get_rgb (rgb);
> +	char s[64];
> +	int n = snprintf (s, sizeof s, "#%02X%02X%02X", rgb[0], rgb[1], rgb[2]);
> +	return gdbscm_scm_from_host_string (s, n);
> +      }
> +
>      case var_boolean:
>        {
>  	if (var.get<bool> ())
> @@ -716,6 +745,44 @@ pascm_set_param_value_x (param_smob *p_smob,
>  	break;
>        }
>  
> +    case var_color:
> +      SCM_ASSERT_TYPE (scm_is_string (value) || scm_is_integer (value),
> +		       value, arg_pos, func_name,
> +		       _("string or integer"));
> +
> +      if (scm_is_integer (value))
> +	{
> +	  int i = scm_to_int (value);
> +	  if (i < -1 || i > 255)
> +	    gdbscm_out_of_range_error (func_name, arg_pos, value,
> +				       _("must be in range from -1 to 255"));
> +	  var.set<ui_file_style::color> (i);
> +	}
> +      else
> +	{
> +	  SCM exception;
> +
> +	  gdb::unique_xmalloc_ptr<char> string
> +	    = gdbscm_scm_to_host_string (value, nullptr, &exception);
> +	  if (string == nullptr)
> +	    gdbscm_throw (exception);
> +
> +	  gdbscm_gdb_exception exc {};
> +	  try
> +	    {
> +	      ui_file_style::color color = parse_var_color (string.get ());
> +	      var.set<ui_file_style::color> (color);
> +	    }
> +	  catch (const gdb_exception &except)
> +	    {
> +	      exc = unpack (except);
> +	    }
> +
> +	  GDBSCM_HANDLE_GDB_EXCEPTION (exc);
> +	}
> +
> +      break;
> +
>      case var_boolean:
>        SCM_ASSERT_TYPE (gdbscm_is_bool (value), value, arg_pos, func_name,
>  		       _("boolean"));
> diff --git a/gdb/python/py-param.c b/gdb/python/py-param.c
> index 5d509ba4658..03db582a3ee 100644
> --- a/gdb/python/py-param.c
> +++ b/gdb/python/py-param.c
> @@ -46,6 +46,7 @@ static struct {
>    { "PARAM_ZUINTEGER", var_zuinteger },
>    { "PARAM_ZUINTEGER_UNLIMITED", var_zuinteger_unlimited },
>    { "PARAM_ENUM", var_enum },
> +  { "PARAM_COLOR", var_color },
>    { NULL, 0 }
>  };
>  
> @@ -70,6 +71,9 @@ union parmpy_variable
>  
>    /* Hold a string, for enums.  */
>    const char *cstringval;
> +
> +  /* Hold a color.  */
> +  ui_file_style::color color = ui_file_style::NONE;
>  };
>  
>  /* A GDB parameter.  */
> @@ -108,6 +112,8 @@ make_setting (parmpy_object *s)
>      return setting (s->type, s->value.stringval);
>    else if (var_type_uses<const char *> (s->type))
>      return setting (s->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");
>  }
> @@ -199,6 +205,49 @@ set_parameter_value (parmpy_object *self, PyObject *value)
>  	break;
>        }
>  
> +    case var_color:
> +      {
> +	if (gdbpy_is_string (value))
> +	  {
> +	    gdb::unique_xmalloc_ptr<char>
> +	      str (python_string_to_host_string (value));
> +	    if (str == NULL)
> +	      return -1;
> +	    try
> +	      {
> +		self->value.color = parse_var_color (str.get());
> +	      }
> +	    catch (const gdb_exception &except)
> +	      {
> +		gdbpy_convert_exception (except);
> +		return -1;
> +	      }
> +	  }
> +	else if (PyLong_Check (value))
> +	  {
> +	    long l;
> +	    if (! gdb_py_int_as_long (value, &l))
> +	      return -1;
> +	    if (l < -1 || l > 255)
> +	      {
> +		PyErr_SetString (PyExc_RuntimeError,
> +				 _("Range exceeded."));
> +		return -1;
> +	      }
> +	    self->value.color = ui_file_style::color (l);
> +	  }
> +	else if (value == Py_None)
> +	  self->value.color = ui_file_style::NONE;
> +	else
> +	  {
> +	    PyErr_SetString (PyExc_RuntimeError,
> +			     _("color arguments must be a string, an integer "
> +			       "or None."));
> +	    return -1;
> +	  }
> +      }
> +      break;
> +
>      case var_boolean:
>        if (! PyBool_Check (value))
>  	{
> @@ -637,6 +686,15 @@ add_setshow_generic (int parmclass, enum command_class cmdclass,
>  				       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.");
>      }
> @@ -758,7 +816,8 @@ parmpy_init (PyObject *self, PyObject *args, PyObject *kwds)
>        && parmclass != var_string && parmclass != var_string_noescape
>        && parmclass != var_optional_filename && parmclass != var_filename
>        && parmclass != var_zinteger && parmclass != var_zuinteger
> -      && parmclass != var_zuinteger_unlimited && parmclass != var_enum)
> +      && parmclass != var_zuinteger_unlimited && parmclass != var_enum
> +      && parmclass != var_color)
>      {
>        PyErr_SetString (PyExc_RuntimeError,
>  		       _("Invalid parameter class argument."));
> @@ -779,7 +838,7 @@ parmpy_init (PyObject *self, PyObject *args, PyObject *kwds)
>    else
>      obj->enumeration = NULL;
>    obj->type = (enum var_types) parmclass;
> -  memset (&obj->value, 0, sizeof (obj->value));
> +  memset (reinterpret_cast<void *> (&obj->value), 0, sizeof (obj->value));
>  
>    if (var_type_uses<std::string> (obj->type))
>      obj->value.stringval = new std::string;
> diff --git a/gdb/python/python.c b/gdb/python/python.c
> index c7d4157b70c..b1caea5665d 100644
> --- a/gdb/python/python.c
> +++ b/gdb/python/python.c
> @@ -484,6 +484,24 @@ 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> ();
> +	if (color.is_none () || color.is_basic ())
> +	  return host_string_to_python_string
> +	    (basic_color_name (color.get_value ())).release ();
> +	else if (color.is_simple ())
> +	  return gdb_py_object_from_longest (color.get_value ()).release ();
> +	else
> +	  {
> +	    uint8_t rgb[3];
> +	    color.get_rgb (rgb);
> +	    char s[64];
> +	    snprintf (s, sizeof s, "#%02X%02X%02X", rgb[0], rgb[1], rgb[2]);
> +	    return host_string_to_python_string (s).release ();
> +	  }
> +      }
> +
>      case var_boolean:
>        {
>  	if (var.get<bool> ())
> diff --git a/gdb/testsuite/gdb.base/style.exp b/gdb/testsuite/gdb.base/style.exp
> index 2242c5bf743..f6e71c89109 100644
> --- a/gdb/testsuite/gdb.base/style.exp
> +++ b/gdb/testsuite/gdb.base/style.exp
> @@ -298,6 +298,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
> +	    # tripplet. 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;255m.*\".*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;255m.*\".*version.*\".*style.*\033\\\[m foreground color is: #FED210" \
> +		"Version's TrueColor foreground style"
> +	}
>      }
>  }
>  
> diff --git a/gdb/ui-style.h b/gdb/ui-style.h
> index fe1b2af611d..a1ba0ff413d 100644
> --- a/gdb/ui-style.h
> +++ b/gdb/ui-style.h
> @@ -73,6 +73,11 @@ struct ui_file_style
>  	      && 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)
> @@ -104,10 +109,17 @@ struct ui_file_style
>        return m_simple && m_value >= BLACK && m_value <= WHITE;
>      }
>  
> -    /* Return the value of a basic color.  */
> +    /* Return true if this is one of the simple colors, false
> +       otherwise.  */
> +    bool is_simple () const
> +    {
> +      return m_simple;
> +    }
> +
> +    /* Return the value of a simple color.  */
>      int get_value () const
>      {
> -      gdb_assert (is_basic ());
> +      gdb_assert (is_simple ());
>        return m_value;
>      }
>  
> -- 
> 2.34.1


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

* Re: [PATCH] Add an option with a color type.
  2022-08-15 21:15 [PATCH] Add an option with a color type Andrei Pikas
  2022-08-17 17:40 ` Andrew Burgess
@ 2022-08-17 17:57 ` Eli Zaretskii
  1 sibling, 0 replies; 13+ messages in thread
From: Eli Zaretskii @ 2022-08-17 17:57 UTC (permalink / raw)
  To: Andrei Pikas; +Cc: gdb-patches, gdb

> From: Andrei Pikas <gdb@mail.api.win>
> Date: Tue, 16 Aug 2022 00:15:34 +0300
> Cc: Andrei Pikas <gdb@mail.api.win>
> 
> Colors can be specified as name of one of the basic colors "none", "black",
> "white", etc., as a number up to 255, or as RGB hexadecimal tripplet #RRGGBB.
> ---
>  gdb/NEWS                         |   3 +
>  gdb/cli/cli-cmds.c               |   8 ++
>  gdb/cli/cli-decode.c             | 200 +++++++++++++++++++++++++++++++
>  gdb/cli/cli-decode.h             |  30 +++++
>  gdb/cli/cli-option.c             |  42 +++++++
>  gdb/cli/cli-option.h             |  21 ++++
>  gdb/cli/cli-setshow.c            |  53 ++++++++
>  gdb/cli/cli-setshow.h            |   6 +
>  gdb/cli/cli-style.c              |  49 ++------
>  gdb/cli/cli-style.h              |   4 +-
>  gdb/command.h                    |  26 +++-
>  gdb/guile/scm-param.c            |  69 ++++++++++-
>  gdb/python/py-param.c            |  63 +++++++++-
>  gdb/python/python.c              |  18 +++
>  gdb/testsuite/gdb.base/style.exp |  15 +++
>  gdb/ui-style.h                   |  16 ++-
>  16 files changed, 575 insertions(+), 48 deletions(-)
> 
> diff --git a/gdb/NEWS b/gdb/NEWS
> index d2efe2a0a58..5234d4f8501 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -55,6 +55,9 @@
>    Python Pygments is still used.  For supported targets, libopcodes
>    styling is used by default.
>  
> +  "set style" commands now supports numeric format for basic colors
> +  from -1 to 255 and #RRGGBB format for truecolor.
> +
>  * New commands

OK for the NEWS part.

Thanks.

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

* Re: [PATCH] Add an option with a color type.
  2022-08-17 17:40 ` Andrew Burgess
@ 2022-08-17 18:46   ` Andrei Pikas
  2022-08-20 12:19   ` [PATCH v2] " Andrei Pikas
  1 sibling, 0 replies; 13+ messages in thread
From: Andrei Pikas @ 2022-08-17 18:46 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches

 >>  /* A GDB parameter.
 >> @@ -193,7 +196,7 @@ pascm_make_param_smob (void)
 >>      scm_gc_malloc (sizeof (param_smob), param_smob_name);
 >>    SCM p_scm;
 >>
 >> -  memset (p_smob, 0, sizeof (*p_smob));
 >> +  memset (reinterpret_cast<void *> (p_smob), 0, sizeof (*p_smob));
 >
 > This seems a bit random for this patch.  If this is needed then this
 > should probably be in a separate patch, with its own justification.
 >

I've changed this because the memset from gdbsupport/posion.h forbids 
the use of param_smob* because it's no longer std::is_pod after I've 
added color into union pascm_variable.

I doubt it makes sense to move this reinterpret_cast to a separate patch 
without adding color.

May I delete this memset at all and replace it with scm_gc_calloc above 
in this same patch?



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

* [PATCH v2] Add an option with a color type.
  2022-08-17 17:40 ` Andrew Burgess
  2022-08-17 18:46   ` Andrei Pikas
@ 2022-08-20 12:19   ` Andrei Pikas
  2022-08-20 12:43     ` Eli Zaretskii
  1 sibling, 1 reply; 13+ messages in thread
From: Andrei Pikas @ 2022-08-20 12:19 UTC (permalink / raw)
  To: gdb-patches; +Cc: aburgess, Andrei Pikas

Colors can be specified as name of one of the basic colors "none", "black",
"white", etc., as a number up to 255, or as RGB hexadecimal tripplet #RRGGBB.
---
 gdb/NEWS                                  |  13 ++
 gdb/cli/cli-cmds.c                        |   7 +
 gdb/cli/cli-decode.c                      | 173 ++++++++++++++++++++++
 gdb/cli/cli-decode.h                      |  21 +++
 gdb/cli/cli-option.c                      |  42 ++++++
 gdb/cli/cli-option.h                      |  21 +++
 gdb/cli/cli-setshow.c                     |  20 +++
 gdb/cli/cli-style.c                       |  49 ++----
 gdb/cli/cli-style.h                       |   4 +-
 gdb/command.h                             |  26 +++-
 gdb/doc/gdb.texinfo                       |  18 ++-
 gdb/doc/guile.texi                        |   8 +
 gdb/doc/python.texi                       |  10 ++
 gdb/guile/scm-param.c                     |  68 ++++++++-
 gdb/python/py-param.c                     |  63 +++++++-
 gdb/python/python.c                       |  10 ++
 gdb/testsuite/gdb.base/style.exp          |  15 ++
 gdb/testsuite/gdb.guile/scm-parameter.exp |  26 ++++
 gdb/testsuite/gdb.python/py-parameter.exp |  46 ++++++
 gdb/ui-style.c                            |  44 ++++++
 gdb/ui-style.h                            |  23 ++-
 21 files changed, 650 insertions(+), 57 deletions(-)

diff --git a/gdb/NEWS b/gdb/NEWS
index d2efe2a0a58..c9d25890ed9 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -55,6 +55,9 @@
   Python Pygments is still used.  For supported targets, libopcodes
   styling is used by default.
 
+  "set style" commands now supports numeric format for basic colors
+  from 0 to 255 and #RRGGBB format for TrueColor.
+
 * New commands
 
 maintenance set ignore-prologue-end-flag on|off
@@ -170,6 +173,16 @@ GNU/Linux/LoongArch (gdbserver)	loongarch*-*-linux*
      can be used to request a shorter representation of a value, the
      way that 'set print frame-arguments scalars' does.
 
+  ** New constant gdb.PARAM_COLOR represents color type of a
+     gdb.Parameter.value.  Parameter's value is either an integer
+     from 0 to 255 or string with color name or #RRGGBB hex triplet.
+
+* Guile API
+
+  ** New constant PARAM_COLOR represents color type of a value
+     of a <gdb:parameter> object.  Parameter's value is either an integer
+     from 0 to 255 or string with color name or #RRGGBB hex triplet.
+
 * New features in the GDB remote stub, GDBserver
 
   ** GDBserver is now supported on LoongArch GNU/Linux.
diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c
index 18fb6e6d869..1a515b99761 100644
--- a/gdb/cli/cli-cmds.c
+++ b/gdb/cli/cli-cmds.c
@@ -2275,6 +2275,12 @@ value_from_setting (const setting &var, struct gdbarch *gdbarch)
 	  return value_cstring ("", 1,
 				builtin_type (gdbarch)->builtin_char);
       }
+    case var_color:
+      {
+	std::string s = var.get<ui_file_style::color> ().to_string ();
+	return value_cstring (s.c_str (), s.size (),
+			      builtin_type (gdbarch)->builtin_char);
+      }
     default:
       gdb_assert_not_reached ("bad var_type");
     }
@@ -2324,6 +2330,7 @@ str_value_from_setting (const setting &var, struct gdbarch *gdbarch)
     case var_auto_boolean:
     case var_uinteger:
     case var_zuinteger:
+    case var_color:
       {
 	std::string cmd_val = get_setshow_command_value_string (var);
 
diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
index fde554c7e6c..b0a4ceeedf6 100644
--- a/gdb/cli/cli-decode.c
+++ b/gdb/cli/cli-decode.c
@@ -24,6 +24,7 @@
 #include "cli/cli-cmds.h"
 #include "cli/cli-decode.h"
 #include "cli/cli-style.h"
+#include "cli/cli-utils.h"
 #include "gdbsupport/gdb_optional.h"
 
 /* Prototypes for local functions.  */
@@ -670,6 +671,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 };
 
@@ -2524,3 +2606,94 @@ 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 NULL 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 %s integer 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 (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;
+}
diff --git a/gdb/cli/cli-decode.h b/gdb/cli/cli-decode.h
index 18db8822af3..7f1233c87ad 100644
--- a/gdb/cli/cli-decode.h
+++ b/gdb/cli/cli-decode.h
@@ -306,6 +306,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.  */
 
diff --git a/gdb/cli/cli-option.c b/gdb/cli/cli-option.c
index b1794ad4b17..ac62c6700fa 100644
--- a/gdb/cli/cli-option.c
+++ b/gdb/cli/cli-option.c
@@ -46,6 +46,9 @@ union option_value
 
   /* For var_string options.  This is malloc-allocated.  */
   std::string *string;
+
+  /* For var_color options.  */
+  ui_file_style::color color = ui_file_style::NONE;
 };
 
 /* Holds an options definition and its value.  */
@@ -424,6 +427,33 @@ 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;
+	val.color = parse_cli_var_color (args);
+	return option_def_and_value {*match, match_ctx, val};
+      }
     case var_string:
       {
 	if (check_for_argument (args, "--"))
@@ -601,6 +631,10 @@ save_option_value_in_ctx (gdb::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:
       *ov->option.var_address.string (ov->option, ov->ctx)
 	= std::move (*ov->value->string);
@@ -677,6 +711,14 @@ get_val_type_str (const option_def &opt, std::string &buffer)
 	  }
 	return buffer.c_str ();
       }
+    case var_color:
+      {
+	buffer = "";
+	for (size_t i = 0; ui_file_style::basic_color_enums[i]; ++i)
+	  buffer.append (ui_file_style::basic_color_enums[i]).append ("|");
+	buffer += "NUMBER|#RRGGBB";
+	return buffer.c_str ();
+      }
     case var_string:
       return "STRING";
     default:
diff --git a/gdb/cli/cli-option.h b/gdb/cli/cli-option.h
index 26a8da3a5a4..d30c7024324 100644
--- a/gdb/cli/cli-option.h
+++ b/gdb/cli/cli-option.h
@@ -87,6 +87,7 @@ struct option_def
       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;
 
@@ -282,6 +283,26 @@ struct string_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
diff --git a/gdb/cli/cli-setshow.c b/gdb/cli/cli-setshow.c
index 139ebaf8323..4e8e5c7f0f8 100644
--- a/gdb/cli/cli-setshow.c
+++ b/gdb/cli/cli-setshow.c
@@ -454,6 +454,12 @@ 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);
+	option_changed = c->var->set<ui_file_style::color> (color);
+      }
+      break;
     case var_zuinteger_unlimited:
       option_changed = c->var->set<int>
 	(parse_cli_var_zuinteger_unlimited (&arg, true));
@@ -535,6 +541,14 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c)
 	  gdb::observers::command_param_changed.notify
 	    (name, c->var->get<const char *> ());
 	  break;
+	case var_color:
+	  {
+	    const ui_file_style::color &color
+	      = c->var->get<ui_file_style::color> ();
+	    gdb::observers::command_param_changed.notify
+	      (name, color.to_string ().c_str ());
+	  }
+	  break;
 	case var_boolean:
 	  {
 	    const char *opt = c->var->get<bool> () ? "on" : "off";
@@ -602,6 +616,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;
diff --git a/gdb/cli/cli-style.c b/gdb/cli/cli-style.c
index abf685561fa..3123a391763 100644
--- a/gdb/cli/cli-style.c
+++ b/gdb/cli/cli-style.c
@@ -43,20 +43,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[] = {
@@ -132,8 +118,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])
 {
 }
@@ -144,32 +130,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)
@@ -181,7 +152,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.  */
@@ -254,9 +225,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,
@@ -266,9 +236,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,
diff --git a/gdb/cli/cli-style.h b/gdb/cli/cli-style.h
index 4090cf01333..4db86b00359 100644
--- a/gdb/cli/cli-style.h
+++ b/gdb/cli/cli-style.h
@@ -67,9 +67,9 @@ class cli_style_option
   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;
 
diff --git a/gdb/command.h b/gdb/command.h
index d901da3c8cb..eb3b2cc20d6 100644
--- a/gdb/command.h
+++ b/gdb/command.h
@@ -119,7 +119,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
   };
 
 /* Return true if a setting of type VAR_TYPE is backed with type T.
@@ -179,6 +181,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>
@@ -665,6 +675,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,
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 382df00ee7d..8de18174809 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -26605,16 +26605,18 @@ 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 a name of a basic color,
+number from 0 to 255 or a hexadecimal RGB triplet in #RRGGBB format.  Valid
+color names 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}.
 
 @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 a name of a basic color,
+number from 0 to 255 or a hexadecimal RGB triplet in #RRGGBB format.  Valid
+color names 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}.
 
 @item set style filename intensity @var{value}
 Set the intensity to @var{value}.  Valid intensities are @samp{normal}
diff --git a/gdb/doc/guile.texi b/gdb/doc/guile.texi
index 63916eed181..36ffdfef00a 100644
--- a/gdb/doc/guile.texi
+++ b/gdb/doc/guile.texi
@@ -2169,6 +2169,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 color number.  String can be a hex RGB triplet in #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
diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index eeb847aeaa8..54733f56c9b 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -4651,6 +4651,16 @@ except the special value -1 should be interpreted to mean
 @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 either a string or an unsigned integer.  Integer from 0 to 255
+means color number.  String can be a hex RGB triplet in #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 table
 
 @node Functions In Python
diff --git a/gdb/guile/scm-param.c b/gdb/guile/scm-param.c
index 54c8c27301a..42820dd666d 100644
--- a/gdb/guile/scm-param.c
+++ b/gdb/guile/scm-param.c
@@ -48,6 +48,9 @@ union pascm_variable
 
   /* Hold a string, for enums.  */
   const char *cstringval;
+
+  /* Hold a color.  */
+  ui_file_style::color color;
 };
 
 /* A GDB parameter.
@@ -129,6 +132,8 @@ make_setting (param_smob *s)
     return setting (s->type, s->value.stringval);
   else if (var_type_uses<const char *> (s->type))
     return setting (s->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");
 }
@@ -190,10 +195,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;
@@ -466,6 +470,13 @@ add_setshow_generic (enum var_types param_type, enum command_class cmd_class,
 				       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");
     }
@@ -545,6 +556,7 @@ static const scheme_integer_constant parameter_types[] =
   { "PARAM_OPTIONAL_FILENAME", var_optional_filename },
   { "PARAM_FILENAME", var_filename },
   { "PARAM_ENUM", var_enum },
+  { "PARAM_COLOR", var_color },
 
   END_INTEGER_CONSTANTS
 };
@@ -611,6 +623,18 @@ 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:
+      {
+	const ui_file_style::color &color = var.get<ui_file_style::color> ();
+	if (color.is_none () || color.is_basic () || !color.is_simple ())
+	  {
+	    std::string s = color.to_string ();
+	    return gdbscm_scm_from_host_string (s.c_str (), s.size ());
+	  }
+	else
+	  return scm_from_int (color.get_value ());
+      }
+
     case var_boolean:
       {
 	if (var.get<bool> ())
@@ -716,6 +740,44 @@ pascm_set_param_value_x (param_smob *p_smob,
 	break;
       }
 
+    case var_color:
+      SCM_ASSERT_TYPE (scm_is_string (value) || scm_is_integer (value),
+		       value, arg_pos, func_name,
+		       _("string or integer"));
+
+      if (scm_is_integer (value))
+	{
+	  int i = scm_to_int (value);
+	  if (i < 0 || i > 255)
+	    gdbscm_out_of_range_error (func_name, arg_pos, value,
+				       _("must be in range from 0 to 255"));
+	  var.set<ui_file_style::color> (i);
+	}
+      else
+	{
+	  SCM exception;
+
+	  gdb::unique_xmalloc_ptr<char> string
+	    = gdbscm_scm_to_host_string (value, nullptr, &exception);
+	  if (string == nullptr)
+	    gdbscm_throw (exception);
+
+	  gdbscm_gdb_exception exc {};
+	  try
+	    {
+	      ui_file_style::color color = parse_var_color (string.get ());
+	      var.set<ui_file_style::color> (color);
+	    }
+	  catch (const gdb_exception &except)
+	    {
+	      exc = unpack (except);
+	    }
+
+	  GDBSCM_HANDLE_GDB_EXCEPTION (exc);
+	}
+
+      break;
+
     case var_boolean:
       SCM_ASSERT_TYPE (gdbscm_is_bool (value), value, arg_pos, func_name,
 		       _("boolean"));
@@ -961,6 +1023,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-param.c b/gdb/python/py-param.c
index 5d509ba4658..a2d720490c1 100644
--- a/gdb/python/py-param.c
+++ b/gdb/python/py-param.c
@@ -46,6 +46,7 @@ static struct {
   { "PARAM_ZUINTEGER", var_zuinteger },
   { "PARAM_ZUINTEGER_UNLIMITED", var_zuinteger_unlimited },
   { "PARAM_ENUM", var_enum },
+  { "PARAM_COLOR", var_color },
   { NULL, 0 }
 };
 
@@ -70,6 +71,9 @@ union parmpy_variable
 
   /* Hold a string, for enums.  */
   const char *cstringval;
+
+  /* Hold a color.  */
+  ui_file_style::color color;
 };
 
 /* A GDB parameter.  */
@@ -108,6 +112,8 @@ make_setting (parmpy_object *s)
     return setting (s->type, s->value.stringval);
   else if (var_type_uses<const char *> (s->type))
     return setting (s->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");
 }
@@ -199,6 +205,49 @@ set_parameter_value (parmpy_object *self, PyObject *value)
 	break;
       }
 
+    case var_color:
+      {
+	if (gdbpy_is_string (value))
+	  {
+	    gdb::unique_xmalloc_ptr<char>
+	      str (python_string_to_host_string (value));
+	    if (str == NULL)
+	      return -1;
+	    try
+	      {
+		self->value.color = parse_var_color (str.get());
+	      }
+	    catch (const gdb_exception &except)
+	      {
+		gdbpy_convert_exception (except);
+		return -1;
+	      }
+	  }
+	else if (PyLong_Check (value))
+	  {
+	    long l;
+	    if (! gdb_py_int_as_long (value, &l))
+	      return -1;
+	    if (l < 0 || l > 255)
+	      {
+		PyErr_SetString (PyExc_RuntimeError,
+				 _("Range exceeded."));
+		return -1;
+	      }
+	    self->value.color = ui_file_style::color (l);
+	  }
+	else if (value == Py_None)
+	  self->value.color = ui_file_style::NONE;
+	else
+	  {
+	    PyErr_SetString (PyExc_RuntimeError,
+			     _("color arguments must be a string, an integer "
+			       "or None."));
+	    return -1;
+	  }
+      }
+      break;
+
     case var_boolean:
       if (! PyBool_Check (value))
 	{
@@ -637,6 +686,15 @@ add_setshow_generic (int parmclass, enum command_class cmdclass,
 				       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.");
     }
@@ -758,7 +816,8 @@ parmpy_init (PyObject *self, PyObject *args, PyObject *kwds)
       && parmclass != var_string && parmclass != var_string_noescape
       && parmclass != var_optional_filename && parmclass != var_filename
       && parmclass != var_zinteger && parmclass != var_zuinteger
-      && parmclass != var_zuinteger_unlimited && parmclass != var_enum)
+      && parmclass != var_zuinteger_unlimited && parmclass != var_enum
+      && parmclass != var_color)
     {
       PyErr_SetString (PyExc_RuntimeError,
 		       _("Invalid parameter class argument."));
@@ -779,7 +838,7 @@ parmpy_init (PyObject *self, PyObject *args, PyObject *kwds)
   else
     obj->enumeration = NULL;
   obj->type = (enum var_types) parmclass;
-  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;
diff --git a/gdb/python/python.c b/gdb/python/python.c
index c7d4157b70c..141d495c2e2 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -484,6 +484,16 @@ 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> ();
+	if (color.is_none () || color.is_basic () || !color.is_simple ())
+	  return host_string_to_python_string
+	    (color.to_string ().c_str ()).release ();
+	else
+	  return gdb_py_object_from_longest (color.get_value ()).release ();
+      }
+
     case var_boolean:
       {
 	if (var.get<bool> ())
diff --git a/gdb/testsuite/gdb.base/style.exp b/gdb/testsuite/gdb.base/style.exp
index 2242c5bf743..82458193d8d 100644
--- a/gdb/testsuite/gdb.base/style.exp
+++ b/gdb/testsuite/gdb.base/style.exp
@@ -298,6 +298,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;255m.*\".*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;255m.*\".*version.*\".*style.*\033\\\[m foreground color is: #FED210" \
+		"Version's TrueColor foreground style"
+	}
     }
 }
 
diff --git a/gdb/testsuite/gdb.guile/scm-parameter.exp b/gdb/testsuite/gdb.guile/scm-parameter.exp
index cf6f2834373..8a9cbcab605 100644
--- a/gdb/testsuite/gdb.guile/scm-parameter.exp
+++ b/gdb/testsuite/gdb.guile/scm-parameter.exp
@@ -91,6 +91,32 @@ with_test_prefix "test-enum-param" {
     gdb_test "set print test-enum-param three" "Undefined item: \"three\".*" "set invalid enum parameter" 
 }
 
+# Test a color parameter.
+
+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 \"green\"))" "" \
+    "(register-parameter! test-color-param)" "" \
+    "end"
+
+with_test_prefix "test-color-param" {
+    gdb_test "guile (print (parameter-value test-color-param))" "green" "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"
+    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))" "255" "color parameter value (255)"
+    gdb_test "set print test-color-param 256" "integer 256 out of range.*" "set invalid color parameter"
+}
+
 # Test a file parameter.
 
 gdb_test_multiline "file gdb parameter" \
diff --git a/gdb/testsuite/gdb.python/py-parameter.exp b/gdb/testsuite/gdb.python/py-parameter.exp
index d6db6ac3bb1..0f269dd86a8 100644
--- a/gdb/testsuite/gdb.python/py-parameter.exp
+++ b/gdb/testsuite/gdb.python/py-parameter.exp
@@ -177,6 +177,51 @@ proc_with_prefix test_enum_parameter { } {
 	"Undefined item: \"three\".*" "set invalid enum parameter"
 }
 
+# Test an color parameter.
+proc_with_prefix test_color_parameter { } {
+    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 = \"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 = 254" \
+	"assign test_color_param.value to 254"
+    gdb_test "python print (repr (test_color_param.value))" "254" \
+	"test color parameter value is integer"
+    gdb_test_no_output "python test_color_param.value =  '#FED210'" \
+	"assign test_color_param.value to #FED210"
+    gdb_test "python print (repr (test_color_param.value))" "'#FED210'" \
+	"test color parameter value is string"
+    gdb_test "set print test-color-param 256" \
+	"integer 256 out of range.*" "set invalid color parameter"
+    gdb_test "python test_color_param.value = 256" \
+	"RuntimeError: Range exceeded.*" "set invalid color value"
+}
+
 # Test a file parameter.
 proc_with_prefix test_file_parameter { } {
     clean_restart
@@ -391,6 +436,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
diff --git a/gdb/ui-style.c b/gdb/ui-style.c
index f1a5b8c4101..167550b2c9a 100644
--- a/gdb/ui-style.c
+++ b/gdb/ui-style.c
@@ -62,6 +62,33 @@ static const uint8_t bright_colors[][3] = {
   { 255, 255, 255 }		/* 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
+};
+
+/* 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.  */
 
 bool
@@ -93,6 +120,23 @@ ui_file_style::color::append_ansi (bool is_fg, std::string *str) const
 
 /* See ui-style.h.  */
 
+std::string
+ui_file_style::color::to_string () const
+{
+  if (!m_simple)
+    {
+      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
 {
diff --git a/gdb/ui-style.h b/gdb/ui-style.h
index fe1b2af611d..f002facf706 100644
--- a/gdb/ui-style.h
+++ b/gdb/ui-style.h
@@ -73,6 +73,11 @@ struct ui_file_style
 	      && 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)
@@ -104,10 +109,17 @@ struct ui_file_style
       return m_simple && m_value >= BLACK && m_value <= WHITE;
     }
 
-    /* Return the value of a basic color.  */
+    /* Return true if this is one of the simple colors, false
+       otherwise.  */
+    bool is_simple () const
+    {
+      return m_simple;
+    }
+
+    /* Return the value of a simple color.  */
     int get_value () const
     {
-      gdb_assert (is_basic ());
+      gdb_assert (is_simple ());
       return m_value;
     }
 
@@ -123,6 +135,10 @@ struct ui_file_style
        color).  */
     bool append_ansi (bool is_fg, std::string *str) 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;
+
   private:
 
     bool m_simple;
@@ -235,6 +251,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;
-- 
2.34.1


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

* Re: [PATCH v2] Add an option with a color type.
  2022-08-20 12:19   ` [PATCH v2] " Andrei Pikas
@ 2022-08-20 12:43     ` Eli Zaretskii
  2022-08-21 20:09       ` Andrei Pikas
  2022-08-21 21:07       ` [PATCH v3 (documentation fixed)] " Andrei Pikas
  0 siblings, 2 replies; 13+ messages in thread
From: Eli Zaretskii @ 2022-08-20 12:43 UTC (permalink / raw)
  To: Andrei Pikas; +Cc: gdb-patches, gdb

> From: Andrei Pikas <gdb@mail.api.win>
> Date: Sat, 20 Aug 2022 15:19:17 +0300
> Cc: Andrei Pikas <gdb@mail.api.win>
> 
> Colors can be specified as name of one of the basic colors "none", "black",
> "white", etc., as a number up to 255, or as RGB hexadecimal tripplet #RRGGBB.

I might be missing something, but where's the correspondence between
the "color number" and the colors, how do we check that TrueColor is
supported by the terminal, and what will happen if they aren't?

> diff --git a/gdb/NEWS b/gdb/NEWS
> index d2efe2a0a58..c9d25890ed9 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -55,6 +55,9 @@
>    Python Pygments is still used.  For supported targets, libopcodes
>    styling is used by default.
>  
> +  "set style" commands now supports numeric format for basic colors
> +  from 0 to 255 and #RRGGBB format for TrueColor.
> +
>  * New commands
>  
>  maintenance set ignore-prologue-end-flag on|off
> @@ -170,6 +173,16 @@ GNU/Linux/LoongArch (gdbserver)	loongarch*-*-linux*
>       can be used to request a shorter representation of a value, the
>       way that 'set print frame-arguments scalars' does.
>  
> +  ** New constant gdb.PARAM_COLOR represents color type of a
> +     gdb.Parameter.value.  Parameter's value is either an integer
> +     from 0 to 255 or string with color name or #RRGGBB hex triplet.
> +
> +* Guile API
> +
> +  ** New constant PARAM_COLOR represents color type of a value
> +     of a <gdb:parameter> object.  Parameter's value is either an integer
> +     from 0 to 255 or string with color name or #RRGGBB hex triplet.
> +
>  * New features in the GDB remote stub, GDBserver

This part is okay (assuming that the implementation is accepted).

> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -26605,16 +26605,18 @@ 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 a name of a basic color,
> +number from 0 to 255 or a hexadecimal RGB triplet in #RRGGBB format.  Valid

"#RRGGBB" should be in @samp, I think (here and elsewhere in the
manual).

> +color names 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}.

This should explain how are the numbers 0..255 related to colors, and
should also mention TrueColor (if that is how the RRGGBB triplets are
interpreted).

Thanks.

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

* Re: [PATCH v2] Add an option with a color type.
  2022-08-20 12:43     ` Eli Zaretskii
@ 2022-08-21 20:09       ` Andrei Pikas
  2022-08-22  2:26         ` Eli Zaretskii
  2022-08-21 21:07       ` [PATCH v3 (documentation fixed)] " Andrei Pikas
  1 sibling, 1 reply; 13+ messages in thread
From: Andrei Pikas @ 2022-08-21 20:09 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: gdb-patches

 > how do we check that TrueColor is
 > supported by the terminal, and what will happen if they aren't?

We don't check that TrueColor is supported by the terminal, because we 
don't use TrueColor by default. Thus, the user is responsible for 
setting the colors supported by his terminal.



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

* [PATCH v3 (documentation fixed)] Add an option with a color type.
  2022-08-20 12:43     ` Eli Zaretskii
  2022-08-21 20:09       ` Andrei Pikas
@ 2022-08-21 21:07       ` Andrei Pikas
  2022-08-22 11:54         ` Eli Zaretskii
  2022-08-23 11:29         ` Andrew Burgess
  1 sibling, 2 replies; 13+ messages in thread
From: Andrei Pikas @ 2022-08-21 21:07 UTC (permalink / raw)
  To: gdb-patches; +Cc: eliz, Andrei Pikas

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 user's responsibility to provide colors which supported by his terminal.
---
 gdb/NEWS                                  |  13 ++
 gdb/cli/cli-cmds.c                        |   7 +
 gdb/cli/cli-decode.c                      | 173 ++++++++++++++++++++++
 gdb/cli/cli-decode.h                      |  21 +++
 gdb/cli/cli-option.c                      |  42 ++++++
 gdb/cli/cli-option.h                      |  21 +++
 gdb/cli/cli-setshow.c                     |  20 +++
 gdb/cli/cli-style.c                       |  49 ++----
 gdb/cli/cli-style.h                       |   4 +-
 gdb/command.h                             |  26 +++-
 gdb/doc/gdb.texinfo                       |  44 +++++-
 gdb/doc/guile.texi                        |   8 +
 gdb/doc/python.texi                       |  10 ++
 gdb/guile/scm-param.c                     |  68 ++++++++-
 gdb/python/py-param.c                     |  63 +++++++-
 gdb/python/python.c                       |  10 ++
 gdb/testsuite/gdb.base/style.exp          |  15 ++
 gdb/testsuite/gdb.guile/scm-parameter.exp |  26 ++++
 gdb/testsuite/gdb.python/py-parameter.exp |  46 ++++++
 gdb/ui-style.c                            |  44 ++++++
 gdb/ui-style.h                            |  23 ++-
 21 files changed, 676 insertions(+), 57 deletions(-)

diff --git a/gdb/NEWS b/gdb/NEWS
index d2efe2a0a58..c9d25890ed9 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -55,6 +55,9 @@
   Python Pygments is still used.  For supported targets, libopcodes
   styling is used by default.
 
+  "set style" commands now supports numeric format for basic colors
+  from 0 to 255 and #RRGGBB format for TrueColor.
+
 * New commands
 
 maintenance set ignore-prologue-end-flag on|off
@@ -170,6 +173,16 @@ GNU/Linux/LoongArch (gdbserver)	loongarch*-*-linux*
      can be used to request a shorter representation of a value, the
      way that 'set print frame-arguments scalars' does.
 
+  ** New constant gdb.PARAM_COLOR represents color type of a
+     gdb.Parameter.value.  Parameter's value is either an integer
+     from 0 to 255 or string with color name or #RRGGBB hex triplet.
+
+* Guile API
+
+  ** New constant PARAM_COLOR represents color type of a value
+     of a <gdb:parameter> object.  Parameter's value is either an integer
+     from 0 to 255 or string with color name or #RRGGBB hex triplet.
+
 * New features in the GDB remote stub, GDBserver
 
   ** GDBserver is now supported on LoongArch GNU/Linux.
diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c
index 18fb6e6d869..1a515b99761 100644
--- a/gdb/cli/cli-cmds.c
+++ b/gdb/cli/cli-cmds.c
@@ -2275,6 +2275,12 @@ value_from_setting (const setting &var, struct gdbarch *gdbarch)
 	  return value_cstring ("", 1,
 				builtin_type (gdbarch)->builtin_char);
       }
+    case var_color:
+      {
+	std::string s = var.get<ui_file_style::color> ().to_string ();
+	return value_cstring (s.c_str (), s.size (),
+			      builtin_type (gdbarch)->builtin_char);
+      }
     default:
       gdb_assert_not_reached ("bad var_type");
     }
@@ -2324,6 +2330,7 @@ str_value_from_setting (const setting &var, struct gdbarch *gdbarch)
     case var_auto_boolean:
     case var_uinteger:
     case var_zuinteger:
+    case var_color:
       {
 	std::string cmd_val = get_setshow_command_value_string (var);
 
diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
index fde554c7e6c..b0a4ceeedf6 100644
--- a/gdb/cli/cli-decode.c
+++ b/gdb/cli/cli-decode.c
@@ -24,6 +24,7 @@
 #include "cli/cli-cmds.h"
 #include "cli/cli-decode.h"
 #include "cli/cli-style.h"
+#include "cli/cli-utils.h"
 #include "gdbsupport/gdb_optional.h"
 
 /* Prototypes for local functions.  */
@@ -670,6 +671,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 };
 
@@ -2524,3 +2606,94 @@ 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 NULL 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 %s integer 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 (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;
+}
diff --git a/gdb/cli/cli-decode.h b/gdb/cli/cli-decode.h
index 18db8822af3..7f1233c87ad 100644
--- a/gdb/cli/cli-decode.h
+++ b/gdb/cli/cli-decode.h
@@ -306,6 +306,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.  */
 
diff --git a/gdb/cli/cli-option.c b/gdb/cli/cli-option.c
index b1794ad4b17..ac62c6700fa 100644
--- a/gdb/cli/cli-option.c
+++ b/gdb/cli/cli-option.c
@@ -46,6 +46,9 @@ union option_value
 
   /* For var_string options.  This is malloc-allocated.  */
   std::string *string;
+
+  /* For var_color options.  */
+  ui_file_style::color color = ui_file_style::NONE;
 };
 
 /* Holds an options definition and its value.  */
@@ -424,6 +427,33 @@ 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;
+	val.color = parse_cli_var_color (args);
+	return option_def_and_value {*match, match_ctx, val};
+      }
     case var_string:
       {
 	if (check_for_argument (args, "--"))
@@ -601,6 +631,10 @@ save_option_value_in_ctx (gdb::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:
       *ov->option.var_address.string (ov->option, ov->ctx)
 	= std::move (*ov->value->string);
@@ -677,6 +711,14 @@ get_val_type_str (const option_def &opt, std::string &buffer)
 	  }
 	return buffer.c_str ();
       }
+    case var_color:
+      {
+	buffer = "";
+	for (size_t i = 0; ui_file_style::basic_color_enums[i]; ++i)
+	  buffer.append (ui_file_style::basic_color_enums[i]).append ("|");
+	buffer += "NUMBER|#RRGGBB";
+	return buffer.c_str ();
+      }
     case var_string:
       return "STRING";
     default:
diff --git a/gdb/cli/cli-option.h b/gdb/cli/cli-option.h
index 26a8da3a5a4..d30c7024324 100644
--- a/gdb/cli/cli-option.h
+++ b/gdb/cli/cli-option.h
@@ -87,6 +87,7 @@ struct option_def
       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;
 
@@ -282,6 +283,26 @@ struct string_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
diff --git a/gdb/cli/cli-setshow.c b/gdb/cli/cli-setshow.c
index 139ebaf8323..4e8e5c7f0f8 100644
--- a/gdb/cli/cli-setshow.c
+++ b/gdb/cli/cli-setshow.c
@@ -454,6 +454,12 @@ 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);
+	option_changed = c->var->set<ui_file_style::color> (color);
+      }
+      break;
     case var_zuinteger_unlimited:
       option_changed = c->var->set<int>
 	(parse_cli_var_zuinteger_unlimited (&arg, true));
@@ -535,6 +541,14 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c)
 	  gdb::observers::command_param_changed.notify
 	    (name, c->var->get<const char *> ());
 	  break;
+	case var_color:
+	  {
+	    const ui_file_style::color &color
+	      = c->var->get<ui_file_style::color> ();
+	    gdb::observers::command_param_changed.notify
+	      (name, color.to_string ().c_str ());
+	  }
+	  break;
 	case var_boolean:
 	  {
 	    const char *opt = c->var->get<bool> () ? "on" : "off";
@@ -602,6 +616,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;
diff --git a/gdb/cli/cli-style.c b/gdb/cli/cli-style.c
index abf685561fa..3123a391763 100644
--- a/gdb/cli/cli-style.c
+++ b/gdb/cli/cli-style.c
@@ -43,20 +43,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[] = {
@@ -132,8 +118,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])
 {
 }
@@ -144,32 +130,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)
@@ -181,7 +152,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.  */
@@ -254,9 +225,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,
@@ -266,9 +236,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,
diff --git a/gdb/cli/cli-style.h b/gdb/cli/cli-style.h
index 4090cf01333..4db86b00359 100644
--- a/gdb/cli/cli-style.h
+++ b/gdb/cli/cli-style.h
@@ -67,9 +67,9 @@ class cli_style_option
   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;
 
diff --git a/gdb/command.h b/gdb/command.h
index d901da3c8cb..eb3b2cc20d6 100644
--- a/gdb/command.h
+++ b/gdb/command.h
@@ -119,7 +119,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
   };
 
 /* Return true if a setting of type VAR_TYPE is backed with type T.
@@ -179,6 +181,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>
@@ -665,6 +675,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,
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 382df00ee7d..df007c63752 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -26605,16 +26605,44 @@ 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\\".
+
+It is user's responsibility to provide colors supported by its terminal.
 
 @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 @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\\".
+
+It is user's responsibility to provide colors supported by its terminal.
 
 @item set style filename intensity @var{value}
 Set the intensity to @var{value}.  Valid intensities are @samp{normal}
diff --git a/gdb/doc/guile.texi b/gdb/doc/guile.texi
index 63916eed181..395cad8891a 100644
--- a/gdb/doc/guile.texi
+++ b/gdb/doc/guile.texi
@@ -2169,6 +2169,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
diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index eeb847aeaa8..5f7864f3f2a 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -4651,6 +4651,16 @@ except the special value -1 should be interpreted to mean
 @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 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 table
 
 @node Functions In Python
diff --git a/gdb/guile/scm-param.c b/gdb/guile/scm-param.c
index 54c8c27301a..42820dd666d 100644
--- a/gdb/guile/scm-param.c
+++ b/gdb/guile/scm-param.c
@@ -48,6 +48,9 @@ union pascm_variable
 
   /* Hold a string, for enums.  */
   const char *cstringval;
+
+  /* Hold a color.  */
+  ui_file_style::color color;
 };
 
 /* A GDB parameter.
@@ -129,6 +132,8 @@ make_setting (param_smob *s)
     return setting (s->type, s->value.stringval);
   else if (var_type_uses<const char *> (s->type))
     return setting (s->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");
 }
@@ -190,10 +195,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;
@@ -466,6 +470,13 @@ add_setshow_generic (enum var_types param_type, enum command_class cmd_class,
 				       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");
     }
@@ -545,6 +556,7 @@ static const scheme_integer_constant parameter_types[] =
   { "PARAM_OPTIONAL_FILENAME", var_optional_filename },
   { "PARAM_FILENAME", var_filename },
   { "PARAM_ENUM", var_enum },
+  { "PARAM_COLOR", var_color },
 
   END_INTEGER_CONSTANTS
 };
@@ -611,6 +623,18 @@ 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:
+      {
+	const ui_file_style::color &color = var.get<ui_file_style::color> ();
+	if (color.is_none () || color.is_basic () || !color.is_simple ())
+	  {
+	    std::string s = color.to_string ();
+	    return gdbscm_scm_from_host_string (s.c_str (), s.size ());
+	  }
+	else
+	  return scm_from_int (color.get_value ());
+      }
+
     case var_boolean:
       {
 	if (var.get<bool> ())
@@ -716,6 +740,44 @@ pascm_set_param_value_x (param_smob *p_smob,
 	break;
       }
 
+    case var_color:
+      SCM_ASSERT_TYPE (scm_is_string (value) || scm_is_integer (value),
+		       value, arg_pos, func_name,
+		       _("string or integer"));
+
+      if (scm_is_integer (value))
+	{
+	  int i = scm_to_int (value);
+	  if (i < 0 || i > 255)
+	    gdbscm_out_of_range_error (func_name, arg_pos, value,
+				       _("must be in range from 0 to 255"));
+	  var.set<ui_file_style::color> (i);
+	}
+      else
+	{
+	  SCM exception;
+
+	  gdb::unique_xmalloc_ptr<char> string
+	    = gdbscm_scm_to_host_string (value, nullptr, &exception);
+	  if (string == nullptr)
+	    gdbscm_throw (exception);
+
+	  gdbscm_gdb_exception exc {};
+	  try
+	    {
+	      ui_file_style::color color = parse_var_color (string.get ());
+	      var.set<ui_file_style::color> (color);
+	    }
+	  catch (const gdb_exception &except)
+	    {
+	      exc = unpack (except);
+	    }
+
+	  GDBSCM_HANDLE_GDB_EXCEPTION (exc);
+	}
+
+      break;
+
     case var_boolean:
       SCM_ASSERT_TYPE (gdbscm_is_bool (value), value, arg_pos, func_name,
 		       _("boolean"));
@@ -961,6 +1023,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-param.c b/gdb/python/py-param.c
index 5d509ba4658..a2d720490c1 100644
--- a/gdb/python/py-param.c
+++ b/gdb/python/py-param.c
@@ -46,6 +46,7 @@ static struct {
   { "PARAM_ZUINTEGER", var_zuinteger },
   { "PARAM_ZUINTEGER_UNLIMITED", var_zuinteger_unlimited },
   { "PARAM_ENUM", var_enum },
+  { "PARAM_COLOR", var_color },
   { NULL, 0 }
 };
 
@@ -70,6 +71,9 @@ union parmpy_variable
 
   /* Hold a string, for enums.  */
   const char *cstringval;
+
+  /* Hold a color.  */
+  ui_file_style::color color;
 };
 
 /* A GDB parameter.  */
@@ -108,6 +112,8 @@ make_setting (parmpy_object *s)
     return setting (s->type, s->value.stringval);
   else if (var_type_uses<const char *> (s->type))
     return setting (s->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");
 }
@@ -199,6 +205,49 @@ set_parameter_value (parmpy_object *self, PyObject *value)
 	break;
       }
 
+    case var_color:
+      {
+	if (gdbpy_is_string (value))
+	  {
+	    gdb::unique_xmalloc_ptr<char>
+	      str (python_string_to_host_string (value));
+	    if (str == NULL)
+	      return -1;
+	    try
+	      {
+		self->value.color = parse_var_color (str.get());
+	      }
+	    catch (const gdb_exception &except)
+	      {
+		gdbpy_convert_exception (except);
+		return -1;
+	      }
+	  }
+	else if (PyLong_Check (value))
+	  {
+	    long l;
+	    if (! gdb_py_int_as_long (value, &l))
+	      return -1;
+	    if (l < 0 || l > 255)
+	      {
+		PyErr_SetString (PyExc_RuntimeError,
+				 _("Range exceeded."));
+		return -1;
+	      }
+	    self->value.color = ui_file_style::color (l);
+	  }
+	else if (value == Py_None)
+	  self->value.color = ui_file_style::NONE;
+	else
+	  {
+	    PyErr_SetString (PyExc_RuntimeError,
+			     _("color arguments must be a string, an integer "
+			       "or None."));
+	    return -1;
+	  }
+      }
+      break;
+
     case var_boolean:
       if (! PyBool_Check (value))
 	{
@@ -637,6 +686,15 @@ add_setshow_generic (int parmclass, enum command_class cmdclass,
 				       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.");
     }
@@ -758,7 +816,8 @@ parmpy_init (PyObject *self, PyObject *args, PyObject *kwds)
       && parmclass != var_string && parmclass != var_string_noescape
       && parmclass != var_optional_filename && parmclass != var_filename
       && parmclass != var_zinteger && parmclass != var_zuinteger
-      && parmclass != var_zuinteger_unlimited && parmclass != var_enum)
+      && parmclass != var_zuinteger_unlimited && parmclass != var_enum
+      && parmclass != var_color)
     {
       PyErr_SetString (PyExc_RuntimeError,
 		       _("Invalid parameter class argument."));
@@ -779,7 +838,7 @@ parmpy_init (PyObject *self, PyObject *args, PyObject *kwds)
   else
     obj->enumeration = NULL;
   obj->type = (enum var_types) parmclass;
-  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;
diff --git a/gdb/python/python.c b/gdb/python/python.c
index c7d4157b70c..141d495c2e2 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -484,6 +484,16 @@ 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> ();
+	if (color.is_none () || color.is_basic () || !color.is_simple ())
+	  return host_string_to_python_string
+	    (color.to_string ().c_str ()).release ();
+	else
+	  return gdb_py_object_from_longest (color.get_value ()).release ();
+      }
+
     case var_boolean:
       {
 	if (var.get<bool> ())
diff --git a/gdb/testsuite/gdb.base/style.exp b/gdb/testsuite/gdb.base/style.exp
index 2242c5bf743..82458193d8d 100644
--- a/gdb/testsuite/gdb.base/style.exp
+++ b/gdb/testsuite/gdb.base/style.exp
@@ -298,6 +298,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;255m.*\".*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;255m.*\".*version.*\".*style.*\033\\\[m foreground color is: #FED210" \
+		"Version's TrueColor foreground style"
+	}
     }
 }
 
diff --git a/gdb/testsuite/gdb.guile/scm-parameter.exp b/gdb/testsuite/gdb.guile/scm-parameter.exp
index cf6f2834373..8a9cbcab605 100644
--- a/gdb/testsuite/gdb.guile/scm-parameter.exp
+++ b/gdb/testsuite/gdb.guile/scm-parameter.exp
@@ -91,6 +91,32 @@ with_test_prefix "test-enum-param" {
     gdb_test "set print test-enum-param three" "Undefined item: \"three\".*" "set invalid enum parameter" 
 }
 
+# Test a color parameter.
+
+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 \"green\"))" "" \
+    "(register-parameter! test-color-param)" "" \
+    "end"
+
+with_test_prefix "test-color-param" {
+    gdb_test "guile (print (parameter-value test-color-param))" "green" "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"
+    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))" "255" "color parameter value (255)"
+    gdb_test "set print test-color-param 256" "integer 256 out of range.*" "set invalid color parameter"
+}
+
 # Test a file parameter.
 
 gdb_test_multiline "file gdb parameter" \
diff --git a/gdb/testsuite/gdb.python/py-parameter.exp b/gdb/testsuite/gdb.python/py-parameter.exp
index d6db6ac3bb1..0f269dd86a8 100644
--- a/gdb/testsuite/gdb.python/py-parameter.exp
+++ b/gdb/testsuite/gdb.python/py-parameter.exp
@@ -177,6 +177,51 @@ proc_with_prefix test_enum_parameter { } {
 	"Undefined item: \"three\".*" "set invalid enum parameter"
 }
 
+# Test an color parameter.
+proc_with_prefix test_color_parameter { } {
+    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 = \"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 = 254" \
+	"assign test_color_param.value to 254"
+    gdb_test "python print (repr (test_color_param.value))" "254" \
+	"test color parameter value is integer"
+    gdb_test_no_output "python test_color_param.value =  '#FED210'" \
+	"assign test_color_param.value to #FED210"
+    gdb_test "python print (repr (test_color_param.value))" "'#FED210'" \
+	"test color parameter value is string"
+    gdb_test "set print test-color-param 256" \
+	"integer 256 out of range.*" "set invalid color parameter"
+    gdb_test "python test_color_param.value = 256" \
+	"RuntimeError: Range exceeded.*" "set invalid color value"
+}
+
 # Test a file parameter.
 proc_with_prefix test_file_parameter { } {
     clean_restart
@@ -391,6 +436,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
diff --git a/gdb/ui-style.c b/gdb/ui-style.c
index f1a5b8c4101..167550b2c9a 100644
--- a/gdb/ui-style.c
+++ b/gdb/ui-style.c
@@ -62,6 +62,33 @@ static const uint8_t bright_colors[][3] = {
   { 255, 255, 255 }		/* 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
+};
+
+/* 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.  */
 
 bool
@@ -93,6 +120,23 @@ ui_file_style::color::append_ansi (bool is_fg, std::string *str) const
 
 /* See ui-style.h.  */
 
+std::string
+ui_file_style::color::to_string () const
+{
+  if (!m_simple)
+    {
+      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
 {
diff --git a/gdb/ui-style.h b/gdb/ui-style.h
index fe1b2af611d..f002facf706 100644
--- a/gdb/ui-style.h
+++ b/gdb/ui-style.h
@@ -73,6 +73,11 @@ struct ui_file_style
 	      && 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)
@@ -104,10 +109,17 @@ struct ui_file_style
       return m_simple && m_value >= BLACK && m_value <= WHITE;
     }
 
-    /* Return the value of a basic color.  */
+    /* Return true if this is one of the simple colors, false
+       otherwise.  */
+    bool is_simple () const
+    {
+      return m_simple;
+    }
+
+    /* Return the value of a simple color.  */
     int get_value () const
     {
-      gdb_assert (is_basic ());
+      gdb_assert (is_simple ());
       return m_value;
     }
 
@@ -123,6 +135,10 @@ struct ui_file_style
        color).  */
     bool append_ansi (bool is_fg, std::string *str) 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;
+
   private:
 
     bool m_simple;
@@ -235,6 +251,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;
-- 
2.34.1


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

* Re: [PATCH v2] Add an option with a color type.
  2022-08-21 20:09       ` Andrei Pikas
@ 2022-08-22  2:26         ` Eli Zaretskii
  0 siblings, 0 replies; 13+ messages in thread
From: Eli Zaretskii @ 2022-08-22  2:26 UTC (permalink / raw)
  To: Andrei Pikas; +Cc: gdb-patches

> Date: Sun, 21 Aug 2022 23:09:16 +0300
> Cc: gdb-patches@sourceware.org
> From: Andrei Pikas <gdb@mail.api.win>
> 
>  > how do we check that TrueColor is
>  > supported by the terminal, and what will happen if they aren't?
> 
> We don't check that TrueColor is supported by the terminal, because we 
> don't use TrueColor by default. Thus, the user is responsible for 
> setting the colors supported by his terminal.

This responsibility of the user should at least be documented.
Probably together with some practical advice how to check that.

Also, IMO we should make sure no catastrophe happens if the user
decides incorrectly, and the result will be limited to somewhat messy
display, nothing worse.

Thanks.

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

* Re: [PATCH v3 (documentation fixed)] Add an option with a color type.
  2022-08-21 21:07       ` [PATCH v3 (documentation fixed)] " Andrei Pikas
@ 2022-08-22 11:54         ` Eli Zaretskii
  2022-08-22 16:26           ` Philippe Waroquiers
  2022-08-23 11:29         ` Andrew Burgess
  1 sibling, 1 reply; 13+ messages in thread
From: Eli Zaretskii @ 2022-08-22 11:54 UTC (permalink / raw)
  To: Andrei Pikas; +Cc: gdb-patches, gdb

> From: Andrei Pikas <gdb@mail.api.win>
> Cc: eliz@gnu.org,
> 	Andrei Pikas <gdb@mail.api.win>
> Date: Mon, 22 Aug 2022 00:07:03 +0300
> 
> +It is user's responsibility to provide colors supported by its terminal.

I'd rephrase this (here and elsewhere in the patch):

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

Thanks.

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

* Re: [PATCH v3 (documentation fixed)] Add an option with a color type.
  2022-08-22 11:54         ` Eli Zaretskii
@ 2022-08-22 16:26           ` Philippe Waroquiers
  0 siblings, 0 replies; 13+ messages in thread
From: Philippe Waroquiers @ 2022-08-22 16:26 UTC (permalink / raw)
  To: Eli Zaretskii, Andrei Pikas; +Cc: gdb-patches

Color settings will typically be done in .gdbinit.
Would be nice to have a way to detect which kind of colors are supported by the terminal,
set it in a convenience variable and let the user do something like:
if $colorsupport == truecolor 
   .... a bunch of settings for true color
elsif $colorsupport == ....
   .... a bunch of settings for this color support
...

Also, as suggested by Eli, GDB might fallback to the closer available color
when the color support is not available for the asked color.

Philippe



On Mon, 2022-08-22 at 14:54 +0300, Eli Zaretskii via Gdb-patches wrote:
> > From: Andrei Pikas <gdb@mail.api.win>
> > Cc: eliz@gnu.org,
> > 	Andrei Pikas <gdb@mail.api.win>
> > Date: Mon, 22 Aug 2022 00:07:03 +0300
> > 
> > +It is user's responsibility to provide colors supported by its terminal.
> 
> I'd rephrase this (here and elsewhere in the patch):
> 
>   It is the responsibility of the user to verify that the terminal
>   supports the specified colors.
> 
> Thanks.



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

* Re: [PATCH v3 (documentation fixed)] Add an option with a color type.
  2022-08-21 21:07       ` [PATCH v3 (documentation fixed)] " Andrei Pikas
  2022-08-22 11:54         ` Eli Zaretskii
@ 2022-08-23 11:29         ` Andrew Burgess
  2022-09-13  0:44           ` [PATCH v4 (documentation + approximation + convenience variable + python and guile types)] " Andrei Pikas
  1 sibling, 1 reply; 13+ messages in thread
From: Andrew Burgess @ 2022-08-23 11:29 UTC (permalink / raw)
  To: Andrei Pikas, gdb-patches; +Cc: Andrei Pikas

Andrei Pikas <gdb@mail.api.win> writes:

> 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 user's responsibility to provide colors which supported by his terminal.
> ---
>  gdb/NEWS                                  |  13 ++
>  gdb/cli/cli-cmds.c                        |   7 +
>  gdb/cli/cli-decode.c                      | 173 ++++++++++++++++++++++
>  gdb/cli/cli-decode.h                      |  21 +++
>  gdb/cli/cli-option.c                      |  42 ++++++
>  gdb/cli/cli-option.h                      |  21 +++
>  gdb/cli/cli-setshow.c                     |  20 +++
>  gdb/cli/cli-style.c                       |  49 ++----
>  gdb/cli/cli-style.h                       |   4 +-
>  gdb/command.h                             |  26 +++-
>  gdb/doc/gdb.texinfo                       |  44 +++++-
>  gdb/doc/guile.texi                        |   8 +
>  gdb/doc/python.texi                       |  10 ++
>  gdb/guile/scm-param.c                     |  68 ++++++++-
>  gdb/python/py-param.c                     |  63 +++++++-
>  gdb/python/python.c                       |  10 ++
>  gdb/testsuite/gdb.base/style.exp          |  15 ++
>  gdb/testsuite/gdb.guile/scm-parameter.exp |  26 ++++
>  gdb/testsuite/gdb.python/py-parameter.exp |  46 ++++++
>  gdb/ui-style.c                            |  44 ++++++
>  gdb/ui-style.h                            |  23 ++-
>  21 files changed, 676 insertions(+), 57 deletions(-)
>
> diff --git a/gdb/NEWS b/gdb/NEWS
> index d2efe2a0a58..c9d25890ed9 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -55,6 +55,9 @@
>    Python Pygments is still used.  For supported targets, libopcodes
>    styling is used by default.
>  
> +  "set style" commands now supports numeric format for basic colors
> +  from 0 to 255 and #RRGGBB format for TrueColor.
> +
>  * New commands
>  
>  maintenance set ignore-prologue-end-flag on|off
> @@ -170,6 +173,16 @@ GNU/Linux/LoongArch (gdbserver)	loongarch*-*-linux*
>       can be used to request a shorter representation of a value, the
>       way that 'set print frame-arguments scalars' does.
>  
> +  ** New constant gdb.PARAM_COLOR represents color type of a
> +     gdb.Parameter.value.  Parameter's value is either an integer
> +     from 0 to 255 or string with color name or #RRGGBB hex triplet.
> +
> +* Guile API
> +
> +  ** New constant PARAM_COLOR represents color type of a value
> +     of a <gdb:parameter> object.  Parameter's value is either an integer
> +     from 0 to 255 or string with color name or #RRGGBB hex triplet.
> +
>  * New features in the GDB remote stub, GDBserver
>  
>    ** GDBserver is now supported on LoongArch GNU/Linux.
> diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c
> index 18fb6e6d869..1a515b99761 100644
> --- a/gdb/cli/cli-cmds.c
> +++ b/gdb/cli/cli-cmds.c
> @@ -2275,6 +2275,12 @@ value_from_setting (const setting &var, struct gdbarch *gdbarch)
>  	  return value_cstring ("", 1,
>  				builtin_type (gdbarch)->builtin_char);
>        }
> +    case var_color:
> +      {
> +	std::string s = var.get<ui_file_style::color> ().to_string ();
> +	return value_cstring (s.c_str (), s.size (),
> +			      builtin_type (gdbarch)->builtin_char);
> +      }
>      default:
>        gdb_assert_not_reached ("bad var_type");
>      }
> @@ -2324,6 +2330,7 @@ str_value_from_setting (const setting &var, struct gdbarch *gdbarch)
>      case var_auto_boolean:
>      case var_uinteger:
>      case var_zuinteger:
> +    case var_color:
>        {
>  	std::string cmd_val = get_setshow_command_value_string (var);
>  
> diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
> index fde554c7e6c..b0a4ceeedf6 100644
> --- a/gdb/cli/cli-decode.c
> +++ b/gdb/cli/cli-decode.c
> @@ -24,6 +24,7 @@
>  #include "cli/cli-cmds.h"
>  #include "cli/cli-decode.h"
>  #include "cli/cli-style.h"
> +#include "cli/cli-utils.h"
>  #include "gdbsupport/gdb_optional.h"
>  
>  /* Prototypes for local functions.  */
> @@ -670,6 +671,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 };
>  
> @@ -2524,3 +2606,94 @@ 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 NULL 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 %s integer 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 (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;
> +}
> diff --git a/gdb/cli/cli-decode.h b/gdb/cli/cli-decode.h
> index 18db8822af3..7f1233c87ad 100644
> --- a/gdb/cli/cli-decode.h
> +++ b/gdb/cli/cli-decode.h
> @@ -306,6 +306,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.  */
>  
> diff --git a/gdb/cli/cli-option.c b/gdb/cli/cli-option.c
> index b1794ad4b17..ac62c6700fa 100644
> --- a/gdb/cli/cli-option.c
> +++ b/gdb/cli/cli-option.c
> @@ -46,6 +46,9 @@ union option_value
>  
>    /* For var_string options.  This is malloc-allocated.  */
>    std::string *string;
> +
> +  /* For var_color options.  */
> +  ui_file_style::color color = ui_file_style::NONE;
>  };
>  
>  /* Holds an options definition and its value.  */
> @@ -424,6 +427,33 @@ 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;
> +	val.color = parse_cli_var_color (args);
> +	return option_def_and_value {*match, match_ctx, val};
> +      }
>      case var_string:
>        {
>  	if (check_for_argument (args, "--"))
> @@ -601,6 +631,10 @@ save_option_value_in_ctx (gdb::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:
>        *ov->option.var_address.string (ov->option, ov->ctx)
>  	= std::move (*ov->value->string);
> @@ -677,6 +711,14 @@ get_val_type_str (const option_def &opt, std::string &buffer)
>  	  }
>  	return buffer.c_str ();
>        }
> +    case var_color:
> +      {
> +	buffer = "";
> +	for (size_t i = 0; ui_file_style::basic_color_enums[i]; ++i)
> +	  buffer.append (ui_file_style::basic_color_enums[i]).append ("|");
> +	buffer += "NUMBER|#RRGGBB";
> +	return buffer.c_str ();
> +      }
>      case var_string:
>        return "STRING";
>      default:
> diff --git a/gdb/cli/cli-option.h b/gdb/cli/cli-option.h
> index 26a8da3a5a4..d30c7024324 100644
> --- a/gdb/cli/cli-option.h
> +++ b/gdb/cli/cli-option.h
> @@ -87,6 +87,7 @@ struct option_def
>        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;
>  
> @@ -282,6 +283,26 @@ struct string_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
> diff --git a/gdb/cli/cli-setshow.c b/gdb/cli/cli-setshow.c
> index 139ebaf8323..4e8e5c7f0f8 100644
> --- a/gdb/cli/cli-setshow.c
> +++ b/gdb/cli/cli-setshow.c
> @@ -454,6 +454,12 @@ 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);
> +	option_changed = c->var->set<ui_file_style::color> (color);
> +      }
> +      break;
>      case var_zuinteger_unlimited:
>        option_changed = c->var->set<int>
>  	(parse_cli_var_zuinteger_unlimited (&arg, true));
> @@ -535,6 +541,14 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c)
>  	  gdb::observers::command_param_changed.notify
>  	    (name, c->var->get<const char *> ());
>  	  break;
> +	case var_color:
> +	  {
> +	    const ui_file_style::color &color
> +	      = c->var->get<ui_file_style::color> ();
> +	    gdb::observers::command_param_changed.notify
> +	      (name, color.to_string ().c_str ());
> +	  }
> +	  break;
>  	case var_boolean:
>  	  {
>  	    const char *opt = c->var->get<bool> () ? "on" : "off";
> @@ -602,6 +616,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;
> diff --git a/gdb/cli/cli-style.c b/gdb/cli/cli-style.c
> index abf685561fa..3123a391763 100644
> --- a/gdb/cli/cli-style.c
> +++ b/gdb/cli/cli-style.c
> @@ -43,20 +43,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[] = {
> @@ -132,8 +118,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])
>  {
>  }
> @@ -144,32 +130,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)
> @@ -181,7 +152,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.  */
> @@ -254,9 +225,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,
> @@ -266,9 +236,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,
> diff --git a/gdb/cli/cli-style.h b/gdb/cli/cli-style.h
> index 4090cf01333..4db86b00359 100644
> --- a/gdb/cli/cli-style.h
> +++ b/gdb/cli/cli-style.h
> @@ -67,9 +67,9 @@ class cli_style_option
>    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;
>  
> diff --git a/gdb/command.h b/gdb/command.h
> index d901da3c8cb..eb3b2cc20d6 100644
> --- a/gdb/command.h
> +++ b/gdb/command.h
> @@ -119,7 +119,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
>    };
>  
>  /* Return true if a setting of type VAR_TYPE is backed with type T.
> @@ -179,6 +181,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>
> @@ -665,6 +675,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,
> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> index 382df00ee7d..df007c63752 100644
> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -26605,16 +26605,44 @@ 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\\".
> +
> +It is user's responsibility to provide colors supported by its terminal.
>  
>  @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 @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\\".
> +
> +It is user's responsibility to provide colors supported by its terminal.
>  
>  @item set style filename intensity @var{value}
>  Set the intensity to @var{value}.  Valid intensities are @samp{normal}
> diff --git a/gdb/doc/guile.texi b/gdb/doc/guile.texi
> index 63916eed181..395cad8891a 100644
> --- a/gdb/doc/guile.texi
> +++ b/gdb/doc/guile.texi
> @@ -2169,6 +2169,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
> diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
> index eeb847aeaa8..5f7864f3f2a 100644
> --- a/gdb/doc/python.texi
> +++ b/gdb/doc/python.texi
> @@ -4651,6 +4651,16 @@ except the special value -1 should be interpreted to mean
>  @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 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 table
>  
>  @node Functions In Python
> diff --git a/gdb/guile/scm-param.c b/gdb/guile/scm-param.c
> index 54c8c27301a..42820dd666d 100644
> --- a/gdb/guile/scm-param.c
> +++ b/gdb/guile/scm-param.c
> @@ -48,6 +48,9 @@ union pascm_variable
>  
>    /* Hold a string, for enums.  */
>    const char *cstringval;
> +
> +  /* Hold a color.  */
> +  ui_file_style::color color;
>  };
>  
>  /* A GDB parameter.
> @@ -129,6 +132,8 @@ make_setting (param_smob *s)
>      return setting (s->type, s->value.stringval);
>    else if (var_type_uses<const char *> (s->type))
>      return setting (s->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");
>  }
> @@ -190,10 +195,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;
> @@ -466,6 +470,13 @@ add_setshow_generic (enum var_types param_type, enum command_class cmd_class,
>  				       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");
>      }
> @@ -545,6 +556,7 @@ static const scheme_integer_constant parameter_types[] =
>    { "PARAM_OPTIONAL_FILENAME", var_optional_filename },
>    { "PARAM_FILENAME", var_filename },
>    { "PARAM_ENUM", var_enum },
> +  { "PARAM_COLOR", var_color },
>  
>    END_INTEGER_CONSTANTS
>  };
> @@ -611,6 +623,18 @@ 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:
> +      {
> +	const ui_file_style::color &color = var.get<ui_file_style::color> ();
> +	if (color.is_none () || color.is_basic () || !color.is_simple ())
> +	  {
> +	    std::string s = color.to_string ();
> +	    return gdbscm_scm_from_host_string (s.c_str (), s.size ());
> +	  }
> +	else
> +	  return scm_from_int (color.get_value ());
> +      }
> +
>      case var_boolean:
>        {
>  	if (var.get<bool> ())
> @@ -716,6 +740,44 @@ pascm_set_param_value_x (param_smob *p_smob,
>  	break;
>        }
>  
> +    case var_color:
> +      SCM_ASSERT_TYPE (scm_is_string (value) || scm_is_integer (value),
> +		       value, arg_pos, func_name,
> +		       _("string or integer"));
> +
> +      if (scm_is_integer (value))
> +	{
> +	  int i = scm_to_int (value);
> +	  if (i < 0 || i > 255)
> +	    gdbscm_out_of_range_error (func_name, arg_pos, value,
> +				       _("must be in range from 0 to 255"));
> +	  var.set<ui_file_style::color> (i);
> +	}
> +      else
> +	{
> +	  SCM exception;
> +
> +	  gdb::unique_xmalloc_ptr<char> string
> +	    = gdbscm_scm_to_host_string (value, nullptr, &exception);
> +	  if (string == nullptr)
> +	    gdbscm_throw (exception);
> +
> +	  gdbscm_gdb_exception exc {};
> +	  try
> +	    {
> +	      ui_file_style::color color = parse_var_color (string.get ());
> +	      var.set<ui_file_style::color> (color);
> +	    }
> +	  catch (const gdb_exception &except)
> +	    {
> +	      exc = unpack (except);
> +	    }
> +
> +	  GDBSCM_HANDLE_GDB_EXCEPTION (exc);
> +	}
> +
> +      break;
> +
>      case var_boolean:
>        SCM_ASSERT_TYPE (gdbscm_is_bool (value), value, arg_pos, func_name,
>  		       _("boolean"));
> @@ -961,6 +1023,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-param.c b/gdb/python/py-param.c
> index 5d509ba4658..a2d720490c1 100644
> --- a/gdb/python/py-param.c
> +++ b/gdb/python/py-param.c
> @@ -46,6 +46,7 @@ static struct {
>    { "PARAM_ZUINTEGER", var_zuinteger },
>    { "PARAM_ZUINTEGER_UNLIMITED", var_zuinteger_unlimited },
>    { "PARAM_ENUM", var_enum },
> +  { "PARAM_COLOR", var_color },
>    { NULL, 0 }
>  };
>  
> @@ -70,6 +71,9 @@ union parmpy_variable
>  
>    /* Hold a string, for enums.  */
>    const char *cstringval;
> +
> +  /* Hold a color.  */
> +  ui_file_style::color color;
>  };
>  
>  /* A GDB parameter.  */
> @@ -108,6 +112,8 @@ make_setting (parmpy_object *s)
>      return setting (s->type, s->value.stringval);
>    else if (var_type_uses<const char *> (s->type))
>      return setting (s->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");
>  }
> @@ -199,6 +205,49 @@ set_parameter_value (parmpy_object *self, PyObject *value)
>  	break;
>        }
>  
> +    case var_color:
> +      {
> +	if (gdbpy_is_string (value))
> +	  {
> +	    gdb::unique_xmalloc_ptr<char>
> +	      str (python_string_to_host_string (value));
> +	    if (str == NULL)
> +	      return -1;
> +	    try
> +	      {
> +		self->value.color = parse_var_color (str.get());
> +	      }
> +	    catch (const gdb_exception &except)
> +	      {
> +		gdbpy_convert_exception (except);
> +		return -1;
> +	      }
> +	  }
> +	else if (PyLong_Check (value))
> +	  {
> +	    long l;
> +	    if (! gdb_py_int_as_long (value, &l))
> +	      return -1;
> +	    if (l < 0 || l > 255)
> +	      {
> +		PyErr_SetString (PyExc_RuntimeError,
> +				 _("Range exceeded."));
> +		return -1;
> +	      }
> +	    self->value.color = ui_file_style::color (l);
> +	  }
> +	else if (value == Py_None)
> +	  self->value.color = ui_file_style::NONE;
> +	else
> +	  {
> +	    PyErr_SetString (PyExc_RuntimeError,
> +			     _("color arguments must be a string, an integer "
> +			       "or None."));
> +	    return -1;
> +	  }
> +      }
> +      break;
> +
>      case var_boolean:
>        if (! PyBool_Check (value))
>  	{
> @@ -637,6 +686,15 @@ add_setshow_generic (int parmclass, enum command_class cmdclass,
>  				       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.");
>      }
> @@ -758,7 +816,8 @@ parmpy_init (PyObject *self, PyObject *args, PyObject *kwds)
>        && parmclass != var_string && parmclass != var_string_noescape
>        && parmclass != var_optional_filename && parmclass != var_filename
>        && parmclass != var_zinteger && parmclass != var_zuinteger
> -      && parmclass != var_zuinteger_unlimited && parmclass != var_enum)
> +      && parmclass != var_zuinteger_unlimited && parmclass != var_enum
> +      && parmclass != var_color)
>      {
>        PyErr_SetString (PyExc_RuntimeError,
>  		       _("Invalid parameter class argument."));
> @@ -779,7 +838,7 @@ parmpy_init (PyObject *self, PyObject *args, PyObject *kwds)
>    else
>      obj->enumeration = NULL;
>    obj->type = (enum var_types) parmclass;
> -  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;
> diff --git a/gdb/python/python.c b/gdb/python/python.c
> index c7d4157b70c..141d495c2e2 100644
> --- a/gdb/python/python.c
> +++ b/gdb/python/python.c
> @@ -484,6 +484,16 @@ 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> ();
> +	if (color.is_none () || color.is_basic () || !color.is_simple ())
> +	  return host_string_to_python_string
> +	    (color.to_string ().c_str ()).release ();
> +	else
> +	  return gdb_py_object_from_longest (color.get_value ()).release ();
> +      }
> +

It feels a little weird to me that for this parameter type we can return
a string in some cases, and an integer in others.  I keep wondering if
we should be handling this as a string in all cases?

The other option I wonder about is if we should instead return an object
of a new gdb.Color type.  The gdb.Color type could have __str__ for
conversion to a string, but we could extend the type later to allow the
integer to be fetched directly, if that was ever important.

Potentially the gdb.Color could also have a method for converting the
color into an escape sequence, if that was ever important?

I'm not really sure what the best solution is here, I just know the
string/integer thing feels like it could trip users up, a user might
assume the result is always a string because they only use the 8 basic
colors, but then, a script is shared, and another user has a color from
the 256-bit space, and gets back an integer...

Do you see a benefit for returning the different types?

Thanks,
Andrew





>      case var_boolean:
>        {
>  	if (var.get<bool> ())
> diff --git a/gdb/testsuite/gdb.base/style.exp b/gdb/testsuite/gdb.base/style.exp
> index 2242c5bf743..82458193d8d 100644
> --- a/gdb/testsuite/gdb.base/style.exp
> +++ b/gdb/testsuite/gdb.base/style.exp
> @@ -298,6 +298,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;255m.*\".*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;255m.*\".*version.*\".*style.*\033\\\[m foreground color is: #FED210" \
> +		"Version's TrueColor foreground style"
> +	}
>      }
>  }
>  
> diff --git a/gdb/testsuite/gdb.guile/scm-parameter.exp b/gdb/testsuite/gdb.guile/scm-parameter.exp
> index cf6f2834373..8a9cbcab605 100644
> --- a/gdb/testsuite/gdb.guile/scm-parameter.exp
> +++ b/gdb/testsuite/gdb.guile/scm-parameter.exp
> @@ -91,6 +91,32 @@ with_test_prefix "test-enum-param" {
>      gdb_test "set print test-enum-param three" "Undefined item: \"three\".*" "set invalid enum parameter" 
>  }
>  
> +# Test a color parameter.
> +
> +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 \"green\"))" "" \
> +    "(register-parameter! test-color-param)" "" \
> +    "end"
> +
> +with_test_prefix "test-color-param" {
> +    gdb_test "guile (print (parameter-value test-color-param))" "green" "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"
> +    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))" "255" "color parameter value (255)"
> +    gdb_test "set print test-color-param 256" "integer 256 out of range.*" "set invalid color parameter"
> +}
> +
>  # Test a file parameter.
>  
>  gdb_test_multiline "file gdb parameter" \
> diff --git a/gdb/testsuite/gdb.python/py-parameter.exp b/gdb/testsuite/gdb.python/py-parameter.exp
> index d6db6ac3bb1..0f269dd86a8 100644
> --- a/gdb/testsuite/gdb.python/py-parameter.exp
> +++ b/gdb/testsuite/gdb.python/py-parameter.exp
> @@ -177,6 +177,51 @@ proc_with_prefix test_enum_parameter { } {
>  	"Undefined item: \"three\".*" "set invalid enum parameter"
>  }
>  
> +# Test an color parameter.
> +proc_with_prefix test_color_parameter { } {
> +    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 = \"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 = 254" \
> +	"assign test_color_param.value to 254"
> +    gdb_test "python print (repr (test_color_param.value))" "254" \
> +	"test color parameter value is integer"
> +    gdb_test_no_output "python test_color_param.value =  '#FED210'" \
> +	"assign test_color_param.value to #FED210"
> +    gdb_test "python print (repr (test_color_param.value))" "'#FED210'" \
> +	"test color parameter value is string"
> +    gdb_test "set print test-color-param 256" \
> +	"integer 256 out of range.*" "set invalid color parameter"
> +    gdb_test "python test_color_param.value = 256" \
> +	"RuntimeError: Range exceeded.*" "set invalid color value"
> +}
> +
>  # Test a file parameter.
>  proc_with_prefix test_file_parameter { } {
>      clean_restart
> @@ -391,6 +436,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
> diff --git a/gdb/ui-style.c b/gdb/ui-style.c
> index f1a5b8c4101..167550b2c9a 100644
> --- a/gdb/ui-style.c
> +++ b/gdb/ui-style.c
> @@ -62,6 +62,33 @@ static const uint8_t bright_colors[][3] = {
>    { 255, 255, 255 }		/* 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
> +};
> +
> +/* 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.  */
>  
>  bool
> @@ -93,6 +120,23 @@ ui_file_style::color::append_ansi (bool is_fg, std::string *str) const
>  
>  /* See ui-style.h.  */
>  
> +std::string
> +ui_file_style::color::to_string () const
> +{
> +  if (!m_simple)
> +    {
> +      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
>  {
> diff --git a/gdb/ui-style.h b/gdb/ui-style.h
> index fe1b2af611d..f002facf706 100644
> --- a/gdb/ui-style.h
> +++ b/gdb/ui-style.h
> @@ -73,6 +73,11 @@ struct ui_file_style
>  	      && 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)
> @@ -104,10 +109,17 @@ struct ui_file_style
>        return m_simple && m_value >= BLACK && m_value <= WHITE;
>      }
>  
> -    /* Return the value of a basic color.  */
> +    /* Return true if this is one of the simple colors, false
> +       otherwise.  */
> +    bool is_simple () const
> +    {
> +      return m_simple;
> +    }
> +
> +    /* Return the value of a simple color.  */
>      int get_value () const
>      {
> -      gdb_assert (is_basic ());
> +      gdb_assert (is_simple ());
>        return m_value;
>      }
>  
> @@ -123,6 +135,10 @@ struct ui_file_style
>         color).  */
>      bool append_ansi (bool is_fg, std::string *str) 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;
> +
>    private:
>  
>      bool m_simple;
> @@ -235,6 +251,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;
> -- 
> 2.34.1


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

* [PATCH v4 (documentation + approximation + convenience variable + python and guile types)] Add an option with a color type.
  2022-08-23 11:29         ` Andrew Burgess
@ 2022-09-13  0:44           ` Andrei Pikas
  0 siblings, 0 replies; 13+ messages in thread
From: Andrei Pikas @ 2022-09-13  0:44 UTC (permalink / raw)
  To: gdb-patches; +Cc: aburgess, philippe.waroquiers, eliz, Andrei Pikas

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.
---
 gdb/Makefile.in                           |   2 +
 gdb/NEWS                                  |  29 ++
 gdb/cli/cli-cmds.c                        |   7 +
 gdb/cli/cli-decode.c                      | 174 +++++++++
 gdb/cli/cli-decode.h                      |  21 +
 gdb/cli/cli-option.c                      |  44 +++
 gdb/cli/cli-option.h                      |  21 +
 gdb/cli/cli-setshow.c                     |  21 +
 gdb/cli/cli-style.c                       |  49 +--
 gdb/cli/cli-style.h                       |   4 +-
 gdb/command.h                             |  26 +-
 gdb/doc/gdb.texinfo                       |  55 ++-
 gdb/doc/guile.texi                        | 102 +++++
 gdb/doc/python.texi                       |  95 +++++
 gdb/guile/guile-internal.h                |   9 +
 gdb/guile/guile.c                         |   1 +
 gdb/guile/scm-color.c                     | 445 ++++++++++++++++++++++
 gdb/guile/scm-param.c                     |  29 +-
 gdb/python/py-color.c                     | 346 +++++++++++++++++
 gdb/python/py-color.h                     |  35 ++
 gdb/python/py-param.c                     |  34 +-
 gdb/python/python-internal.h              |   2 +
 gdb/python/python.c                       |   8 +
 gdb/testsuite/gdb.base/style.exp          | 209 +++++++++-
 gdb/testsuite/gdb.guile/scm-color.exp     | 114 ++++++
 gdb/testsuite/gdb.guile/scm-parameter.exp |  43 +++
 gdb/testsuite/gdb.python/py-color.exp     | 101 +++++
 gdb/testsuite/gdb.python/py-parameter.exp |  53 +++
 gdb/testsuite/lib/gdb-utils.exp           |  22 +-
 gdb/top.c                                 |  13 +
 gdb/ui-style.c                            | 331 ++++++++++++----
 gdb/ui-style.h                            | 142 ++++++-
 gdb/unittests/style-selftests.c           |   6 +-
 33 files changed, 2433 insertions(+), 160 deletions(-)
 create mode 100644 gdb/guile/scm-color.c
 create mode 100644 gdb/python/py-color.c
 create mode 100644 gdb/python/py-color.h
 create mode 100644 gdb/testsuite/gdb.guile/scm-color.exp
 create mode 100644 gdb/testsuite/gdb.python/py-color.exp

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 57c29a78b7a..3317a6c4fb4 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -355,6 +355,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 \
@@ -391,6 +392,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-disasm.c \
diff --git a/gdb/NEWS b/gdb/NEWS
index d2efe2a0a58..00173c09227 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -55,6 +55,23 @@
   Python Pygments is still used.  For supported targets, libopcodes
   styling is used by default.
 
+  "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.  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 set ignore-prologue-end-flag on|off
@@ -170,6 +187,18 @@ GNU/Linux/LoongArch (gdbserver)	loongarch*-*-linux*
      can be used to request a shorter representation of a value, the
      way that 'set print frame-arguments scalars' does.
 
+  ** 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.
+
 * New features in the GDB remote stub, GDBserver
 
   ** GDBserver is now supported on LoongArch GNU/Linux.
diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c
index 18fb6e6d869..1a515b99761 100644
--- a/gdb/cli/cli-cmds.c
+++ b/gdb/cli/cli-cmds.c
@@ -2275,6 +2275,12 @@ value_from_setting (const setting &var, struct gdbarch *gdbarch)
 	  return value_cstring ("", 1,
 				builtin_type (gdbarch)->builtin_char);
       }
+    case var_color:
+      {
+	std::string s = var.get<ui_file_style::color> ().to_string ();
+	return value_cstring (s.c_str (), s.size (),
+			      builtin_type (gdbarch)->builtin_char);
+      }
     default:
       gdb_assert_not_reached ("bad var_type");
     }
@@ -2324,6 +2330,7 @@ str_value_from_setting (const setting &var, struct gdbarch *gdbarch)
     case var_auto_boolean:
     case var_uinteger:
     case var_zuinteger:
+    case var_color:
       {
 	std::string cmd_val = get_setshow_command_value_string (var);
 
diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
index fde554c7e6c..b4484c185bd 100644
--- a/gdb/cli/cli-decode.c
+++ b/gdb/cli/cli-decode.c
@@ -24,6 +24,7 @@
 #include "cli/cli-cmds.h"
 #include "cli/cli-decode.h"
 #include "cli/cli-style.h"
+#include "cli/cli-utils.h"
 #include "gdbsupport/gdb_optional.h"
 
 /* Prototypes for local functions.  */
@@ -670,6 +671,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 };
 
@@ -2524,3 +2606,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 NULL 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 %s integer 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;
+}
diff --git a/gdb/cli/cli-decode.h b/gdb/cli/cli-decode.h
index 18db8822af3..7f1233c87ad 100644
--- a/gdb/cli/cli-decode.h
+++ b/gdb/cli/cli-decode.h
@@ -306,6 +306,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.  */
 
diff --git a/gdb/cli/cli-option.c b/gdb/cli/cli-option.c
index b1794ad4b17..fbb2d35e6c3 100644
--- a/gdb/cli/cli-option.c
+++ b/gdb/cli/cli-option.c
@@ -46,6 +46,9 @@ union option_value
 
   /* For var_string options.  This is malloc-allocated.  */
   std::string *string;
+
+  /* For var_color options.  */
+  ui_file_style::color color = ui_file_style::NONE;
 };
 
 /* Holds an options definition and its value.  */
@@ -424,6 +427,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, "--"))
@@ -601,6 +633,10 @@ save_option_value_in_ctx (gdb::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:
       *ov->option.var_address.string (ov->option, ov->ctx)
 	= std::move (*ov->value->string);
@@ -677,6 +713,14 @@ get_val_type_str (const option_def &opt, std::string &buffer)
 	  }
 	return buffer.c_str ();
       }
+    case var_color:
+      {
+	buffer = "";
+	for (size_t i = 0; ui_file_style::basic_color_enums[i]; ++i)
+	  buffer.append (ui_file_style::basic_color_enums[i]).append ("|");
+	buffer += "NUMBER|#RRGGBB";
+	return buffer.c_str ();
+      }
     case var_string:
       return "STRING";
     default:
diff --git a/gdb/cli/cli-option.h b/gdb/cli/cli-option.h
index 26a8da3a5a4..d30c7024324 100644
--- a/gdb/cli/cli-option.h
+++ b/gdb/cli/cli-option.h
@@ -87,6 +87,7 @@ struct option_def
       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;
 
@@ -282,6 +283,26 @@ struct string_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
diff --git a/gdb/cli/cli-setshow.c b/gdb/cli/cli-setshow.c
index 139ebaf8323..3f6e9d0a0b5 100644
--- a/gdb/cli/cli-setshow.c
+++ b/gdb/cli/cli-setshow.c
@@ -454,6 +454,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;
     case var_zuinteger_unlimited:
       option_changed = c->var->set<int>
 	(parse_cli_var_zuinteger_unlimited (&arg, true));
@@ -535,6 +542,14 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c)
 	  gdb::observers::command_param_changed.notify
 	    (name, c->var->get<const char *> ());
 	  break;
+	case var_color:
+	  {
+	    const ui_file_style::color &color
+	      = c->var->get<ui_file_style::color> ();
+	    gdb::observers::command_param_changed.notify
+	      (name, color.to_string ().c_str ());
+	  }
+	  break;
 	case var_boolean:
 	  {
 	    const char *opt = c->var->get<bool> () ? "on" : "off";
@@ -602,6 +617,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;
diff --git a/gdb/cli/cli-style.c b/gdb/cli/cli-style.c
index abf685561fa..3123a391763 100644
--- a/gdb/cli/cli-style.c
+++ b/gdb/cli/cli-style.c
@@ -43,20 +43,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[] = {
@@ -132,8 +118,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])
 {
 }
@@ -144,32 +130,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)
@@ -181,7 +152,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.  */
@@ -254,9 +225,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,
@@ -266,9 +236,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,
diff --git a/gdb/cli/cli-style.h b/gdb/cli/cli-style.h
index 4090cf01333..4db86b00359 100644
--- a/gdb/cli/cli-style.h
+++ b/gdb/cli/cli-style.h
@@ -67,9 +67,9 @@ class cli_style_option
   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;
 
diff --git a/gdb/command.h b/gdb/command.h
index d901da3c8cb..eb3b2cc20d6 100644
--- a/gdb/command.h
+++ b/gdb/command.h
@@ -119,7 +119,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
   };
 
 /* Return true if a setting of type VAR_TYPE is backed with type T.
@@ -179,6 +181,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>
@@ -665,6 +675,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,
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 382df00ee7d..26c8f4c77c5 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -12638,6 +12638,15 @@ 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}
+Comma-separated list of 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}.
+
 @end table
 
 @node Convenience Funs
@@ -26605,16 +26614,46 @@ 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\\".
+
+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 @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\\".
+
+It is the responsibility of the user to verify that the terminal supports
+the specified colors.
 
 @item set style filename intensity @var{value}
 Set the intensity to @var{value}.  Valid intensities are @samp{normal}
diff --git a/gdb/doc/guile.texi b/gdb/doc/guile.texi
index 63916eed181..d906a0e7e84 100644
--- a/gdb/doc/guile.texi
+++ b/gdb/doc/guile.texi
@@ -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
@@ -2169,6 +2173,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
@@ -3780,6 +3792,96 @@ 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.
+
+@var{is_foreground} if this color should be applied to foreground or background
+@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 forms 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
diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index eeb847aeaa8..617fccc9ebd 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -4651,6 +4651,11 @@ except the special value -1 should be interpreted to mean
 @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
@@ -6241,6 +6246,96 @@ resolve this to the lazy string's character type, use the type's
 writable.
 @end defvar
 
+@node Colors In Python
+@subsection 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.
+
+@var{is_foreground} if this color should be applied to foreground or background
+@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 forms 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
diff --git a/gdb/guile/guile-internal.h b/gdb/guile/guile-internal.h
index 28e4889bfa9..1e4991a0186 100644
--- a/gdb/guile/guile-internal.h
+++ b/gdb/guile/guile-internal.h
@@ -446,6 +446,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;
@@ -624,6 +632,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);
diff --git a/gdb/guile/guile.c b/gdb/guile/guile.c
index e5565b627d9..90bb787c87d 100644
--- a/gdb/guile/guile.c
+++ b/gdb/guile/guile.c
@@ -593,6 +593,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
index 00000000000..249111630af
--- /dev/null
+++ b/gdb/guile/scm-color.c
@@ -0,0 +1,445 @@
+/* GDB parameters implemented in Guile.
+
+   Copyright (C) 2008-2022 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 "defs.h"
+#include "value.h"
+#include "charset.h"
+#include "gdbcmd.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 non-zero if COLORSPACE is a valid color space.  */
+
+static int
+coscm_valid_colorspace_p (int colorspace)
+{
+  for (int i = 0; colorspaces[i].name != NULL; ++i)
+    {
+      if (colorspaces[i].value == colorspace)
+	return 1;
+    }
+
+  return 0;
+}
+
+/* Return COLORSPACE as a string.  */
+
+static const char *
+coscm_colorspace_name (color_space colorspace)
+{
+  for (int i = 0; colorspaces[i].name != NULL; ++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 (coscm_valid_colorspace_p (colorspace_int))
+	colorspace = static_cast<color_space> (colorspace_int);
+      else
+	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 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");
+}
diff --git a/gdb/guile/scm-param.c b/gdb/guile/scm-param.c
index 54c8c27301a..60cc17432f0 100644
--- a/gdb/guile/scm-param.c
+++ b/gdb/guile/scm-param.c
@@ -48,6 +48,9 @@ union pascm_variable
 
   /* Hold a string, for enums.  */
   const char *cstringval;
+
+  /* Hold a color.  */
+  ui_file_style::color color;
 };
 
 /* A GDB parameter.
@@ -129,6 +132,8 @@ make_setting (param_smob *s)
     return setting (s->type, s->value.stringval);
   else if (var_type_uses<const char *> (s->type))
     return setting (s->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");
 }
@@ -190,10 +195,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;
@@ -466,6 +470,13 @@ add_setshow_generic (enum var_types param_type, enum command_class cmd_class,
 				       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");
     }
@@ -545,6 +556,7 @@ static const scheme_integer_constant parameter_types[] =
   { "PARAM_OPTIONAL_FILENAME", var_optional_filename },
   { "PARAM_FILENAME", var_filename },
   { "PARAM_ENUM", var_enum },
+  { "PARAM_COLOR", var_color },
 
   END_INTEGER_CONSTANTS
 };
@@ -611,6 +623,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> ())
@@ -716,6 +733,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"));
@@ -961,6 +984,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
index 00000000000..fb816a06328
--- /dev/null
+++ b/gdb/python/py-color.c
@@ -0,0 +1,346 @@
+/* Python interface to ui_file_style::color objects.
+
+   Copyright (C) 2008-2022 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 "defs.h"
+#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
+    CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("parmpy_object");
+
+/* 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 == NULL)
+    return NULL;
+
+  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 color.is_none () ? Py_True : Py_False;
+
+  if (! PyUnicode_CompareWithASCIIString (attr_name, "is_indexed"))
+    return color.is_indexed () ? Py_True : Py_False;
+
+  if (! PyUnicode_CompareWithASCIIString (attr_name, "is_direct"))
+    return color.is_direct () ? Py_True : Py_False;
+
+  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)
+	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 = PyObject_IsTrue (is_fg_obj);
+  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 (colorspace_id < INT_MIN || colorspace_id > INT_MAX)
+		error (_("colorspace %ld is out of range."), colorspace_id);
+	      colorspace = static_cast<color_space> (colorspace_id);
+	    }
+	  else if (colorspace_obj == Py_None)
+	    colorspace_obj = nullptr;
+	  else
+	    error (_("colorspace must be None or integer"));
+	}
+
+      if (!value_obj || 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 || 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 != 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)
+	    return -1;
+	  obj->color = parse_var_color (str.get());
+
+	  if (colorspace_obj && colorspace != obj->color.colorspace ())
+	    error (_("colorspace doesn't match to value."));
+	}
+      else
+	error (_("value must be one of None, integer, tuple or str."));
+    }
+  catch (const gdb_exception &except)
+    {
+      gdbpy_convert_exception (except);
+      return -1;
+    }
+
+  Py_INCREF (self);
+  return 0;
+}
+
+/* Deallocate function for a gdb.Color.  */
+
+static void
+colorpy_dealloc (PyObject *)
+{
+}
+
+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.  */
+int
+gdbpy_initialize_color (void)
+{
+  colorpy_object_type.tp_new = PyType_GenericNew;
+  if (PyType_Ready (&colorpy_object_type) < 0)
+    return -1;
+
+  for (auto & pair : colorspace_constants)
+      if (PyModule_AddIntConstant (gdb_module, pair.name,
+				   static_cast<long> (pair.value)) < 0)
+	return -1;
+
+  return gdb_pymodule_addobject (gdb_module, "Color",
+				 (PyObject *) &colorpy_object_type);
+}
+
+/* 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."},
+  {NULL}
+};
+
+PyTypeObject colorpy_object_type =
+{
+  PyVarObject_HEAD_INIT (NULL, 0)
+  "gdb.Color",			  /*tp_name*/
+  sizeof (colorpy_object),	  /*tp_basicsize*/
+  0,				  /*tp_itemsize*/
+  colorpy_dealloc,		  /*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 */
+};
diff --git a/gdb/python/py-color.h b/gdb/python/py-color.h
new file mode 100644
index 00000000000..95726faa19a
--- /dev/null
+++ b/gdb/python/py-color.h
@@ -0,0 +1,35 @@
+/* Python interface to ui_file_style::color objects.
+
+   Copyright (C) 2009-2022 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 */
diff --git a/gdb/python/py-param.c b/gdb/python/py-param.c
index 5d509ba4658..265b638416f 100644
--- a/gdb/python/py-param.c
+++ b/gdb/python/py-param.c
@@ -27,6 +27,7 @@
 #include "completer.h"
 #include "language.h"
 #include "arch-utils.h"
+#include "py-color.h"
 
 /* Parameter constants and their values.  */
 static struct {
@@ -46,6 +47,7 @@ static struct {
   { "PARAM_ZUINTEGER", var_zuinteger },
   { "PARAM_ZUINTEGER_UNLIMITED", var_zuinteger_unlimited },
   { "PARAM_ENUM", var_enum },
+  { "PARAM_COLOR", var_color },
   { NULL, 0 }
 };
 
@@ -70,6 +72,9 @@ union parmpy_variable
 
   /* Hold a string, for enums.  */
   const char *cstringval;
+
+  /* Hold a color.  */
+  ui_file_style::color color;
 };
 
 /* A GDB parameter.  */
@@ -108,6 +113,8 @@ make_setting (parmpy_object *s)
     return setting (s->type, s->value.stringval);
   else if (var_type_uses<const char *> (s->type))
     return setting (s->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");
 }
@@ -199,6 +206,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))
 	{
@@ -637,6 +657,15 @@ add_setshow_generic (int parmclass, enum command_class cmdclass,
 				       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.");
     }
@@ -758,7 +787,8 @@ parmpy_init (PyObject *self, PyObject *args, PyObject *kwds)
       && parmclass != var_string && parmclass != var_string_noescape
       && parmclass != var_optional_filename && parmclass != var_filename
       && parmclass != var_zinteger && parmclass != var_zuinteger
-      && parmclass != var_zuinteger_unlimited && parmclass != var_enum)
+      && parmclass != var_zuinteger_unlimited && parmclass != var_enum
+      && parmclass != var_color)
     {
       PyErr_SetString (PyExc_RuntimeError,
 		       _("Invalid parameter class argument."));
@@ -779,7 +809,7 @@ parmpy_init (PyObject *self, PyObject *args, PyObject *kwds)
   else
     obj->enumeration = NULL;
   obj->type = (enum var_types) parmclass;
-  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;
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index 08749d14200..f3877e04ffa 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -545,6 +545,8 @@ int gdbpy_initialize_micommands (void)
 void gdbpy_finalize_micommands ();
 int gdbpy_initialize_disasm ()
   CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
+int gdbpy_initialize_color (void)
+  CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
 
 PyMODINIT_FUNC gdbpy_events_mod_func ();
 
diff --git a/gdb/python/python.c b/gdb/python/python.c
index c7d4157b70c..0eb47cbd543 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -37,6 +37,7 @@
 #include "run-on-main-thread.h"
 #include "gdbsupport/selftest.h"
 #include "observable.h"
+#include "py-color.h"
 
 /* Declared constants and enum for python stack printing.  */
 static const char python_excp_none[] = "none";
@@ -484,6 +485,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> ())
@@ -2148,6 +2155,7 @@ do_start_initialization ()
       || gdbpy_initialize_membuf () < 0
       || gdbpy_initialize_connection () < 0
       || gdbpy_initialize_tui () < 0
+      || gdbpy_initialize_color () < 0
       || gdbpy_initialize_micommands () < 0)
     return false;
 
diff --git a/gdb/testsuite/gdb.base/style.exp b/gdb/testsuite/gdb.base/style.exp
index 2242c5bf743..775d1b45e12 100644
--- a/gdb/testsuite/gdb.base/style.exp
+++ b/gdb/testsuite/gdb.base/style.exp
@@ -68,10 +68,12 @@ proc clean_restart_and_disable { prefix args } {
 proc run_style_tests { } {
     global testfile srcfile hex binfile
     global currently_disabled_style decimal hex
+    global env
 
-    save_vars { env(TERM) } {
+    save_vars { env(TERM) env(COLORTERM) } {
 	# We need an ANSI-capable terminal to get the output.
-	setenv TERM ansi
+	setenv TERM xterm-256color
+	setenv COLORTERM truecolor
 
 	# Restart GDB with the correct TERM variable setting, this
 	# means that GDB will enable styling.
@@ -298,6 +300,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"
+	}
     }
 }
 
@@ -370,6 +387,188 @@ 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" {
+	global env
+	save_vars { env(TERM) env(COLORTERM) } {
+	    setenv TERM dumb
+	    setenv COLORTERM ""
+	    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" {
+	global env
+	save_vars { env(TERM) env(COLORTERM) } {
+	    setenv TERM ansi
+	    setenv COLORTERM ""
+	    gdb_exit
+	    gdb_start
+	    gdb_test "print \$_colorsupport" \
+		"\\\$1 = \"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" {
+	global env
+	save_vars { env(TERM) env(COLORTERM) } {
+	    setenv TERM xterm-256color
+	    setenv COLORTERM ""
+	    gdb_exit
+	    gdb_start
+	    gdb_test "print \$_colorsupport" \
+		"\\\$1 = \"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" {
+	global env
+	save_vars { env(TERM) env(COLORTERM) } {
+	    setenv TERM xterm-256color
+	    setenv COLORTERM truecolor
+	    gdb_exit
+	    gdb_start
+	    gdb_test "print \$_colorsupport" \
+		"\\\$1 = \"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" {
+	global env
+	save_vars { env(TERM) env(COLORTERM) } {
+	    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.
@@ -402,3 +601,9 @@ if { $python_disassembly_styling } {
 
 # 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
index 00000000000..e6e9dd4838c
--- /dev/null
+++ b/gdb/testsuite/gdb.guile/scm-color.exp
@@ -0,0 +1,114 @@
+# Copyright (C) 2010-2022 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
+
+# Start with a fresh gdb.
+gdb_exit
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+
+# Skip all tests if Guile scripting is not enabled.
+if { [skip_guile_tests] } { continue }
+
+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"
+
diff --git a/gdb/testsuite/gdb.guile/scm-parameter.exp b/gdb/testsuite/gdb.guile/scm-parameter.exp
index cf6f2834373..6ea540d16fd 100644
--- a/gdb/testsuite/gdb.guile/scm-parameter.exp
+++ b/gdb/testsuite/gdb.guile/scm-parameter.exp
@@ -206,3 +206,46 @@ with_test_prefix "previously-ambiguous" {
     gdb_test "help set print s" "This command is not documented." "set help"
     gdb_test "help set print" "set print s -- This command is not documented.*" "general help"
 }
+
+# Test a color parameter.
+
+save_vars { env(TERM) env(COLORTERM) } {
+  # 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" {
+      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"
+      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
index 00000000000..5764342d89d
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-color.exp
@@ -0,0 +1,101 @@
+# Copyright (C) 2010-2022 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
+
+# Start with a fresh gdb.
+clean_restart
+
+# Skip all tests if Python scripting is not enabled.
+if { [skip_python_tests] } { continue }
+
+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"
+
diff --git a/gdb/testsuite/gdb.python/py-parameter.exp b/gdb/testsuite/gdb.python/py-parameter.exp
index d6db6ac3bb1..9254380dbcb 100644
--- a/gdb/testsuite/gdb.python/py-parameter.exp
+++ b/gdb/testsuite/gdb.python/py-parameter.exp
@@ -177,6 +177,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
+    save_vars { env(TERM) env(COLORTERM) } {
+	# 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)" \
+	    ".*gdb.error: Palette color index 256 is out of range.*" "set invalid color value"
+    }
+}
+
 # Test a file parameter.
 proc_with_prefix test_file_parameter { } {
     clean_restart
@@ -391,6 +443,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
diff --git a/gdb/testsuite/lib/gdb-utils.exp b/gdb/testsuite/lib/gdb-utils.exp
index ffdfb75557c..e7bfbfbd29c 100644
--- a/gdb/testsuite/lib/gdb-utils.exp
+++ b/gdb/testsuite/lib/gdb-utils.exp
@@ -59,16 +59,20 @@ proc string_list_to_regexp { args } {
 # "function", "variable", or "address".
 
 proc style {str style} {
+    set fg 39
+    set bg 49
+    set intensity 22
+    set reverse 27
     switch -exact -- $style {
-	title { 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" }
+	title { 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 }
 	none { return $str }
     }
-    return "\033\\\[${style}m${str}\033\\\[m"
+    return "\033\\\[${fg};${bg};${intensity};${reverse}m${str}\033\\\[m"
 }
diff --git a/gdb/top.c b/gdb/top.c
index 60835acd5e5..b9d153d3933 100644
--- a/gdb/top.c
+++ b/gdb/top.c
@@ -2242,6 +2242,17 @@ init_gdb_version_vars (void)
   set_internalvar_integer (minor_version_var, vminor + (vrevision > 0));
 }
 
+static void
+init_colorsupport_var (void)
+{
+  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)
 {
@@ -2457,6 +2468,8 @@ gdb_init ()
 
   /* Create $_gdb_major and $_gdb_minor convenience variables.  */
   init_gdb_version_vars ();
+  /* Create $_colorsupport convenience variable.  */
+  init_colorsupport_var ();
 }
 
 void _initialize_top ();
diff --git a/gdb/ui-style.c b/gdb/ui-style.c
index f1a5b8c4101..f41f87ac67a 100644
--- a/gdb/ui-style.c
+++ b/gdb/ui-style.c
@@ -18,6 +18,7 @@
 
 #include "defs.h"
 #include "ui-style.h"
+#include "gdb_curses.h"
 #include "gdbsupport/gdb_regex.h"
 
 /* A regular expression that is used for matching ANSI terminal escape
@@ -47,91 +48,229 @@ 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
+};
+
+/* 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.  */
 
 bool
 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)
     {
       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));
     }
+  else
+    return false;
+
   return true;
 }
 
 /* See ui-style.h.  */
+std::string
+ui_file_style::color::to_ansi (bool is_fg) const
+{
+  std::string s = "\033[";
+  if (!append_ansi (is_fg, &s))
+    return {};
+  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.  */
+
+ui_file_style::color
+ui_file_style::color::approximate (const std::vector<color_space> &spaces) const
+{
+  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)
+    {
+      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 (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)
+    {
+      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;
+	}
+    }
+
+  return result;
 }
 
 /* See ui-style.h.  */
@@ -140,26 +279,23 @@ std::string
 ui_file_style::to_ansi () const
 {
   std::string result ("\033[");
-  bool need_semi = m_foreground.append_ansi (true, &result);
-  if (!m_background.is_none ())
+  if (!is_default ())
     {
+      bool need_semi = m_foreground.append_ansi (true, &result);
       if (need_semi)
 	result.push_back (';');
-      m_background.append_ansi (false, &result);
-      need_semi = true;
-    }
-  if (m_intensity != NORMAL)
-    {
+      need_semi = m_background.append_ansi (false, &result);
       if (need_semi)
 	result.push_back (';');
-      result.append (std::to_string (m_intensity));
-      need_semi = true;
-    }
-  if (m_reverse)
-    {
-      if (need_semi)
-	result.push_back (';');
-      result.push_back ('7');
+      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;
@@ -316,9 +452,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:
@@ -329,9 +467,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:
@@ -413,3 +553,44 @@ _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 ((char *)"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 && (!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";
+    }
+  error (_("Color space %d has no name."), static_cast<int> (c));
+}
diff --git a/gdb/ui-style.h b/gdb/ui-style.h
index fe1b2af611d..a481baf8ad5 100644
--- a/gdb/ui-style.h
+++ b/gdb/ui-style.h
@@ -19,6 +19,26 @@
 #ifndef UI_STYLE_H
 #define UI_STYLE_H
 
+/* One of the color spaces that usually supported by terminals.  */
+enum class color_space
+{
+  MONOCHROME, // one default terminal color
+  ANSI_8COLOR, // foreground colors \e[30m ... \e[37m,
+	       // background colors \e[40m ... \e[47m
+  AIXTERM_16COLOR, // foreground colors \e[30m ... \e[37m, \e[90m ... \e[97m
+		   // background colors \e[40m ... \e[47m, \e[100m ... \e107m
+  XTERM_256COLOR, // foreground colors \e[38;5;0m ... \e[38;5;255m
+		  // background colors \e[48;5;0m ... \e[48;5;255m
+  RGB_24BIT // 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
+};
+
+/* 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);
+
 /* Styles that can be applied to a ui_file.  */
 struct ui_file_style
 {
@@ -43,20 +63,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 +126,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 +157,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 the value of a basic color.  */
+    /* 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 simple color.  */
     int get_value () const
     {
-      gdb_assert (is_basic ());
+      gdb_assert (is_simple ());
       return m_value;
     }
 
@@ -119,13 +216,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).  */
+       false otherwise.  */
     bool 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 +342,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;
diff --git a/gdb/unittests/style-selftests.c b/gdb/unittests/style-selftests.c
index 8ad803594c3..34b0d80adc6 100644
--- a/gdb/unittests/style-selftests.c
+++ b/gdb/unittests/style-selftests.c
@@ -59,7 +59,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));
@@ -69,7 +69,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));
@@ -82,7 +82,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));
-- 
2.34.1


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

end of thread, other threads:[~2022-09-13  0:44 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-08-15 21:15 [PATCH] Add an option with a color type Andrei Pikas
2022-08-17 17:40 ` Andrew Burgess
2022-08-17 18:46   ` Andrei Pikas
2022-08-20 12:19   ` [PATCH v2] " Andrei Pikas
2022-08-20 12:43     ` Eli Zaretskii
2022-08-21 20:09       ` Andrei Pikas
2022-08-22  2:26         ` Eli Zaretskii
2022-08-21 21:07       ` [PATCH v3 (documentation fixed)] " Andrei Pikas
2022-08-22 11:54         ` Eli Zaretskii
2022-08-22 16:26           ` Philippe Waroquiers
2022-08-23 11:29         ` Andrew Burgess
2022-09-13  0:44           ` [PATCH v4 (documentation + approximation + convenience variable + python and guile types)] " Andrei Pikas
2022-08-17 17:57 ` [PATCH] " Eli Zaretskii

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).