[v8] Add an option with a color type.

Message ID 20241005192744.976782-1-gdb@mail.api.win
State New
Headers
Series [v8] Add an option with a color type. |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gdb_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_gdb_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_gdb_check--master-arm success Test passed
linaro-tcwg-bot/tcwg_gdb_check--master-aarch64 success Test passed

Commit Message

Andrei Pikas Oct. 5, 2024, 7:27 p.m. UTC
  Colors can be specified as "none" for terminal's default color, as a name of
one of the eight standard colors of ISO/IEC 6429 "black", "red", "green", etc.,
as an RGB hexadecimal tripplet #RRGGBB for 24-bit TrueColor, or as an
integer from 0 to 255.  Integers 0 to 7 are the synonyms for the standard
colors.  Integers 8-15 are used for the so-called bright colors from the
aixterm extended 16-color palette.  Integers 16-255 are the indexes into xterm
extended 256-color palette (usually 6x6x6 cube plus gray ramp).  In
general, 256-color palette is terminal dependent and sometimes can be
changed with OSC 4 sequences, e.g. "\033]4;1;rgb:00/FF/00\033\\".

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

PATCH v5 changes: documentation fixed.
PATCH v6 changes: documentation fixed.
PATCH v7 changes: rebase onto master and fixes after review.
PATCH v8 changes: fixes after review.
---
 gdb/Makefile.in                           |   2 +
 gdb/NEWS                                  |  31 ++
 gdb/cli/cli-cmds.c                        |   6 +
 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                       |  46 ++-
 gdb/doc/guile.texi                        | 104 ++++++
 gdb/doc/python.texi                       |  98 +++++
 gdb/guile/guile-internal.h                |   9 +
 gdb/guile/guile.c                         |   1 +
 gdb/guile/scm-color.c                     | 427 ++++++++++++++++++++++
 gdb/guile/scm-param.c                     |  33 +-
 gdb/python/py-color.c                     | 336 +++++++++++++++++
 gdb/python/py-color.h                     |  35 ++
 gdb/python/py-param.c                     |  40 +-
 gdb/python/python.c                       |   7 +
 gdb/testsuite/gdb.base/default.exp        |   8 +-
 gdb/testsuite/gdb.base/style.exp          | 197 ++++++++++
 gdb/testsuite/gdb.guile/scm-color.exp     | 110 ++++++
 gdb/testsuite/gdb.guile/scm-parameter.exp |  47 +++
 gdb/testsuite/gdb.python/py-color.exp     | 100 +++++
 gdb/testsuite/gdb.python/py-parameter.exp |  53 +++
 gdb/testsuite/lib/gdb-utils.exp           |  24 +-
 gdb/testsuite/lib/gdb.exp                 |   3 +-
 gdb/top.c                                 |  14 +
 gdb/ui-style.c                            | 353 ++++++++++++++----
 gdb/ui-style.h                            | 157 +++++++-
 gdb/unittests/style-selftests.c           |   6 +-
 34 files changed, 2436 insertions(+), 171 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
  

Comments

Eli Zaretskii Oct. 6, 2024, 5:40 a.m. UTC | #1
> From: Andrei Pikas <gdb@mail.api.win>
> Cc: gdb-patches@sourceware.org,
> 	Andrei Pikas <gdb@mail.api.win>
> Date: Sat,  5 Oct 2024 22:27:44 +0300
> 
>  gdb/Makefile.in                           |   2 +
>  gdb/NEWS                                  |  31 ++
>  gdb/cli/cli-cmds.c                        |   6 +
>  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                       |  46 ++-
>  gdb/doc/guile.texi                        | 104 ++++++
>  gdb/doc/python.texi                       |  98 +++++
>  gdb/guile/guile-internal.h                |   9 +
>  gdb/guile/guile.c                         |   1 +
>  gdb/guile/scm-color.c                     | 427 ++++++++++++++++++++++
>  gdb/guile/scm-param.c                     |  33 +-
>  gdb/python/py-color.c                     | 336 +++++++++++++++++
>  gdb/python/py-color.h                     |  35 ++
>  gdb/python/py-param.c                     |  40 +-
>  gdb/python/python.c                       |   7 +
>  gdb/testsuite/gdb.base/default.exp        |   8 +-
>  gdb/testsuite/gdb.base/style.exp          | 197 ++++++++++
>  gdb/testsuite/gdb.guile/scm-color.exp     | 110 ++++++
>  gdb/testsuite/gdb.guile/scm-parameter.exp |  47 +++
>  gdb/testsuite/gdb.python/py-color.exp     | 100 +++++
>  gdb/testsuite/gdb.python/py-parameter.exp |  53 +++
>  gdb/testsuite/lib/gdb-utils.exp           |  24 +-
>  gdb/testsuite/lib/gdb.exp                 |   3 +-
>  gdb/top.c                                 |  14 +
>  gdb/ui-style.c                            | 353 ++++++++++++++----
>  gdb/ui-style.h                            | 157 +++++++-
>  gdb/unittests/style-selftests.c           |   6 +-
>  34 files changed, 2436 insertions(+), 171 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

Thanks, some comments for the documentation parts below.

> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -49,11 +49,42 @@
>    history has been reached.  It also specifies that the forward execution can
>    continue, and the recording will also continue.
>  
> +* "set style" commands now supports numeric format for basic colors
> +  from 0 to 255 and #RRGGBB format for TrueColor.
> +
> +* New built-in convenience variable $_colorsupport provides comma-separated
> +  list of color space names supported by terminal.  Each color space name is one
> +  of monochrome, ansi_8color, aixterm_16color, xterm_256color or rgb_24bit.
> +  It is handy for conditionally using styling colors based on terminal features.
> +  For example:
> +
> +  (gdb) if $_regex ($_colorsupport, ".*(^|,)rgb_24bit($|,).*")
> +   >set style filename background #FACADE
> +   >else
> +   >if $_regex ($_colorsupport, ".*(^|,)xterm_256color($|,).*")
> +    >set style filename background 224
> +    >else
> +    >set style filename background red
> +    >end
> +   >end
> +
>  * Python API
>  
>    ** Added gdb.record.clear.  Clears the trace data of the current recording.
>       This forces re-decoding of the trace for successive commands.
>  
> +  ** 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.
> +
>  * Debugger Adapter Protocol changes

The NEWS part is okay.

> +@item $_colorsupport
> +@vindex $_colorsupport@r{, convenience variable}
> +@cindex color space
> +Comma-separated list of @dfn{color space} names supported by terminal.  Names

Please always put all the index commands _before_ @item, not after it.
This is so following the index entry in an Info reader places you at
the line corresponding to @item and not on the line after that.

>  @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}.

To make sure the reader understands that these 8 names are what is
mentioned earlier as "the eight standard colors", I suggest to start
this paragraph with "Valid names of the eight standard colors are..."

> +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.

As followup to my previous questions: can these bright colors also be
used on the Windows terminal?

Also, shouldn't we document in the manual the names of those bright
colors?

> Integers 16-255 are the indexes into xterm extended 256-color palette
> +(usually 6x6x6 cube plus gray ramp).  In general, 256-color palette is terminal
> +dependent and sometimes can be changed with OSC 4 sequences, e.g.
> +"\033]4;1;rgb:00/FF/00\033\\". A hexadecimal 24-bit TrueColor is specified in
                             ^^ ^^
What is that double backslash there? are those two literal
backslashes?  Also, please leave two spaces between sentences.

> +@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

This text repeats what you say below in "Colors in Guile".  It is
better to replace it with a cross-reference to that mode, saying here
just that the value can be either an integer or a string.

Btw, since this is describing a programmatic interface, should we also
say what happens if the value is an integer or a string that the
terminal does not support?

> +@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.                                ^^^^^^^^^^^^^^^^
   ^^^^^^^^^^
This is the first and the only time you mention tuple of components in
the Guile section.  If tuples are indeed allowed, they should be
mentioned in the other places and also documented in more detail.

> +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.

COLOR-SPACE is an optional argument, right?  If so, this should
document what happens if it is omitted.

More generally, I'm a bit confused by the notion of "palette" or
"color space" (which are the same, right?).  If I have a color whose
index is N < 8, doesn't that refer to the same color regardless of the
palette?  And if I specify a color with #RRGGBB whose value exactly
corresponds to one of the 8 standard colors, don't I get the same
color regardless of the palette?  If so, it sounds like the palette
only makes a difference when the color is specified by a value (index
or RGB string) that does NOT belong to the palette, right?  And if so,
why do we at all need this notion of the palette in GDB?

> +@deffn {Scheme Procedure} color-none? color
> +Return @code{#t} if @var{color} is terminal's default.

Is it a good idea to use 'color-none?' as the name? why not
color-default?  Also, AFAIU a terminal has two default colors: one for
the foreground, the other for the background.  How does this procedure
know which of these two is the caller asking about?

> +@deffn {Scheme Procedure} color-indexed? color
> +Return @code{#t} if @var{color} is indexed, i.e. belongs to some palette.

Please use "i.e.@:" or "i.e.,".  Otherwise, TeX could decide that the
period ends the sentence.

> +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

I'm a bit confused about these predicates: don't at least some,
perhaps even all, of the colors satisfy both indexed? and direct?
predicates?  If so, when will these return #f?  And why have these
predicates in the first place? when will they be useful?

> +@deffn {Scheme Procedure} color-string color
> +Return the textual representation of a @code{<gdb:color>} object.
> +@end deffn

This should probably explain what is the returned string.  I'm
guessing it's the name for the 8 standard colors (or maybe for the 16
standard colors of a 16-color terminal), and #RRGGBB for the rest, but
the manual should tell that explicitly.

> +@deffn {Scheme Procedure} color-index color
> +Return index of the color of a @code{<gdb:color>} object in a palette.
> +@end deffn

What does this return for TrueColor colors?

> +@deffn {Scheme Procedure} color-components color
> +Return components of the direct @code{<gdb:color>} object.
> +@end deffn

Please document the form of the return value, i.e. how the components
are returned.

> +@deffn {Scheme Procedure} color-escape-sequence color is_foreground
> +Return string to change terminal's color to this.
          ^^^^^^
"the string"

> +When color is initialized, its color space must be specified.

What do you mean by "color is initialized"?  This is the first time a
color's initialization is mentioned, and it must be explained.

> +@vtable @code
> +@item COLORSPACE_MONOCHROME
> +Palette with only terminal's default color.

Two colors, right?  One for foreground, the other for background.  Or
does this refer only to the foreground color?

> +@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.
                                                              ^^^^^^^
"versions".

> +@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

Same questions/comments here as for the equivalent Guile APIs.

> +
> +@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

The corresponding Guile APIs didn't include the conditions under which
the values are significant.  Why not?

> +When color is initialized, its color space must be specified.  The

Does "initialized" refer to Color.__init__?  If so, I think this
should be said explicitly, perhaps where that function is described.

Finally, I see from the code that we sometimes approximate the
requested color (which is reasonable), but I see nothing about that in
the documentation.  I think we should say something about that in the
manual.
  

Patch

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index bcf1ee45a70..502b4b6f9e5 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -359,6 +359,7 @@  SUBDIR_GUILE_SRCS = \
 	guile/scm-block.c \
 	guile/scm-breakpoint.c \
 	guile/scm-cmd.c \
+	guile/scm-color.c \
 	guile/scm-disasm.c \
 	guile/scm-exception.c \
 	guile/scm-frame.c \
@@ -395,6 +396,7 @@  SUBDIR_PYTHON_SRCS = \
 	python/py-bpevent.c \
 	python/py-breakpoint.c \
 	python/py-cmd.c \
+	python/py-color.c \
 	python/py-connection.c \
 	python/py-continueevent.c \
 	python/py-dap.c \
diff --git a/gdb/NEWS b/gdb/NEWS
index 3a0c8d1049f..f0b17000401 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -49,11 +49,42 @@ 
   history has been reached.  It also specifies that the forward execution can
   continue, and the recording will also continue.
 
+* "set style" commands now supports numeric format for basic colors
+  from 0 to 255 and #RRGGBB format for TrueColor.
+
+* New built-in convenience variable $_colorsupport provides comma-separated
+  list of color space names supported by terminal.  Each color space name is one
+  of monochrome, ansi_8color, aixterm_16color, xterm_256color or rgb_24bit.
+  It is handy for conditionally using styling colors based on terminal features.
+  For example:
+
+  (gdb) if $_regex ($_colorsupport, ".*(^|,)rgb_24bit($|,).*")
+   >set style filename background #FACADE
+   >else
+   >if $_regex ($_colorsupport, ".*(^|,)xterm_256color($|,).*")
+    >set style filename background 224
+    >else
+    >set style filename background red
+    >end
+   >end
+
 * Python API
 
   ** Added gdb.record.clear.  Clears the trace data of the current recording.
      This forces re-decoding of the trace for successive commands.
 
+  ** 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.
+
 * Debugger Adapter Protocol changes
 
   ** The "scopes" request will now return a scope holding global
diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c
index ea2e156a00c..12d5ac5fe45 100644
--- a/gdb/cli/cli-cmds.c
+++ b/gdb/cli/cli-cmds.c
@@ -2390,6 +2390,11 @@  value_from_setting (const setting &var, struct gdbarch *gdbarch)
 
 	return current_language->value_string (gdbarch, value, len);
       }
+    case var_color:
+      {
+	std::string s = var.get<ui_file_style::color> ().to_string ();
+	return current_language->value_string (gdbarch, s.c_str (), s.size ());
+      }
     default:
       gdb_assert_not_reached ("bad var_type");
     }
@@ -2437,6 +2442,7 @@  str_value_from_setting (const setting &var, struct gdbarch *gdbarch)
     case var_pinteger:
     case var_boolean:
     case var_auto_boolean:
+    case var_color:
       {
 	std::string cmd_val = get_setshow_command_value_string (var);
 
diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
index 163012a6bec..527a96ed3e4 100644
--- a/gdb/cli/cli-decode.c
+++ b/gdb/cli/cli-decode.c
@@ -23,6 +23,7 @@ 
 #include "cli/cli-cmds.h"
 #include "cli/cli-decode.h"
 #include "cli/cli-style.h"
+#include "cli/cli-utils.h"
 #include <optional>
 
 /* Prototypes for local functions.  */
@@ -739,6 +740,87 @@  add_setshow_enum_cmd (const char *name, command_class theclass,
   return cmds;
 }
 
+/* See cli-decode.h.  */
+
+void
+complete_on_color (completion_tracker &tracker,
+		   const char *text, const char *word)
+{
+  complete_on_enum (tracker, ui_file_style::basic_color_enums.data (),
+		    text, word);
+  if (*text == '\0')
+    {
+      /* Convenience to let the user know what the option
+	 can accept.  Note there's no common prefix between
+	 the strings on purpose, so that complete_on_enum doesn't do
+	 a partial match.  */
+      tracker.add_completion (make_unique_xstrdup ("NUMBER"));
+      tracker.add_completion (make_unique_xstrdup ("#RRGGBB"));
+    }
+}
+
+/* Completer used in color commands.  */
+
+static void
+color_completer (struct cmd_list_element *ignore,
+		 completion_tracker &tracker,
+		 const char *text, const char *word)
+{
+  complete_on_color (tracker, text, word);
+}
+
+
+/* Add element named NAME to command list LIST (the list for set or
+   some sublist thereof).  CLASS is as in add_cmd.  VAR is address
+   of the variable which will contain the color.  */
+
+set_show_commands
+add_setshow_color_cmd (const char *name,
+		       enum command_class theclass,
+		       ui_file_style::color *var,
+		       const char *set_doc,
+		       const char *show_doc,
+		       const char *help_doc,
+		       cmd_func_ftype *set_func,
+		       show_value_ftype *show_func,
+		       struct cmd_list_element **set_list,
+		       struct cmd_list_element **show_list)
+{
+  set_show_commands commands = add_setshow_cmd_full<ui_file_style::color>
+    (name, theclass, var_color, var,
+     set_doc, show_doc, help_doc,
+     nullptr, nullptr, set_func, show_func,
+     set_list, show_list);
+
+  set_cmd_completer (commands.set, color_completer);
+
+  return commands;
+}
+
+/* Same as above but using a getter and a setter function instead of a pointer
+   to a global storage buffer.  */
+
+set_show_commands
+add_setshow_color_cmd (const char *name, command_class theclass,
+		       const char *set_doc, const char *show_doc,
+		       const char *help_doc,
+		       setting_func_types<ui_file_style::color>::set set_func,
+		       setting_func_types<ui_file_style::color>::get get_func,
+		       show_value_ftype *show_func,
+		       cmd_list_element **set_list,
+		       cmd_list_element **show_list)
+{
+  auto cmds = add_setshow_cmd_full<ui_file_style::color>
+    (name, theclass, var_color, nullptr,
+     set_doc, show_doc, help_doc,
+     set_func, get_func, nullptr, show_func,
+     set_list, show_list);
+
+  set_cmd_completer (cmds.set, color_completer);
+
+  return cmds;
+}
+
 /* See cli-decode.h.  */
 const char * const auto_boolean_enums[] = { "on", "off", "auto", NULL };
 
@@ -2749,3 +2831,95 @@  cli_user_command_p (struct cmd_list_element *cmd)
 {
   return cmd->theclass == class_user && cmd->func == do_simple_func;
 }
+
+/* See cli-decode.h.  */
+
+ui_file_style::color
+parse_cli_var_color (const char **args)
+{
+  /* Do a "set" command.  ARG is nullptr if no argument, or the
+     text of the argument.  */
+
+  if (args == nullptr || *args == nullptr || **args == '\0')
+    {
+      std::string msg;
+
+      for (size_t i = 0; ui_file_style::basic_color_enums[i]; ++i)
+	{
+	  msg.append ("\"");
+	  msg.append (ui_file_style::basic_color_enums[i]);
+	  msg.append ("\", ");
+	}
+
+      error (_("Requires an argument. Valid arguments are %sinteger from -1 "
+	     "to 255 or an RGB hex triplet in a format #RRGGBB"),
+	     msg.c_str ());
+    }
+
+  const char *p = skip_to_space (*args);
+  size_t len = p - *args;
+
+  int nmatches = 0;
+  ui_file_style::basic_color match = ui_file_style::NONE;
+  for (int i = 0; ui_file_style::basic_color_enums[i]; ++i)
+    if (strncmp (*args, ui_file_style::basic_color_enums[i], len) == 0)
+      {
+	match = static_cast<ui_file_style::basic_color> (i - 1);
+	if (ui_file_style::basic_color_enums[i][len] == '\0')
+	  {
+	    nmatches = 1;
+	    break; /* Exact match.  */
+	  }
+	else
+	  nmatches++;
+      }
+
+  if (nmatches == 1)
+    {
+      *args += len;
+      return ui_file_style::color (match);
+    }
+
+  if (nmatches > 1)
+    error (_("Ambiguous item \"%.*s\"."), (int) len, *args);
+
+  if (**args != '#')
+    {
+      ULONGEST num = get_ulongest (args);
+      if (num > 255)
+	error (_("integer %s out of range"), pulongest (num));
+      return ui_file_style::color (color_space::XTERM_256COLOR,
+				   static_cast<int> (num));
+    }
+
+  /* Try to parse #RRGGBB string.  */
+  if (len != 7)
+    error_no_arg (_("invalid RGB hex triplet format"));
+
+  uint8_t r, g, b;
+  int scanned_chars = 0;
+  int parsed_args = sscanf (*args, "#%2" SCNx8 "%2" SCNx8 "%2" SCNx8 "%n",
+			    &r, &g, &b, &scanned_chars);
+
+  if (parsed_args != 3 || scanned_chars != 7)
+    error_no_arg (_("invalid RGB hex triplet format"));
+
+  *args += len;
+  return ui_file_style::color (r, g, b);
+}
+
+/* See cli-decode.h.  */
+
+ui_file_style::color
+parse_var_color (const char *arg)
+{
+  const char *end_arg = arg;
+  ui_file_style::color color = parse_cli_var_color (&end_arg);
+
+  int len = end_arg - arg;
+  const char *after = skip_spaces (end_arg);
+  if (*after != '\0')
+    error (_("Junk after item \"%.*s\": %s"), len, arg, after);
+
+  return color;
+}
diff --git a/gdb/cli/cli-decode.h b/gdb/cli/cli-decode.h
index 7365c3f0157..5918d3b9dbe 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 05539285c80..3a165ef6d2c 100644
--- a/gdb/cli/cli-option.c
+++ b/gdb/cli/cli-option.c
@@ -45,6 +45,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.  */
@@ -433,6 +436,35 @@  parse_option (gdb::array_view<const option_def_group> options_group,
 	val.enumeration = parse_cli_var_enum (args, match->enums);
 	return option_def_and_value {*match, match_ctx, val};
       }
+    case var_color:
+      {
+	if (completion != nullptr)
+	  {
+	    const char *after_arg = skip_to_space (*args);
+	    if (*after_arg == '\0')
+	      {
+		complete_on_color (completion->tracker, *args, *args);
+
+		if (completion->tracker.have_completions ())
+		  return {};
+	      }
+	  }
+
+	if (check_for_argument (args, "--"))
+	  {
+	    /* Treat e.g., "backtrace -entry-values --" as if there
+	       was no argument after "-entry-values".  This makes
+	       parse_cli_var_color throw an error with a suggestion of
+	       what are the valid options.  */
+	    args = nullptr;
+	  }
+
+	option_value val;
+	ui_file_style::color color = parse_cli_var_color (args);
+	ui_file_style::color approx_color = color.approximate (colorsupport ());
+	val.color = approx_color;
+	return option_def_and_value {*match, match_ctx, val};
+      }
     case var_string:
       {
 	if (check_for_argument (args, "--"))
@@ -611,6 +643,10 @@  save_option_value_in_ctx (std::optional<option_def_and_value> &ov)
       *ov->option.var_address.enumeration (ov->option, ov->ctx)
 	= ov->value->enumeration;
       break;
+    case var_color:
+      *ov->option.var_address.color (ov->option, ov->ctx)
+	= ov->value->color;
+      break;
     case var_string:
       *ov->option.var_address.string (ov->option, ov->ctx)
 	= std::move (*ov->value->string);
@@ -699,6 +735,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 bbe281d9721..2fc354ef1a2 100644
--- a/gdb/cli/cli-option.h
+++ b/gdb/cli/cli-option.h
@@ -91,6 +91,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;
 
@@ -308,6 +309,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 bcc793b3148..a8f22c9bde6 100644
--- a/gdb/cli/cli-setshow.c
+++ b/gdb/cli/cli-setshow.c
@@ -443,6 +443,13 @@  do_set_command (const char *arg, int from_tty, struct cmd_list_element *c)
 	option_changed = c->var->set<const char *> (match);
       }
       break;
+    case var_color:
+      {
+	ui_file_style::color color = parse_var_color (arg);
+	ui_file_style::color approx_color = color.approximate (colorsupport ());
+	option_changed = c->var->set<ui_file_style::color> (approx_color);
+      }
+      break;
     default:
       error (_("gdb internal error: bad var_type in do_setshow_command"));
     }
@@ -520,6 +527,14 @@  do_set_command (const char *arg, int from_tty, struct cmd_list_element *c)
 	  interps_notify_param_changed
 	    (name, c->var->get<const char *> ());
 	  break;
+	case var_color:
+	  {
+	    const ui_file_style::color &color
+	      = c->var->get<ui_file_style::color> ();
+	    interps_notify_param_changed
+	      (name, color.to_string ().c_str ());
+	  }
+	  break;
 	case var_boolean:
 	  {
 	    const char *opt = c->var->get<bool> () ? "on" : "off";
@@ -585,6 +600,12 @@  get_setshow_command_value_string (const setting &var)
 	  stb.puts (value);
       }
       break;
+    case var_color:
+      {
+	const ui_file_style::color &value = var.get<ui_file_style::color> ();
+	stb.puts (value.to_string ().c_str ());
+      }
+      break;
     case var_boolean:
       stb.puts (var.get<bool> () ? "on" : "off");
       break;
diff --git a/gdb/cli/cli-style.c b/gdb/cli/cli-style.c
index 36a8bd9c526..8a47daadcf9 100644
--- a/gdb/cli/cli-style.c
+++ b/gdb/cli/cli-style.c
@@ -42,20 +42,6 @@  bool source_styling = true;
 
 bool disassembler_styling = true;
 
-/* Name of colors; must correspond to ui_file_style::basic_color.  */
-static const char * const cli_colors[] = {
-  "none",
-  "black",
-  "red",
-  "green",
-  "yellow",
-  "blue",
-  "magenta",
-  "cyan",
-  "white",
-  nullptr
-};
-
 /* Names of intensities; must correspond to
    ui_file_style::intensity.  */
 static const char * const cli_intensities[] = {
@@ -135,8 +121,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])
 {
 }
@@ -147,32 +133,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)
@@ -184,7 +155,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.  */
@@ -257,9 +228,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,
@@ -269,9 +239,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 5052b867cfa..5af299f1c6f 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 0ceaf3e2a6d..715fcd8096f 100644
--- a/gdb/command.h
+++ b/gdb/command.h
@@ -111,7 +111,9 @@  enum var_types
     /* Enumerated type.  Can only have one of the specified values.
        *VAR is a char pointer to the name of the element that we
        find.  */
-    var_enum
+    var_enum,
+    /* Color type.  *VAR is a ui_file_style::color structure.  */
+    var_color
   };
 
 /* A structure describing an extra literal accepted and shown in place
@@ -185,6 +187,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>
@@ -681,6 +691,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 cc1b69c6978..e49a0425e59 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -12973,6 +12973,20 @@  and @code{$_shell_exitsignal} according to the exit status of the last
 launched command.  These variables are set and used similarly to
 the variables @code{$_exitcode} and @code{$_exitsignal}.
 
+@item $_colorsupport
+@vindex $_colorsupport@r{, convenience variable}
+@cindex color space
+Comma-separated list of @dfn{color space} names supported by terminal.  Names
+could be any of @samp{monochrome}, @samp{ansi_8color}, @samp{aixterm_16color},
+@samp{xterm_256color}, @samp{rgb_24bit}.  E.g., for plain linux terminal the
+value could be @samp{monochrome,ansi_8color} and for terminal with truecolor
+support it could be
+@samp{monochrome,ansi_8color,aixterm_16color,xterm_256color,rgb_24bit}.
+@samp{rgb_24bit} availability is determined by the @env{COLORTERM} environment
+variable which may be @samp{truecolor} or @samp{24bit}. Other color spaces are
+determined by the "Co" termcap which in turn depends on the @env{TERM}
+environment variable.
+
 @end table
 
 @node Convenience Funs
@@ -27815,16 +27829,32 @@  For example, the style of file names can be controlled using the
 
 @table @code
 @item set style filename background @var{color}
-Set the background to @var{color}.  Valid colors are @samp{none}
-(meaning the terminal's default color), @samp{black}, @samp{red},
-@samp{green}, @samp{yellow}, @samp{blue}, @samp{magenta}, @samp{cyan},
-and@samp{white}.
+Set the background to @var{color}.  @var{color} can be @samp{none}
+(meaning the terminal's default color), a name of one of the eight standard
+colors of ISO/IEC 6429, index from 0 to 255 into terminal's color
+palette or a hexadecimal RGB triplet in @samp{#RRGGBB} format for
+24-bit TrueColor.
+
+Valid color names are @samp{black}, @samp{red}, @samp{green},
+@samp{yellow}, @samp{blue}, @samp{magenta}, @samp{cyan}, and
+@samp{white}.
+
+Integers 0 to 7 are the synonyms for the standard colors.  Integers 8-15 are
+used for the so-called bright colors from the aixterm extended 16-color
+palette.  Integers 16-255 are the indexes into xterm extended 256-color palette
+(usually 6x6x6 cube plus gray ramp).  In general, 256-color palette is terminal
+dependent and sometimes can be changed with OSC 4 sequences, e.g.
+"\033]4;1;rgb:00/FF/00\033\\". A hexadecimal 24-bit TrueColor is specified in
+the format @samp{#RRGGBB} where RR, GG and BB are the 2-digit hexadecimal
+integers specifiing the intensity of the red, green and blue color components,
+respectively.
+
+It is the responsibility of the user to verify that the terminal supports
+the specified colors.
 
 @item set style filename foreground @var{color}
-Set the foreground to @var{color}.  Valid colors are @samp{none}
-(meaning the terminal's default color), @samp{black}, @samp{red},
-@samp{green}, @samp{yellow}, @samp{blue}, @samp{magenta}, @samp{cyan},
-and@samp{white}.
+Set the foreground to @var{color}.  @var{color} can be given in the same ways
+as for the background.
 
 @item set style filename intensity @var{value}
 Set the intensity to @var{value}.  Valid intensities are @samp{normal}
diff --git a/gdb/doc/guile.texi b/gdb/doc/guile.texi
index a66e8bfe16b..0cb01663d6e 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
@@ -2184,6 +2188,14 @@  The value is a filename.  This is just like
 @item PARAM_ENUM
 The value is a string, which must be one of a collection of string
 constants provided when the parameter is created.
+
+@item PARAM_COLOR
+The value is either a string or an unsigned integer.  Integer from 0 to 255
+means index into terminal's color palette.  String can be a hex RGB triplet in
+@samp{#RRGGBB} format or one of the following color names:
+@samp{none} (meaning the terminal's default color), @samp{black}, @samp{red},
+@samp{green}, @samp{yellow}, @samp{blue}, @samp{magenta}, @samp{cyan},
+or @samp{white}.
 @end vtable
 
 @node Progspaces In Guile
@@ -3820,6 +3832,98 @@  Run @var{iterator} until the result of @code{(pred element)} is true
 and return that as the result.  Otherwise return @code{#f}.
 @end deffn
 
+@node Colors In Guile
+@subsubsection Colors In Guile
+
+@cindex guile colors
+@tindex <gdb:color>
+You can assign instance of @code{<gdb:color>} to the value of
+a @code{<gdb:parameter>} object created with @code{PARAM_COLOR}.
+
+@code{<gdb:color>} may refer to an index from color palette or contain
+components of a color from some colorspace.
+
+@deffn {Scheme Procedure} make-color @w{@r{[}value} @
+    @w{@r{[}#:color-space color-space@r{]}@r{]}}
+
+@var{value} is an integer index of a color in palette, tuple with color
+components or a string.  String can be a hex RGB triplet in @samp{#RRGGBB}
+format or one of the following color names:
+@samp{none} (meaning the terminal's default color), @samp{black}, @samp{red},
+@samp{green}, @samp{yellow}, @samp{blue}, @samp{magenta}, @samp{cyan},
+or @samp{white}.
+
+@var{color-space} should be one of the @samp{COLORSPACE_} constants.  This
+argument tells @value{GDBN} which color space @var{value} belongs.
+@end deffn
+
+@deffn {Scheme Procedure} color? object
+Return @code{#t} if @var{object} is a @code{<gdb:color>} object.
+Otherwise return @code{#f}.
+@end deffn
+
+@deffn {Scheme Procedure} color-none? color
+Return @code{#t} if @var{color} is terminal's default.
+Otherwise return @code{#f}.
+@end deffn
+
+@deffn {Scheme Procedure} color-indexed? color
+Return @code{#t} if @var{color} is indexed, i.e. belongs to some palette.
+Otherwise return @code{#f}.
+@end deffn
+
+@deffn {Scheme Procedure} color-direct? color
+Return @code{#t} if @var{color} is direct in the sense of ISO/IEC 8613-6.
+Otherwise return @code{#f}.
+@end deffn
+
+@deffn {Scheme Procedure} color-string color
+Return the textual representation of a @code{<gdb:color>} object.
+@end deffn
+
+@deffn {Scheme Procedure} color-colorspace color
+Return the color space of a @code{<gdb:color>} object.
+@end deffn
+
+@deffn {Scheme Procedure} color-index color
+Return index of the color of a @code{<gdb:color>} object in a palette.
+@end deffn
+
+@deffn {Scheme Procedure} color-components color
+Return components of the direct @code{<gdb:color>} object.
+@end deffn
+
+@deffn {Scheme Procedure} color-escape-sequence color is_foreground
+Return string to change terminal's color to this.
+
+If @var{is_foreground} is @code{#t}, then the returned sequence will change
+foreground color.  Otherwise, the returned sequence will change background
+color.
+@end deffn
+
+When color is initialized, its color space must be specified.  The
+available color spaces are represented by constants defined in the @code{gdb}
+module:
+
+@vtable @code
+@item COLORSPACE_MONOCHROME
+Palette with only terminal's default color.
+
+@item COLORSPACE_ANSI_8COLOR
+Palette with eight standard colors of ISO/IEC 6429 "black", "red", "green", etc.
+
+@item COLORSPACE_AIXTERM_16COLOR
+Palette with 16 colors.  First eight are standard colors of ISO/IEC 6429
+"black", "red", "green", etc.  Next eight are their bright version.
+
+@item COLORSPACE_XTERM_256COLOR
+Palette with 256 colors.  First 16 are from COLORSPACE_AIXTERM_16COLOR.  Next
+216 colors are 6x6x6 RGB cube.  And last 24 colors form grayscale ramp.
+
+@item COLORSPACE_RGB_24BIT
+Direct 24-bit RGB colors.
+@end vtable
+
 @node Guile Auto-loading
 @subsection Guile Auto-loading
 @cindex guile auto-loading
diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index 22f0e6c6d0a..e27bc68bf20 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -225,6 +225,7 @@  optional arguments while skipping others.  Example:
 * Finish Breakpoints in Python:: Setting Breakpoints on function return
                                 using Python.
 * Lazy Strings In Python::      Python representation of lazy strings.
+* Colors In Python::            Python representation of colors.
 * Architectures In Python::     Python representation of architectures.
 * Registers In Python::         Python representation of registers.
 * Connections In Python::       Python representation of connections.
@@ -5230,6 +5231,11 @@  except the special value -1 is returned for the setting of ``unlimited''.
 @item gdb.PARAM_ENUM
 The value is a string, which must be one of a collection string
 constants provided when the parameter is created.
+
+@findex PARAM_COLOR
+@findex gdb.PARAM_COLOR
+@item gdb.PARAM_COLOR
+The value is @code{gdb.Color} instance.
 @end table
 
 @node Functions In Python
@@ -6999,6 +7005,98 @@  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.
+
+If @var{is_foreground} is @code{True}, then the returned sequence will change
+foreground color.  Otherwise, the returned sequence will change background
+color.
+@end defun
+
+When color is initialized, its color space must be specified.  The
+available color spaces are represented by constants defined in the @code{gdb}
+module:
+
+@table @code
+@findex COLORSPACE_MONOCHROME
+@findex gdb.COLORSPACE_MONOCHROME
+@item gdb.COLORSPACE_MONOCHROME
+Palette with only terminal's default color.
+
+@findex COLORSPACE_ANSI_8COLOR
+@findex gdb.COLORSPACE_ANSI_8COLOR
+@item gdb.COLORSPACE_ANSI_8COLOR
+Palette with eight standard colors of ISO/IEC 6429 "black", "red", "green", etc.
+
+@findex COLORSPACE_AIXTERM_16COLOR
+@findex gdb.COLORSPACE_AIXTERM_16COLOR
+@item gdb.COLORSPACE_AIXTERM_16COLOR
+Palette with 16 colors.  First eight are standard colors of ISO/IEC 6429
+"black", "red", "green", etc.  Next eight are their bright version.
+
+@findex COLORSPACE_XTERM_256COLOR
+@findex gdb.COLORSPACE_XTERM_256COLOR
+@item gdb.COLORSPACE_XTERM_256COLOR
+Palette with 256 colors.  First 16 are from COLORSPACE_AIXTERM_16COLOR.  Next
+216 colors are 6x6x6 RGB cube.  And last 24 colors form grayscale ramp.
+
+@findex COLORSPACE_RGB_24BIT
+@findex gdb.COLORSPACE_RGB_24BIT
+@item gdb.COLORSPACE_RGB_24BIT
+Direct 24-bit RGB colors.
+
+@end table
+
 @node Architectures In Python
 @subsubsection Python representation of architectures
 @cindex Python architectures
diff --git a/gdb/guile/guile-internal.h b/gdb/guile/guile-internal.h
index dea78845088..b137713d133 100644
--- a/gdb/guile/guile-internal.h
+++ b/gdb/guile/guile-internal.h
@@ -439,6 +439,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;
@@ -617,6 +625,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 432093b6aea..5358b8330a3 100644
--- a/gdb/guile/guile.c
+++ b/gdb/guile/guile.c
@@ -594,6 +594,7 @@  initialize_gdb_module (void *data)
   gdbscm_initialize_auto_load ();
   gdbscm_initialize_blocks ();
   gdbscm_initialize_breakpoints ();
+  gdbscm_initialize_colors ();
   gdbscm_initialize_commands ();
   gdbscm_initialize_disasm ();
   gdbscm_initialize_frames ();
diff --git a/gdb/guile/scm-color.c b/gdb/guile/scm-color.c
new file mode 100644
index 00000000000..6ebe252f125
--- /dev/null
+++ b/gdb/guile/scm-color.c
@@ -0,0 +1,427 @@ 
+/* GDB parameters implemented in Guile.
+
+   Copyright (C) 2008-2024 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "value.h"
+#include "charset.h"
+#include "cli/cli-decode.h"
+#include "completer.h"
+#include "language.h"
+#include "arch-utils.h"
+#include "guile-internal.h"
+
+/* A GDB color.  */
+
+struct color_smob
+{
+  /* This always appears first.  */
+  gdb_smob base;
+
+  /* Underlying value.  */
+  ui_file_style::color color;
+};
+
+static const char color_smob_name[] = "gdb:color";
+
+/* The tag Guile knows the color smob by.  */
+static scm_t_bits color_smob_tag;
+
+/* Keywords used by make-color.  */
+static SCM colorspace_keyword;
+
+static const char *coscm_colorspace_name (color_space colorspace);
+
+/* Administrivia for color smobs.  */
+
+static int
+coscm_print_color_smob (SCM self, SCM port, scm_print_state *pstate)
+{
+  const ui_file_style::color &color = coscm_get_color (self);
+
+  gdbscm_printf (port, "#<%s", color_smob_name);
+
+  gdbscm_printf (port, " %s", color.to_string ().c_str ());
+  gdbscm_printf (port, " %s", coscm_colorspace_name (color.colorspace ()));
+  scm_puts (">", port);
+
+  scm_remember_upto_here_1 (self);
+
+  /* Non-zero means success.  */
+  return 1;
+}
+
+/* Create an empty (uninitialized) color.  */
+
+static SCM
+coscm_make_color_smob (void)
+{
+  color_smob *c_smob = (color_smob *)
+    scm_gc_calloc (sizeof (color_smob), color_smob_name);
+  SCM c_scm;
+
+  c_smob->color = ui_file_style::color (ui_file_style::NONE);
+  c_scm = scm_new_smob (color_smob_tag, (scm_t_bits) c_smob);
+  gdbscm_init_gsmob (&c_smob->base);
+
+  return c_scm;
+}
+
+/* Return the <gdb:color> object that encapsulates COLOR.  */
+
+SCM
+coscm_scm_from_color (const ui_file_style::color &color)
+{
+  SCM c_scm = coscm_make_color_smob ();
+  color_smob *c_smob = (color_smob *) SCM_SMOB_DATA (c_scm);
+  c_smob->color = color;
+  return c_scm;
+}
+
+/* Return the color field of color_smob.  */
+
+const ui_file_style::color &
+coscm_get_color (SCM color_scm)
+{
+  SCM_ASSERT_TYPE (coscm_is_color (color_scm), color_scm, SCM_ARG1, FUNC_NAME,
+		   _("<gdb:color>"));
+
+  color_smob *c_smob = (color_smob *) SCM_SMOB_DATA (color_scm);
+  return c_smob->color;
+
+}
+
+/* Returns non-zero if SCM is a <gdb:color> object.  */
+
+int
+coscm_is_color (SCM scm)
+{
+  return SCM_SMOB_PREDICATE (color_smob_tag, scm);
+}
+
+/* (gdb:color? scm) -> boolean */
+
+static SCM
+gdbscm_color_p (SCM scm)
+{
+  return scm_from_bool (coscm_is_color (scm));
+}
+
+static const scheme_integer_constant colorspaces[] =
+{
+  { "COLORSPACE_MONOCHROME", (int) color_space::MONOCHROME },
+  { "COLORSPACE_ANSI_8COLOR", (int) color_space::ANSI_8COLOR },
+  { "COLORSPACE_AIXTERM_16COLOR", (int) color_space::AIXTERM_16COLOR },
+  { "COLORSPACE_XTERM_256COLOR", (int) color_space::XTERM_256COLOR },
+  { "COLORSPACE_RGB_24BIT", (int) color_space::RGB_24BIT },
+
+  END_INTEGER_CONSTANTS
+};
+
+/* Return COLORSPACE as a string.  */
+
+static const char *
+coscm_colorspace_name (color_space colorspace)
+{
+  for (int i = 0; colorspaces[i].name != nullptr; ++i)
+    {
+      if (colorspaces[i].value == static_cast<int> (colorspace))
+	return colorspaces[i].name;
+    }
+
+  gdb_assert_not_reached ("bad color space");
+}
+
+/* Free function for a color_smob.  */
+static size_t
+coscm_free_color_smob (SCM self)
+{
+  (void) self;
+  return 0;
+}
+
+/* Color Scheme functions.  */
+
+/* (make-color [value
+     [#:color-space colorspace]]) -> <gdb:color>
+
+   VALUE is the value of the color.  It may be SCM_UNDEFINED, string, number
+   or list.
+
+   COLORSPACE is the color space of the VALUE.  It should be one of the
+   COLORSPACE_* constants defined in the gdb module.
+
+   The result is the <gdb:color> Scheme object.  */
+
+static SCM
+gdbscm_make_color (SCM value_scm, SCM rest)
+{
+  SCM colorspace_arg = SCM_UNDEFINED;
+  color_space colorspace = color_space::MONOCHROME;
+
+  scm_c_bind_keyword_arguments (FUNC_NAME, rest,
+				static_cast<scm_t_keyword_arguments_flags> (0),
+				colorspace_keyword, &colorspace_arg,
+				SCM_UNDEFINED);
+
+  if (!SCM_UNBNDP (colorspace_arg))
+    {
+      SCM_ASSERT_TYPE (scm_is_integer (colorspace_arg), colorspace_arg,
+		       SCM_ARG2, FUNC_NAME, _("int"));
+      int colorspace_int = scm_to_int (colorspace_arg);
+      if (!color_space_safe_cast (&colorspace, colorspace_int))
+	gdbscm_out_of_range_error (FUNC_NAME, SCM_ARG2,
+				   scm_from_int (colorspace_int),
+				   _("invalid colorspace argument"));
+    }
+
+  ui_file_style::color color = ui_file_style::NONE;
+  gdbscm_gdb_exception exc {};
+
+  try
+    {
+      if (SCM_UNBNDP (value_scm) || scm_is_integer (value_scm))
+	{
+	  int i = -1;
+	  if (scm_is_integer (value_scm))
+	    {
+	      i = scm_to_int (value_scm);
+	      if (i < 0)
+		gdbscm_out_of_range_error (FUNC_NAME, SCM_ARG1, value_scm,
+					   _("negative color index"));
+	    }
+
+	  if (SCM_UNBNDP (colorspace_arg))
+	    color = ui_file_style::color (i);
+	  else
+	    color = ui_file_style::color (colorspace, i);
+	}
+      else if (gdbscm_is_true (scm_list_p (value_scm)))
+	{
+	  if (SCM_UNBNDP (colorspace_arg)
+	      || colorspace != color_space::RGB_24BIT)
+	    error (_("colorspace must be COLORSPACE_RGB_24BIT with "
+		   "value of list type."));
+
+	  if (scm_ilength (value_scm) != 3)
+	    error (_("List value with RGB must be of size 3."));
+
+	  uint8_t rgb[3] = {};
+	  int i = 0;
+	  for (; i < 3 && !scm_is_eq (value_scm, SCM_EOL); ++i)
+	    {
+	      SCM item = scm_car (value_scm);
+
+	      SCM_ASSERT_TYPE (scm_is_integer (item), item, SCM_ARG1, FUNC_NAME,
+			       _("int"));
+	      int component = scm_to_int (item);
+	      if (component < 0 || component > UINT8_MAX)
+		gdbscm_out_of_range_error (FUNC_NAME, SCM_ARG1, item,
+					   _("invalid rgb component"));
+	      rgb[i] = static_cast<uint8_t> (component);
+
+	      value_scm = scm_cdr (value_scm);
+	    }
+
+	  gdb_assert (i == 3);
+
+	  color = ui_file_style::color (rgb[0], rgb[1], rgb[2]);
+	}
+      else if (scm_is_string (value_scm))
+	{
+	  SCM exception;
+
+	  gdb::unique_xmalloc_ptr<char> string
+	    = gdbscm_scm_to_host_string (value_scm, nullptr, &exception);
+	  if (string == nullptr)
+	    gdbscm_throw (exception);
+
+	  color = parse_var_color (string.get ());
+
+	  if (!SCM_UNBNDP (colorspace_arg) && colorspace != color.colorspace ())
+	    error (_("colorspace doesn't match to the value."));
+
+	}
+      else
+	scm_wrong_type_arg_msg (FUNC_NAME, SCM_ARG1, value_scm,
+				"integer, string or list");
+    }
+  catch (const gdb_exception &except)
+    {
+      exc = unpack (except);
+    }
+
+  GDBSCM_HANDLE_GDB_EXCEPTION (exc);
+
+  return coscm_scm_from_color (color);
+}
+
+/* (color-string <gdb:color>) -> value */
+
+static SCM
+gdbscm_color_string (SCM self)
+{
+  const ui_file_style::color &color = coscm_get_color (self);
+  std::string s = color.to_string ();
+  return gdbscm_scm_from_host_string (s.c_str (), s.size ());
+}
+
+/* (color-colorspace <gdb:color>) -> value */
+
+static SCM
+gdbscm_color_colorspace (SCM self)
+{
+  const ui_file_style::color &color = coscm_get_color (self);
+  return scm_from_int (static_cast<int> (color.colorspace ()));
+}
+
+/* (color-none? scm) -> boolean */
+
+static SCM
+gdbscm_color_none_p (SCM self)
+{
+  const ui_file_style::color &color = coscm_get_color (self);
+  return scm_from_bool (color.is_none ());
+}
+
+/* (color-indexed? scm) -> boolean */
+
+static SCM
+gdbscm_color_indexed_p (SCM self)
+{
+  const ui_file_style::color &color = coscm_get_color (self);
+  return scm_from_bool (color.is_indexed ());
+}
+
+/* (color-direct? scm) -> boolean */
+
+static SCM
+gdbscm_color_direct_p (SCM self)
+{
+  const ui_file_style::color &color = coscm_get_color (self);
+  return scm_from_bool (color.is_direct ());
+}
+
+/* (color-index <gdb:color>) -> value */
+
+static SCM
+gdbscm_color_index (SCM self)
+{
+  const ui_file_style::color &color = coscm_get_color (self);
+
+  if (!color.is_indexed ())
+    gdbscm_misc_error (FUNC_NAME, SCM_ARG1, self, "color is not indexed");
+  return scm_from_int (color.get_value ());
+}
+
+/* (color-components <gdb:color>) -> value */
+
+static SCM
+gdbscm_color_components (SCM self)
+{
+  const ui_file_style::color &color = coscm_get_color (self);
+
+  if (!color.is_direct ())
+    gdbscm_misc_error (FUNC_NAME, SCM_ARG1, self, "color is not direct");
+
+  uint8_t rgb[3] = {};
+  color.get_rgb (rgb);
+  SCM red = scm_from_uint8 (rgb[0]);
+  SCM green = scm_from_uint8 (rgb[1]);
+  SCM blue = scm_from_uint8 (rgb[2]);
+  return scm_list_3 (red, green, blue);
+}
+
+/* (color-escape-sequence <gdb:color> is_fg) -> value */
+
+static SCM
+gdbscm_color_escape_sequence (SCM self, SCM is_fg_scm)
+{
+  const ui_file_style::color &color = coscm_get_color (self);
+  SCM_ASSERT_TYPE (gdbscm_is_bool (is_fg_scm), is_fg_scm, SCM_ARG2, FUNC_NAME,
+		   _("boolean"));
+  bool is_fg = gdbscm_is_true (is_fg_scm);
+  std::string s = color.to_ansi (is_fg);
+  return gdbscm_scm_from_host_string (s.c_str (), s.size ());
+}
+
+/* Initialize the Scheme color support.  */
+
+static const scheme_function color_functions[] =
+{
+  { "make-color", 0, 1, 1, as_a_scm_t_subr (gdbscm_make_color),
+    "\
+Make a GDB color object.\n\
+\n\
+  Arguments: [value\n\
+      [#:color-space <colorspace>]]\n\
+    value: The name of the color.  It may be string, number with color index\n\
+      or list with RGB components.\n\
+    colorspace: The color space of the color, one of COLORSPACE_*." },
+
+  { "color?", 1, 0, 0, as_a_scm_t_subr (gdbscm_color_p),
+    "\
+Return #t if the object is a <gdb:color> object." },
+
+  { "color-none?", 1, 0, 0, as_a_scm_t_subr (gdbscm_color_none_p),
+    "\
+Return #t if the <gdb:color> object has default color." },
+
+  { "color-indexed?", 1, 0, 0, as_a_scm_t_subr (gdbscm_color_indexed_p),
+    "\
+Return #t if the <gdb:color> object is from indexed color space." },
+
+  { "color-direct?", 1, 0, 0, as_a_scm_t_subr (gdbscm_color_direct_p),
+    "\
+Return #t if the <gdb:color> object has direct color (e.g. RGB, CMY, CMYK)." },
+
+  { "color-string", 1, 0, 0, as_a_scm_t_subr (gdbscm_color_string),
+    "\
+Return the textual representation of a <gdb:color> object." },
+
+  { "color-colorspace", 1, 0, 0, as_a_scm_t_subr (gdbscm_color_colorspace),
+    "\
+Return the color space of a <gdb:color> object." },
+
+  { "color-index", 1, 0, 0, as_a_scm_t_subr (gdbscm_color_index),
+    "\
+Return index of the color of a <gdb:color> object in a palette." },
+
+  { "color-components", 1, 0, 0, as_a_scm_t_subr (gdbscm_color_components),
+    "\
+Return components of the direct <gdb:color> object." },
+
+  { "color-escape-sequence", 2, 0, 0,
+    as_a_scm_t_subr (gdbscm_color_escape_sequence),
+    "\
+Return string to change terminal's color to this." },
+
+  END_FUNCTIONS
+};
+
+void
+gdbscm_initialize_colors (void)
+{
+  color_smob_tag = gdbscm_make_smob_type (color_smob_name, sizeof (color_smob));
+  scm_set_smob_free (color_smob_tag, coscm_free_color_smob);
+  scm_set_smob_print (color_smob_tag, coscm_print_color_smob);
+
+  gdbscm_define_integer_constants (colorspaces, 1);
+  gdbscm_define_functions (color_functions, 1);
+
+  colorspace_keyword = scm_from_latin1_keyword ("color-space");
+}
diff --git a/gdb/guile/scm-param.c b/gdb/guile/scm-param.c
index 3a1e1583f54..749c5ea1f7d 100644
--- a/gdb/guile/scm-param.c
+++ b/gdb/guile/scm-param.c
@@ -47,6 +47,9 @@  union pascm_variable
 
   /* Hold a string, for enums.  */
   const char *cstringval;
+
+  /* Hold a color.  */
+  ui_file_style::color color;
 };
 
 /* A GDB parameter.
@@ -130,6 +133,7 @@  enum scm_param_types
   param_optional_filename,
   param_filename,
   param_enum,
+  param_color,
 };
 
 /* Translation from Guile parameters to GDB variable types.  Keep in the
@@ -155,7 +159,8 @@  param_to_var[] =
   { var_string_noescape },
   { var_optional_filename },
   { var_filename },
-  { var_enum }
+  { var_enum },
+  { var_color }
 };
 
 /* Wraps a setting around an existing param_smob.  This abstraction
@@ -179,6 +184,8 @@  make_setting (param_smob *s)
     return setting (type, s->value.stringval);
   else if (var_type_uses<const char *> (type))
     return setting (type, &s->value.cstringval);
+  else if (var_type_uses<ui_file_style::color> (s->type))
+    return setting (s->type, &s->value.color);
   else
     gdb_assert_not_reached ("unhandled var type");
 }
@@ -239,10 +246,9 @@  static SCM
 pascm_make_param_smob (void)
 {
   param_smob *p_smob = (param_smob *)
-    scm_gc_malloc (sizeof (param_smob), param_smob_name);
+    scm_gc_calloc (sizeof (param_smob), param_smob_name);
   SCM p_scm;
 
-  memset (p_smob, 0, sizeof (*p_smob));
   p_smob->cmd_class = no_class;
   p_smob->type = var_boolean; /* ARI: var_boolean */
   p_smob->set_func = SCM_BOOL_F;
@@ -511,6 +517,13 @@  add_setshow_generic (enum var_types param_type,
 				       set_list, show_list);
       break;
 
+    case var_color:
+      commands = add_setshow_color_cmd (cmd_name, cmd_class, &self->value.color,
+					set_doc, show_doc, help_doc,
+					set_func, show_func,
+					set_list, show_list);
+      break;
+
     default:
       gdb_assert_not_reached ("bad param_type value");
     }
@@ -588,6 +601,7 @@  static const scheme_integer_constant parameter_types[] =
   { "PARAM_OPTIONAL_FILENAME", param_optional_filename },
   { "PARAM_FILENAME", param_filename },
   { "PARAM_ENUM", param_enum },
+  { "PARAM_COLOR", param_color },
 
   END_INTEGER_CONSTANTS
 };
@@ -650,6 +664,11 @@  pascm_param_value (const setting &var, int arg_pos, const char *func_name)
 	return gdbscm_scm_from_host_string (str, strlen (str));
       }
 
+    case var_color:
+      {
+	return coscm_scm_from_color (var.get<ui_file_style::color> ());
+      }
+
     case var_boolean:
       {
 	if (var.get<bool> ())
@@ -764,6 +783,12 @@  pascm_set_param_value_x (param_smob *p_smob,
 	break;
       }
 
+    case var_color:
+      SCM_ASSERT_TYPE (coscm_is_color (value), value, arg_pos, func_name,
+		       _("<gdb:color>"));
+      var.set<ui_file_style::color> (coscm_get_color (value));
+      break;
+
     case var_boolean:
       SCM_ASSERT_TYPE (gdbscm_is_bool (value), value, arg_pos, func_name,
 		       _("boolean"));
@@ -1050,6 +1075,8 @@  gdbscm_make_parameter (SCM name_scm, SCM rest)
   scm_set_smob_free (parameter_smob_tag, pascm_free_parameter_smob);
   if (var_type_uses<std::string> (p_smob->type))
     p_smob->value.stringval = new std::string;
+  else if (var_type_uses<ui_file_style::color> (p_smob->type))
+    p_smob->value.color = ui_file_style::NONE;
 
   if (initial_value_arg_pos > 0)
     {
diff --git a/gdb/python/py-color.c b/gdb/python/py-color.c
new file mode 100644
index 00000000000..3a90b620d5f
--- /dev/null
+++ b/gdb/python/py-color.c
@@ -0,0 +1,336 @@ 
+/* Python interface to ui_file_style::color objects.
+
+   Copyright (C) 2008-2024 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+
+#include "python-internal.h"
+#include "py-color.h"
+#include "cli/cli-decode.h"
+
+/* Colorspace constants and their values.  */
+static struct {
+  const char *name;
+  color_space value;
+} colorspace_constants[] =
+{
+  { "COLORSPACE_MONOCHROME", color_space::MONOCHROME },
+  { "COLORSPACE_ANSI_8COLOR", color_space::ANSI_8COLOR },
+  { "COLORSPACE_AIXTERM_16COLOR", color_space::AIXTERM_16COLOR },
+  { "COLORSPACE_XTERM_256COLOR", color_space::XTERM_256COLOR },
+  { "COLORSPACE_RGB_24BIT", color_space::RGB_24BIT },
+};
+
+/* A color.  */
+struct colorpy_object
+{
+  PyObject_HEAD
+
+  /* Underlying value.  */
+  ui_file_style::color color;
+};
+
+extern PyTypeObject colorpy_object_type;
+
+/* See py-color.h.  */
+gdbpy_ref<>
+create_color_object (const ui_file_style::color &color)
+{
+  gdbpy_ref<colorpy_object> color_obj (PyObject_New (colorpy_object,
+						     &colorpy_object_type));
+
+  if (color_obj == nullptr)
+    return nullptr;
+
+  color_obj->color = color;
+  return gdbpy_ref<> ((PyObject *) color_obj.release ());
+}
+
+/* See py-color.h.  */
+bool
+gdbpy_is_color (PyObject *obj)
+{
+  return PyObject_IsInstance (obj, (PyObject *) &colorpy_object_type);
+}
+
+/* See py-color.h.  */
+const ui_file_style::color &
+gdbpy_get_color (PyObject *obj)
+{
+  gdb_assert (gdbpy_is_color (obj));
+  colorpy_object *self = (colorpy_object *) obj;
+  return self->color;
+}
+
+/* Get an attribute.  */
+static PyObject *
+get_attr (PyObject *obj, PyObject *attr_name)
+{
+  if (! PyUnicode_Check (attr_name))
+    return PyObject_GenericGetAttr (obj, attr_name);
+
+  colorpy_object *self = (colorpy_object *) obj;
+  const ui_file_style::color &color = self->color;
+
+  if (! PyUnicode_CompareWithASCIIString (attr_name, "colorspace"))
+    {
+      int value = static_cast<int> (color.colorspace ());
+      return gdb_py_object_from_longest (value).release ();
+    }
+
+  if (! PyUnicode_CompareWithASCIIString (attr_name, "is_none"))
+    return PyBool_FromLong (color.is_none ());
+
+  if (! PyUnicode_CompareWithASCIIString (attr_name, "is_indexed"))
+    return PyBool_FromLong (color.is_indexed ());
+
+  if (! PyUnicode_CompareWithASCIIString (attr_name, "is_direct"))
+    return PyBool_FromLong (color.is_direct ());
+
+  if (color.is_indexed ()
+      && ! PyUnicode_CompareWithASCIIString (attr_name, "index"))
+    return gdb_py_object_from_longest (color.get_value ()).release ();
+
+  if (color.is_direct ()
+      && ! PyUnicode_CompareWithASCIIString (attr_name, "components"))
+    {
+      uint8_t rgb[3];
+      color.get_rgb (rgb);
+
+      gdbpy_ref<> rgb_objects[3];
+      for (int i = 0; i < 3; ++i)
+	{
+	  rgb_objects[i] = gdb_py_object_from_ulongest (rgb[i]);
+	  if (rgb_objects[i] == nullptr)
+	    return nullptr;
+	}
+
+      PyObject *comp = PyTuple_New (3);
+      if (comp == nullptr)
+	return nullptr;
+
+      for (int i = 0; i < 3; ++i)
+	PyTuple_SET_ITEM (comp, i, rgb_objects[i].release ());
+
+      return comp;
+    }
+
+  return PyObject_GenericGetAttr (obj, attr_name);
+}
+
+/* Implementation of Color.escape_sequence (self, is_fg) -> str.  */
+
+static PyObject *
+colorpy_escape_sequence (PyObject *self, PyObject *is_fg_obj)
+{
+  if (!gdbpy_is_color (self))
+    {
+      PyErr_SetString (PyExc_RuntimeError,
+		       _("Object is not gdb.Color."));
+      return nullptr;
+    }
+
+  if (! PyBool_Check (is_fg_obj))
+    {
+      PyErr_SetString (PyExc_RuntimeError,
+		       _("A boolean argument is required."));
+      return nullptr;
+    }
+
+  bool is_fg = is_fg_obj == Py_True;
+  std::string s = gdbpy_get_color (self).to_ansi (is_fg);
+
+  return host_string_to_python_string (s.c_str ()).release ();
+}
+
+/* Object initializer; fills color with value.
+
+   Use: __init__(VALUE = None, COLORSPACE = None)
+
+   VALUE is a string, integer, RGB-tuple or None.
+
+   COLORSPACE is the color space index.
+
+   Returns -1 on error, with a python exception set.  */
+
+static int
+colorpy_init (PyObject *self, PyObject *args, PyObject *kwds)
+{
+  colorpy_object *obj = (colorpy_object *) self;
+  PyObject *value_obj = nullptr;
+  PyObject *colorspace_obj = nullptr;
+  color_space colorspace = color_space::MONOCHROME;
+
+  if (! PyArg_ParseTuple (args, "|OO", &value_obj, &colorspace_obj))
+    return -1;
+
+  try
+    {
+      if (colorspace_obj)
+	{
+	  if (PyLong_Check (colorspace_obj))
+	    {
+	      long colorspace_id = -1;
+	      if (! gdb_py_int_as_long (colorspace_obj, &colorspace_id))
+		return -1;
+	      if (!color_space_safe_cast (&colorspace, colorspace_id))
+		error (_("colorspace %ld is out of range."), colorspace_id);
+	    }
+	  else if (colorspace_obj == Py_None)
+	    colorspace_obj = nullptr;
+	  else
+	    error (_("colorspace must be None or integer"));
+	}
+
+      if (value_obj == nullptr || value_obj == Py_None)
+	  obj->color = ui_file_style::color (colorspace, -1);
+      else if (PyLong_Check (value_obj))
+	{
+	  long value = -1;
+	  if (! gdb_py_int_as_long (value_obj, &value))
+	    return -1;
+	  if (value < 0 || value > INT_MAX)
+	    error (_("value %ld is out of range."), value);
+	  if (colorspace_obj)
+	    obj->color = ui_file_style::color (colorspace, value);
+	  else
+	    obj->color = ui_file_style::color (value);
+	}
+      else if (PyTuple_Check (value_obj))
+	{
+	  if (colorspace_obj == nullptr || colorspace != color_space::RGB_24BIT)
+	    error (_("colorspace must be gdb.COLORSPACE_RGB_24BIT with "
+		   "value of tuple type."));
+	  Py_ssize_t tuple_size = PyTuple_Size (value_obj);
+	  if (tuple_size < 0)
+	    return -1;
+	  if (tuple_size != 3)
+	    error (_("Tuple value with RGB must be of size 3."));
+	  uint8_t rgb[3];
+	  for (int i = 0; i < 3; ++i)
+	    {
+	      PyObject *item = PyTuple_GetItem (value_obj, i);
+	      if (!PyLong_Check (item))
+		error (_("Item %d of an RGB tuple must be integer."), i);
+	      long item_value = -1;
+	      if (!gdb_py_int_as_long (item, &item_value))
+		return -1;
+	      if (item_value < 0 || item_value > UINT8_MAX)
+		error (_("RGB item %ld is out of byte range."), item_value);
+	      rgb[i] = static_cast<uint8_t> (item_value);
+	    }
+
+	  obj->color = ui_file_style::color (rgb[0], rgb[1], rgb[2]);
+	}
+      else if (PyUnicode_Check (value_obj))
+	{
+	  gdb::unique_xmalloc_ptr<char>
+	    str (python_string_to_host_string (value_obj));
+	  if (str == nullptr)
+	    return -1;
+	  obj->color = parse_var_color (str.get());
+
+	  if (colorspace_obj != nullptr
+	      && colorspace != obj->color.colorspace ())
+	    error (_("colorspace doesn't match to the value."));
+	}
+      else
+	error (_("value must be one of None, integer, tuple or str."));
+    }
+  catch (const gdb_exception &except)
+    {
+      return gdbpy_handle_gdb_exception (-1, except);
+    }
+
+  Py_INCREF (self);
+  return 0;
+}
+
+static PyObject *
+colorpy_str (PyObject *self)
+{
+  colorpy_object *obj = reinterpret_cast<colorpy_object *> (self);
+
+  return PyUnicode_FromString (obj->color.to_string ().c_str ());
+}
+
+/* Initialize the 'color' module.  */
+static int
+gdbpy_initialize_color (void)
+{
+  for (auto & pair : colorspace_constants)
+      if (PyModule_AddIntConstant (gdb_module, pair.name,
+				   static_cast<long> (pair.value)) < 0)
+	return -1;
+
+  colorpy_object_type.tp_new = PyType_GenericNew;
+  return gdbpy_type_ready (&colorpy_object_type, gdb_module);
+}
+
+/* Color methods.  */
+
+static PyMethodDef color_methods[] =
+{
+  { "escape_sequence", colorpy_escape_sequence, METH_O,
+    "escape_sequence (is_foreground) -> str.\n\
+Return the ANSI escape sequence for this color.\n\
+IS_FOREGROUND indicates whether this is a foreground or background color."},
+  {nullptr}
+};
+
+PyTypeObject colorpy_object_type =
+{
+  PyVarObject_HEAD_INIT (nullptr, 0)
+  "gdb.Color",			  /*tp_name*/
+  sizeof (colorpy_object),	  /*tp_basicsize*/
+  0,				  /*tp_itemsize*/
+  0,				  /*tp_dealloc*/
+  0,				  /*tp_print*/
+  0,				  /*tp_getattr*/
+  0,				  /*tp_setattr*/
+  0,				  /*tp_compare*/
+  0,				  /*tp_repr*/
+  0,				  /*tp_as_number*/
+  0,				  /*tp_as_sequence*/
+  0,				  /*tp_as_mapping*/
+  0,				  /*tp_hash */
+  0,				  /*tp_call*/
+  colorpy_str,			  /*tp_str*/
+  get_attr,			  /*tp_getattro*/
+  0,				  /*tp_setattro*/
+  0,				  /*tp_as_buffer*/
+  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+  "GDB color object",	  	  /* tp_doc */
+  0,				  /* tp_traverse */
+  0,				  /* tp_clear */
+  0,				  /* tp_richcompare */
+  0,				  /* tp_weaklistoffset */
+  0,				  /* tp_iter */
+  0,				  /* tp_iternext */
+  color_methods,		  /* tp_methods */
+  0,				  /* tp_members */
+  0,				  /* tp_getset */
+  0,				  /* tp_base */
+  0,				  /* tp_dict */
+  0,				  /* tp_descr_get */
+  0,				  /* tp_descr_set */
+  0,				  /* tp_dictoffset */
+  colorpy_init,			  /* tp_init */
+  0,				  /* tp_alloc */
+};
+
+GDBPY_INITIALIZE_FILE (gdbpy_initialize_color);
diff --git a/gdb/python/py-color.h b/gdb/python/py-color.h
new file mode 100644
index 00000000000..a778d5ba06e
--- /dev/null
+++ b/gdb/python/py-color.h
@@ -0,0 +1,35 @@ 
+/* Python interface to ui_file_style::color objects.
+
+   Copyright (C) 2009-2024 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef PYTHON_PY_COLOR_H
+#define PYTHON_PY_COLOR_H
+
+#include "python-internal.h"
+#include "ui-style.h"
+
+/* Create a new gdb.Color object from COLOR.  */
+extern gdbpy_ref<> create_color_object (const ui_file_style::color &color);
+
+/* Check if OBJ is instance of a gdb.Color type.  */
+extern bool gdbpy_is_color (PyObject *obj);
+
+/* Extracts value from OBJ object of gdb.Color type.  */
+extern const ui_file_style::color &gdbpy_get_color (PyObject *obj);
+
+#endif /* PYTHON_PY_COLOR_H */
diff --git a/gdb/python/py-param.c b/gdb/python/py-param.c
index 9741782d3f1..0e79d2199f6 100644
--- a/gdb/python/py-param.c
+++ b/gdb/python/py-param.c
@@ -26,6 +26,7 @@ 
 #include "completer.h"
 #include "language.h"
 #include "arch-utils.h"
+#include "py-color.h"
 
 /* Python parameter types as in PARM_CONSTANTS below.  */
 
@@ -43,6 +44,7 @@  enum py_param_types
   param_zuinteger,
   param_zuinteger_unlimited,
   param_enum,
+  param_color,
 };
 
 /* Translation from Python parameters to GDB variable types.  Keep in the
@@ -69,7 +71,8 @@  param_to_var[] =
   { var_integer },
   { var_uinteger },
   { var_pinteger, pinteger_unlimited_literals },
-  { var_enum }
+  { var_enum },
+  { var_color }
 };
 
 /* Parameter constants and their values.  */
@@ -90,6 +93,7 @@  static struct {
   { "PARAM_ZUINTEGER", param_zuinteger },
   { "PARAM_ZUINTEGER_UNLIMITED", param_zuinteger_unlimited },
   { "PARAM_ENUM", param_enum },
+  { "PARAM_COLOR", param_color },
   { NULL, 0 }
 };
 
@@ -114,6 +118,9 @@  union parmpy_variable
 
   /* Hold a string, for enums.  */
   const char *cstringval;
+
+  /* Hold a color.  */
+  ui_file_style::color color;
 };
 
 /* A GDB parameter.  */
@@ -157,6 +164,8 @@  make_setting (parmpy_object *s)
     return setting (type, s->value.stringval);
   else if (var_type_uses<const char *> (type))
     return setting (type, &s->value.cstringval);
+  else if (var_type_uses<ui_file_style::color> (s->type))
+    return setting (s->type, &s->value.color);
   else
     gdb_assert_not_reached ("unhandled var type");
 }
@@ -248,6 +257,19 @@  set_parameter_value (parmpy_object *self, PyObject *value)
 	break;
       }
 
+    case var_color:
+      {
+	if (gdbpy_is_color (value))
+	  self->value.color = gdbpy_get_color (value);
+	else
+	  {
+	    PyErr_SetString (PyExc_RuntimeError,
+			     _("color argument must be a gdb.Color object."));
+	    return -1;
+	  }
+      }
+      break;
+
     case var_boolean:
       if (! PyBool_Check (value))
 	{
@@ -707,6 +729,15 @@  add_setshow_generic (enum var_types type, const literal_def *extra_literals,
 				       get_show_value, set_list, show_list);
       break;
 
+    case var_color:
+      /* Initialize the value, just in case.  */
+      self->value.color = ui_file_style::NONE;
+      commands = add_setshow_color_cmd (cmd_name.get (), cmdclass,
+					&self->value.color, set_doc,
+					show_doc, help_doc, get_set_value,
+					get_show_value, set_list, show_list);
+      break;
+
     default:
       gdb_assert_not_reached ("Unhandled parameter class.");
     }
@@ -830,7 +861,8 @@  parmpy_init (PyObject *self, PyObject *args, PyObject *kwds)
       && parmclass != param_string && parmclass != param_string_noescape
       && parmclass != param_optional_filename && parmclass != param_filename
       && parmclass != param_zinteger && parmclass != param_zuinteger
-      && parmclass != param_zuinteger_unlimited && parmclass != param_enum)
+      && parmclass != param_zuinteger_unlimited && parmclass != param_enum
+      && parmclass != param_color)
     {
       PyErr_SetString (PyExc_RuntimeError,
 		       _("Invalid parameter class argument."));
@@ -854,7 +886,7 @@  parmpy_init (PyObject *self, PyObject *args, PyObject *kwds)
   extra_literals = param_to_var[parmclass].extra_literals;
   obj->type = type;
   obj->extra_literals = extra_literals;
-  memset (&obj->value, 0, sizeof (obj->value));
+  obj->value = {}; /* zeros initialization */
 
   if (var_type_uses<std::string> (obj->type))
     obj->value.stringval = new std::string;
@@ -900,6 +932,8 @@  parmpy_dealloc (PyObject *obj)
 
   if (var_type_uses<std::string> (parm_obj->type))
     delete parm_obj->value.stringval;
+  else if (var_type_uses<ui_file_style::color> (parm_obj->type))
+    parm_obj->value.color.~color();
 }
 
 /* Initialize the 'parameters' module.  */
diff --git a/gdb/python/python.c b/gdb/python/python.c
index cc06526ffcf..aed7b8c0ae1 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -35,6 +35,7 @@ 
 #include "location.h"
 #include "run-on-main-thread.h"
 #include "observable.h"
+#include "py-color.h"
 
 #if GDB_SELF_TEST
 #include "gdbsupport/selftest.h"
@@ -506,6 +507,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> ())
diff --git a/gdb/testsuite/gdb.base/default.exp b/gdb/testsuite/gdb.base/default.exp
index 628f0705b7f..a5fbebbbea5 100644
--- a/gdb/testsuite/gdb.base/default.exp
+++ b/gdb/testsuite/gdb.base/default.exp
@@ -15,8 +15,11 @@ 
 
 # Start with a fresh gdb
 
-gdb_exit
-gdb_start
+with_ansi_styling_terminal {
+    setenv TERM dumb
+    gdb_exit
+    gdb_start
+}
 
 set prev_timeout $timeout
 set timeout 60
@@ -684,6 +687,7 @@  set show_conv_list \
 	{$_probe_arg10 = <error: No frame selected>} \
 	{$_probe_arg11 = <error: No frame selected>} \
 	{$_cimag = <internal function _cimag>} \
+	{$_colorsupport = "monochrome"} \
 	{$_creal = <internal function _creal>} \
 	{$_isvoid = <internal function _isvoid>} \
 	{$_shell = <internal function _shell>} \
diff --git a/gdb/testsuite/gdb.base/style.exp b/gdb/testsuite/gdb.base/style.exp
index d29b2381923..14f6a0a8597 100644
--- a/gdb/testsuite/gdb.base/style.exp
+++ b/gdb/testsuite/gdb.base/style.exp
@@ -70,6 +70,9 @@  proc run_style_tests { } {
     global currently_disabled_style decimal hex
 
     with_ansi_styling_terminal {
+	setenv TERM xterm-256color
+	setenv COLORTERM truecolor
+
 	# Restart GDB with the correct TERM variable setting, this
 	# means that GDB will enable styling.
 	clean_restart_and_disable "restart 1" ${binfile}
@@ -310,6 +313,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"
+	}
     }
 }
 
@@ -486,6 +504,179 @@  proc test_startup_version_string { } {
     gdb_test "" "${vers}.*" "version is styled at startup"
 }
 
+# Color support is disabled when TERM=dumb and COLORTERM="".
+# All colors are approximated to none when set.
+proc test_colorsupport_monochrome { } {
+    with_test_prefix "colorsupport_monochrome" {
+	with_ansi_styling_terminal {
+	    setenv TERM dumb
+	    gdb_exit
+	    gdb_start
+	    gdb_test "print \$_colorsupport" \
+		"\\\$1 = \"monochrome\"" \
+		"color support is monochrome"
+	    gdb_test_no_output "set style enabled off"
+	    gdb_test_no_output "set style filename foreground none"
+	    gdb_test "show style filename foreground" \
+	      "The \"filename\" style foreground color is: none" \
+	      "none without approximation"
+	    gdb_test_no_output "set style filename foreground blue"
+	    gdb_test "show style filename foreground" \
+	      "The \"filename\" style foreground color is: none" \
+	      "blue approximated to none"
+	}
+    }
+}
+
+# Color support is limited by 8 colors when TERM=ansi and COLORTERM="".
+# All colors are approximated to basic colors when set.
+proc test_colorsupport_8color { } {
+    with_test_prefix "colorsupport_8color" {
+	with_ansi_styling_terminal {
+	    gdb_exit
+	    gdb_start
+	    gdb_test "print \$_colorsupport" \
+		"\033\\\[.*m\\\$1\033\\\[m = \"monochrome,ansi_8color\"" \
+		"color support is 8 color"
+	    gdb_test_no_output "set style enabled off"
+	    gdb_test_no_output "set style filename foreground none"
+	    gdb_test "show style filename foreground" \
+	      "The \"filename\" style foreground color is: none" \
+	      "none without approximation"
+	    gdb_test_no_output "set style filename foreground yellow"
+	    gdb_test "show style filename foreground" \
+	      "The \"filename\" style foreground color is: yellow" \
+	      "yellow without approximation"
+	    gdb_test_no_output "set style filename foreground 9"
+	    gdb_test "show style filename foreground" \
+	      "The \"filename\" style foreground color is: red" \
+	      "9 approximated to red"
+	    gdb_test_no_output "set style filename foreground 118"
+	    gdb_test "show style filename foreground" \
+	      "The \"filename\" style foreground color is: green" \
+	      "118 approximated to green"
+	    gdb_test_no_output "set style filename foreground #000ABC"
+	    gdb_test "show style filename foreground" \
+	      "The \"filename\" style foreground color is: blue" \
+	      "#000ABC approximated to blue"
+	}
+    }
+}
+
+# Color support is limited by 256 colors when TERM=xterm-256color and
+# COLORTERM="".  All colors are approximated by 256 color palette when set.
+proc test_colorsupport_256color { } {
+    with_test_prefix "colorsupport_256color" {
+	with_ansi_styling_terminal {
+	    setenv TERM xterm-256color
+	    gdb_exit
+	    gdb_start
+	    gdb_test "print \$_colorsupport" \
+		"\033\\\[.*m\\\$1\033\\\[m = \"monochrome,ansi_8color,aixterm_16color,xterm_256color\"" \
+		"color support is 256 color"
+	    gdb_test_no_output "set style enabled off"
+	    gdb_test_no_output "set style filename foreground none"
+	    gdb_test "show style filename foreground" \
+	      "The \"filename\" style foreground color is: none" \
+	      "none without approximation"
+	    gdb_test_no_output "set style filename foreground red"
+	    gdb_test "show style filename foreground" \
+	      "The \"filename\" style foreground color is: red" \
+	      "red without approximation"
+	    gdb_test_no_output "set style filename foreground 9"
+	    gdb_test "show style filename foreground" \
+	      "The \"filename\" style foreground color is: 9" \
+	      "9 without approximation"
+	    gdb_test_no_output "set style filename foreground 118"
+	    gdb_test "show style filename foreground" \
+	      "The \"filename\" style foreground color is: 118" \
+	      "118 without approximation"
+	    gdb_test_no_output "set style filename foreground #CD00CD"
+	    gdb_test "show style filename foreground" \
+	      "The \"filename\" style foreground color is: 5" \
+	      "#CD00CD approximated to 5"
+	    gdb_test_no_output "set style filename foreground #FFAF12"
+	    gdb_test "show style filename foreground" \
+	      "The \"filename\" style foreground color is: 214" \
+	      "#FFAF12 approximated to 214"
+	}
+    }
+}
+
+# Color support is limited by 16777216 colors when TERM=xterm-256color and
+# COLORTERM="truecolor".  No approximation needed.
+proc test_colorsupport_truecolor { } {
+    with_test_prefix "colorsupport_truecolor" {
+	with_ansi_styling_terminal {
+	    setenv TERM xterm-256color
+	    setenv COLORTERM truecolor
+	    gdb_exit
+	    gdb_start
+	    gdb_test "print \$_colorsupport" \
+		"\033\\\[.*m\\\$1\033\\\[m = \"monochrome,ansi_8color,aixterm_16color,xterm_256color,rgb_24bit\"" \
+		"color support is truecolor"
+	    gdb_test_no_output "set style enabled off"
+	    gdb_test_no_output "set style filename foreground none"
+	    gdb_test "show style filename foreground" \
+	      "The \"filename\" style foreground color is: none" \
+	      "none without approximation"
+	    gdb_test_no_output "set style filename foreground red"
+	    gdb_test "show style filename foreground" \
+	      "The \"filename\" style foreground color is: red" \
+	      "red without approximation"
+	    gdb_test_no_output "set style filename foreground 9"
+	    gdb_test "show style filename foreground" \
+	      "The \"filename\" style foreground color is: 9" \
+	      "9 without approximation"
+	    gdb_test_no_output "set style filename foreground 118"
+	    gdb_test "show style filename foreground" \
+	      "The \"filename\" style foreground color is: 118" \
+	      "118 without approximation"
+	    gdb_test_no_output "set style filename foreground #CD00CD"
+	    gdb_test "show style filename foreground" \
+	      "The \"filename\" style foreground color is: #CD00CD" \
+	      "#CD00CD without approximation"
+	}
+    }
+}
+
+# Color support is limited by 16777216 colors when TERM=dumb and
+# COLORTERM=24bit.  No approximation needed.  Basic colors replaced with RGB.
+proc test_colorsupport_truecolor_only { } {
+    with_test_prefix "colorsupport_truecolor_only" {
+	with_ansi_styling_terminal {
+	    setenv TERM dumb
+	    setenv COLORTERM truecolor
+	    gdb_exit
+	    gdb_start
+	    gdb_test "print \$_colorsupport" \
+		"\\\$1 = \"monochrome,rgb_24bit\"" \
+		"color support is truecolor only"
+	    gdb_test_no_output "set style enabled off"
+	    gdb_test_no_output "set style filename foreground none"
+	    gdb_test "show style filename foreground" \
+	      "The \"filename\" style foreground color is: none" \
+	      "none without approximation"
+	    gdb_test_no_output "set style filename foreground red"
+	    gdb_test "show style filename foreground" \
+	      "The \"filename\" style foreground color is: #DE382B" \
+	      "red replaced by #DE382B"
+	    gdb_test_no_output "set style filename foreground 9"
+	    gdb_test "show style filename foreground" \
+	      "The \"filename\" style foreground color is: #FF0000" \
+	      "9 replaced by #FF0000"
+	    gdb_test_no_output "set style filename foreground 118"
+	    gdb_test "show style filename foreground" \
+	      "The \"filename\" style foreground color is: #87FF00" \
+	      "118 replaced by #87FF00"
+	    gdb_test_no_output "set style filename foreground #CD00CD"
+	    gdb_test "show style filename foreground" \
+	      "The \"filename\" style foreground color is: #CD00CD" \
+	      "#CD00CD without approximation"
+	}
+    }
+}
+
 # Check to see if the Python styling of disassembler output is
 # expected or not, this styling requires Python support in GDB, and
 # the Python pygments module to be available.
@@ -520,3 +711,9 @@  test_disassembler_error_handling
 
 # Finally, check the styling of the version string during startup.
 test_startup_version_string
+
+test_colorsupport_monochrome
+test_colorsupport_8color
+test_colorsupport_256color
+test_colorsupport_truecolor
+test_colorsupport_truecolor_only
diff --git a/gdb/testsuite/gdb.guile/scm-color.exp b/gdb/testsuite/gdb.guile/scm-color.exp
new file mode 100644
index 00000000000..9fd7f38b495
--- /dev/null
+++ b/gdb/testsuite/gdb.guile/scm-color.exp
@@ -0,0 +1,110 @@ 
+# Copyright (C) 2010-2024 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# This file is part of the GDB testsuite.
+# It tests GDB parameter support in Guile.
+
+load_lib gdb-guile.exp
+
+require allow_guile_tests
+
+clean_restart
+
+gdb_install_guile_utils
+gdb_install_guile_module
+
+gdb_test_no_output [concat "guile (define (print_color_attrs c) " \
+    "(display (color-string c)) (display \" \") " \
+    "(display (color-colorspace c)) (display \" \") " \
+    "(display (color-none? c)) (display \" \") " \
+    "(display (color-indexed? c)) (display \" \") " \
+    "(display (color-direct? c)) (newline))"] \
+    "print_color_attrs helper"
+
+gdb_test "guile (print_color_attrs (make-color))" \
+    "none 0 #t #f #f" \
+    "print attrs of a color without params"
+
+gdb_test_no_output "guile (define c (make-color \"green\"))" \
+    "create color from basic name string"
+gdb_test "guile (print_color_attrs c)" "green 1 #f #t #f" \
+    "print attrs of a basic color name"
+gdb_test "guile (print (color-index c))" "2" \
+    "print index of a basic color name"
+
+gdb_test_no_output "guile (define c (make-color 2))" \
+    "create color from basic index"
+gdb_test "guile (print_color_attrs c)" "green 1 #f #t #f" \
+    "print attrs of a basic color"
+gdb_test "guile (print (color-index c))" "2" \
+    "print index of a basic color"
+
+gdb_test_no_output "guile (define c (make-color 14))" \
+    "create color from integer 14"
+gdb_test "guile (print_color_attrs c)" "14 2 #f #t #f" \
+    "print attrs of an color 14"
+gdb_test "guile (print (color-index c))" "14" \
+    "print index of color 14"
+
+gdb_test_no_output "guile (define c (make-color 2 #:color-space COLORSPACE_ANSI_8COLOR))" \
+    "create color from basic index and ansi colorspace"
+gdb_test "guile (print_color_attrs c)" "green 1 #f #t #f" \
+    "print attrs of a basic color with ansi colorspace"
+gdb_test "guile (print (color-index c))" "2" \
+    "print index of a basic color with ansi colorspace"
+
+gdb_test_no_output "guile (define c (make-color 2 #:color-space COLORSPACE_XTERM_256COLOR))" \
+    "create color from basic index and xterm256 colorspace"
+gdb_test "guile (print_color_attrs c)" "2 3 #f #t #f" \
+    "print attrs of a basic color with xterm256 colorspace"
+gdb_test "guile (print (color-index c))" "2" \
+    "print index of a basic color with xterm256 colorspace"
+
+gdb_test_no_output "guile (define c (make-color '(171 205 239) #:color-space COLORSPACE_RGB_24BIT))" \
+    "create color from rgb components"
+gdb_test "guile (print_color_attrs c)" "#ABCDEF 4 #f #f #t" \
+    "print attrs of an RGB color"
+gdb_test "guile (print (color-components c))" "\\(171 205 239\\)" \
+    "print components of an RGB color"
+
+gdb_test_no_output "guile (define c (make-color \"none\"))" \
+    "create color from string none"
+gdb_test "guile (print_color_attrs c)" "none 0 #t #f #f" \
+    "print attrs of a color none"
+
+gdb_test_no_output "guile (define c (make-color \"254\"))" \
+    "create color from string 254"
+gdb_test "guile (print_color_attrs c)" "254 3 #f #t #f" \
+    "print attrs of an color 254"
+gdb_test "guile (print (color-index c))" "254" \
+    "print index of color 254"
+
+gdb_test_no_output "guile (define c_none (make-color \"none\"))" \
+    "save default color"
+gdb_test_no_output "guile (define c_red (make-color \"red\"))" \
+    "save blue color"
+gdb_test_no_output "guile (define c_green (make-color \"green\"))" \
+    "save yellow color"
+gdb_test [concat "guile " \
+    "(display (color-escape-sequence c_red #t)) " \
+    "(display (color-escape-sequence c_green #f)) " \
+    "(display \"red on green\") " \
+    "(display (color-escape-sequence c_none #f)) " \
+    "(display \" red on default\") " \
+    "(display (color-escape-sequence c_none #t)) " \
+    "(newline)"] \
+    "\033\\\[31m\033\\\[42mred on green\033\\\[49m red on default\033\\\[39m" \
+    "escape sequences"
+
diff --git a/gdb/testsuite/gdb.guile/scm-parameter.exp b/gdb/testsuite/gdb.guile/scm-parameter.exp
index d10e9d24065..eabd17980e7 100644
--- a/gdb/testsuite/gdb.guile/scm-parameter.exp
+++ b/gdb/testsuite/gdb.guile/scm-parameter.exp
@@ -389,3 +389,50 @@  with_test_prefix "previously-ambiguous" {
 }
 
 rename scm_param_test_maybe_no_output ""
+
+# Test a color parameter.
+
+with_ansi_styling_terminal {
+    # This enables 256 colors support and disables colors approximation.
+    setenv TERM xterm-256color
+    setenv COLORTERM truecolor
+
+    # Start with a fresh gdb.
+    gdb_exit
+    gdb_start
+    gdb_reinitialize_dir $srcdir/$subdir
+
+    gdb_install_guile_utils
+    gdb_install_guile_module
+
+    # We use "." here instead of ":" so that this works on win32 too.
+    set escaped_directory [string_to_regexp "$srcdir/$subdir"]
+
+    gdb_test_multiline "color gdb parameter" \
+	"guile" "" \
+	"(define test-color-param" "" \
+	"  (make-parameter \"print test-color-param\"" "" \
+	"   #:command-class COMMAND_DATA" "" \
+	"   #:parameter-type PARAM_COLOR" "" \
+	"   #:doc \"When set, test param does something useful. When disabled, does nothing.\"" "" \
+	"   #:show-doc \"Show the state of the test-color-param.\"" "" \
+	"   #:set-doc \"Set the state of the test-color-param.\"" "" \
+	"   #:show-func (lambda (self value)" "" \
+	"      (format #f \"The state of the test-color-param is ~a.\" value))" "" \
+	"   #:initial-value (make-color \"green\")))" "" \
+	"(register-parameter! test-color-param)" "" \
+	"end"
+
+    with_test_prefix "test-color-param" {
+	with_test_prefix "initial-value" {
+	    gdb_test "guile (print (parameter-value test-color-param))" "= #<gdb:color green COLORSPACE_ANSI_8COLOR>" "color parameter value (green)"
+	    gdb_test "show print test-color-param" "The state of the test-color-param is green." "show initial value"
+	    gdb_test_no_output "set print test-color-param 255"
+	}
+	with_test_prefix "new-value" {
+	    gdb_test "show print test-color-param" "The state of the test-color-param is 255." "show new value"
+	    gdb_test "guile (print (parameter-value test-color-param))" "= #<gdb:color 255 COLORSPACE_XTERM_256COLOR>" "color parameter value (255)"
+	    gdb_test "set print test-color-param 256" "integer 256 out of range.*" "set invalid color parameter"
+	}
+    }
+}
diff --git a/gdb/testsuite/gdb.python/py-color.exp b/gdb/testsuite/gdb.python/py-color.exp
new file mode 100644
index 00000000000..eb62d7f84a2
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-color.exp
@@ -0,0 +1,100 @@ 
+# Copyright (C) 2010-2024 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# This file is part of the GDB testsuite.
+# It tests gdb.parameter and gdb.Parameter.
+
+load_lib gdb-python.exp
+
+require allow_python_tests
+
+# Start with a fresh gdb.
+clean_restart
+
+gdb_test_no_output "python print_color_attrs = lambda c: print (c, c.colorspace, c.is_none, c.is_indexed, c.is_direct)" \
+    "print_color_attrs helper"
+
+gdb_test_no_output "python c = gdb.Color ()" \
+    "create color without params"
+gdb_test "python print_color_attrs (c)" "none 0 True False False" \
+    "print attrs of a color without params"
+
+gdb_test_no_output "python c = gdb.Color ('green')" \
+    "create color from basic name string"
+gdb_test "python print_color_attrs (c)" "green 1 False True False" \
+    "print attrs of a basic color name"
+gdb_test "python print (c.index)" "2" \
+    "print index of a basic color name"
+
+gdb_test_no_output "python c = gdb.Color (2)" \
+    "create color from basic index"
+gdb_test "python print_color_attrs (c)" "green 1 False True False" \
+    "print attrs of a basic color"
+gdb_test "python print (c.index)" "2" \
+    "print index of a basic color"
+
+gdb_test_no_output "python c = gdb.Color (14)" \
+    "create color from integer 14"
+gdb_test "python print_color_attrs (c)" "14 2 False True False" \
+    "print attrs of an color 14"
+gdb_test "python print (c.index)" "14" \
+    "print index of color 14"
+
+gdb_test_no_output "python c = gdb.Color (2, gdb.COLORSPACE_ANSI_8COLOR)" \
+    "create color from basic index and ansi colorspace"
+gdb_test "python print_color_attrs (c)" "green 1 False True False" \
+    "print attrs of a basic color with ansi colorspace"
+gdb_test "python print (c.index)" "2" \
+    "print index of a basic color with ansi colorspace"
+
+gdb_test_no_output "python c = gdb.Color (2, gdb.COLORSPACE_XTERM_256COLOR)" \
+    "create color from basic index and xterm256 colorspace"
+gdb_test "python print_color_attrs (c)" "2 3 False True False" \
+    "print attrs of a basic color with xterm256 colorspace"
+gdb_test "python print (c.index)" "2" \
+    "print index of a basic color with xterm256 colorspace"
+
+gdb_test_no_output "python c = gdb.Color ((171, 205, 239), gdb.COLORSPACE_RGB_24BIT)" \
+    "create color from rgb components"
+gdb_test "python print_color_attrs (c)" "#ABCDEF 4 False False True" \
+    "print attrs of an RGB color"
+gdb_test "python print (c.components)" "\\(171, 205, 239\\)" \
+    "print components of an RGB color"
+
+gdb_test_no_output "python c = gdb.Color ('none')" \
+    "create color from string none"
+gdb_test "python print_color_attrs (c)" "none 0 True False False" \
+    "print attrs of a color none"
+
+gdb_test_no_output "python c = gdb.Color ('254')" \
+    "create color from string 254"
+gdb_test "python print_color_attrs (c)" "254 3 False True False" \
+    "print attrs of an color 254"
+gdb_test "python print (c.index)" "254" \
+    "print index of color 254"
+
+gdb_test_no_output "python c_none = gdb.Color ('none')" \
+    "save default color"
+gdb_test_no_output "python c_red = gdb.Color ('red')" \
+    "save blue color"
+gdb_test_no_output "python c_green = gdb.Color ('green')" \
+    "save yellow color"
+gdb_test [concat "python print (c_red.escape_sequence (True) + " \
+    "c_green.escape_sequence (False) + 'red on green' + " \
+    "c_none.escape_sequence (False) + ' red on default' + " \
+    "c_none.escape_sequence (True))"] \
+    "\033\\\[31m\033\\\[42mred on green\033\\\[49m red on default\033\\\[39m" \
+    "escape sequences"
+
diff --git a/gdb/testsuite/gdb.python/py-parameter.exp b/gdb/testsuite/gdb.python/py-parameter.exp
index de524f49ad6..74e4178bf35 100644
--- a/gdb/testsuite/gdb.python/py-parameter.exp
+++ b/gdb/testsuite/gdb.python/py-parameter.exp
@@ -185,6 +185,58 @@  proc_with_prefix test_enum_parameter { } {
 	"Undefined item: \"three\".*" "set invalid enum parameter"
 }
 
+# Test an color parameter.
+proc_with_prefix test_color_parameter { } {
+    global env
+    with_ansi_styling_terminal {
+	# This enables 256 colors support and disables colors approximation.
+	setenv TERM xterm-256color
+	setenv COLORTERM truecolor
+
+	clean_restart
+
+	gdb_test_multiline "color gdb parameter" \
+	    "python" "" \
+	    "class TestColorParam (gdb.Parameter):" "" \
+	    "   \"\"\"When set, test param does something useful. When disabled, does nothing.\"\"\"" "" \
+	    "   show_doc = \"Show the state of the color\"" ""\
+	    "   set_doc = \"Set the state of the color\"" "" \
+	    "   def get_show_string (self, pvalue):" ""\
+	    "      return \"The state of the color is \" + str(pvalue)" ""\
+	    "   def get_set_string (self):" ""\
+	    "      return \"The state of the color has been set to \" + str(self.value)" ""\
+	    "   def __init__ (self, name):" "" \
+	    "      super (TestColorParam, self).__init__ (name, gdb.COMMAND_DATA, gdb.PARAM_COLOR)" "" \
+	    "      self.value = gdb.Color(\"green\")" "" \
+	    "test_color_param = TestColorParam ('print test-color-param')" ""\
+	    "end"
+
+	gdb_test "python print (test_color_param.value)" "green" \
+	    "test color parameter value is green"
+	gdb_test "show print test-color-param" \
+	    "The state of the color is green.*" \
+	    "show parameter is initial value"
+	gdb_test "set print test-color-param 255" \
+	    "The state of the color has been set to 255" "set color to 255"
+	gdb_test "show print test-color-param" \
+	    "The state of the color is 255.*" "show parameter is new value"
+	gdb_test "python print (test_color_param.value)" "255" \
+	    "test color parameter value is 255"
+	gdb_test_no_output "python test_color_param.value = gdb.Color(254)" \
+	    "assign test_color_param.value to 254"
+	gdb_test "python print (test_color_param.value)" "254" \
+	    "test color parameter value is integer"
+	gdb_test_no_output "python test_color_param.value =  gdb.Color('#FED210')" \
+	    "assign test_color_param.value to #FED210"
+	gdb_test "python print (test_color_param.value.components)" "\\(254, 210, 16\\)" \
+	    "test color parameter components from RGB hex tripple value"
+	gdb_test "set print test-color-param 256" \
+	    "integer 256 out of range.*" "set invalid color parameter"
+	gdb_test "python test_color_param.value = gdb.Color(256)" \
+	    ".*Error occurred in Python: Palette color index 256 is out of range.*" "set invalid color value"
+    }
+}
+
 # Test a file parameter.
 proc_with_prefix test_file_parameter { } {
     clean_restart
@@ -623,6 +675,7 @@  test_directories
 test_data_directory
 test_boolean_parameter
 test_enum_parameter
+test_color_parameter
 test_file_parameter
 test_undocumented_parameter
 test_really_undocumented_parameter
diff --git a/gdb/testsuite/lib/gdb-utils.exp b/gdb/testsuite/lib/gdb-utils.exp
index a1fdf7381a1..52e73a3d9fd 100644
--- a/gdb/testsuite/lib/gdb-utils.exp
+++ b/gdb/testsuite/lib/gdb-utils.exp
@@ -67,19 +67,23 @@  proc string_list_to_regexp { args } {
 # "function", "variable", "address", etc.
 
 proc style {str style} {
+    set fg 39
+    set bg 49
+    set intensity 22
+    set reverse 27
     switch -exact -- $style {
-	title { set style 1 }
-	file { set style 32 }
-	function { set style 33 }
-	highlight { set style 31 }
-	variable { set style 36 }
-	address { set style 34 }
-	metadata { set style 2 }
-	version { set style "35;1" }
-	line-number { set style 2 }
+	title { set intensity 1 }
+	file { set fg 32 }
+	function { set fg 33 }
+	highlight { set fg 31 }
+	variable { set fg 36 }
+	address { set fg 34 }
+	metadata { set intensity 2 }
+	version { set fg 35; set intensity 1 }
+	line-number { set intensity 2 }
 	none { return $str }
     }
-    return "\033\\\[${style}m${str}\033\\\[m"
+    return "\033\\\[${fg};${bg};${intensity};${reverse}m${str}\033\\\[m"
 }
 
 # gdb_get_bp_addr num
diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index 738cd2fe9c6..f7dcec8a597 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -10126,10 +10126,11 @@  proc with_override { name override body } {
 # Run BODY after setting the TERM environment variable to 'ansi', and
 # unsetting the NO_COLOR environment variable.
 proc with_ansi_styling_terminal { body } {
-    save_vars { ::env(TERM) ::env(NO_COLOR) } {
+    save_vars { ::env(TERM) ::env(NO_COLOR) ::env(COLORTERM) } {
 	# Set environment variables to allow styling.
 	setenv TERM ansi
 	unset -nocomplain ::env(NO_COLOR)
+	unset -nocomplain ::env(COLORTERM)
 
 	set code [catch {uplevel 1 $body} result]
     }
diff --git a/gdb/top.c b/gdb/top.c
index eae54aae1ff..0352997dc93 100644
--- a/gdb/top.c
+++ b/gdb/top.c
@@ -2112,6 +2112,17 @@  show_startup_quiet (struct ui_file *file, int from_tty,
 	      value);
 }
 
+static void
+init_colorsupport_var ()
+{
+  const std::vector<color_space> &cs = colorsupport ();
+  std::string s;
+  for (color_space c : cs)
+    s.append (s.empty () ? "" : ",").append (color_space_name (c));
+  struct internalvar *colorsupport_var = create_internalvar ("_colorsupport");
+  set_internalvar_string (colorsupport_var, s.c_str ());
+}
+
 static void
 init_main (void)
 {
@@ -2316,6 +2327,9 @@  gdb_init ()
      during startup.  */
   set_language (language_c);
   expected_language = current_language;	/* Don't warn about the change.  */
+
+  /* Create $_colorsupport convenience variable.  */
+  init_colorsupport_var ();
 }
 
 void _initialize_top ();
diff --git a/gdb/ui-style.c b/gdb/ui-style.c
index 952102e30c3..f0a8e81d637 100644
--- a/gdb/ui-style.c
+++ b/gdb/ui-style.c
@@ -17,6 +17,7 @@ 
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 #include "ui-style.h"
+#include "gdb_curses.h"
 #include "gdbsupport/gdb_regex.h"
 
 /* A regular expression that is used for matching ANSI terminal escape
@@ -46,119 +47,250 @@  static const char ansi_regex_text[] =
 
 static regex_t ansi_regex;
 
-/* This maps bright colors to RGB triples.  The index is the bright
-   color index, starting with bright black.  The values come from
-   xterm.  */
-
-static const uint8_t bright_colors[][3] = {
-  { 127, 127, 127 },		/* Black.  */
-  { 255, 0, 0 },		/* Red.  */
-  { 0, 255, 0 },		/* Green.  */
-  { 255, 255, 0 },		/* Yellow.  */
-  { 92, 92, 255 },		/* Blue.  */
-  { 255, 0, 255 },		/* Magenta.  */
-  { 0, 255, 255 },		/* Cyan.  */
-  { 255, 255, 255 }		/* White.  */
+/* This maps 8-color palette to RGB triples.  The values come from
+   plain linux terminal.  */
+
+static const uint8_t palette_8colors[][3] = {
+  { 1, 1, 1 },			/* Black.  */
+  { 222, 56, 43 },		/* Red.  */
+  { 57, 181, 74 },		/* Green.  */
+  { 255, 199, 6 },		/* Yellow.  */
+  { 0, 111, 184 },		/* Blue.  */
+  { 118, 38, 113 },		/* Magenta.  */
+  { 44, 181, 233 },		/* Cyan.  */
+  { 204, 204, 204 },		/* White.  */
+};
+
+/* This maps 16-color palette to RGB triples.  The values come from xterm.  */
+
+static const uint8_t palette_16colors[][3] = {
+  { 0, 0, 0 },			/* Black.  */
+  { 205, 0, 0 },		/* Red.  */
+  { 0, 205, 0 },		/* Green.  */
+  { 205, 205, 0 },		/* Yellow.  */
+  { 0, 0, 238 },		/* Blue.  */
+  { 205, 0, 205 },		/* Magenta.  */
+  { 0, 205, 205 },		/* Cyan.  */
+  { 229, 229, 229 },		/* White.  */
+  { 127, 127, 127 },		/* Bright Black.  */
+  { 255, 0, 0 },		/* Bright Red.  */
+  { 0, 255, 0 },		/* Bright Green.  */
+  { 255, 255, 0 },		/* Bright Yellow.  */
+  { 92, 92, 255 },		/* Bright Blue.  */
+  { 255, 0, 255 },		/* Bright Magenta.  */
+  { 0, 255, 255 },		/* Bright Cyan.  */
+  { 255, 255, 255 }		/* Bright White.  */
 };
 
 /* See ui-style.h.  */
+/* Must correspond to ui_file_style::basic_color.  */
+const std::vector<const char *> ui_file_style::basic_color_enums = {
+  "none",
+  "black",
+  "red",
+  "green",
+  "yellow",
+  "blue",
+  "magenta",
+  "cyan",
+  "white",
+  nullptr
+};
 
-bool
+/* Returns text representation of a basic COLOR.  */
+
+static const char *
+basic_color_name (int color)
+{
+  int pos = color - ui_file_style::NONE;
+  if (0 <= pos && pos < ui_file_style::basic_color_enums.size ())
+    if (const char *s = ui_file_style::basic_color_enums[pos])
+      return s;
+  error (_("Basic color %d has no name."), color);
+}
+
+/* See ui-style.h.  */
+
+void
 ui_file_style::color::append_ansi (bool is_fg, std::string *str) const
 {
-  if (m_simple)
-    {
-      if (m_value >= BLACK && m_value <= WHITE)
-	str->append (std::to_string (m_value + (is_fg ? 30 : 40)));
-      else if (m_value > WHITE && m_value <= WHITE + 8)
-	str->append (std::to_string (m_value - WHITE + (is_fg ? 90 : 100)));
-      else if (m_value != -1)
-	{
-	  str->append (is_fg ? "38;5;" : "48;5;");
-	  str->append (std::to_string (m_value));
-	}
-      else
-	return false;
-    }
-  else
+  if (m_color_space == color_space::MONOCHROME)
+    str->append (is_fg ? "39" : "49");
+  else if (is_basic ())
+    str->append (std::to_string (m_value + (is_fg ? 30 : 40)));
+  else if (m_color_space == color_space::AIXTERM_16COLOR)
+    str->append (std::to_string (m_value - WHITE - 1 + (is_fg ? 90 : 100)));
+  else if (m_color_space == color_space::XTERM_256COLOR)
+    str->append (is_fg ? "38;5;" : "48;5;").append (std::to_string (m_value));
+  else if (m_color_space == color_space::RGB_24BIT)
     {
+      // See ISO/IEC 8613-6 (or ITU T.416) 13.1.8 Select Graphic Rendition (SGR)
       str->append (is_fg ? "38;2;" : "48;2;");
       str->append (std::to_string (m_red)
 		   + ";" + std::to_string (m_green)
 		   + ";" + std::to_string (m_blue));
     }
-  return true;
+  else
+    gdb_assert_not_reached ("no valid ansi representation of the color");
 }
 
 /* See ui-style.h.  */
+std::string
+ui_file_style::color::to_ansi (bool is_fg) const
+{
+  std::string s = "\033[";
+  append_ansi (is_fg, &s);
+  s.push_back ('m');
+  return s;
+}
 
-void
-ui_file_style::color::get_rgb (uint8_t *rgb) const
+/* See ui-style.h.  */
+
+std::string
+ui_file_style::color::to_string () const
 {
-  if (m_simple)
+  if (m_color_space == color_space::RGB_24BIT)
     {
-      /* Can't call this for a basic color or NONE -- those will end
-	 up in the assert below.  */
-      if (m_value >= 8 && m_value <= 15)
-	memcpy (rgb, bright_colors[m_value - 8], 3 * sizeof (uint8_t));
-      else if (m_value >= 16 && m_value <= 231)
-	{
-	  int value = m_value;
-	  value -= 16;
-	  /* This obscure formula seems to be what terminals actually
-	     do.  */
-	  int component = value / 36;
-	  rgb[0] = component == 0 ? 0 : (55 + component * 40);
-	  value %= 36;
-	  component = value / 6;
-	  rgb[1] = component == 0 ? 0 : (55 + component * 40);
-	  value %= 6;
-	  rgb[2] = value == 0 ? 0 : (55 + value * 40);
-	}
-      else if (m_value >= 232)
-	{
-	  uint8_t v = (m_value - 232) * 10 + 8;
-	  rgb[0] = v;
-	  rgb[1] = v;
-	  rgb[2] = v;
-	}
-      else
-	gdb_assert_not_reached ("get_rgb called on invalid color");
+      char s[64];
+      snprintf (s, sizeof s, "#%02X%02X%02X", m_red, m_green, m_blue);
+      return s;
     }
+  else if (is_none () || is_basic ())
+    return basic_color_name (m_value);
   else
+    return std::to_string (get_value ());
+}
+
+/* See ui-style.h.  */
+
+void
+ui_file_style::color::get_rgb (uint8_t *rgb) const
+{
+  if (m_color_space == color_space::RGB_24BIT)
     {
       rgb[0] = m_red;
       rgb[1] = m_green;
       rgb[2] = m_blue;
     }
+  else if (m_color_space == color_space::ANSI_8COLOR
+	   && 0 <= m_value && m_value <= 7)
+    memcpy (rgb, palette_8colors[m_value], 3 * sizeof (uint8_t));
+  else if (m_color_space == color_space::AIXTERM_16COLOR
+	   && 0 <= m_value && m_value <= 15)
+    memcpy (rgb, palette_16colors[m_value], 3 * sizeof (uint8_t));
+  else if (m_color_space != color_space::XTERM_256COLOR)
+    gdb_assert_not_reached ("get_rgb called on invalid color");
+  else if (0 <= m_value && m_value <= 15)
+    memcpy (rgb, palette_16colors[m_value], 3 * sizeof (uint8_t));
+  else if (m_value >= 16 && m_value <= 231)
+    {
+      int value = m_value;
+      value -= 16;
+      /* This obscure formula seems to be what terminals actually
+	 do.  */
+      int component = value / 36;
+      rgb[0] = component == 0 ? 0 : (55 + component * 40);
+      value %= 36;
+      component = value / 6;
+      rgb[1] = component == 0 ? 0 : (55 + component * 40);
+      value %= 6;
+      rgb[2] = value == 0 ? 0 : (55 + value * 40);
+    }
+  else if (232 <= m_value && m_value <= 255)
+    {
+      uint8_t v = (m_value - 232) * 10 + 8;
+      rgb[0] = v;
+      rgb[1] = v;
+      rgb[2] = v;
+    }
+  else
+    gdb_assert_not_reached ("get_rgb called on invalid color");
 }
 
 /* See ui-style.h.  */
 
-std::string
-ui_file_style::to_ansi () const
+ui_file_style::color
+ui_file_style::color::approximate (const std::vector<color_space> &spaces) const
 {
-  std::string result ("\033[");
-  bool need_semi = m_foreground.append_ansi (true, &result);
-  if (!m_background.is_none ())
+  if (spaces.empty () || is_none ())
+    return NONE;
+
+  color_space target_space = color_space::MONOCHROME;
+  for (color_space sp : spaces)
+    if (sp == m_color_space)
+      return *this;
+    else if (sp > target_space)
+      target_space = sp;
+
+  if (target_space == color_space::RGB_24BIT)
     {
-      if (need_semi)
-	result.push_back (';');
-      m_background.append_ansi (false, &result);
-      need_semi = true;
+      uint8_t rgb[3];
+      get_rgb (rgb);
+      return color (rgb[0], rgb[1], rgb[2]);
+    }
+
+  int target_size = 0;
+  switch (target_space)
+    {
+    case color_space::ANSI_8COLOR:
+      target_size = 8;
+      break;
+    case color_space::AIXTERM_16COLOR:
+      target_size = 16;
+      break;
+    case color_space::XTERM_256COLOR:
+      target_size = 256;
+      break;
     }
-  if (m_intensity != NORMAL)
+
+  if (is_simple() && m_value < target_size)
+    return color (target_space, m_value);
+
+  color result = NONE;
+  int best_distance = std::numeric_limits<int>::max ();
+  uint8_t rgb[3];
+  get_rgb (rgb);
+
+  for (int i = 0; i < target_size; ++i)
     {
-      if (need_semi)
-	result.push_back (';');
-      result.append (std::to_string (m_intensity));
-      need_semi = true;
+      uint8_t c_rgb[3];
+      color c (target_space, i);
+      c.get_rgb (c_rgb);
+      int d_red = std::abs (rgb[0] - c_rgb[0]);
+      int d_green = std::abs (rgb[1] - c_rgb[1]);
+      int d_blue = std::abs (rgb[2] - c_rgb[2]);
+      int dist = d_red * d_red + d_green * d_green + d_blue * d_blue;
+      if (dist < best_distance)
+	{
+	  best_distance = dist;
+	  result = c;
+	}
     }
-  if (m_reverse)
+
+  return result;
+}
+
+/* See ui-style.h.  */
+
+std::string
+ui_file_style::to_ansi () const
+{
+  std::string result ("\033[");
+  if (!is_default ())
     {
-      if (need_semi)
-	result.push_back (';');
-      result.push_back ('7');
+      m_foreground.append_ansi (true, &result);
+      result.push_back (';');
+      m_background.append_ansi (false, &result);
+      result.push_back (';');
+      if (m_intensity == NORMAL)
+	result.append ("22");
+      else
+	result.append (std::to_string (m_intensity));
+      result.push_back (';');
+      if (m_reverse)
+	result.push_back ('7');
+      else
+	result.append ("27");
     }
   result.push_back ('m');
   return result;
@@ -315,9 +447,11 @@  ui_file_style::parse (const char *buf, size_t *n_read)
 	    case 35:
 	    case 36:
 	    case 37:
+	      m_foreground = color (value - 30);
+	      break;
 	      /* Note: not 38.  */
 	    case 39:
-	      m_foreground = color (value - 30);
+	      m_foreground = NONE;
 	      break;
 
 	    case 40:
@@ -328,9 +462,11 @@  ui_file_style::parse (const char *buf, size_t *n_read)
 	    case 45:
 	    case 46:
 	    case 47:
+	      m_background = color (value - 40);
+	      break;
 	      /* Note: not 48.  */
 	    case 49:
-	      m_background = color (value - 40);
+	      m_background = NONE;
 	      break;
 
 	    case 90:
@@ -412,3 +548,60 @@  _initialize_ui_style ()
      error.  */
   gdb_assert (code == 0);
 }
+
+/* See ui-style.h.  */
+
+const std::vector<color_space> &
+colorsupport ()
+{
+  static const std::vector<color_space> value = []
+    {
+      std::vector<color_space> result = {color_space::MONOCHROME};
+
+      int colors = tgetnum ("Co");
+      if (colors >= 8)
+	result.push_back (color_space::ANSI_8COLOR);
+      if (colors >= 16)
+	result.push_back (color_space::AIXTERM_16COLOR);
+      if (colors >= 256)
+	result.push_back (color_space::XTERM_256COLOR);
+
+      const char *colorterm = getenv ("COLORTERM");
+      if (colorterm != nullptr && (!strcmp (colorterm, "truecolor")
+	  || !strcmp (colorterm, "24bit")))
+	result.push_back (color_space::RGB_24BIT);
+
+      return result;
+    } ();
+  return value;
+}
+
+const char *
+color_space_name (color_space c)
+{
+  switch (c)
+    {
+    case color_space::MONOCHROME: return "monochrome";
+    case color_space::ANSI_8COLOR: return "ansi_8color";
+    case color_space::AIXTERM_16COLOR: return "aixterm_16color";
+    case color_space::XTERM_256COLOR: return "xterm_256color";
+    case color_space::RGB_24BIT: return "rgb_24bit";
+    }
+  gdb_assert_not_reached ("color_space_name called on invalid color");
+}
+
+bool
+color_space_safe_cast (color_space *result, long c)
+{
+  switch (static_cast<color_space>(c))
+    {
+    case color_space::MONOCHROME:
+    case color_space::ANSI_8COLOR:
+    case color_space::AIXTERM_16COLOR:
+    case color_space::XTERM_256COLOR:
+    case color_space::RGB_24BIT:
+      *result = static_cast<color_space>(c);
+      return true;
+    }
+  return false;
+}
diff --git a/gdb/ui-style.h b/gdb/ui-style.h
index 1b7b5fafb9d..0a1b87d5137 100644
--- a/gdb/ui-style.h
+++ b/gdb/ui-style.h
@@ -19,6 +19,38 @@ 
 #ifndef UI_STYLE_H
 #define UI_STYLE_H
 
+/* One of the color spaces that usually supported by terminals.  */
+enum class color_space
+{
+  /* one default terminal color  */
+  MONOCHROME,
+
+  /* foreground colors \e[30m ... \e[37m,
+     background colors \e[40m ... \e[47m  */
+  ANSI_8COLOR,
+
+  /* foreground colors \e[30m ... \e[37m, \e[90m ... \e[97m
+     background colors \e[40m ... \e[47m, \e[100m ... \e107m  */
+  AIXTERM_16COLOR,
+
+  /* foreground colors \e[38;5;0m ... \e[38;5;255m
+     background colors \e[48;5;0m ... \e[48;5;255m  */
+  XTERM_256COLOR,
+
+  /* foreground colors \e[38;2;0;0;0m ... \e[38;2;255;255;255m
+     background colors \e[48;2;0;0;0m ... \e[48;2;255;255;255m  */
+  RGB_24BIT
+};
+
+/* Color spaces supported by terminal.  */
+extern const std::vector<color_space> & colorsupport ();
+
+/* Textual representation of C.  */
+extern const char * color_space_name (color_space c);
+
+/* Cast C to RESULT and return true if it's value is valid; false otherwise.  */
+extern bool color_space_safe_cast (color_space *result, long c);
+
 /* Styles that can be applied to a ui_file.  */
 struct ui_file_style
 {
@@ -43,20 +75,61 @@  struct ui_file_style
   public:
 
     color (basic_color c)
-      : m_simple (true),
+      : m_color_space (c == NONE ? color_space::MONOCHROME
+				 : color_space::ANSI_8COLOR),
 	m_value (c)
     {
     }
 
     color (int c)
-      : m_simple (true),
+      : m_value (c)
+    {
+      if (c < -1 || c > 255)
+	error (_("Palette color index %d is out of range."), c);
+      if (c == -1)
+	m_color_space = color_space::MONOCHROME;
+      else if (c <= 7)
+	m_color_space = color_space::ANSI_8COLOR;
+      else if (c <= 15)
+	m_color_space = color_space::AIXTERM_16COLOR;
+      else
+	m_color_space = color_space::XTERM_256COLOR;
+    }
+
+    color (color_space cs, int c)
+      : m_color_space (cs),
 	m_value (c)
     {
-      gdb_assert (c >= -1 && c <= 255);
+      if (c < -1 || c > 255)
+	error (_("Palette color index %d is out of range."), c);
+
+      std::pair<int, int> range;
+      switch (cs)
+	{
+	case color_space::MONOCHROME:
+	  range = {-1, -1};
+	  break;
+	case color_space::ANSI_8COLOR:
+	  range = {0, 7};
+	  break;
+	case color_space::AIXTERM_16COLOR:
+	  range = {0, 15};
+	  break;
+	case color_space::XTERM_256COLOR:
+	  range = {0, 255};
+	  break;
+	default:
+	  error (_("Color space %d is incompatible with indexed colors."),
+		 static_cast<int> (cs));
+	}
+
+      if (c < range.first || c > range.second)
+	error (_("Color %d is out of range [%d, %d] of color space %d."),
+	       c, range.first, range.second, static_cast<int> (cs));
     }
 
     color (uint8_t r, uint8_t g, uint8_t b)
-      : m_simple (false),
+      : m_color_space (color_space::RGB_24BIT),
 	m_red (r),
 	m_green (g),
 	m_blue (b)
@@ -65,19 +138,24 @@  struct ui_file_style
 
     bool operator== (const color &other) const
     {
-      if (m_simple != other.m_simple)
+      if (m_color_space != other.m_color_space)
 	return false;
-      if (m_simple)
+      if (is_simple ())
 	return m_value == other.m_value;
       return (m_red == other.m_red && m_green == other.m_green
 	      && m_blue == other.m_blue);
     }
 
+    bool operator!= (const color &other) const
+    {
+      return ! (*this == other);
+    }
+
     bool operator< (const color &other) const
     {
-      if (m_simple != other.m_simple)
-	return m_simple < other.m_simple;
-      if (m_simple)
+      if (m_color_space != other.m_color_space)
+	return m_color_space < other.m_color_space;
+      if (is_simple ())
 	return m_value < other.m_value;
       if (m_red < other.m_red)
 	return true;
@@ -91,23 +169,54 @@  struct ui_file_style
       return false;
     }
 
+    color_space colorspace () const
+    {
+      return m_color_space;
+    }
+
     /* Return true if this is the "NONE" color, false otherwise.  */
     bool is_none () const
     {
-      return m_simple && m_value == NONE;
+      return m_color_space == color_space::MONOCHROME && m_value == NONE;
     }
 
     /* Return true if this is one of the basic colors, false
        otherwise.  */
     bool is_basic () const
     {
-      return m_simple && m_value >= BLACK && m_value <= WHITE;
+      if (m_color_space == color_space::ANSI_8COLOR
+	  || m_color_space == color_space::AIXTERM_16COLOR)
+	return BLACK <= m_value && m_value <= WHITE;
+      else
+	return false;
+    }
+
+    /* Return true if this is one of the colors, stored as int, false
+       otherwise.  */
+    bool is_simple () const
+    {
+      return m_color_space != color_space::RGB_24BIT;
+    }
+
+    /* Return true if this is one of the indexed colors, false
+       otherwise.  */
+    bool is_indexed () const
+    {
+      return m_color_space != color_space::RGB_24BIT
+	     && m_color_space != color_space::MONOCHROME;
+    }
+
+    /* Return true if this is one of the direct colors (RGB, CMY, CMYK), false
+       otherwise.  */
+    bool is_direct () const
+    {
+      return m_color_space == color_space::RGB_24BIT;
     }
 
-    /* Return the value of a basic color.  */
+    /* Return the value of a simple color.  */
     int get_value () const
     {
-      gdb_assert (is_basic ());
+      gdb_assert (is_simple ());
       return m_value;
     }
 
@@ -118,14 +227,23 @@  struct ui_file_style
 
     /* Append the ANSI terminal escape sequence for this color to STR.
        IS_FG indicates whether this is a foreground or background
-       color.  Returns true if any characters were written; returns
-       false otherwise (which can only happen for the "NONE"
-       color).  */
-    bool append_ansi (bool is_fg, std::string *str) const;
+       color.  */
+    void append_ansi (bool is_fg, std::string *str) const;
+
+    /* Return the ANSI escape sequence for this color.
+       IS_FG indicates whether this is a foreground or background color.  */
+    std::string to_ansi (bool is_fg) const;
+
+    /* Returns text representation of this object.
+       It is "none", name of a basic color, number or a #RRGGBB hex triplet.  */
+    std::string to_string () const;
+
+    /* Approximates THIS color by closest one from SPACES.  */
+    color approximate (const std::vector<color_space> &spaces) const;
 
   private:
 
-    bool m_simple;
+    color_space m_color_space;
     union
     {
       int m_value;
@@ -235,6 +353,9 @@  struct ui_file_style
     return this;
   }
 
+  /* nullptr-terminated list of names corresponding to enum basic_color.  */
+  static const std::vector<const char *> basic_color_enums;
+
 private:
 
   color m_foreground = NONE;
diff --git a/gdb/unittests/style-selftests.c b/gdb/unittests/style-selftests.c
index 4dc3c842c47..72bc878582a 100644
--- a/gdb/unittests/style-selftests.c
+++ b/gdb/unittests/style-selftests.c
@@ -58,7 +58,7 @@  run_tests ()
   SELF_CHECK (style.get_background ().is_none ());
   SELF_CHECK (style.get_intensity () == ui_file_style::NORMAL);
   SELF_CHECK (style.is_reverse ());
-  SELF_CHECK (style.to_ansi () == "\033[7m");
+  SELF_CHECK (style.to_ansi () == "\033[39;49;22;7m");
 
   style = ui_file_style ();
   SELF_CHECK (style.parse ("\033[32;1m", &n_read));
@@ -68,7 +68,7 @@  run_tests ()
   SELF_CHECK (style.get_background ().is_none ());
   SELF_CHECK (style.get_intensity () == ui_file_style::BOLD);
   SELF_CHECK (!style.is_reverse ());
-  SELF_CHECK (style.to_ansi () == "\033[32;1m");
+  SELF_CHECK (style.to_ansi () == "\033[32;49;1;27m");
 
   style = ui_file_style ();
   SELF_CHECK (style.parse ("\033[38;5;112;48;5;249m", &n_read));
@@ -81,7 +81,7 @@  run_tests ()
   CHECK_RGB (0xb2, 0xb2, 0xb2);
   SELF_CHECK (style.get_intensity () == ui_file_style::NORMAL);
   SELF_CHECK (!style.is_reverse ());
-  SELF_CHECK (style.to_ansi () == "\033[38;5;112;48;5;249m");
+  SELF_CHECK (style.to_ansi () == "\033[38;5;112;48;5;249;22;27m");
 
   style = ui_file_style ();
   SELF_CHECK (style.parse ("\033[38;2;83;84;85;48;2;0;1;254;2;7m", &n_read));