[v6] Add an option with a color type.

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

Commit Message

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

Comments

Eli Zaretskii Oct. 16, 2022, 4:45 p.m. UTC | #1
> From: Andrei Pikas <gdb@mail.api.win>
> Cc: eliz@gnu.org,
> 	Andrei Pikas <gdb@mail.api.win>
> Date: Sun, 16 Oct 2022 19:28:09 +0300
> 
> 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.
> ---
>  gdb/Makefile.in                           |   2 +
>  gdb/NEWS                                  |  29 ++
>  gdb/cli/cli-cmds.c                        |   7 +
>  gdb/cli/cli-decode.c                      | 174 +++++++++
>  gdb/cli/cli-decode.h                      |  21 +
>  gdb/cli/cli-option.c                      |  44 +++
>  gdb/cli/cli-option.h                      |  21 +
>  gdb/cli/cli-setshow.c                     |  21 +
>  gdb/cli/cli-style.c                       |  49 +--
>  gdb/cli/cli-style.h                       |   4 +-
>  gdb/command.h                             |  26 +-
>  gdb/doc/gdb.texinfo                       |  38 +-
>  gdb/doc/guile.texi                        | 104 +++++
>  gdb/doc/python.texi                       |  97 +++++
>  gdb/guile/guile-internal.h                |   9 +
>  gdb/guile/guile.c                         |   1 +
>  gdb/guile/scm-color.c                     | 445 ++++++++++++++++++++++
>  gdb/guile/scm-param.c                     |  29 +-
>  gdb/python/py-color.c                     | 346 +++++++++++++++++
>  gdb/python/py-color.h                     |  35 ++
>  gdb/python/py-param.c                     |  34 +-
>  gdb/python/python-internal.h              |   2 +
>  gdb/python/python.c                       |   8 +
>  gdb/testsuite/gdb.base/style.exp          | 209 +++++++++-
>  gdb/testsuite/gdb.guile/scm-color.exp     | 114 ++++++
>  gdb/testsuite/gdb.guile/scm-parameter.exp |  43 +++
>  gdb/testsuite/gdb.python/py-color.exp     | 101 +++++
>  gdb/testsuite/gdb.python/py-parameter.exp |  53 +++
>  gdb/testsuite/lib/gdb-utils.exp           |  22 +-
>  gdb/top.c                                 |  13 +
>  gdb/ui-style.c                            | 331 ++++++++++++----
>  gdb/ui-style.h                            | 142 ++++++-
>  gdb/unittests/style-selftests.c           |   6 +-
>  33 files changed, 2420 insertions(+), 160 deletions(-)
>  create mode 100644 gdb/guile/scm-color.c
>  create mode 100644 gdb/python/py-color.c
>  create mode 100644 gdb/python/py-color.h
>  create mode 100644 gdb/testsuite/gdb.guile/scm-color.exp
>  create mode 100644 gdb/testsuite/gdb.python/py-color.exp

OK for the documentation parts, thanks.
  
Tom Tromey April 19, 2024, 7:33 p.m. UTC | #2
>>>>> "Andrei" == Andrei Pikas <gdb@mail.api.win> writes:

Hi.

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

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

Whatever happened with this patch?

IIRC I didn't review it, since I thought someone else was handling it,
and then it never went in... anyway I was recently thinking about RGB
support for styles, and I remembered it.

Do you have a copyright assignment?
If so I can review it and we can try again to get it in.

thanks,
Tom
  
Andrei Pikas April 19, 2024, 7:52 p.m. UTC | #3
Hi. In PATCH v6 I have fixed all problems mentioned in all previous 
versions. But after that it became stale and no one reviewed it. What is 
a copyright assignment and how can I give it to you? I allow anyone to 
use my code from this patch without any restrictions and I will be glad 
if it gets be merged.

On 19/04/2024 22:33, Tom Tromey wrote:
>>>>>> "Andrei" == Andrei Pikas <gdb@mail.api.win> writes:
> Hi.
>
> Andrei> Colors can be specified as "none" for terminal's default color, as a name of
> Andrei> one of the eight standard colors of ISO/IEC 6429 "black", "red", "green", etc.,
> Andrei> as an RGB hexadecimal tripplet #RRGGBB for 24-bit TrueColor, or as an
> Andrei> integer from 0 to 255.  Integers 0 to 7 are the synonyms for the standard
> Andrei> colors.  Integers 8-15 are used for the so-called bright colors from the
> Andrei> aixterm extended 16-color palette.  Integers 16-255 are the indexes into xterm
> Andrei> extended 256-color palette (usually 6x6x6 cube plus gray ramp).  In
> Andrei> general, 256-color palette is terminal dependent and sometimes can be
> Andrei> changed with OSC 4 sequences, e.g. "\033]4;1;rgb:00/FF/00\033\\".
>
> Andrei> It is the responsibility of the user to verify that the terminal supports
> Andrei> the specified colors.
>
> Whatever happened with this patch?
>
> IIRC I didn't review it, since I thought someone else was handling it,
> and then it never went in... anyway I was recently thinking about RGB
> support for styles, and I remembered it.
>
> Do you have a copyright assignment?
> If so I can review it and we can try again to get it in.
>
> thanks,
> Tom
  
Tom Tromey April 19, 2024, 8:19 p.m. UTC | #4
>>>>> Andrei Pikas <gdb@mail.api.win> writes:

> Hi. In PATCH v6 I have fixed all problems mentioned in all previous
> versions. But after that it became stale and no one reviewed it. What
> is a copyright assignment and how can I give it to you? I allow anyone
> to use my code from this patch without any restrictions and I will be
> glad if it gets be merged.

We have to have some paperwork before we can accept large patches.
Email 'assign@gnu.org' and say that you have a patch submission to gdb.
They will get you started on the process.

thanks,
Tom
  
Tom Tromey April 20, 2024, 6:24 p.m. UTC | #5
>>>>> "Tom" == Tom Tromey <tom@tromey.com> writes:

Tom> We have to have some paperwork before we can accept large patches.
Tom> Email 'assign@gnu.org' and say that you have a patch submission to gdb.
Tom> They will get you started on the process.

I forgot to mention -- please keep us informed.  We may not get any
notice if/when your assignment is complete.

Tom
  
Andrei Pikas April 20, 2024, 6:32 p.m. UTC | #6
OK. I sent an email to assign@gnu.org but have not yet received a response.

On 20/04/2024 21:24, Tom Tromey wrote:
>>>>>> "Tom" == Tom Tromey <tom@tromey.com> writes:
> Tom> We have to have some paperwork before we can accept large patches.
> Tom> Email 'assign@gnu.org' and say that you have a patch submission to gdb.
> Tom> They will get you started on the process.
>
> I forgot to mention -- please keep us informed.  We may not get any
> notice if/when your assignment is complete.
>
> Tom
  
Andrei Pikas May 11, 2024, 3:17 p.m. UTC | #7
Hi. I have signed copyright assignment on April 30th and sent it to 
assign@gnu.org. They wrote: "Once the FSF has signed it, we will send 
you a digital copy in PDF format for your records". But I haven't 
received a response yet. I don't know whether I should wait for a copy 
signed by the FSF or not.


On 20/04/2024 21:24, Tom Tromey wrote:
>>>>>> "Tom" == Tom Tromey <tom@tromey.com> writes:
> Tom> We have to have some paperwork before we can accept large patches.
> Tom> Email 'assign@gnu.org' and say that you have a patch submission to gdb.
> Tom> They will get you started on the process.
>
> I forgot to mention -- please keep us informed.  We may not get any
> notice if/when your assignment is complete.
>
> Tom
  
Tom Tromey May 13, 2024, 7:02 p.m. UTC | #8
> Hi. I have signed copyright assignment on April 30th and sent it to
> assign@gnu.org. They wrote: "Once the FSF has signed it, we will send
> you a digital copy in PDF format for your records". But I haven't
> received a response yet. I don't know whether I should wait for a copy
> signed by the FSF or not.

Unfortunately I think we have to wait to get some official notification,
I think sometimes before signing they may find some problem.

Tom
  
Andrei Pikas May 21, 2024, 9 a.m. UTC | #9
Hello. I've received confirmation from the FSF that says "Your 
assignment/disclaimer process with the FSF is currently complete".

On 13/05/2024 22:02, Tom Tromey wrote:
>> Hi. I have signed copyright assignment on April 30th and sent it to
>> assign@gnu.org. They wrote: "Once the FSF has signed it, we will send
>> you a digital copy in PDF format for your records". But I haven't
>> received a response yet. I don't know whether I should wait for a copy
>> signed by the FSF or not.
> Unfortunately I think we have to wait to get some official notification,
> I think sometimes before signing they may find some problem.
>
> Tom
  
Tom Tromey Sept. 13, 2024, 8:16 p.m. UTC | #10
>>>>> Andrei Pikas <gdb@mail.api.win> writes:

Hello.  Thanks for the patch and for getting a copyright assignment.
I'm sorry it's taken so long to get back to this.

It's a large & ambitious patch.  I read through it once and sent some
notes.  I assume I'll have more next time through as well.

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

I often fantasize of getting rid of curses and just assuming an ANSI
terminal.

> +++ b/gdb/guile/scm-color.c
> @@ -0,0 +1,445 @@
...
> +#include "defs.h"

Since you wrote this, this area changed and defs.h doesn't have to be
manually included any more.

> diff --git a/gdb/python/py-color.c b/gdb/python/py-color.c
> new file mode 100644
> index 00000000000..fb816a06328
> --- /dev/null
> +++ b/gdb/python/py-color.c

> +extern PyTypeObject colorpy_object_type
> +    CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("parmpy_object");

You can just remove the CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF thing.  AFAIK
nobody has tried the checker in years and IIRC it doesn't work on C++
anyway.

> +  if (color_obj == NULL)
> +    return NULL;

Prefer nullptr in new code.

> +  if (! PyUnicode_CompareWithASCIIString (attr_name, "is_none"))
> +    return color.is_none () ? Py_True : Py_False;

In the Python C API, the True and False singletons still need to be
reference counted.

A simple way to do this is:

    return PyBool_FromLong (color.is_none ());

This affects a few spots.

> +  if (color.is_direct ()
> +      && ! PyUnicode_CompareWithASCIIString (attr_name, "components"))

TIL.

> +      PyObject *comp = PyTuple_New (3);
> +      if (!comp)

We started preferring 'comp == nullptr' for this kind of thing.

> +      int colors = tgetnum ((char *)"Co");

I wonder if that cast is really needed any longer.

Tom
  
Andrei Pikas Sept. 14, 2024, 6:06 p.m. UTC | #11
Hello. Thanks for the review.

>> +      int colors = tgetnum ((char *)"Co");
> I wonder if that cast is really needed any longer.

On my machine with Ubuntu 22.04 and libncurses-dev 6.3 there is no need 
to cast because of const char * in the signature of tgetnum.

But in many manuals on the Internet the signature is described as 
tgetnum(char *).

Since I can't test it on every supported OS with every curses 
implementation I would leave this cast as everywhere in the gdb sources.
  
Tom Tromey Sept. 14, 2024, 7:38 p.m. UTC | #12
Andrei> Since I can't test it on every supported OS with every curses
Andrei> implementation I would leave this cast as everywhere in the gdb
Andrei> sources.

Maybe the real one to worry about is Windows.

Personally I'd leave it out.  If this breaks the build then we'll get a
bit of information about which versions of libraries are really in use
in the wild.

Also I meant to ask -- IIRC I think your patch didn't touch tui/?
Perhaps no change was needed there though.

thanks,
Tom
  
Andrei Pikas Sept. 14, 2024, 7:43 p.m. UTC | #13
> Also I meant to ask -- IIRC I think your patch didn't touch tui/?
> Perhaps no change was needed there though.
No, I didn't touch tui.
  

Patch

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 57c29a78b7a..3317a6c4fb4 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -355,6 +355,7 @@  SUBDIR_GUILE_SRCS = \
 	guile/scm-block.c \
 	guile/scm-breakpoint.c \
 	guile/scm-cmd.c \
+	guile/scm-color.c \
 	guile/scm-disasm.c \
 	guile/scm-exception.c \
 	guile/scm-frame.c \
@@ -391,6 +392,7 @@  SUBDIR_PYTHON_SRCS = \
 	python/py-bpevent.c \
 	python/py-breakpoint.c \
 	python/py-cmd.c \
+	python/py-color.c \
 	python/py-connection.c \
 	python/py-continueevent.c \
 	python/py-disasm.c \
diff --git a/gdb/NEWS b/gdb/NEWS
index d2efe2a0a58..00173c09227 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -55,6 +55,23 @@ 
   Python Pygments is still used.  For supported targets, libopcodes
   styling is used by default.
 
+  "set style" commands now supports numeric format for basic colors
+  from 0 to 255 and #RRGGBB format for TrueColor.
+
+* New built-in convenience variable $_colorsupport provides comma-separated
+  list of color space names supported by terminal.  It is handy for
+  conditionally using styling colors based on terminal features.  For example:
+
+  (gdb) if $_regex ($_colorsupport, ".*(^|,)rgb_24bit($|,).*")
+   >set style filename background #FACADE
+   >else
+   >if $_regex ($_colorsupport, ".*(^|,)xterm_256color($|,).*")
+    >set style filename background 224
+    >else
+    >set style filename background red
+    >end
+   >end
+
 * New commands
 
 maintenance set ignore-prologue-end-flag on|off
@@ -170,6 +187,18 @@  GNU/Linux/LoongArch (gdbserver)	loongarch*-*-linux*
      can be used to request a shorter representation of a value, the
      way that 'set print frame-arguments scalars' does.
 
+  ** New class gdb.Color for dealing with colors.
+
+  ** New constant gdb.PARAM_COLOR represents color type of a
+     gdb.Parameter.value.  Parameter's value is gdb.Color instance.
+
+* Guile API
+
+  ** New type <gdb:color> for dealing with colors.
+
+  ** New constant PARAM_COLOR represents color type of a value
+     of a <gdb:parameter> object.  Parameter's value is <gdb::color> instance.
+
 * New features in the GDB remote stub, GDBserver
 
   ** GDBserver is now supported on LoongArch GNU/Linux.
diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c
index 18fb6e6d869..1a515b99761 100644
--- a/gdb/cli/cli-cmds.c
+++ b/gdb/cli/cli-cmds.c
@@ -2275,6 +2275,12 @@  value_from_setting (const setting &var, struct gdbarch *gdbarch)
 	  return value_cstring ("", 1,
 				builtin_type (gdbarch)->builtin_char);
       }
+    case var_color:
+      {
+	std::string s = var.get<ui_file_style::color> ().to_string ();
+	return value_cstring (s.c_str (), s.size (),
+			      builtin_type (gdbarch)->builtin_char);
+      }
     default:
       gdb_assert_not_reached ("bad var_type");
     }
@@ -2324,6 +2330,7 @@  str_value_from_setting (const setting &var, struct gdbarch *gdbarch)
     case var_auto_boolean:
     case var_uinteger:
     case var_zuinteger:
+    case var_color:
       {
 	std::string cmd_val = get_setshow_command_value_string (var);
 
diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
index fde554c7e6c..b4484c185bd 100644
--- a/gdb/cli/cli-decode.c
+++ b/gdb/cli/cli-decode.c
@@ -24,6 +24,7 @@ 
 #include "cli/cli-cmds.h"
 #include "cli/cli-decode.h"
 #include "cli/cli-style.h"
+#include "cli/cli-utils.h"
 #include "gdbsupport/gdb_optional.h"
 
 /* Prototypes for local functions.  */
@@ -670,6 +671,87 @@  add_setshow_enum_cmd (const char *name, command_class theclass,
   return cmds;
 }
 
+/* See cli-decode.h.  */
+
+void
+complete_on_color (completion_tracker &tracker,
+		   const char *text, const char *word)
+{
+  complete_on_enum (tracker, ui_file_style::basic_color_enums.data (),
+		    text, word);
+  if (*text == '\0')
+    {
+      /* Convenience to let the user know what the option
+	 can accept.  Note there's no common prefix between
+	 the strings on purpose, so that complete_on_enum doesn't do
+	 a partial match.  */
+      tracker.add_completion (make_unique_xstrdup ("NUMBER"));
+      tracker.add_completion (make_unique_xstrdup ("#RRGGBB"));
+    }
+}
+
+/* Completer used in color commands.  */
+
+static void
+color_completer (struct cmd_list_element *ignore,
+		 completion_tracker &tracker,
+		 const char *text, const char *word)
+{
+  complete_on_color (tracker, text, word);
+}
+
+
+/* Add element named NAME to command list LIST (the list for set or
+   some sublist thereof).  CLASS is as in add_cmd.  VAR is address
+   of the variable which will contain the color.  */
+
+set_show_commands
+add_setshow_color_cmd (const char *name,
+		       enum command_class theclass,
+		       ui_file_style::color *var,
+		       const char *set_doc,
+		       const char *show_doc,
+		       const char *help_doc,
+		       cmd_func_ftype *set_func,
+		       show_value_ftype *show_func,
+		       struct cmd_list_element **set_list,
+		       struct cmd_list_element **show_list)
+{
+  set_show_commands commands = add_setshow_cmd_full<ui_file_style::color>
+    (name, theclass, var_color, var,
+     set_doc, show_doc, help_doc,
+     nullptr, nullptr, set_func, show_func,
+     set_list, show_list);
+
+  set_cmd_completer (commands.set, color_completer);
+
+  return commands;
+}
+
+/* Same as above but using a getter and a setter function instead of a pointer
+   to a global storage buffer.  */
+
+set_show_commands
+add_setshow_color_cmd (const char *name, command_class theclass,
+		       const char *set_doc, const char *show_doc,
+		       const char *help_doc,
+		       setting_func_types<ui_file_style::color>::set set_func,
+		       setting_func_types<ui_file_style::color>::get get_func,
+		       show_value_ftype *show_func,
+		       cmd_list_element **set_list,
+		       cmd_list_element **show_list)
+{
+  auto cmds = add_setshow_cmd_full<ui_file_style::color>
+    (name, theclass, var_color, nullptr,
+     set_doc, show_doc, help_doc,
+     set_func, get_func, nullptr, show_func,
+     set_list, show_list);
+
+  set_cmd_completer (cmds.set, color_completer);
+
+  return cmds;
+}
+
 /* See cli-decode.h.  */
 const char * const auto_boolean_enums[] = { "on", "off", "auto", NULL };
 
@@ -2524,3 +2606,95 @@  cli_user_command_p (struct cmd_list_element *cmd)
 {
   return cmd->theclass == class_user && cmd->func == do_simple_func;
 }
+
+/* See cli-decode.h.  */
+
+ui_file_style::color
+parse_cli_var_color (const char **args)
+{
+  /* Do a "set" command.  ARG is NULL if no argument, or the
+     text of the argument.  */
+
+  if (args == nullptr || *args == nullptr || **args == '\0')
+    {
+      std::string msg;
+
+      for (size_t i = 0; ui_file_style::basic_color_enums[i]; ++i)
+	{
+	  msg.append ("\"");
+	  msg.append (ui_file_style::basic_color_enums[i]);
+	  msg.append ("\", ");
+	}
+
+      error (_("Requires an argument. Valid arguments are %s integer from -1 "
+	     "to 255 or an RGB hex triplet in a format #RRGGBB"),
+	     msg.c_str ());
+    }
+
+  const char *p = skip_to_space (*args);
+  size_t len = p - *args;
+
+  int nmatches = 0;
+  ui_file_style::basic_color match = ui_file_style::NONE;
+  for (int i = 0; ui_file_style::basic_color_enums[i]; ++i)
+    if (strncmp (*args, ui_file_style::basic_color_enums[i], len) == 0)
+      {
+	match = static_cast<ui_file_style::basic_color> (i - 1);
+	if (ui_file_style::basic_color_enums[i][len] == '\0')
+	  {
+	    nmatches = 1;
+	    break; /* Exact match.  */
+	  }
+	else
+	  nmatches++;
+      }
+
+  if (nmatches == 1)
+    {
+      *args += len;
+      return ui_file_style::color (match);
+    }
+
+  if (nmatches > 1)
+    error (_("Ambiguous item \"%.*s\"."), (int) len, *args);
+
+  if (**args != '#')
+    {
+      ULONGEST num = get_ulongest (args);
+      if (num > 255)
+	error (_("integer %s out of range"), pulongest (num));
+      return ui_file_style::color (color_space::XTERM_256COLOR,
+				   static_cast<int> (num));
+    }
+
+  /* Try to parse #RRGGBB string.  */
+  if (len != 7)
+    error_no_arg (_("invalid RGB hex triplet format"));
+
+  uint8_t r, g, b;
+  int scanned_chars = 0;
+  int parsed_args = sscanf (*args, "#%2" SCNx8 "%2" SCNx8 "%2" SCNx8 "%n",
+			    &r, &g, &b, &scanned_chars);
+
+  if (parsed_args != 3 || scanned_chars != 7)
+    error_no_arg (_("invalid RGB hex triplet format"));
+
+  *args += len;
+  return ui_file_style::color (r, g, b);
+}
+
+/* See cli-decode.h.  */
+
+ui_file_style::color
+parse_var_color (const char *arg)
+{
+  const char *end_arg = arg;
+  ui_file_style::color color = parse_cli_var_color (&end_arg);
+
+  int len = end_arg - arg;
+  const char *after = skip_spaces (end_arg);
+  if (*after != '\0')
+    error (_("Junk after item \"%.*s\": %s"), len, arg, after);
+
+  return color;
+}
diff --git a/gdb/cli/cli-decode.h b/gdb/cli/cli-decode.h
index 18db8822af3..7f1233c87ad 100644
--- a/gdb/cli/cli-decode.h
+++ b/gdb/cli/cli-decode.h
@@ -306,6 +306,27 @@  extern const char * const boolean_enums[];
 /* The enums of auto-boolean commands.  */
 extern const char * const auto_boolean_enums[];
 
+/* Add the different possible completions of TEXT with color.
+
+   WORD points in the same buffer as TEXT, and completions should be
+   returned relative to this position.  For example, suppose TEXT is "foo"
+   and we want to complete to "foobar".  If WORD is "oo", return
+   "oobar"; if WORD is "baz/foo", return "baz/foobar".  */
+
+extern void complete_on_color (completion_tracker &tracker,
+			       const char *text, const char *word);
+
+/* Parse ARGS, an option to a var_color variable.
+ *
+   Either returns the parsed value on success or throws an error.  ARGS may be
+   one of strings {none, black, red, green, yellow, blue, magenta,
+   cyan, white}, or color number from 0 to 255, or RGB hex triplet #RRGGBB.
+   */
+extern ui_file_style::color parse_cli_var_color (const char **args);
+
+/* Same as above but additionally check that there is no junk in the end.  */
+extern ui_file_style::color parse_var_color (const char *arg);
+
 /* Verify whether a given cmd_list_element is a user-defined command.
    Return 1 if it is user-defined.  Return 0 otherwise.  */
 
diff --git a/gdb/cli/cli-option.c b/gdb/cli/cli-option.c
index b1794ad4b17..fbb2d35e6c3 100644
--- a/gdb/cli/cli-option.c
+++ b/gdb/cli/cli-option.c
@@ -46,6 +46,9 @@  union option_value
 
   /* For var_string options.  This is malloc-allocated.  */
   std::string *string;
+
+  /* For var_color options.  */
+  ui_file_style::color color = ui_file_style::NONE;
 };
 
 /* Holds an options definition and its value.  */
@@ -424,6 +427,35 @@  parse_option (gdb::array_view<const option_def_group> options_group,
 	val.enumeration = parse_cli_var_enum (args, match->enums);
 	return option_def_and_value {*match, match_ctx, val};
       }
+    case var_color:
+      {
+	if (completion != nullptr)
+	  {
+	    const char *after_arg = skip_to_space (*args);
+	    if (*after_arg == '\0')
+	      {
+		complete_on_color (completion->tracker, *args, *args);
+
+		if (completion->tracker.have_completions ())
+		  return {};
+	      }
+	  }
+
+	if (check_for_argument (args, "--"))
+	  {
+	    /* Treat e.g., "backtrace -entry-values --" as if there
+	       was no argument after "-entry-values".  This makes
+	       parse_cli_var_color throw an error with a suggestion of
+	       what are the valid options.  */
+	    args = nullptr;
+	  }
+
+	option_value val;
+	ui_file_style::color color = parse_cli_var_color (args);
+	ui_file_style::color approx_color = color.approximate (colorsupport ());
+	val.color = approx_color;
+	return option_def_and_value {*match, match_ctx, val};
+      }
     case var_string:
       {
 	if (check_for_argument (args, "--"))
@@ -601,6 +633,10 @@  save_option_value_in_ctx (gdb::optional<option_def_and_value> &ov)
       *ov->option.var_address.enumeration (ov->option, ov->ctx)
 	= ov->value->enumeration;
       break;
+    case var_color:
+      *ov->option.var_address.color (ov->option, ov->ctx)
+	= ov->value->color;
+      break;
     case var_string:
       *ov->option.var_address.string (ov->option, ov->ctx)
 	= std::move (*ov->value->string);
@@ -677,6 +713,14 @@  get_val_type_str (const option_def &opt, std::string &buffer)
 	  }
 	return buffer.c_str ();
       }
+    case var_color:
+      {
+	buffer = "";
+	for (size_t i = 0; ui_file_style::basic_color_enums[i]; ++i)
+	  buffer.append (ui_file_style::basic_color_enums[i]).append ("|");
+	buffer += "NUMBER|#RRGGBB";
+	return buffer.c_str ();
+      }
     case var_string:
       return "STRING";
     default:
diff --git a/gdb/cli/cli-option.h b/gdb/cli/cli-option.h
index 26a8da3a5a4..d30c7024324 100644
--- a/gdb/cli/cli-option.h
+++ b/gdb/cli/cli-option.h
@@ -87,6 +87,7 @@  struct option_def
       int *(*integer) (const option_def &, void *ctx);
       const char **(*enumeration) (const option_def &, void *ctx);
       std::string *(*string) (const option_def &, void *ctx);
+      ui_file_style::color *(*color) (const option_def &, void *ctx);
     }
   var_address;
 
@@ -282,6 +283,26 @@  struct string_option_def : option_def
   }
 };
 
+/* A var_color command line option.  */
+
+template<typename Context>
+struct color_option_def : option_def
+{
+  color_option_def (const char *long_option_,
+		    ui_file_style::color *(*get_var_address_cb_) (Context *),
+		    show_value_ftype *show_cmd_cb_,
+		    const char *set_doc_,
+		    const char *show_doc_ = nullptr,
+		    const char *help_doc_ = nullptr)
+    : option_def (long_option_, var_color,
+		  (erased_get_var_address_ftype *) get_var_address_cb_,
+		  show_cmd_cb_,
+		  set_doc_, show_doc_, help_doc_)
+  {
+    var_address.color = detail::get_var_address<ui_file_style::color, Context>;
+  }
+};
+
 /* A group of options that all share the same context pointer to pass
    to the options' get-current-value callbacks.  */
 struct option_def_group
diff --git a/gdb/cli/cli-setshow.c b/gdb/cli/cli-setshow.c
index 139ebaf8323..3f6e9d0a0b5 100644
--- a/gdb/cli/cli-setshow.c
+++ b/gdb/cli/cli-setshow.c
@@ -454,6 +454,13 @@  do_set_command (const char *arg, int from_tty, struct cmd_list_element *c)
 	option_changed = c->var->set<const char *> (match);
       }
       break;
+    case var_color:
+      {
+	ui_file_style::color color = parse_var_color (arg);
+	ui_file_style::color approx_color = color.approximate (colorsupport ());
+	option_changed = c->var->set<ui_file_style::color> (approx_color);
+      }
+      break;
     case var_zuinteger_unlimited:
       option_changed = c->var->set<int>
 	(parse_cli_var_zuinteger_unlimited (&arg, true));
@@ -535,6 +542,14 @@  do_set_command (const char *arg, int from_tty, struct cmd_list_element *c)
 	  gdb::observers::command_param_changed.notify
 	    (name, c->var->get<const char *> ());
 	  break;
+	case var_color:
+	  {
+	    const ui_file_style::color &color
+	      = c->var->get<ui_file_style::color> ();
+	    gdb::observers::command_param_changed.notify
+	      (name, color.to_string ().c_str ());
+	  }
+	  break;
 	case var_boolean:
 	  {
 	    const char *opt = c->var->get<bool> () ? "on" : "off";
@@ -602,6 +617,12 @@  get_setshow_command_value_string (const setting &var)
 	  stb.puts (value);
       }
       break;
+    case var_color:
+      {
+	const ui_file_style::color &value = var.get<ui_file_style::color> ();
+	stb.puts (value.to_string ().c_str ());
+      }
+      break;
     case var_boolean:
       stb.puts (var.get<bool> () ? "on" : "off");
       break;
diff --git a/gdb/cli/cli-style.c b/gdb/cli/cli-style.c
index abf685561fa..3123a391763 100644
--- a/gdb/cli/cli-style.c
+++ b/gdb/cli/cli-style.c
@@ -43,20 +43,6 @@  bool source_styling = true;
 
 bool disassembler_styling = true;
 
-/* Name of colors; must correspond to ui_file_style::basic_color.  */
-static const char * const cli_colors[] = {
-  "none",
-  "black",
-  "red",
-  "green",
-  "yellow",
-  "blue",
-  "magenta",
-  "cyan",
-  "white",
-  nullptr
-};
-
 /* Names of intensities; must correspond to
    ui_file_style::intensity.  */
 static const char * const cli_intensities[] = {
@@ -132,8 +118,8 @@  cli_style_option::cli_style_option (const char *name,
 				    ui_file_style::intensity intensity)
   : changed (name),
     m_name (name),
-    m_foreground (cli_colors[fg - ui_file_style::NONE]),
-    m_background (cli_colors[0]),
+    m_foreground (fg),
+    m_background (ui_file_style::NONE),
     m_intensity (cli_intensities[intensity])
 {
 }
@@ -144,32 +130,17 @@  cli_style_option::cli_style_option (const char *name,
 				    ui_file_style::intensity i)
   : changed (name),
     m_name (name),
-    m_foreground (cli_colors[0]),
-    m_background (cli_colors[0]),
+    m_foreground (ui_file_style::NONE),
+    m_background (ui_file_style::NONE),
     m_intensity (cli_intensities[i])
 {
 }
 
-/* Return the color number corresponding to COLOR.  */
-
-static int
-color_number (const char *color)
-{
-  for (int i = 0; i < ARRAY_SIZE (cli_colors); ++i)
-    {
-      if (color == cli_colors[i])
-	return i - 1;
-    }
-  gdb_assert_not_reached ("color not found");
-}
-
 /* See cli-style.h.  */
 
 ui_file_style
 cli_style_option::style () const
 {
-  int fg = color_number (m_foreground);
-  int bg = color_number (m_background);
   ui_file_style::intensity intensity = ui_file_style::NORMAL;
 
   for (int i = 0; i < ARRAY_SIZE (cli_intensities); ++i)
@@ -181,7 +152,7 @@  cli_style_option::style () const
 	}
     }
 
-  return ui_file_style (fg, bg, intensity);
+  return ui_file_style (m_foreground, m_background, intensity);
 }
 
 /* See cli-style.h.  */
@@ -254,9 +225,8 @@  cli_style_option::add_setshow_commands (enum command_class theclass,
 
   set_show_commands commands;
 
-  commands = add_setshow_enum_cmd
-    ("foreground", theclass, cli_colors,
-     &m_foreground,
+  commands = add_setshow_color_cmd
+    ("foreground", theclass, &m_foreground,
      _("Set the foreground color for this property."),
      _("Show the foreground color for this property."),
      nullptr,
@@ -266,9 +236,8 @@  cli_style_option::add_setshow_commands (enum command_class theclass,
   commands.set->set_context (this);
   commands.show->set_context (this);
 
-  commands = add_setshow_enum_cmd
-    ("background", theclass, cli_colors,
-     &m_background,
+  commands = add_setshow_color_cmd
+    ("background", theclass, &m_background,
      _("Set the background color for this property."),
      _("Show the background color for this property."),
      nullptr,
diff --git a/gdb/cli/cli-style.h b/gdb/cli/cli-style.h
index 4090cf01333..4db86b00359 100644
--- a/gdb/cli/cli-style.h
+++ b/gdb/cli/cli-style.h
@@ -67,9 +67,9 @@  class cli_style_option
   const char *m_name;
 
   /* The foreground.  */
-  const char *m_foreground;
+  ui_file_style::color m_foreground;
   /* The background.  */
-  const char *m_background;
+  ui_file_style::color m_background;
   /* The intensity.  */
   const char *m_intensity;
 
diff --git a/gdb/command.h b/gdb/command.h
index d901da3c8cb..eb3b2cc20d6 100644
--- a/gdb/command.h
+++ b/gdb/command.h
@@ -119,7 +119,9 @@  enum var_types
     /* Enumerated type.  Can only have one of the specified values.
        *VAR is a char pointer to the name of the element that we
        find.  */
-    var_enum
+    var_enum,
+    /* Color type.  *VAR is a ui_file_style::color structure.  */
+    var_color
   };
 
 /* Return true if a setting of type VAR_TYPE is backed with type T.
@@ -179,6 +181,14 @@  inline bool var_type_uses<const char *> (var_types t)
   return t == var_enum;
 }
 
+/* Return true if a setting of type T is backed by an ui_file_style::color
+   variable.  */
+template<>
+inline bool var_type_uses<ui_file_style::color> (var_types t)
+{
+  return t == var_color;
+}
+
 template<bool is_scalar, typename T> struct setting_func_types_1;
 
 template<typename T>
@@ -665,6 +675,20 @@  extern set_show_commands add_setshow_enum_cmd
    setting_func_types<const char *>::get get_func, show_value_ftype *show_func,
    cmd_list_element **set_list, cmd_list_element **show_list);
 
+extern set_show_commands add_setshow_color_cmd
+  (const char *name, command_class theclass, ui_file_style::color *var,
+   const char *set_doc, const char *show_doc, const char *help_doc,
+   cmd_func_ftype *set_func, show_value_ftype *show_func,
+   cmd_list_element **set_list, cmd_list_element **show_list);
+
+extern set_show_commands add_setshow_color_cmd
+  (const char *name, command_class theclass,
+   const char *set_doc, const char *show_doc, const char *help_doc,
+   setting_func_types<ui_file_style::color>::set set_func,
+   setting_func_types<ui_file_style::color>::get get_func,
+   show_value_ftype *show_func, cmd_list_element **set_list,
+   cmd_list_element **show_list);
+
 extern set_show_commands add_setshow_auto_boolean_cmd
   (const char *name, command_class theclass, auto_boolean *var,
    const char *set_doc, const char *show_doc, const char *help_doc,
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 382df00ee7d..48c727c9a3b 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -12638,6 +12638,15 @@  and @code{$_shell_exitsignal} according to the exit status of the last
 launched command.  These variables are set and used similarly to
 the variables @code{$_exitcode} and @code{$_exitsignal}.
 
+@item $_colorsupport
+@vindex $_colorsupport@r{, convenience variable}
+Comma-separated list of color space names supported by terminal.  Names could
+be any of @samp{monochrome}, @samp{ansi_8color}, @samp{aixterm_16color},
+@samp{xterm_256color}, @samp{rgb_24bit}.  E.g., for plain linux terminal the
+value could be @samp{monochrome,ansi_8color} and for terminal with truecolor
+support it could be
+@samp{monochrome,ansi_8color,aixterm_16color,xterm_256color,rgb_24bit}.
+
 @end table
 
 @node Convenience Funs
@@ -26605,16 +26614,29 @@  For example, the style of file names can be controlled using the
 
 @table @code
 @item set style filename background @var{color}
-Set the background to @var{color}.  Valid colors are @samp{none}
-(meaning the terminal's default color), @samp{black}, @samp{red},
-@samp{green}, @samp{yellow}, @samp{blue}, @samp{magenta}, @samp{cyan},
-and@samp{white}.
+Set the background to @var{color}.  @var{color} can be @samp{none}
+(meaning the terminal's default color), a name of one of the eight standard
+colors of ISO/IEC 6429, index from 0 to 255 into terminal's color
+palette or a hexadecimal RGB triplet in @samp{#RRGGBB} format for
+24-bit TrueColor.
+
+Valid color names are @samp{black}, @samp{red}, @samp{green},
+@samp{yellow}, @samp{blue}, @samp{magenta}, @samp{cyan}, and
+@samp{white}.
+
+Integers 0 to 7 are the synonyms for the standard colors.  Integers 8-15 are
+used for the so-called bright colors from the aixterm extended 16-color
+palette.  Integers 16-255 are the indexes into xterm extended 256-color palette
+(usually 6x6x6 cube plus gray ramp).  In general, 256-color palette is terminal
+dependent and sometimes can be changed with OSC 4 sequences, e.g.
+"\033]4;1;rgb:00/FF/00\033\\".
+
+It is the responsibility of the user to verify that the terminal supports
+the specified colors.
 
 @item set style filename foreground @var{color}
-Set the foreground to @var{color}.  Valid colors are @samp{none}
-(meaning the terminal's default color), @samp{black}, @samp{red},
-@samp{green}, @samp{yellow}, @samp{blue}, @samp{magenta}, @samp{cyan},
-and@samp{white}.
+Set the foreground to @var{color}.  @var{color} can be 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 63916eed181..dfaf201c927 100644
--- a/gdb/doc/guile.texi
+++ b/gdb/doc/guile.texi
@@ -155,6 +155,7 @@  from the Guile interactive prompt.
 * I/O Ports in Guile::       GDB I/O ports
 * Memory Ports in Guile::    Accessing memory through ports and bytevectors
 * Iterators In Guile::       Basic iterator support
+* Colors In Guile::          Colorize output with Guile
 @end menu
 
 @node Basic Guile
@@ -408,6 +409,9 @@  as a symbol.
 
 @item <gdb:value>
 @xref{Values From Inferior In Guile}.
+
+@item <gdb:color>
+@xref{Colors In Guile}.
 @end table
 
 The following @value{GDBN} objects are managed internally so that the
@@ -2169,6 +2173,14 @@  The value is a filename.  This is just like
 @item PARAM_ENUM
 The value is a string, which must be one of a collection of string
 constants provided when the parameter is created.
+
+@item PARAM_COLOR
+The value is either a string or an unsigned integer.  Integer from 0 to 255
+means index into terminal's color palette.  String can be a hex RGB triplet in
+@samp{#RRGGBB} format or one of the following color names:
+@samp{none} (meaning the terminal's default color), @samp{black}, @samp{red},
+@samp{green}, @samp{yellow}, @samp{blue}, @samp{magenta}, @samp{cyan},
+or @samp{white}.
 @end vtable
 
 @node Progspaces In Guile
@@ -3780,6 +3792,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 eeb847aeaa8..a7122e697cb 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -4651,6 +4651,11 @@  except the special value -1 should be interpreted to mean
 @item gdb.PARAM_ENUM
 The value is a string, which must be one of a collection string
 constants provided when the parameter is created.
+
+@findex PARAM_COLOR
+@findex gdb.PARAM_COLOR
+@item gdb.PARAM_COLOR
+The value is @code{gdb.Color} instance.
 @end table
 
 @node Functions In Python
@@ -6241,6 +6246,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 28e4889bfa9..1e4991a0186 100644
--- a/gdb/guile/guile-internal.h
+++ b/gdb/guile/guile-internal.h
@@ -446,6 +446,14 @@  extern int gdbscm_valid_command_class_p (int command_class);
 extern char *gdbscm_canonicalize_command_name (const char *name,
 					       int want_trailing_space);
 
+/* scm-color.c */
+
+extern SCM coscm_scm_from_color (const ui_file_style::color &color);
+
+extern int coscm_is_color (SCM scm);
+
+extern const ui_file_style::color & coscm_get_color (SCM color_scm);
+
 /* scm-frame.c */
 
 struct frame_smob;
@@ -624,6 +632,7 @@  extern void gdbscm_initialize_arches (void);
 extern void gdbscm_initialize_auto_load (void);
 extern void gdbscm_initialize_blocks (void);
 extern void gdbscm_initialize_breakpoints (void);
+extern void gdbscm_initialize_colors (void);
 extern void gdbscm_initialize_commands (void);
 extern void gdbscm_initialize_disasm (void);
 extern void gdbscm_initialize_exceptions (void);
diff --git a/gdb/guile/guile.c b/gdb/guile/guile.c
index e5565b627d9..90bb787c87d 100644
--- a/gdb/guile/guile.c
+++ b/gdb/guile/guile.c
@@ -593,6 +593,7 @@  initialize_gdb_module (void *data)
   gdbscm_initialize_auto_load ();
   gdbscm_initialize_blocks ();
   gdbscm_initialize_breakpoints ();
+  gdbscm_initialize_colors ();
   gdbscm_initialize_commands ();
   gdbscm_initialize_disasm ();
   gdbscm_initialize_frames ();
diff --git a/gdb/guile/scm-color.c b/gdb/guile/scm-color.c
new file mode 100644
index 00000000000..249111630af
--- /dev/null
+++ b/gdb/guile/scm-color.c
@@ -0,0 +1,445 @@ 
+/* GDB parameters implemented in Guile.
+
+   Copyright (C) 2008-2022 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "defs.h"
+#include "value.h"
+#include "charset.h"
+#include "gdbcmd.h"
+#include "cli/cli-decode.h"
+#include "completer.h"
+#include "language.h"
+#include "arch-utils.h"
+#include "guile-internal.h"
+
+/* A GDB color.  */
+
+struct color_smob
+{
+  /* This always appears first.  */
+  gdb_smob base;
+
+  /* Underlying value.  */
+  ui_file_style::color color;
+};
+
+static const char color_smob_name[] = "gdb:color";
+
+/* The tag Guile knows the color smob by.  */
+static scm_t_bits color_smob_tag;
+
+/* Keywords used by make-color.  */
+static SCM colorspace_keyword;
+
+static const char *coscm_colorspace_name (color_space colorspace);
+
+/* Administrivia for color smobs.  */
+
+static int
+coscm_print_color_smob (SCM self, SCM port, scm_print_state *pstate)
+{
+  const ui_file_style::color &color = coscm_get_color (self);
+
+  gdbscm_printf (port, "#<%s", color_smob_name);
+
+  gdbscm_printf (port, " %s", color.to_string ().c_str ());
+  gdbscm_printf (port, " %s", coscm_colorspace_name (color.colorspace ()));
+  scm_puts (">", port);
+
+  scm_remember_upto_here_1 (self);
+
+  /* Non-zero means success.  */
+  return 1;
+}
+
+/* Create an empty (uninitialized) color.  */
+
+static SCM
+coscm_make_color_smob (void)
+{
+  color_smob *c_smob = (color_smob *)
+    scm_gc_calloc (sizeof (color_smob), color_smob_name);
+  SCM c_scm;
+
+  c_smob->color = ui_file_style::color (ui_file_style::NONE);
+  c_scm = scm_new_smob (color_smob_tag, (scm_t_bits) c_smob);
+  gdbscm_init_gsmob (&c_smob->base);
+
+  return c_scm;
+}
+
+/* Return the <gdb:color> object that encapsulates COLOR.  */
+
+SCM
+coscm_scm_from_color (const ui_file_style::color &color)
+{
+  SCM c_scm = coscm_make_color_smob ();
+  color_smob *c_smob = (color_smob *) SCM_SMOB_DATA (c_scm);
+  c_smob->color = color;
+  return c_scm;
+}
+
+/* Return the color field of color_smob.  */
+
+const ui_file_style::color &
+coscm_get_color (SCM color_scm)
+{
+  SCM_ASSERT_TYPE (coscm_is_color (color_scm), color_scm, SCM_ARG1, FUNC_NAME,
+		   _("<gdb:color>"));
+
+  color_smob *c_smob = (color_smob *) SCM_SMOB_DATA (color_scm);
+  return c_smob->color;
+
+}
+
+/* Returns non-zero if SCM is a <gdb:color> object.  */
+
+int
+coscm_is_color (SCM scm)
+{
+  return SCM_SMOB_PREDICATE (color_smob_tag, scm);
+}
+
+/* (gdb:color? scm) -> boolean */
+
+static SCM
+gdbscm_color_p (SCM scm)
+{
+  return scm_from_bool (coscm_is_color (scm));
+}
+
+static const scheme_integer_constant colorspaces[] =
+{
+  { "COLORSPACE_MONOCHROME", (int) color_space::MONOCHROME },
+  { "COLORSPACE_ANSI_8COLOR", (int) color_space::ANSI_8COLOR },
+  { "COLORSPACE_AIXTERM_16COLOR", (int) color_space::AIXTERM_16COLOR },
+  { "COLORSPACE_XTERM_256COLOR", (int) color_space::XTERM_256COLOR },
+  { "COLORSPACE_RGB_24BIT", (int) color_space::RGB_24BIT },
+
+  END_INTEGER_CONSTANTS
+};
+
+/* Return non-zero if COLORSPACE is a valid color space.  */
+
+static int
+coscm_valid_colorspace_p (int colorspace)
+{
+  for (int i = 0; colorspaces[i].name != NULL; ++i)
+    {
+      if (colorspaces[i].value == colorspace)
+	return 1;
+    }
+
+  return 0;
+}
+
+/* Return COLORSPACE as a string.  */
+
+static const char *
+coscm_colorspace_name (color_space colorspace)
+{
+  for (int i = 0; colorspaces[i].name != NULL; ++i)
+    {
+      if (colorspaces[i].value == static_cast<int> (colorspace))
+	return colorspaces[i].name;
+    }
+
+  gdb_assert_not_reached ("bad color space");
+}
+
+/* Free function for a color_smob.  */
+static size_t
+coscm_free_color_smob (SCM self)
+{
+  (void) self;
+  return 0;
+}
+
+/* Color Scheme functions.  */
+
+/* (make-color [value
+     [#:color-space colorspace]]) -> <gdb:color>
+
+   VALUE is the value of the color.  It may be SCM_UNDEFINED, string, number
+   or list.
+
+   COLORSPACE is the color space of the VALUE.  It should be one of the
+   COLORSPACE_* constants defined in the gdb module.
+
+   The result is the <gdb:color> Scheme object.  */
+
+static SCM
+gdbscm_make_color (SCM value_scm, SCM rest)
+{
+  SCM colorspace_arg = SCM_UNDEFINED;
+  color_space colorspace = color_space::MONOCHROME;
+
+  scm_c_bind_keyword_arguments (FUNC_NAME, rest,
+				static_cast<scm_t_keyword_arguments_flags> (0),
+				colorspace_keyword, &colorspace_arg,
+				SCM_UNDEFINED);
+
+  if (!SCM_UNBNDP (colorspace_arg))
+    {
+      SCM_ASSERT_TYPE (scm_is_integer (colorspace_arg), colorspace_arg,
+		       SCM_ARG2, FUNC_NAME, _("int"));
+      int colorspace_int = scm_to_int (colorspace_arg);
+      if (coscm_valid_colorspace_p (colorspace_int))
+	colorspace = static_cast<color_space> (colorspace_int);
+      else
+	gdbscm_out_of_range_error (FUNC_NAME, SCM_ARG2,
+				   scm_from_int (colorspace_int),
+				   _("invalid colorspace argument"));
+    }
+
+  ui_file_style::color color = ui_file_style::NONE;
+  gdbscm_gdb_exception exc {};
+
+  try
+    {
+      if (SCM_UNBNDP (value_scm) || scm_is_integer (value_scm))
+	{
+	  int i = -1;
+	  if (scm_is_integer (value_scm))
+	    {
+	      i = scm_to_int (value_scm);
+	      if (i < 0)
+		gdbscm_out_of_range_error (FUNC_NAME, SCM_ARG1, value_scm,
+					   _("negative color index"));
+	    }
+
+	  if (SCM_UNBNDP (colorspace_arg))
+	    color = ui_file_style::color (i);
+	  else
+	    color = ui_file_style::color (colorspace, i);
+	}
+      else if (gdbscm_is_true (scm_list_p (value_scm)))
+	{
+	  if (SCM_UNBNDP (colorspace_arg)
+	      || colorspace != color_space::RGB_24BIT)
+	    error (_("colorspace must be COLORSPACE_RGB_24BIT with "
+		   "value of list type."));
+
+	  if (scm_ilength (value_scm) != 3)
+	    error (_("List value with RGB must be of size 3."));
+
+	  uint8_t rgb[3] = {};
+	  int i = 0;
+	  for (; i < 3 && !scm_is_eq (value_scm, SCM_EOL); ++i)
+	    {
+	      SCM item = scm_car (value_scm);
+
+	      SCM_ASSERT_TYPE (scm_is_integer (item), item, SCM_ARG1, FUNC_NAME,
+			       _("int"));
+	      int component = scm_to_int (item);
+	      if (component < 0 || component > UINT8_MAX)
+		gdbscm_out_of_range_error (FUNC_NAME, SCM_ARG1, item,
+					   _("invalid rgb component"));
+	      rgb[i] = static_cast<uint8_t> (component);
+
+	      value_scm = scm_cdr (value_scm);
+	    }
+
+	  gdb_assert (i == 3);
+
+	  color = ui_file_style::color (rgb[0], rgb[1], rgb[2]);
+	}
+      else if (scm_is_string (value_scm))
+	{
+	  SCM exception;
+
+	  gdb::unique_xmalloc_ptr<char> string
+	    = gdbscm_scm_to_host_string (value_scm, nullptr, &exception);
+	  if (string == nullptr)
+	    gdbscm_throw (exception);
+
+	  color = parse_var_color (string.get ());
+
+	  if (!SCM_UNBNDP (colorspace_arg) && colorspace != color.colorspace ())
+	    error (_("colorspace doesn't match to value."));
+
+	}
+      else
+	scm_wrong_type_arg_msg (FUNC_NAME, SCM_ARG1, value_scm,
+				"integer, string or list");
+    }
+  catch (const gdb_exception &except)
+    {
+      exc = unpack (except);
+    }
+
+  GDBSCM_HANDLE_GDB_EXCEPTION (exc);
+
+  return coscm_scm_from_color (color);
+}
+
+/* (color-string <gdb:color>) -> value */
+
+static SCM
+gdbscm_color_string (SCM self)
+{
+  const ui_file_style::color &color = coscm_get_color (self);
+  std::string s = color.to_string ();
+  return gdbscm_scm_from_host_string (s.c_str (), s.size ());
+}
+
+/* (color-colorspace <gdb:color>) -> value */
+
+static SCM
+gdbscm_color_colorspace (SCM self)
+{
+  const ui_file_style::color &color = coscm_get_color (self);
+  return scm_from_int (static_cast<int> (color.colorspace ()));
+}
+
+/* (color-none? scm) -> boolean */
+
+static SCM
+gdbscm_color_none_p (SCM self)
+{
+  const ui_file_style::color &color = coscm_get_color (self);
+  return scm_from_bool (color.is_none ());
+}
+
+/* (color-indexed? scm) -> boolean */
+
+static SCM
+gdbscm_color_indexed_p (SCM self)
+{
+  const ui_file_style::color &color = coscm_get_color (self);
+  return scm_from_bool (color.is_indexed ());
+}
+
+/* (color-direct? scm) -> boolean */
+
+static SCM
+gdbscm_color_direct_p (SCM self)
+{
+  const ui_file_style::color &color = coscm_get_color (self);
+  return scm_from_bool (color.is_direct ());
+}
+
+/* (color-index <gdb:color>) -> value */
+
+static SCM
+gdbscm_color_index (SCM self)
+{
+  const ui_file_style::color &color = coscm_get_color (self);
+
+  if (!color.is_indexed ())
+    gdbscm_misc_error (FUNC_NAME, SCM_ARG1, self, "color is not indexed");
+  return scm_from_int (color.get_value ());
+}
+
+/* (color-components <gdb:color>) -> value */
+
+static SCM
+gdbscm_color_components (SCM self)
+{
+  const ui_file_style::color &color = coscm_get_color (self);
+
+  if (!color.is_direct ())
+    gdbscm_misc_error (FUNC_NAME, SCM_ARG1, self, "color is not direct");
+
+  uint8_t rgb[3] = {};
+  color.get_rgb (rgb);
+  SCM red = scm_from_uint8 (rgb[0]);
+  SCM green = scm_from_uint8 (rgb[1]);
+  SCM blue = scm_from_uint8 (rgb[2]);
+  return scm_list_3 (red, green, blue);
+}
+
+/* (color-escape-sequence <gdb:color> is_fg) -> value */
+
+static SCM
+gdbscm_color_escape_sequence (SCM self, SCM is_fg_scm)
+{
+  const ui_file_style::color &color = coscm_get_color (self);
+  SCM_ASSERT_TYPE (gdbscm_is_bool (is_fg_scm), is_fg_scm, SCM_ARG2, FUNC_NAME,
+		   _("boolean"));
+  bool is_fg = gdbscm_is_true (is_fg_scm);
+  std::string s = color.to_ansi (is_fg);
+  return gdbscm_scm_from_host_string (s.c_str (), s.size ());
+}
+
+/* Initialize the Scheme color support.  */
+
+static const scheme_function color_functions[] =
+{
+  { "make-color", 0, 1, 1, as_a_scm_t_subr (gdbscm_make_color),
+    "\
+Make a GDB color object.\n\
+\n\
+  Arguments: [value\n\
+      [#:color-space <colorspace>]]\n\
+    value: The name of the color.  It may be string, number with color index\n\
+      or list with RGB components.\n\
+    colorspace: The color space of the color, one of COLORSPACE_*." },
+
+  { "color?", 1, 0, 0, as_a_scm_t_subr (gdbscm_color_p),
+    "\
+Return #t if the object is a <gdb:color> object." },
+
+  { "color-none?", 1, 0, 0, as_a_scm_t_subr (gdbscm_color_none_p),
+    "\
+Return #t if the <gdb:color> object has default color." },
+
+  { "color-indexed?", 1, 0, 0, as_a_scm_t_subr (gdbscm_color_indexed_p),
+    "\
+Return #t if the <gdb:color> object is from indexed color space." },
+
+  { "color-direct?", 1, 0, 0, as_a_scm_t_subr (gdbscm_color_direct_p),
+    "\
+Return #t if the <gdb:color> object has direct color (e.g. RGB, CMY, CMYK)." },
+
+  { "color-string", 1, 0, 0, as_a_scm_t_subr (gdbscm_color_string),
+    "\
+Return the textual representation of a <gdb:color> object." },
+
+  { "color-colorspace", 1, 0, 0, as_a_scm_t_subr (gdbscm_color_colorspace),
+    "\
+Return the color space of a <gdb:color> object." },
+
+  { "color-index", 1, 0, 0, as_a_scm_t_subr (gdbscm_color_index),
+    "\
+Return index of the color of a <gdb:color> object in a palette." },
+
+  { "color-components", 1, 0, 0, as_a_scm_t_subr (gdbscm_color_components),
+    "\
+Return components of the direct <gdb:color> object." },
+
+  { "color-escape-sequence", 2, 0, 0,
+    as_a_scm_t_subr (gdbscm_color_escape_sequence),
+    "\
+Return string to change terminal's color to this." },
+
+  END_FUNCTIONS
+};
+
+void
+gdbscm_initialize_colors (void)
+{
+  color_smob_tag = gdbscm_make_smob_type (color_smob_name, sizeof (color_smob));
+  scm_set_smob_free (color_smob_tag, coscm_free_color_smob);
+  scm_set_smob_print (color_smob_tag, coscm_print_color_smob);
+
+  gdbscm_define_integer_constants (colorspaces, 1);
+  gdbscm_define_functions (color_functions, 1);
+
+  colorspace_keyword = scm_from_latin1_keyword ("color-space");
+}
diff --git a/gdb/guile/scm-param.c b/gdb/guile/scm-param.c
index 54c8c27301a..60cc17432f0 100644
--- a/gdb/guile/scm-param.c
+++ b/gdb/guile/scm-param.c
@@ -48,6 +48,9 @@  union pascm_variable
 
   /* Hold a string, for enums.  */
   const char *cstringval;
+
+  /* Hold a color.  */
+  ui_file_style::color color;
 };
 
 /* A GDB parameter.
@@ -129,6 +132,8 @@  make_setting (param_smob *s)
     return setting (s->type, s->value.stringval);
   else if (var_type_uses<const char *> (s->type))
     return setting (s->type, &s->value.cstringval);
+  else if (var_type_uses<ui_file_style::color> (s->type))
+    return setting (s->type, &s->value.color);
   else
     gdb_assert_not_reached ("unhandled var type");
 }
@@ -190,10 +195,9 @@  static SCM
 pascm_make_param_smob (void)
 {
   param_smob *p_smob = (param_smob *)
-    scm_gc_malloc (sizeof (param_smob), param_smob_name);
+    scm_gc_calloc (sizeof (param_smob), param_smob_name);
   SCM p_scm;
 
-  memset (p_smob, 0, sizeof (*p_smob));
   p_smob->cmd_class = no_class;
   p_smob->type = var_boolean; /* ARI: var_boolean */
   p_smob->set_func = SCM_BOOL_F;
@@ -466,6 +470,13 @@  add_setshow_generic (enum var_types param_type, enum command_class cmd_class,
 				       set_list, show_list);
       break;
 
+    case var_color:
+      commands = add_setshow_color_cmd (cmd_name, cmd_class, &self->value.color,
+					set_doc, show_doc, help_doc,
+					set_func, show_func,
+					set_list, show_list);
+      break;
+
     default:
       gdb_assert_not_reached ("bad param_type value");
     }
@@ -545,6 +556,7 @@  static const scheme_integer_constant parameter_types[] =
   { "PARAM_OPTIONAL_FILENAME", var_optional_filename },
   { "PARAM_FILENAME", var_filename },
   { "PARAM_ENUM", var_enum },
+  { "PARAM_COLOR", var_color },
 
   END_INTEGER_CONSTANTS
 };
@@ -611,6 +623,11 @@  pascm_param_value (const setting &var, int arg_pos, const char *func_name)
 	return gdbscm_scm_from_host_string (str, strlen (str));
       }
 
+    case var_color:
+      {
+	return coscm_scm_from_color (var.get<ui_file_style::color> ());
+      }
+
     case var_boolean:
       {
 	if (var.get<bool> ())
@@ -716,6 +733,12 @@  pascm_set_param_value_x (param_smob *p_smob,
 	break;
       }
 
+    case var_color:
+      SCM_ASSERT_TYPE (coscm_is_color (value), value, arg_pos, func_name,
+		       _("<gdb:color>"));
+      var.set<ui_file_style::color> (coscm_get_color (value));
+      break;
+
     case var_boolean:
       SCM_ASSERT_TYPE (gdbscm_is_bool (value), value, arg_pos, func_name,
 		       _("boolean"));
@@ -961,6 +984,8 @@  gdbscm_make_parameter (SCM name_scm, SCM rest)
   scm_set_smob_free (parameter_smob_tag, pascm_free_parameter_smob);
   if (var_type_uses<std::string> (p_smob->type))
     p_smob->value.stringval = new std::string;
+  else if (var_type_uses<ui_file_style::color> (p_smob->type))
+    p_smob->value.color = ui_file_style::NONE;
 
   if (initial_value_arg_pos > 0)
     {
diff --git a/gdb/python/py-color.c b/gdb/python/py-color.c
new file mode 100644
index 00000000000..fb816a06328
--- /dev/null
+++ b/gdb/python/py-color.c
@@ -0,0 +1,346 @@ 
+/* Python interface to ui_file_style::color objects.
+
+   Copyright (C) 2008-2022 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+
+#include "defs.h"
+#include "python-internal.h"
+#include "py-color.h"
+#include "cli/cli-decode.h"
+
+/* Colorspace constants and their values.  */
+static struct {
+  const char *name;
+  color_space value;
+} colorspace_constants[] =
+{
+  { "COLORSPACE_MONOCHROME", color_space::MONOCHROME },
+  { "COLORSPACE_ANSI_8COLOR", color_space::ANSI_8COLOR },
+  { "COLORSPACE_AIXTERM_16COLOR", color_space::AIXTERM_16COLOR },
+  { "COLORSPACE_XTERM_256COLOR", color_space::XTERM_256COLOR },
+  { "COLORSPACE_RGB_24BIT", color_space::RGB_24BIT },
+};
+
+/* A color.  */
+struct colorpy_object
+{
+  PyObject_HEAD
+
+  /* Underlying value.  */
+  ui_file_style::color color;
+};
+
+extern PyTypeObject colorpy_object_type
+    CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("parmpy_object");
+
+/* See py-color.h.  */
+gdbpy_ref<>
+create_color_object (const ui_file_style::color &color)
+{
+  gdbpy_ref<colorpy_object> color_obj (PyObject_New (colorpy_object,
+						     &colorpy_object_type));
+
+  if (color_obj == NULL)
+    return NULL;
+
+  color_obj->color = color;
+  return gdbpy_ref<> ((PyObject *) color_obj.release ());
+}
+
+/* See py-color.h.  */
+bool
+gdbpy_is_color (PyObject *obj)
+{
+  return PyObject_IsInstance (obj, (PyObject *)&colorpy_object_type);
+}
+
+/* See py-color.h.  */
+const ui_file_style::color &
+gdbpy_get_color (PyObject *obj)
+{
+  gdb_assert (gdbpy_is_color (obj));
+  colorpy_object *self = (colorpy_object *) obj;
+  return self->color;
+}
+
+/* Get an attribute.  */
+static PyObject *
+get_attr (PyObject *obj, PyObject *attr_name)
+{
+  if (! PyUnicode_Check (attr_name))
+    return PyObject_GenericGetAttr (obj, attr_name);
+
+  colorpy_object *self = (colorpy_object *) obj;
+  const ui_file_style::color &color = self->color;
+
+  if (! PyUnicode_CompareWithASCIIString (attr_name, "colorspace"))
+    {
+      int value = static_cast<int> (color.colorspace ());
+      return gdb_py_object_from_longest (value).release ();
+    }
+
+  if (! PyUnicode_CompareWithASCIIString (attr_name, "is_none"))
+    return color.is_none () ? Py_True : Py_False;
+
+  if (! PyUnicode_CompareWithASCIIString (attr_name, "is_indexed"))
+    return color.is_indexed () ? Py_True : Py_False;
+
+  if (! PyUnicode_CompareWithASCIIString (attr_name, "is_direct"))
+    return color.is_direct () ? Py_True : Py_False;
+
+  if (color.is_indexed ()
+      && ! PyUnicode_CompareWithASCIIString (attr_name, "index"))
+    return gdb_py_object_from_longest (color.get_value ()).release ();
+
+  if (color.is_direct ()
+      && ! PyUnicode_CompareWithASCIIString (attr_name, "components"))
+    {
+      uint8_t rgb[3];
+      color.get_rgb (rgb);
+
+      gdbpy_ref<> rgb_objects[3];
+      for (int i = 0; i < 3; ++i)
+	{
+	  rgb_objects[i] = gdb_py_object_from_ulongest (rgb[i]);
+	  if (rgb_objects[i] == nullptr)
+	    return nullptr;
+	}
+
+      PyObject *comp = PyTuple_New (3);
+      if (!comp)
+	return nullptr;
+
+      for (int i = 0; i < 3; ++i)
+	PyTuple_SET_ITEM (comp, i, rgb_objects[i].release ());
+
+      return comp;
+    }
+
+  return PyObject_GenericGetAttr (obj, attr_name);
+}
+
+/* Implementation of Color.escape_sequence (self, is_fg) -> str.  */
+
+static PyObject *
+colorpy_escape_sequence (PyObject *self, PyObject *is_fg_obj)
+{
+  if (!gdbpy_is_color (self))
+    {
+      PyErr_SetString (PyExc_RuntimeError,
+		       _("Object is not gdb.Color."));
+      return nullptr;
+    }
+
+  if (! PyBool_Check (is_fg_obj))
+    {
+      PyErr_SetString (PyExc_RuntimeError,
+		       _("A boolean argument is required."));
+      return nullptr;
+    }
+
+  bool is_fg = PyObject_IsTrue (is_fg_obj);
+  std::string s = gdbpy_get_color (self).to_ansi (is_fg);
+
+  return host_string_to_python_string (s.c_str ()).release ();
+}
+
+/* Object initializer; fills color with value.
+
+   Use: __init__(VALUE = None, COLORSPACE = None)
+
+   VALUE is a string, integer, RGB-tuple or None.
+
+   COLORSPACE is the color space index.
+
+   Returns -1 on error, with a python exception set.  */
+
+static int
+colorpy_init (PyObject *self, PyObject *args, PyObject *kwds)
+{
+  colorpy_object *obj = (colorpy_object *) self;
+  PyObject *value_obj = nullptr;
+  PyObject *colorspace_obj = nullptr;
+  color_space colorspace = color_space::MONOCHROME;
+
+  if (! PyArg_ParseTuple (args, "|OO", &value_obj, &colorspace_obj))
+    return -1;
+
+  try
+    {
+      if (colorspace_obj)
+	{
+	  if (PyLong_Check (colorspace_obj))
+	    {
+	      long colorspace_id = -1;
+	      if (! gdb_py_int_as_long (colorspace_obj, &colorspace_id))
+		return -1;
+	      if (colorspace_id < INT_MIN || colorspace_id > INT_MAX)
+		error (_("colorspace %ld is out of range."), colorspace_id);
+	      colorspace = static_cast<color_space> (colorspace_id);
+	    }
+	  else if (colorspace_obj == Py_None)
+	    colorspace_obj = nullptr;
+	  else
+	    error (_("colorspace must be None or integer"));
+	}
+
+      if (!value_obj || value_obj == Py_None)
+	  obj->color = ui_file_style::color (colorspace, -1);
+      else if (PyLong_Check (value_obj))
+	{
+	  long value = -1;
+	  if (! gdb_py_int_as_long (value_obj, &value))
+	    return -1;
+	  if (value < 0 || value > INT_MAX)
+	    error (_("value %ld is out of range."), value);
+	  if (colorspace_obj)
+	    obj->color = ui_file_style::color (colorspace, value);
+	  else
+	    obj->color = ui_file_style::color (value);
+	}
+      else if (PyTuple_Check (value_obj))
+	{
+	  if (!colorspace_obj || colorspace != color_space::RGB_24BIT)
+	    error (_("colorspace must be gdb.COLORSPACE_RGB_24BIT with "
+		   "value of tuple type."));
+	  Py_ssize_t tuple_size = PyTuple_Size (value_obj);
+	  if (tuple_size != 3)
+	    error (_("Tuple value with RGB must be of size 3."));
+	  uint8_t rgb[3];
+	  for (int i = 0; i < 3; ++i)
+	    {
+	      PyObject *item = PyTuple_GetItem (value_obj, i);
+	      if (!PyLong_Check (item))
+		error (_("Item %d of an RGB tuple must be integer."), i);
+	      long item_value = -1;
+	      if (!gdb_py_int_as_long (item, &item_value))
+		return -1;
+	      if (item_value < 0 || item_value > UINT8_MAX)
+		error (_("RGB item %ld is out of byte range."), item_value);
+	      rgb[i] = static_cast<uint8_t> (item_value);
+	    }
+
+	  obj->color = ui_file_style::color (rgb[0], rgb[1], rgb[2]);
+	}
+      else if (PyUnicode_Check (value_obj))
+	{
+	  gdb::unique_xmalloc_ptr<char>
+	    str (python_string_to_host_string (value_obj));
+	  if (!str)
+	    return -1;
+	  obj->color = parse_var_color (str.get());
+
+	  if (colorspace_obj && colorspace != obj->color.colorspace ())
+	    error (_("colorspace doesn't match to value."));
+	}
+      else
+	error (_("value must be one of None, integer, tuple or str."));
+    }
+  catch (const gdb_exception &except)
+    {
+      gdbpy_convert_exception (except);
+      return -1;
+    }
+
+  Py_INCREF (self);
+  return 0;
+}
+
+/* Deallocate function for a gdb.Color.  */
+
+static void
+colorpy_dealloc (PyObject *)
+{
+}
+
+static PyObject *
+colorpy_str (PyObject *self)
+{
+  colorpy_object *obj = reinterpret_cast<colorpy_object *> (self);
+
+  return PyUnicode_FromString (obj->color.to_string ().c_str ());
+}
+
+/* Initialize the 'color' module.  */
+int
+gdbpy_initialize_color (void)
+{
+  colorpy_object_type.tp_new = PyType_GenericNew;
+  if (PyType_Ready (&colorpy_object_type) < 0)
+    return -1;
+
+  for (auto & pair : colorspace_constants)
+      if (PyModule_AddIntConstant (gdb_module, pair.name,
+				   static_cast<long> (pair.value)) < 0)
+	return -1;
+
+  return gdb_pymodule_addobject (gdb_module, "Color",
+				 (PyObject *) &colorpy_object_type);
+}
+
+/* Color methods.  */
+
+static PyMethodDef color_methods[] =
+{
+  { "escape_sequence", colorpy_escape_sequence, METH_O,
+    "escape_sequence (is_foreground) -> str.\n\
+Return the ANSI escape sequence for this color.\n\
+IS_FOREGROUND indicates whether this is a foreground or background color."},
+  {NULL}
+};
+
+PyTypeObject colorpy_object_type =
+{
+  PyVarObject_HEAD_INIT (NULL, 0)
+  "gdb.Color",			  /*tp_name*/
+  sizeof (colorpy_object),	  /*tp_basicsize*/
+  0,				  /*tp_itemsize*/
+  colorpy_dealloc,		  /*tp_dealloc*/
+  0,				  /*tp_print*/
+  0,				  /*tp_getattr*/
+  0,				  /*tp_setattr*/
+  0,				  /*tp_compare*/
+  0,				  /*tp_repr*/
+  0,				  /*tp_as_number*/
+  0,				  /*tp_as_sequence*/
+  0,				  /*tp_as_mapping*/
+  0,				  /*tp_hash */
+  0,				  /*tp_call*/
+  colorpy_str,			  /*tp_str*/
+  get_attr,			  /*tp_getattro*/
+  0,				  /*tp_setattro*/
+  0,				  /*tp_as_buffer*/
+  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+  "GDB color object",	  	  /* tp_doc */
+  0,				  /* tp_traverse */
+  0,				  /* tp_clear */
+  0,				  /* tp_richcompare */
+  0,				  /* tp_weaklistoffset */
+  0,				  /* tp_iter */
+  0,				  /* tp_iternext */
+  color_methods,		  /* tp_methods */
+  0,				  /* tp_members */
+  0,				  /* tp_getset */
+  0,				  /* tp_base */
+  0,				  /* tp_dict */
+  0,				  /* tp_descr_get */
+  0,				  /* tp_descr_set */
+  0,				  /* tp_dictoffset */
+  colorpy_init,			  /* tp_init */
+  0,				  /* tp_alloc */
+};
diff --git a/gdb/python/py-color.h b/gdb/python/py-color.h
new file mode 100644
index 00000000000..95726faa19a
--- /dev/null
+++ b/gdb/python/py-color.h
@@ -0,0 +1,35 @@ 
+/* Python interface to ui_file_style::color objects.
+
+   Copyright (C) 2009-2022 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef PYTHON_PY_COLOR_H
+#define PYTHON_PY_COLOR_H
+
+#include "python-internal.h"
+#include "ui-style.h"
+
+/* Create a new gdb.Color object from COLOR.  */
+extern gdbpy_ref<> create_color_object (const ui_file_style::color &color);
+
+/* Check if OBJ is instance of a gdb.Color type.  */
+extern bool gdbpy_is_color (PyObject *obj);
+
+/* Extracts value from OBJ object of gdb.Color type.  */
+extern const ui_file_style::color & gdbpy_get_color (PyObject *obj);
+
+#endif /* PYTHON_PY_COLOR_H */
diff --git a/gdb/python/py-param.c b/gdb/python/py-param.c
index 5d509ba4658..265b638416f 100644
--- a/gdb/python/py-param.c
+++ b/gdb/python/py-param.c
@@ -27,6 +27,7 @@ 
 #include "completer.h"
 #include "language.h"
 #include "arch-utils.h"
+#include "py-color.h"
 
 /* Parameter constants and their values.  */
 static struct {
@@ -46,6 +47,7 @@  static struct {
   { "PARAM_ZUINTEGER", var_zuinteger },
   { "PARAM_ZUINTEGER_UNLIMITED", var_zuinteger_unlimited },
   { "PARAM_ENUM", var_enum },
+  { "PARAM_COLOR", var_color },
   { NULL, 0 }
 };
 
@@ -70,6 +72,9 @@  union parmpy_variable
 
   /* Hold a string, for enums.  */
   const char *cstringval;
+
+  /* Hold a color.  */
+  ui_file_style::color color;
 };
 
 /* A GDB parameter.  */
@@ -108,6 +113,8 @@  make_setting (parmpy_object *s)
     return setting (s->type, s->value.stringval);
   else if (var_type_uses<const char *> (s->type))
     return setting (s->type, &s->value.cstringval);
+  else if (var_type_uses<ui_file_style::color> (s->type))
+    return setting (s->type, &s->value.color);
   else
     gdb_assert_not_reached ("unhandled var type");
 }
@@ -199,6 +206,19 @@  set_parameter_value (parmpy_object *self, PyObject *value)
 	break;
       }
 
+    case var_color:
+      {
+	if (gdbpy_is_color (value))
+	  self->value.color = gdbpy_get_color (value);
+	else
+	  {
+	    PyErr_SetString (PyExc_RuntimeError,
+			     _("color argument must be a gdb.Color object."));
+	    return -1;
+	  }
+      }
+      break;
+
     case var_boolean:
       if (! PyBool_Check (value))
 	{
@@ -637,6 +657,15 @@  add_setshow_generic (int parmclass, enum command_class cmdclass,
 				       get_show_value, set_list, show_list);
       break;
 
+    case var_color:
+      /* Initialize the value, just in case.  */
+      self->value.color = ui_file_style::NONE;
+      commands = add_setshow_color_cmd (cmd_name.get (), cmdclass,
+					&self->value.color, set_doc,
+					show_doc, help_doc, get_set_value,
+					get_show_value, set_list, show_list);
+      break;
+
     default:
       gdb_assert_not_reached ("Unhandled parameter class.");
     }
@@ -758,7 +787,8 @@  parmpy_init (PyObject *self, PyObject *args, PyObject *kwds)
       && parmclass != var_string && parmclass != var_string_noescape
       && parmclass != var_optional_filename && parmclass != var_filename
       && parmclass != var_zinteger && parmclass != var_zuinteger
-      && parmclass != var_zuinteger_unlimited && parmclass != var_enum)
+      && parmclass != var_zuinteger_unlimited && parmclass != var_enum
+      && parmclass != var_color)
     {
       PyErr_SetString (PyExc_RuntimeError,
 		       _("Invalid parameter class argument."));
@@ -779,7 +809,7 @@  parmpy_init (PyObject *self, PyObject *args, PyObject *kwds)
   else
     obj->enumeration = NULL;
   obj->type = (enum var_types) parmclass;
-  memset (&obj->value, 0, sizeof (obj->value));
+  obj->value = {}; /* zeros initialization */
 
   if (var_type_uses<std::string> (obj->type))
     obj->value.stringval = new std::string;
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index 08749d14200..f3877e04ffa 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -545,6 +545,8 @@  int gdbpy_initialize_micommands (void)
 void gdbpy_finalize_micommands ();
 int gdbpy_initialize_disasm ()
   CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
+int gdbpy_initialize_color (void)
+  CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
 
 PyMODINIT_FUNC gdbpy_events_mod_func ();
 
diff --git a/gdb/python/python.c b/gdb/python/python.c
index c7d4157b70c..0eb47cbd543 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -37,6 +37,7 @@ 
 #include "run-on-main-thread.h"
 #include "gdbsupport/selftest.h"
 #include "observable.h"
+#include "py-color.h"
 
 /* Declared constants and enum for python stack printing.  */
 static const char python_excp_none[] = "none";
@@ -484,6 +485,12 @@  gdbpy_parameter_value (const setting &var)
 	return host_string_to_python_string (str).release ();
       }
 
+    case var_color:
+      {
+	const ui_file_style::color &color = var.get<ui_file_style::color> ();
+	return create_color_object (color).release ();
+      }
+
     case var_boolean:
       {
 	if (var.get<bool> ())
@@ -2148,6 +2155,7 @@  do_start_initialization ()
       || gdbpy_initialize_membuf () < 0
       || gdbpy_initialize_connection () < 0
       || gdbpy_initialize_tui () < 0
+      || gdbpy_initialize_color () < 0
       || gdbpy_initialize_micommands () < 0)
     return false;
 
diff --git a/gdb/testsuite/gdb.base/style.exp b/gdb/testsuite/gdb.base/style.exp
index 2242c5bf743..775d1b45e12 100644
--- a/gdb/testsuite/gdb.base/style.exp
+++ b/gdb/testsuite/gdb.base/style.exp
@@ -68,10 +68,12 @@  proc clean_restart_and_disable { prefix args } {
 proc run_style_tests { } {
     global testfile srcfile hex binfile
     global currently_disabled_style decimal hex
+    global env
 
-    save_vars { env(TERM) } {
+    save_vars { env(TERM) env(COLORTERM) } {
 	# We need an ANSI-capable terminal to get the output.
-	setenv TERM ansi
+	setenv TERM xterm-256color
+	setenv COLORTERM truecolor
 
 	# Restart GDB with the correct TERM variable setting, this
 	# means that GDB will enable styling.
@@ -298,6 +300,21 @@  proc run_style_tests { } {
 	set url [limited_style "http:.*html" file]
 	gdb_test "show version" "${vers}.*<$url>.*" \
 	    "'show version' is styled"
+
+	if { $currently_disabled_style != "version" } {
+	    # Check that colors in styling can be set as integer and as RGB hex
+	    # triplet.  Check that the version string is styled in the output of
+	    # 'show version' according to the set colors.
+	    gdb_test_no_output "set style version intensity normal"
+	    gdb_test_no_output "set style version background 255"
+	    gdb_test_no_output "set style version foreground #FED210"
+	    gdb_test "show style version background" \
+		"The \033\\\[38;2;254;210;16;48;5;255;22;27m.*\".*version.*\".*style.*\033\\\[m background color is: 255" \
+		"Version's 256-color background style"
+	    gdb_test "show style version foreground" \
+		"The \033\\\[38;2;254;210;16;48;5;255;22;27m.*\".*version.*\".*style.*\033\\\[m foreground color is: #FED210" \
+		"Version's TrueColor foreground style"
+	}
     }
 }
 
@@ -370,6 +387,188 @@  proc test_startup_version_string { } {
     gdb_test "" "${vers}.*" "version is styled at startup"
 }
 
+# Color support is disabled when TERM=dumb and COLORTERM="".
+# All colors are approximated to none when set.
+proc test_colorsupport_monochrome { } {
+    with_test_prefix "colorsupport_monochrome" {
+	global env
+	save_vars { env(TERM) env(COLORTERM) } {
+	    setenv TERM dumb
+	    setenv COLORTERM ""
+	    gdb_exit
+	    gdb_start
+	    gdb_test "print \$_colorsupport" \
+		"\\\$1 = \"monochrome\"" \
+		"color support is monochrome"
+	    gdb_test_no_output "set style enabled off"
+	    gdb_test_no_output "set style filename foreground none"
+	    gdb_test "show style filename foreground" \
+	      "The \"filename\" style foreground color is: none" \
+	      "none without approximation"
+	    gdb_test_no_output "set style filename foreground blue"
+	    gdb_test "show style filename foreground" \
+	      "The \"filename\" style foreground color is: none" \
+	      "blue approximated to none"
+	}
+    }
+}
+
+# Color support is limited by 8 colors when TERM=ansi and COLORTERM="".
+# All colors are approximated to basic colors when set.
+proc test_colorsupport_8color { } {
+    with_test_prefix "colorsupport_8color" {
+	global env
+	save_vars { env(TERM) env(COLORTERM) } {
+	    setenv TERM ansi
+	    setenv COLORTERM ""
+	    gdb_exit
+	    gdb_start
+	    gdb_test "print \$_colorsupport" \
+		"\\\$1 = \"monochrome,ansi_8color\"" \
+		"color support is 8 color"
+	    gdb_test_no_output "set style enabled off"
+	    gdb_test_no_output "set style filename foreground none"
+	    gdb_test "show style filename foreground" \
+	      "The \"filename\" style foreground color is: none" \
+	      "none without approximation"
+	    gdb_test_no_output "set style filename foreground yellow"
+	    gdb_test "show style filename foreground" \
+	      "The \"filename\" style foreground color is: yellow" \
+	      "yellow without approximation"
+	    gdb_test_no_output "set style filename foreground 9"
+	    gdb_test "show style filename foreground" \
+	      "The \"filename\" style foreground color is: red" \
+	      "9 approximated to red"
+	    gdb_test_no_output "set style filename foreground 118"
+	    gdb_test "show style filename foreground" \
+	      "The \"filename\" style foreground color is: green" \
+	      "118 approximated to green"
+	    gdb_test_no_output "set style filename foreground #000ABC"
+	    gdb_test "show style filename foreground" \
+	      "The \"filename\" style foreground color is: blue" \
+	      "#000ABC approximated to blue"
+	}
+    }
+}
+
+# Color support is limited by 256 colors when TERM=xterm-256color and
+# COLORTERM="".  All colors are approximated by 256 color palette when set.
+proc test_colorsupport_256color { } {
+    with_test_prefix "colorsupport_256color" {
+	global env
+	save_vars { env(TERM) env(COLORTERM) } {
+	    setenv TERM xterm-256color
+	    setenv COLORTERM ""
+	    gdb_exit
+	    gdb_start
+	    gdb_test "print \$_colorsupport" \
+		"\\\$1 = \"monochrome,ansi_8color,aixterm_16color,xterm_256color\"" \
+		"color support is 256 color"
+	    gdb_test_no_output "set style enabled off"
+	    gdb_test_no_output "set style filename foreground none"
+	    gdb_test "show style filename foreground" \
+	      "The \"filename\" style foreground color is: none" \
+	      "none without approximation"
+	    gdb_test_no_output "set style filename foreground red"
+	    gdb_test "show style filename foreground" \
+	      "The \"filename\" style foreground color is: red" \
+	      "red without approximation"
+	    gdb_test_no_output "set style filename foreground 9"
+	    gdb_test "show style filename foreground" \
+	      "The \"filename\" style foreground color is: 9" \
+	      "9 without approximation"
+	    gdb_test_no_output "set style filename foreground 118"
+	    gdb_test "show style filename foreground" \
+	      "The \"filename\" style foreground color is: 118" \
+	      "118 without approximation"
+	    gdb_test_no_output "set style filename foreground #CD00CD"
+	    gdb_test "show style filename foreground" \
+	      "The \"filename\" style foreground color is: 5" \
+	      "#CD00CD approximated to 5"
+	    gdb_test_no_output "set style filename foreground #FFAF12"
+	    gdb_test "show style filename foreground" \
+	      "The \"filename\" style foreground color is: 214" \
+	      "#FFAF12 approximated to 214"
+	}
+    }
+}
+
+# Color support is limited by 16777216 colors when TERM=xterm-256color and
+# COLORTERM="truecolor".  No approximation needed.
+proc test_colorsupport_truecolor { } {
+    with_test_prefix "colorsupport_truecolor" {
+	global env
+	save_vars { env(TERM) env(COLORTERM) } {
+	    setenv TERM xterm-256color
+	    setenv COLORTERM truecolor
+	    gdb_exit
+	    gdb_start
+	    gdb_test "print \$_colorsupport" \
+		"\\\$1 = \"monochrome,ansi_8color,aixterm_16color,xterm_256color,rgb_24bit\"" \
+		"color support is truecolor"
+	    gdb_test_no_output "set style enabled off"
+	    gdb_test_no_output "set style filename foreground none"
+	    gdb_test "show style filename foreground" \
+	      "The \"filename\" style foreground color is: none" \
+	      "none without approximation"
+	    gdb_test_no_output "set style filename foreground red"
+	    gdb_test "show style filename foreground" \
+	      "The \"filename\" style foreground color is: red" \
+	      "red without approximation"
+	    gdb_test_no_output "set style filename foreground 9"
+	    gdb_test "show style filename foreground" \
+	      "The \"filename\" style foreground color is: 9" \
+	      "9 without approximation"
+	    gdb_test_no_output "set style filename foreground 118"
+	    gdb_test "show style filename foreground" \
+	      "The \"filename\" style foreground color is: 118" \
+	      "118 without approximation"
+	    gdb_test_no_output "set style filename foreground #CD00CD"
+	    gdb_test "show style filename foreground" \
+	      "The \"filename\" style foreground color is: #CD00CD" \
+	      "#CD00CD without approximation"
+	}
+    }
+}
+
+# Color support is limited by 16777216 colors when TERM=dumb and
+# COLORTERM=24bit.  No approximation needed.  Basic colors replaced with RGB.
+proc test_colorsupport_truecolor_only { } {
+    with_test_prefix "colorsupport_truecolor_only" {
+	global env
+	save_vars { env(TERM) env(COLORTERM) } {
+	    setenv TERM dumb
+	    setenv COLORTERM truecolor
+	    gdb_exit
+	    gdb_start
+	    gdb_test "print \$_colorsupport" \
+		"\\\$1 = \"monochrome,rgb_24bit\"" \
+		"color support is truecolor only"
+	    gdb_test_no_output "set style enabled off"
+	    gdb_test_no_output "set style filename foreground none"
+	    gdb_test "show style filename foreground" \
+	      "The \"filename\" style foreground color is: none" \
+	      "none without approximation"
+	    gdb_test_no_output "set style filename foreground red"
+	    gdb_test "show style filename foreground" \
+	      "The \"filename\" style foreground color is: #DE382B" \
+	      "red replaced by #DE382B"
+	    gdb_test_no_output "set style filename foreground 9"
+	    gdb_test "show style filename foreground" \
+	      "The \"filename\" style foreground color is: #FF0000" \
+	      "9 replaced by #FF0000"
+	    gdb_test_no_output "set style filename foreground 118"
+	    gdb_test "show style filename foreground" \
+	      "The \"filename\" style foreground color is: #87FF00" \
+	      "118 replaced by #87FF00"
+	    gdb_test_no_output "set style filename foreground #CD00CD"
+	    gdb_test "show style filename foreground" \
+	      "The \"filename\" style foreground color is: #CD00CD" \
+	      "#CD00CD without approximation"
+	}
+    }
+}
+
 # Check to see if the Python styling of disassembler output is
 # expected or not, this styling requires Python support in GDB, and
 # the Python pygments module to be available.
@@ -402,3 +601,9 @@  if { $python_disassembly_styling } {
 
 # Finally, check the styling of the version string during startup.
 test_startup_version_string
+
+test_colorsupport_monochrome
+test_colorsupport_8color
+test_colorsupport_256color
+test_colorsupport_truecolor
+test_colorsupport_truecolor_only
diff --git a/gdb/testsuite/gdb.guile/scm-color.exp b/gdb/testsuite/gdb.guile/scm-color.exp
new file mode 100644
index 00000000000..e6e9dd4838c
--- /dev/null
+++ b/gdb/testsuite/gdb.guile/scm-color.exp
@@ -0,0 +1,114 @@ 
+# Copyright (C) 2010-2022 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# This file is part of the GDB testsuite.
+# It tests GDB parameter support in Guile.
+
+load_lib gdb-guile.exp
+
+# Start with a fresh gdb.
+gdb_exit
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+
+# Skip all tests if Guile scripting is not enabled.
+if { [skip_guile_tests] } { continue }
+
+gdb_install_guile_utils
+gdb_install_guile_module
+
+gdb_test_no_output [concat "guile (define (print_color_attrs c) " \
+    "(display (color-string c)) (display \" \") " \
+    "(display (color-colorspace c)) (display \" \") " \
+    "(display (color-none? c)) (display \" \") " \
+    "(display (color-indexed? c)) (display \" \") " \
+    "(display (color-direct? c)) (newline))"] \
+    "print_color_attrs helper"
+
+gdb_test "guile (print_color_attrs (make-color))" \
+    "none 0 #t #f #f" \
+    "print attrs of a color without params"
+
+gdb_test_no_output "guile (define c (make-color \"green\"))" \
+    "create color from basic name string"
+gdb_test "guile (print_color_attrs c)" "green 1 #f #t #f" \
+    "print attrs of a basic color name"
+gdb_test "guile (print (color-index c))" "2" \
+    "print index of a basic color name"
+
+gdb_test_no_output "guile (define c (make-color 2))" \
+    "create color from basic index"
+gdb_test "guile (print_color_attrs c)" "green 1 #f #t #f" \
+    "print attrs of a basic color"
+gdb_test "guile (print (color-index c))" "2" \
+    "print index of a basic color"
+
+gdb_test_no_output "guile (define c (make-color 14))" \
+    "create color from integer 14"
+gdb_test "guile (print_color_attrs c)" "14 2 #f #t #f" \
+    "print attrs of an color 14"
+gdb_test "guile (print (color-index c))" "14" \
+    "print index of color 14"
+
+gdb_test_no_output "guile (define c (make-color 2 #:color-space COLORSPACE_ANSI_8COLOR))" \
+    "create color from basic index and ansi colorspace"
+gdb_test "guile (print_color_attrs c)" "green 1 #f #t #f" \
+    "print attrs of a basic color with ansi colorspace"
+gdb_test "guile (print (color-index c))" "2" \
+    "print index of a basic color with ansi colorspace"
+
+gdb_test_no_output "guile (define c (make-color 2 #:color-space COLORSPACE_XTERM_256COLOR))" \
+    "create color from basic index and xterm256 colorspace"
+gdb_test "guile (print_color_attrs c)" "2 3 #f #t #f" \
+    "print attrs of a basic color with xterm256 colorspace"
+gdb_test "guile (print (color-index c))" "2" \
+    "print index of a basic color with xterm256 colorspace"
+
+gdb_test_no_output "guile (define c (make-color '(171 205 239) #:color-space COLORSPACE_RGB_24BIT))" \
+    "create color from rgb components"
+gdb_test "guile (print_color_attrs c)" "#ABCDEF 4 #f #f #t" \
+    "print attrs of an RGB color"
+gdb_test "guile (print (color-components c))" "\\(171 205 239\\)" \
+    "print components of an RGB color"
+
+gdb_test_no_output "guile (define c (make-color \"none\"))" \
+    "create color from string none"
+gdb_test "guile (print_color_attrs c)" "none 0 #t #f #f" \
+    "print attrs of a color none"
+
+gdb_test_no_output "guile (define c (make-color \"254\"))" \
+    "create color from string 254"
+gdb_test "guile (print_color_attrs c)" "254 3 #f #t #f" \
+    "print attrs of an color 254"
+gdb_test "guile (print (color-index c))" "254" \
+    "print index of color 254"
+
+gdb_test_no_output "guile (define c_none (make-color \"none\"))" \
+    "save default color"
+gdb_test_no_output "guile (define c_red (make-color \"red\"))" \
+    "save blue color"
+gdb_test_no_output "guile (define c_green (make-color \"green\"))" \
+    "save yellow color"
+gdb_test [concat "guile " \
+    "(display (color-escape-sequence c_red #t)) " \
+    "(display (color-escape-sequence c_green #f)) " \
+    "(display \"red on green\") " \
+    "(display (color-escape-sequence c_none #f)) " \
+    "(display \" red on default\") " \
+    "(display (color-escape-sequence c_none #t)) " \
+    "(newline)"] \
+    "\033\\\[31m\033\\\[42mred on green\033\\\[49m red on default\033\\\[39m" \
+    "escape sequences"
+
diff --git a/gdb/testsuite/gdb.guile/scm-parameter.exp b/gdb/testsuite/gdb.guile/scm-parameter.exp
index cf6f2834373..6ea540d16fd 100644
--- a/gdb/testsuite/gdb.guile/scm-parameter.exp
+++ b/gdb/testsuite/gdb.guile/scm-parameter.exp
@@ -206,3 +206,46 @@  with_test_prefix "previously-ambiguous" {
     gdb_test "help set print s" "This command is not documented." "set help"
     gdb_test "help set print" "set print s -- This command is not documented.*" "general help"
 }
+
+# Test a color parameter.
+
+save_vars { env(TERM) env(COLORTERM) } {
+  # This enables 256 colors support and disables colors approximation.
+  setenv TERM xterm-256color
+  setenv COLORTERM truecolor
+
+  # Start with a fresh gdb.
+  gdb_exit
+  gdb_start
+  gdb_reinitialize_dir $srcdir/$subdir
+
+  gdb_install_guile_utils
+  gdb_install_guile_module
+
+  # We use "." here instead of ":" so that this works on win32 too.
+  set escaped_directory [string_to_regexp "$srcdir/$subdir"]
+
+  gdb_test_multiline "color gdb parameter" \
+      "guile" "" \
+      "(define test-color-param" "" \
+      "  (make-parameter \"print test-color-param\"" "" \
+      "   #:command-class COMMAND_DATA" "" \
+      "   #:parameter-type PARAM_COLOR" "" \
+      "   #:doc \"When set, test param does something useful. When disabled, does nothing.\"" "" \
+      "   #:show-doc \"Show the state of the test-color-param.\"" "" \
+      "   #:set-doc \"Set the state of the test-color-param.\"" "" \
+      "   #:show-func (lambda (self value)" "" \
+      "      (format #f \"The state of the test-color-param is ~a.\" value))" "" \
+      "   #:initial-value (make-color \"green\")))" "" \
+      "(register-parameter! test-color-param)" "" \
+      "end"
+
+  with_test_prefix "test-color-param" {
+      gdb_test "guile (print (parameter-value test-color-param))" "= #<gdb:color green COLORSPACE_ANSI_8COLOR>" "color parameter value (green)"
+      gdb_test "show print test-color-param" "The state of the test-color-param is green." "show initial value"
+      gdb_test_no_output "set print test-color-param 255"
+      gdb_test "show print test-color-param" "The state of the test-color-param is 255." "show new value"
+      gdb_test "guile (print (parameter-value test-color-param))" "= #<gdb:color 255 COLORSPACE_XTERM_256COLOR>" "color parameter value (255)"
+      gdb_test "set print test-color-param 256" "integer 256 out of range.*" "set invalid color parameter"
+  }
+}
diff --git a/gdb/testsuite/gdb.python/py-color.exp b/gdb/testsuite/gdb.python/py-color.exp
new file mode 100644
index 00000000000..5764342d89d
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-color.exp
@@ -0,0 +1,101 @@ 
+# Copyright (C) 2010-2022 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# This file is part of the GDB testsuite.
+# It tests gdb.parameter and gdb.Parameter.
+
+load_lib gdb-python.exp
+
+# Start with a fresh gdb.
+clean_restart
+
+# Skip all tests if Python scripting is not enabled.
+if { [skip_python_tests] } { continue }
+
+gdb_test_no_output "python print_color_attrs = lambda c: print (c, c.colorspace, c.is_none, c.is_indexed, c.is_direct)" \
+    "print_color_attrs helper"
+
+gdb_test_no_output "python c = gdb.Color ()" \
+    "create color without params"
+gdb_test "python print_color_attrs (c)" "none 0 True False False" \
+    "print attrs of a color without params"
+
+gdb_test_no_output "python c = gdb.Color ('green')" \
+    "create color from basic name string"
+gdb_test "python print_color_attrs (c)" "green 1 False True False" \
+    "print attrs of a basic color name"
+gdb_test "python print (c.index)" "2" \
+    "print index of a basic color name"
+
+gdb_test_no_output "python c = gdb.Color (2)" \
+    "create color from basic index"
+gdb_test "python print_color_attrs (c)" "green 1 False True False" \
+    "print attrs of a basic color"
+gdb_test "python print (c.index)" "2" \
+    "print index of a basic color"
+
+gdb_test_no_output "python c = gdb.Color (14)" \
+    "create color from integer 14"
+gdb_test "python print_color_attrs (c)" "14 2 False True False" \
+    "print attrs of an color 14"
+gdb_test "python print (c.index)" "14" \
+    "print index of color 14"
+
+gdb_test_no_output "python c = gdb.Color (2, gdb.COLORSPACE_ANSI_8COLOR)" \
+    "create color from basic index and ansi colorspace"
+gdb_test "python print_color_attrs (c)" "green 1 False True False" \
+    "print attrs of a basic color with ansi colorspace"
+gdb_test "python print (c.index)" "2" \
+    "print index of a basic color with ansi colorspace"
+
+gdb_test_no_output "python c = gdb.Color (2, gdb.COLORSPACE_XTERM_256COLOR)" \
+    "create color from basic index and xterm256 colorspace"
+gdb_test "python print_color_attrs (c)" "2 3 False True False" \
+    "print attrs of a basic color with xterm256 colorspace"
+gdb_test "python print (c.index)" "2" \
+    "print index of a basic color with xterm256 colorspace"
+
+gdb_test_no_output "python c = gdb.Color ((171, 205, 239), gdb.COLORSPACE_RGB_24BIT)" \
+    "create color from rgb components"
+gdb_test "python print_color_attrs (c)" "#ABCDEF 4 False False True" \
+    "print attrs of an RGB color"
+gdb_test "python print (c.components)" "\\(171, 205, 239\\)" \
+    "print components of an RGB color"
+
+gdb_test_no_output "python c = gdb.Color ('none')" \
+    "create color from string none"
+gdb_test "python print_color_attrs (c)" "none 0 True False False" \
+    "print attrs of a color none"
+
+gdb_test_no_output "python c = gdb.Color ('254')" \
+    "create color from string 254"
+gdb_test "python print_color_attrs (c)" "254 3 False True False" \
+    "print attrs of an color 254"
+gdb_test "python print (c.index)" "254" \
+    "print index of color 254"
+
+gdb_test_no_output "python c_none = gdb.Color ('none')" \
+    "save default color"
+gdb_test_no_output "python c_red = gdb.Color ('red')" \
+    "save blue color"
+gdb_test_no_output "python c_green = gdb.Color ('green')" \
+    "save yellow color"
+gdb_test [concat "python print (c_red.escape_sequence (True) + " \
+    "c_green.escape_sequence (False) + 'red on green' + " \
+    "c_none.escape_sequence (False) + ' red on default' + " \
+    "c_none.escape_sequence (True))"] \
+    "\033\\\[31m\033\\\[42mred on green\033\\\[49m red on default\033\\\[39m" \
+    "escape sequences"
+
diff --git a/gdb/testsuite/gdb.python/py-parameter.exp b/gdb/testsuite/gdb.python/py-parameter.exp
index d6db6ac3bb1..9254380dbcb 100644
--- a/gdb/testsuite/gdb.python/py-parameter.exp
+++ b/gdb/testsuite/gdb.python/py-parameter.exp
@@ -177,6 +177,58 @@  proc_with_prefix test_enum_parameter { } {
 	"Undefined item: \"three\".*" "set invalid enum parameter"
 }
 
+# Test an color parameter.
+proc_with_prefix test_color_parameter { } {
+    global env
+    save_vars { env(TERM) env(COLORTERM) } {
+	# This enables 256 colors support and disables colors approximation.
+	setenv TERM xterm-256color
+	setenv COLORTERM truecolor
+
+	clean_restart
+
+	gdb_test_multiline "color gdb parameter" \
+	    "python" "" \
+	    "class TestColorParam (gdb.Parameter):" "" \
+	    "   \"\"\"When set, test param does something useful. When disabled, does nothing.\"\"\"" "" \
+	    "   show_doc = \"Show the state of the color\"" ""\
+	    "   set_doc = \"Set the state of the color\"" "" \
+	    "   def get_show_string (self, pvalue):" ""\
+	    "      return \"The state of the color is \" + str(pvalue)" ""\
+	    "   def get_set_string (self):" ""\
+	    "      return \"The state of the color has been set to \" + str(self.value)" ""\
+	    "   def __init__ (self, name):" "" \
+	    "      super (TestColorParam, self).__init__ (name, gdb.COMMAND_DATA, gdb.PARAM_COLOR)" "" \
+	    "      self.value = gdb.Color(\"green\")" "" \
+	    "test_color_param = TestColorParam ('print test-color-param')" ""\
+	    "end"
+
+	gdb_test "python print (test_color_param.value)" "green" \
+	    "test color parameter value is green"
+	gdb_test "show print test-color-param" \
+	    "The state of the color is green.*" \
+	    "show parameter is initial value"
+	gdb_test "set print test-color-param 255" \
+	    "The state of the color has been set to 255" "set color to 255"
+	gdb_test "show print test-color-param" \
+	    "The state of the color is 255.*" "show parameter is new value"
+	gdb_test "python print (test_color_param.value)" "255" \
+	    "test color parameter value is 255"
+	gdb_test_no_output "python test_color_param.value = gdb.Color(254)" \
+	    "assign test_color_param.value to 254"
+	gdb_test "python print (test_color_param.value)" "254" \
+	    "test color parameter value is integer"
+	gdb_test_no_output "python test_color_param.value =  gdb.Color('#FED210')" \
+	    "assign test_color_param.value to #FED210"
+	gdb_test "python print (test_color_param.value.components)" "\\(254, 210, 16\\)" \
+	    "test color parameter components from RGB hex tripple value"
+	gdb_test "set print test-color-param 256" \
+	    "integer 256 out of range.*" "set invalid color parameter"
+	gdb_test "python test_color_param.value = gdb.Color(256)" \
+	    ".*gdb.error: Palette color index 256 is out of range.*" "set invalid color value"
+    }
+}
+
 # Test a file parameter.
 proc_with_prefix test_file_parameter { } {
     clean_restart
@@ -391,6 +443,7 @@  test_directories
 test_data_directory
 test_boolean_parameter
 test_enum_parameter
+test_color_parameter
 test_file_parameter
 test_undocumented_parameter
 test_really_undocumented_parameter
diff --git a/gdb/testsuite/lib/gdb-utils.exp b/gdb/testsuite/lib/gdb-utils.exp
index ffdfb75557c..e7bfbfbd29c 100644
--- a/gdb/testsuite/lib/gdb-utils.exp
+++ b/gdb/testsuite/lib/gdb-utils.exp
@@ -59,16 +59,20 @@  proc string_list_to_regexp { args } {
 # "function", "variable", or "address".
 
 proc style {str style} {
+    set fg 39
+    set bg 49
+    set intensity 22
+    set reverse 27
     switch -exact -- $style {
-	title { set style 1 }
-	file { set style 32 }
-	function { set style 33 }
-	highlight { set style 31 }
-	variable { set style 36 }
-	address { set style 34 }
-	metadata { set style 2 }
-	version { set style "35;1" }
+	title { set intensity 1 }
+	file { set fg 32 }
+	function { set fg 33 }
+	highlight { set fg 31 }
+	variable { set fg 36 }
+	address { set fg 34 }
+	metadata { set intensity 2 }
+	version { set fg 35; set intensity 1 }
 	none { return $str }
     }
-    return "\033\\\[${style}m${str}\033\\\[m"
+    return "\033\\\[${fg};${bg};${intensity};${reverse}m${str}\033\\\[m"
 }
diff --git a/gdb/top.c b/gdb/top.c
index 60835acd5e5..b9d153d3933 100644
--- a/gdb/top.c
+++ b/gdb/top.c
@@ -2242,6 +2242,17 @@  init_gdb_version_vars (void)
   set_internalvar_integer (minor_version_var, vminor + (vrevision > 0));
 }
 
+static void
+init_colorsupport_var (void)
+{
+  const std::vector<color_space>& cs = colorsupport ();
+  std::string s;
+  for (color_space c : cs)
+    s.append (s.empty () ? "" : ",").append (color_space_name (c));
+  struct internalvar *colorsupport_var = create_internalvar ("_colorsupport");
+  set_internalvar_string (colorsupport_var, s.c_str ());
+}
+
 static void
 init_main (void)
 {
@@ -2457,6 +2468,8 @@  gdb_init ()
 
   /* Create $_gdb_major and $_gdb_minor convenience variables.  */
   init_gdb_version_vars ();
+  /* Create $_colorsupport convenience variable.  */
+  init_colorsupport_var ();
 }
 
 void _initialize_top ();
diff --git a/gdb/ui-style.c b/gdb/ui-style.c
index f1a5b8c4101..f41f87ac67a 100644
--- a/gdb/ui-style.c
+++ b/gdb/ui-style.c
@@ -18,6 +18,7 @@ 
 
 #include "defs.h"
 #include "ui-style.h"
+#include "gdb_curses.h"
 #include "gdbsupport/gdb_regex.h"
 
 /* A regular expression that is used for matching ANSI terminal escape
@@ -47,91 +48,229 @@  static const char ansi_regex_text[] =
 
 static regex_t ansi_regex;
 
-/* This maps bright colors to RGB triples.  The index is the bright
-   color index, starting with bright black.  The values come from
-   xterm.  */
-
-static const uint8_t bright_colors[][3] = {
-  { 127, 127, 127 },		/* Black.  */
-  { 255, 0, 0 },		/* Red.  */
-  { 0, 255, 0 },		/* Green.  */
-  { 255, 255, 0 },		/* Yellow.  */
-  { 92, 92, 255 },		/* Blue.  */
-  { 255, 0, 255 },		/* Magenta.  */
-  { 0, 255, 255 },		/* Cyan.  */
-  { 255, 255, 255 }		/* White.  */
+/* This maps 8-color palette to RGB triples.  The values come from
+   plain linux terminal.  */
+
+static const uint8_t palette_8colors[][3] = {
+  { 1, 1, 1 },			/* Black.  */
+  { 222, 56, 43 },		/* Red.  */
+  { 57, 181, 74 },		/* Green.  */
+  { 255, 199, 6 },		/* Yellow.  */
+  { 0, 111, 184 },		/* Blue.  */
+  { 118, 38, 113 },		/* Magenta.  */
+  { 44, 181, 233 },		/* Cyan.  */
+  { 204, 204, 204 },		/* White.  */
 };
 
+/* This maps 16-color palette to RGB triples.  The values come from xterm.  */
+
+static const uint8_t palette_16colors[][3] = {
+  { 0, 0, 0 },			/* Black.  */
+  { 205, 0, 0 },		/* Red.  */
+  { 0, 205, 0 },		/* Green.  */
+  { 205, 205, 0 },		/* Yellow.  */
+  { 0, 0, 238 },		/* Blue.  */
+  { 205, 0, 205 },		/* Magenta.  */
+  { 0, 205, 205 },		/* Cyan.  */
+  { 229, 229, 229 },		/* White.  */
+  { 127, 127, 127 },		/* Bright Black.  */
+  { 255, 0, 0 },		/* Bright Red.  */
+  { 0, 255, 0 },		/* Bright Green.  */
+  { 255, 255, 0 },		/* Bright Yellow.  */
+  { 92, 92, 255 },		/* Bright Blue.  */
+  { 255, 0, 255 },		/* Bright Magenta.  */
+  { 0, 255, 255 },		/* Bright Cyan.  */
+  { 255, 255, 255 }		/* Bright White.  */
+};
+
+/* See ui-style.h.  */
+/* Must correspond to ui_file_style::basic_color.  */
+const std::vector<const char *> ui_file_style::basic_color_enums = {
+  "none",
+  "black",
+  "red",
+  "green",
+  "yellow",
+  "blue",
+  "magenta",
+  "cyan",
+  "white",
+  nullptr
+};
+
+/* Returns text representation of a basic COLOR.  */
+
+static const char *
+basic_color_name (int color)
+{
+  int pos = color - ui_file_style::NONE;
+  if (0 <= pos && pos < ui_file_style::basic_color_enums.size ())
+    if (const char *s = ui_file_style::basic_color_enums[pos])
+      return s;
+  error (_("Basic color %d has no name."), color);
+}
+
 /* See ui-style.h.  */
 
 bool
 ui_file_style::color::append_ansi (bool is_fg, std::string *str) const
 {
-  if (m_simple)
-    {
-      if (m_value >= BLACK && m_value <= WHITE)
-	str->append (std::to_string (m_value + (is_fg ? 30 : 40)));
-      else if (m_value > WHITE && m_value <= WHITE + 8)
-	str->append (std::to_string (m_value - WHITE + (is_fg ? 90 : 100)));
-      else if (m_value != -1)
-	{
-	  str->append (is_fg ? "38;5;" : "48;5;");
-	  str->append (std::to_string (m_value));
-	}
-      else
-	return false;
-    }
-  else
+  if (m_color_space == color_space::MONOCHROME)
+    str->append (is_fg ? "39" : "49");
+  else if (is_basic ())
+    str->append (std::to_string (m_value + (is_fg ? 30 : 40)));
+  else if (m_color_space == color_space::AIXTERM_16COLOR)
+    str->append (std::to_string (m_value - WHITE - 1 + (is_fg ? 90 : 100)));
+  else if (m_color_space == color_space::XTERM_256COLOR)
+    str->append (is_fg ? "38;5;" : "48;5;").append (std::to_string (m_value));
+  else if (m_color_space == color_space::RGB_24BIT)
     {
       str->append (is_fg ? "38;2;" : "48;2;");
       str->append (std::to_string (m_red)
 		   + ";" + std::to_string (m_green)
 		   + ";" + std::to_string (m_blue));
     }
+  else
+    return false;
+
   return true;
 }
 
 /* See ui-style.h.  */
+std::string
+ui_file_style::color::to_ansi (bool is_fg) const
+{
+  std::string s = "\033[";
+  if (!append_ansi (is_fg, &s))
+    return {};
+  s.push_back ('m');
+  return s;
+}
 
-void
-ui_file_style::color::get_rgb (uint8_t *rgb) const
+/* See ui-style.h.  */
+
+std::string
+ui_file_style::color::to_string () const
 {
-  if (m_simple)
+  if (m_color_space == color_space::RGB_24BIT)
     {
-      /* Can't call this for a basic color or NONE -- those will end
-	 up in the assert below.  */
-      if (m_value >= 8 && m_value <= 15)
-	memcpy (rgb, bright_colors[m_value - 8], 3 * sizeof (uint8_t));
-      else if (m_value >= 16 && m_value <= 231)
-	{
-	  int value = m_value;
-	  value -= 16;
-	  /* This obscure formula seems to be what terminals actually
-	     do.  */
-	  int component = value / 36;
-	  rgb[0] = component == 0 ? 0 : (55 + component * 40);
-	  value %= 36;
-	  component = value / 6;
-	  rgb[1] = component == 0 ? 0 : (55 + component * 40);
-	  value %= 6;
-	  rgb[2] = value == 0 ? 0 : (55 + value * 40);
-	}
-      else if (m_value >= 232)
-	{
-	  uint8_t v = (m_value - 232) * 10 + 8;
-	  rgb[0] = v;
-	  rgb[1] = v;
-	  rgb[2] = v;
-	}
-      else
-	gdb_assert_not_reached ("get_rgb called on invalid color");
+      char s[64];
+      snprintf (s, sizeof s, "#%02X%02X%02X", m_red, m_green, m_blue);
+      return s;
     }
+  else if (is_none () || is_basic ())
+    return basic_color_name (m_value);
   else
+    return std::to_string (get_value ());
+}
+
+/* See ui-style.h.  */
+
+void
+ui_file_style::color::get_rgb (uint8_t *rgb) const
+{
+  if (m_color_space == color_space::RGB_24BIT)
     {
       rgb[0] = m_red;
       rgb[1] = m_green;
       rgb[2] = m_blue;
     }
+  else if (m_color_space == color_space::ANSI_8COLOR
+	   && 0 <= m_value && m_value <= 7)
+    memcpy (rgb, palette_8colors[m_value], 3 * sizeof (uint8_t));
+  else if (m_color_space == color_space::AIXTERM_16COLOR
+	   && 0 <= m_value && m_value <= 15)
+    memcpy (rgb, palette_16colors[m_value], 3 * sizeof (uint8_t));
+  else if (m_color_space != color_space::XTERM_256COLOR)
+    gdb_assert_not_reached ("get_rgb called on invalid color");
+  else if (0 <= m_value && m_value <= 15)
+    memcpy (rgb, palette_16colors[m_value], 3 * sizeof (uint8_t));
+  else if (m_value >= 16 && m_value <= 231)
+    {
+      int value = m_value;
+      value -= 16;
+      /* This obscure formula seems to be what terminals actually
+	 do.  */
+      int component = value / 36;
+      rgb[0] = component == 0 ? 0 : (55 + component * 40);
+      value %= 36;
+      component = value / 6;
+      rgb[1] = component == 0 ? 0 : (55 + component * 40);
+      value %= 6;
+      rgb[2] = value == 0 ? 0 : (55 + value * 40);
+    }
+  else if (232 <= m_value && m_value <= 255)
+    {
+      uint8_t v = (m_value - 232) * 10 + 8;
+      rgb[0] = v;
+      rgb[1] = v;
+      rgb[2] = v;
+    }
+  else
+    gdb_assert_not_reached ("get_rgb called on invalid color");
+}
+
+/* See ui-style.h.  */
+
+ui_file_style::color
+ui_file_style::color::approximate (const std::vector<color_space> &spaces) const
+{
+  if (spaces.empty () || is_none ())
+    return NONE;
+
+  color_space target_space = color_space::MONOCHROME;
+  for (color_space sp : spaces)
+    if (sp == m_color_space)
+      return *this;
+    else if (sp > target_space)
+      target_space = sp;
+
+  if (target_space == color_space::RGB_24BIT)
+    {
+      uint8_t rgb[3];
+      get_rgb (rgb);
+      return color (rgb[0], rgb[1], rgb[2]);
+    }
+
+  int target_size = 0;
+  switch (target_space)
+    {
+    case color_space::ANSI_8COLOR:
+      target_size = 8;
+      break;
+    case color_space::AIXTERM_16COLOR:
+      target_size = 16;
+      break;
+    case color_space::XTERM_256COLOR:
+      target_size = 256;
+      break;
+    }
+
+  if (is_simple() && m_value < target_size)
+    return color (target_space, m_value);
+
+  color result = NONE;
+  int best_distance = std::numeric_limits<int>::max ();
+  uint8_t rgb[3];
+  get_rgb (rgb);
+
+  for (int i = 0; i < target_size; ++i)
+    {
+      uint8_t c_rgb[3];
+      color c (target_space, i);
+      c.get_rgb (c_rgb);
+      int d_red = std::abs (rgb[0] - c_rgb[0]);
+      int d_green = std::abs (rgb[1] - c_rgb[1]);
+      int d_blue = std::abs (rgb[2] - c_rgb[2]);
+      int dist = d_red * d_red + d_green * d_green + d_blue * d_blue;
+      if (dist < best_distance)
+	{
+	  best_distance = dist;
+	  result = c;
+	}
+    }
+
+  return result;
 }
 
 /* See ui-style.h.  */
@@ -140,26 +279,23 @@  std::string
 ui_file_style::to_ansi () const
 {
   std::string result ("\033[");
-  bool need_semi = m_foreground.append_ansi (true, &result);
-  if (!m_background.is_none ())
+  if (!is_default ())
     {
+      bool need_semi = m_foreground.append_ansi (true, &result);
       if (need_semi)
 	result.push_back (';');
-      m_background.append_ansi (false, &result);
-      need_semi = true;
-    }
-  if (m_intensity != NORMAL)
-    {
+      need_semi = m_background.append_ansi (false, &result);
       if (need_semi)
 	result.push_back (';');
-      result.append (std::to_string (m_intensity));
-      need_semi = true;
-    }
-  if (m_reverse)
-    {
-      if (need_semi)
-	result.push_back (';');
-      result.push_back ('7');
+      if (m_intensity == NORMAL)
+	result.append ("22");
+      else
+	result.append (std::to_string (m_intensity));
+      result.push_back (';');
+      if (m_reverse)
+	result.push_back ('7');
+      else
+	result.append ("27");
     }
   result.push_back ('m');
   return result;
@@ -316,9 +452,11 @@  ui_file_style::parse (const char *buf, size_t *n_read)
 	    case 35:
 	    case 36:
 	    case 37:
+	      m_foreground = color (value - 30);
+	      break;
 	      /* Note: not 38.  */
 	    case 39:
-	      m_foreground = color (value - 30);
+	      m_foreground = NONE;
 	      break;
 
 	    case 40:
@@ -329,9 +467,11 @@  ui_file_style::parse (const char *buf, size_t *n_read)
 	    case 45:
 	    case 46:
 	    case 47:
+	      m_background = color (value - 40);
+	      break;
 	      /* Note: not 48.  */
 	    case 49:
-	      m_background = color (value - 40);
+	      m_background = NONE;
 	      break;
 
 	    case 90:
@@ -413,3 +553,44 @@  _initialize_ui_style ()
      error.  */
   gdb_assert (code == 0);
 }
+
+/* See ui-style.h.  */
+
+const std::vector<color_space> &
+colorsupport ()
+{
+  static const std::vector<color_space> value = []
+    {
+      std::vector<color_space> result = {color_space::MONOCHROME};
+
+      int colors = tgetnum ((char *)"Co");
+      if (colors >= 8)
+	result.push_back (color_space::ANSI_8COLOR);
+      if (colors >= 16)
+	result.push_back (color_space::AIXTERM_16COLOR);
+      if (colors >= 256)
+	result.push_back (color_space::XTERM_256COLOR);
+
+      const char *colorterm = getenv ("COLORTERM");
+      if (colorterm && (!strcmp (colorterm, "truecolor")
+	  || !strcmp (colorterm, "24bit")))
+	result.push_back (color_space::RGB_24BIT);
+
+      return result;
+    } ();
+  return value;
+}
+
+const char *
+color_space_name (color_space c)
+{
+  switch (c)
+    {
+    case color_space::MONOCHROME: return "monochrome";
+    case color_space::ANSI_8COLOR: return "ansi_8color";
+    case color_space::AIXTERM_16COLOR: return "aixterm_16color";
+    case color_space::XTERM_256COLOR: return "xterm_256color";
+    case color_space::RGB_24BIT: return "rgb_24bit";
+    }
+  error (_("Color space %d has no name."), static_cast<int> (c));
+}
diff --git a/gdb/ui-style.h b/gdb/ui-style.h
index fe1b2af611d..a481baf8ad5 100644
--- a/gdb/ui-style.h
+++ b/gdb/ui-style.h
@@ -19,6 +19,26 @@ 
 #ifndef UI_STYLE_H
 #define UI_STYLE_H
 
+/* One of the color spaces that usually supported by terminals.  */
+enum class color_space
+{
+  MONOCHROME, // one default terminal color
+  ANSI_8COLOR, // foreground colors \e[30m ... \e[37m,
+	       // background colors \e[40m ... \e[47m
+  AIXTERM_16COLOR, // foreground colors \e[30m ... \e[37m, \e[90m ... \e[97m
+		   // background colors \e[40m ... \e[47m, \e[100m ... \e107m
+  XTERM_256COLOR, // foreground colors \e[38;5;0m ... \e[38;5;255m
+		  // background colors \e[48;5;0m ... \e[48;5;255m
+  RGB_24BIT // foreground colors \e[38;2;0;0;0m ... \e[38;2;255;255;255m
+	    // background colors \e[48;2;0;0;0m ... \e[48;2;255;255;255m
+};
+
+/* Color spaces supported by terminal.  */
+extern const std::vector<color_space> & colorsupport ();
+
+/* Textual representation of C.  */
+extern const char * color_space_name (color_space c);
+
 /* Styles that can be applied to a ui_file.  */
 struct ui_file_style
 {
@@ -43,20 +63,61 @@  struct ui_file_style
   public:
 
     color (basic_color c)
-      : m_simple (true),
+      : m_color_space (c == NONE ? color_space::MONOCHROME
+				 : color_space::ANSI_8COLOR),
 	m_value (c)
     {
     }
 
     color (int c)
-      : m_simple (true),
+      : m_value (c)
+    {
+      if (c < -1 || c > 255)
+	error (_("Palette color index %d is out of range."), c);
+      if (c == -1)
+	m_color_space = color_space::MONOCHROME;
+      else if (c <= 7)
+	m_color_space = color_space::ANSI_8COLOR;
+      else if (c <= 15)
+	m_color_space = color_space::AIXTERM_16COLOR;
+      else
+	m_color_space = color_space::XTERM_256COLOR;
+    }
+
+    color (color_space cs, int c)
+      : m_color_space (cs),
 	m_value (c)
     {
-      gdb_assert (c >= -1 && c <= 255);
+      if (c < -1 || c > 255)
+	error (_("Palette color index %d is out of range."), c);
+
+      std::pair<int, int> range;
+      switch (cs)
+	{
+	case color_space::MONOCHROME:
+	  range = {-1, -1};
+	  break;
+	case color_space::ANSI_8COLOR:
+	  range = {0, 7};
+	  break;
+	case color_space::AIXTERM_16COLOR:
+	  range = {0, 15};
+	  break;
+	case color_space::XTERM_256COLOR:
+	  range = {0, 255};
+	  break;
+	default:
+	  error (_("Color space %d is incompatible with indexed colors."),
+		 static_cast<int> (cs));
+	}
+
+      if (c < range.first || c > range.second)
+	error (_("Color %d is out of range [%d, %d] of color space %d."),
+	       c, range.first, range.second, static_cast<int> (cs));
     }
 
     color (uint8_t r, uint8_t g, uint8_t b)
-      : m_simple (false),
+      : m_color_space (color_space::RGB_24BIT),
 	m_red (r),
 	m_green (g),
 	m_blue (b)
@@ -65,19 +126,24 @@  struct ui_file_style
 
     bool operator== (const color &other) const
     {
-      if (m_simple != other.m_simple)
+      if (m_color_space != other.m_color_space)
 	return false;
-      if (m_simple)
+      if (is_simple ())
 	return m_value == other.m_value;
       return (m_red == other.m_red && m_green == other.m_green
 	      && m_blue == other.m_blue);
     }
 
+    bool operator!= (const color &other) const
+    {
+      return ! (*this == other);
+    }
+
     bool operator< (const color &other) const
     {
-      if (m_simple != other.m_simple)
-	return m_simple < other.m_simple;
-      if (m_simple)
+      if (m_color_space != other.m_color_space)
+	return m_color_space < other.m_color_space;
+      if (is_simple ())
 	return m_value < other.m_value;
       if (m_red < other.m_red)
 	return true;
@@ -91,23 +157,54 @@  struct ui_file_style
       return false;
     }
 
+    color_space colorspace () const
+    {
+      return m_color_space;
+    }
+
     /* Return true if this is the "NONE" color, false otherwise.  */
     bool is_none () const
     {
-      return m_simple && m_value == NONE;
+      return m_color_space == color_space::MONOCHROME && m_value == NONE;
     }
 
     /* Return true if this is one of the basic colors, false
        otherwise.  */
     bool is_basic () const
     {
-      return m_simple && m_value >= BLACK && m_value <= WHITE;
+      if (m_color_space == color_space::ANSI_8COLOR
+	  || m_color_space == color_space::AIXTERM_16COLOR)
+	return BLACK <= m_value && m_value <= WHITE;
+      else
+	return false;
+    }
+
+    /* Return true if this is one of the colors, stored as int, false
+       otherwise.  */
+    bool is_simple () const
+    {
+      return m_color_space != color_space::RGB_24BIT;
+    }
+
+    /* Return true if this is one of the indexed colors, false
+       otherwise.  */
+    bool is_indexed () const
+    {
+      return m_color_space != color_space::RGB_24BIT
+	     && m_color_space != color_space::MONOCHROME;
     }
 
-    /* Return the value of a basic color.  */
+    /* Return true if this is one of the direct colors (RGB, CMY, CMYK), false
+       otherwise.  */
+    bool is_direct () const
+    {
+      return m_color_space == color_space::RGB_24BIT;
+    }
+
+    /* Return the value of a simple color.  */
     int get_value () const
     {
-      gdb_assert (is_basic ());
+      gdb_assert (is_simple ());
       return m_value;
     }
 
@@ -119,13 +216,23 @@  struct ui_file_style
     /* Append the ANSI terminal escape sequence for this color to STR.
        IS_FG indicates whether this is a foreground or background
        color.  Returns true if any characters were written; returns
-       false otherwise (which can only happen for the "NONE"
-       color).  */
+       false otherwise.  */
     bool append_ansi (bool is_fg, std::string *str) const;
 
+    /* Return the ANSI escape sequence for this color.
+       IS_FG indicates whether this is a foreground or background color.  */
+    std::string to_ansi (bool is_fg) const;
+
+    /* Returns text representation of this object.
+       It is "none", name of a basic color, number or a #RRGGBB hex triplet.  */
+    std::string to_string () const;
+
+    /* Approximates THIS color by closest one from SPACES.  */
+    color approximate (const std::vector<color_space> &spaces) const;
+
   private:
 
-    bool m_simple;
+    color_space m_color_space;
     union
     {
       int m_value;
@@ -235,6 +342,9 @@  struct ui_file_style
     return this;
   }
 
+  /* nullptr-terminated list of names corresponding to enum basic_color.  */
+  static const std::vector<const char *> basic_color_enums;
+
 private:
 
   color m_foreground = NONE;
diff --git a/gdb/unittests/style-selftests.c b/gdb/unittests/style-selftests.c
index 8ad803594c3..34b0d80adc6 100644
--- a/gdb/unittests/style-selftests.c
+++ b/gdb/unittests/style-selftests.c
@@ -59,7 +59,7 @@  run_tests ()
   SELF_CHECK (style.get_background ().is_none ());
   SELF_CHECK (style.get_intensity () == ui_file_style::NORMAL);
   SELF_CHECK (style.is_reverse ());
-  SELF_CHECK (style.to_ansi () == "\033[7m");
+  SELF_CHECK (style.to_ansi () == "\033[39;49;22;7m");
 
   style = ui_file_style ();
   SELF_CHECK (style.parse ("\033[32;1m", &n_read));
@@ -69,7 +69,7 @@  run_tests ()
   SELF_CHECK (style.get_background ().is_none ());
   SELF_CHECK (style.get_intensity () == ui_file_style::BOLD);
   SELF_CHECK (!style.is_reverse ());
-  SELF_CHECK (style.to_ansi () == "\033[32;1m");
+  SELF_CHECK (style.to_ansi () == "\033[32;49;1;27m");
 
   style = ui_file_style ();
   SELF_CHECK (style.parse ("\033[38;5;112;48;5;249m", &n_read));
@@ -82,7 +82,7 @@  run_tests ()
   CHECK_RGB (0xb2, 0xb2, 0xb2);
   SELF_CHECK (style.get_intensity () == ui_file_style::NORMAL);
   SELF_CHECK (!style.is_reverse ());
-  SELF_CHECK (style.to_ansi () == "\033[38;5;112;48;5;249m");
+  SELF_CHECK (style.to_ansi () == "\033[38;5;112;48;5;249;22;27m");
 
   style = ui_file_style ();
   SELF_CHECK (style.parse ("\033[38;2;83;84;85;48;2;0;1;254;2;7m", &n_read));