* [PATCH v4] Add an option with a color type.
@ 2022-10-16 11:40 Andrei Pikas
2022-10-16 13:45 ` Eli Zaretskii via Gdb-patches
0 siblings, 1 reply; 33+ messages in thread
From: Andrei Pikas @ 2022-10-16 11:40 UTC (permalink / raw)
To: gdb-patches; +Cc: Andrei Pikas
Colors can be specified as "none" for terminal's default color, as a name of
one of the eight standard colors of ISO/IEC 6429 "black", "red", "green", etc.,
as an RGB hexadecimal tripplet #RRGGBB for 24-bit TrueColor, or as an
integer from 0 to 255. Integers 0 to 7 are the synonyms for the standard
colors. Integers 8-15 are used for the so-called bright colors from the
aixterm extended 16-color palette. Integers 16-255 are the indexes into xterm
extended 256-color palette (usually 6x6x6 cube plus gray ramp). In
general, 256-color palette is terminal dependent and sometimes can be
changed with OSC 4 sequences, e.g. "\033]4;1;rgb:00/FF/00\033\\".
It is the responsibility of the user to verify that the terminal supports
the specified colors.
---
gdb/Makefile.in | 2 +
gdb/NEWS | 29 ++
gdb/cli/cli-cmds.c | 7 +
gdb/cli/cli-decode.c | 174 +++++++++
gdb/cli/cli-decode.h | 21 +
gdb/cli/cli-option.c | 44 +++
gdb/cli/cli-option.h | 21 +
gdb/cli/cli-setshow.c | 21 +
gdb/cli/cli-style.c | 49 +--
gdb/cli/cli-style.h | 4 +-
gdb/command.h | 26 +-
gdb/doc/gdb.texinfo | 55 ++-
gdb/doc/guile.texi | 102 +++++
gdb/doc/python.texi | 95 +++++
gdb/guile/guile-internal.h | 9 +
gdb/guile/guile.c | 1 +
gdb/guile/scm-color.c | 445 ++++++++++++++++++++++
gdb/guile/scm-param.c | 29 +-
gdb/python/py-color.c | 346 +++++++++++++++++
gdb/python/py-color.h | 35 ++
gdb/python/py-param.c | 34 +-
gdb/python/python-internal.h | 2 +
gdb/python/python.c | 8 +
gdb/testsuite/gdb.base/style.exp | 209 +++++++++-
gdb/testsuite/gdb.guile/scm-color.exp | 114 ++++++
gdb/testsuite/gdb.guile/scm-parameter.exp | 43 +++
gdb/testsuite/gdb.python/py-color.exp | 101 +++++
gdb/testsuite/gdb.python/py-parameter.exp | 53 +++
gdb/testsuite/lib/gdb-utils.exp | 22 +-
gdb/top.c | 13 +
gdb/ui-style.c | 331 ++++++++++++----
gdb/ui-style.h | 142 ++++++-
gdb/unittests/style-selftests.c | 6 +-
33 files changed, 2433 insertions(+), 160 deletions(-)
create mode 100644 gdb/guile/scm-color.c
create mode 100644 gdb/python/py-color.c
create mode 100644 gdb/python/py-color.h
create mode 100644 gdb/testsuite/gdb.guile/scm-color.exp
create mode 100644 gdb/testsuite/gdb.python/py-color.exp
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 57c29a78b7a..3317a6c4fb4 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -355,6 +355,7 @@ SUBDIR_GUILE_SRCS = \
guile/scm-block.c \
guile/scm-breakpoint.c \
guile/scm-cmd.c \
+ guile/scm-color.c \
guile/scm-disasm.c \
guile/scm-exception.c \
guile/scm-frame.c \
@@ -391,6 +392,7 @@ SUBDIR_PYTHON_SRCS = \
python/py-bpevent.c \
python/py-breakpoint.c \
python/py-cmd.c \
+ python/py-color.c \
python/py-connection.c \
python/py-continueevent.c \
python/py-disasm.c \
diff --git a/gdb/NEWS b/gdb/NEWS
index d2efe2a0a58..00173c09227 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -55,6 +55,23 @@
Python Pygments is still used. For supported targets, libopcodes
styling is used by default.
+ "set style" commands now supports numeric format for basic colors
+ from 0 to 255 and #RRGGBB format for TrueColor.
+
+* New built-in convenience variable $_colorsupport provides comma-separated
+ list of color space names supported by terminal. It is handy for
+ conditionally using styling colors based on terminal features. For example:
+
+ (gdb) if $_regex ($_colorsupport, ".*(^|,)rgb_24bit($|,).*")
+ >set style filename background #FACADE
+ >else
+ >if $_regex ($_colorsupport, ".*(^|,)xterm_256color($|,).*")
+ >set style filename background 224
+ >else
+ >set style filename background red
+ >end
+ >end
+
* New commands
maintenance set ignore-prologue-end-flag on|off
@@ -170,6 +187,18 @@ GNU/Linux/LoongArch (gdbserver) loongarch*-*-linux*
can be used to request a shorter representation of a value, the
way that 'set print frame-arguments scalars' does.
+ ** New class gdb.Color for dealing with colors.
+
+ ** New constant gdb.PARAM_COLOR represents color type of a
+ gdb.Parameter.value. Parameter's value is gdb.Color instance.
+
+* Guile API
+
+ ** New type <gdb:color> for dealing with colors.
+
+ ** New constant PARAM_COLOR represents color type of a value
+ of a <gdb:parameter> object. Parameter's value is <gdb::color> instance.
+
* New features in the GDB remote stub, GDBserver
** GDBserver is now supported on LoongArch GNU/Linux.
diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c
index 18fb6e6d869..1a515b99761 100644
--- a/gdb/cli/cli-cmds.c
+++ b/gdb/cli/cli-cmds.c
@@ -2275,6 +2275,12 @@ value_from_setting (const setting &var, struct gdbarch *gdbarch)
return value_cstring ("", 1,
builtin_type (gdbarch)->builtin_char);
}
+ case var_color:
+ {
+ std::string s = var.get<ui_file_style::color> ().to_string ();
+ return value_cstring (s.c_str (), s.size (),
+ builtin_type (gdbarch)->builtin_char);
+ }
default:
gdb_assert_not_reached ("bad var_type");
}
@@ -2324,6 +2330,7 @@ str_value_from_setting (const setting &var, struct gdbarch *gdbarch)
case var_auto_boolean:
case var_uinteger:
case var_zuinteger:
+ case var_color:
{
std::string cmd_val = get_setshow_command_value_string (var);
diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
index fde554c7e6c..b4484c185bd 100644
--- a/gdb/cli/cli-decode.c
+++ b/gdb/cli/cli-decode.c
@@ -24,6 +24,7 @@
#include "cli/cli-cmds.h"
#include "cli/cli-decode.h"
#include "cli/cli-style.h"
+#include "cli/cli-utils.h"
#include "gdbsupport/gdb_optional.h"
/* Prototypes for local functions. */
@@ -670,6 +671,87 @@ add_setshow_enum_cmd (const char *name, command_class theclass,
return cmds;
}
+/* See cli-decode.h. */
+
+void
+complete_on_color (completion_tracker &tracker,
+ const char *text, const char *word)
+{
+ complete_on_enum (tracker, ui_file_style::basic_color_enums.data (),
+ text, word);
+ if (*text == '\0')
+ {
+ /* Convenience to let the user know what the option
+ can accept. Note there's no common prefix between
+ the strings on purpose, so that complete_on_enum doesn't do
+ a partial match. */
+ tracker.add_completion (make_unique_xstrdup ("NUMBER"));
+ tracker.add_completion (make_unique_xstrdup ("#RRGGBB"));
+ }
+}
+
+/* Completer used in color commands. */
+
+static void
+color_completer (struct cmd_list_element *ignore,
+ completion_tracker &tracker,
+ const char *text, const char *word)
+{
+ complete_on_color (tracker, text, word);
+}
+
+
+/* Add element named NAME to command list LIST (the list for set or
+ some sublist thereof). CLASS is as in add_cmd. VAR is address
+ of the variable which will contain the color. */
+
+set_show_commands
+add_setshow_color_cmd (const char *name,
+ enum command_class theclass,
+ ui_file_style::color *var,
+ const char *set_doc,
+ const char *show_doc,
+ const char *help_doc,
+ cmd_func_ftype *set_func,
+ show_value_ftype *show_func,
+ struct cmd_list_element **set_list,
+ struct cmd_list_element **show_list)
+{
+ set_show_commands commands = add_setshow_cmd_full<ui_file_style::color>
+ (name, theclass, var_color, var,
+ set_doc, show_doc, help_doc,
+ nullptr, nullptr, set_func, show_func,
+ set_list, show_list);
+
+ set_cmd_completer (commands.set, color_completer);
+
+ return commands;
+}
+
+/* Same as above but using a getter and a setter function instead of a pointer
+ to a global storage buffer. */
+
+set_show_commands
+add_setshow_color_cmd (const char *name, command_class theclass,
+ const char *set_doc, const char *show_doc,
+ const char *help_doc,
+ setting_func_types<ui_file_style::color>::set set_func,
+ setting_func_types<ui_file_style::color>::get get_func,
+ show_value_ftype *show_func,
+ cmd_list_element **set_list,
+ cmd_list_element **show_list)
+{
+ auto cmds = add_setshow_cmd_full<ui_file_style::color>
+ (name, theclass, var_color, nullptr,
+ set_doc, show_doc, help_doc,
+ set_func, get_func, nullptr, show_func,
+ set_list, show_list);
+
+ set_cmd_completer (cmds.set, color_completer);
+
+ return cmds;
+}
+
/* See cli-decode.h. */
const char * const auto_boolean_enums[] = { "on", "off", "auto", NULL };
@@ -2524,3 +2606,95 @@ cli_user_command_p (struct cmd_list_element *cmd)
{
return cmd->theclass == class_user && cmd->func == do_simple_func;
}
+
+/* See cli-decode.h. */
+
+ui_file_style::color
+parse_cli_var_color (const char **args)
+{
+ /* Do a "set" command. ARG is NULL if no argument, or the
+ text of the argument. */
+
+ if (args == nullptr || *args == nullptr || **args == '\0')
+ {
+ std::string msg;
+
+ for (size_t i = 0; ui_file_style::basic_color_enums[i]; ++i)
+ {
+ msg.append ("\"");
+ msg.append (ui_file_style::basic_color_enums[i]);
+ msg.append ("\", ");
+ }
+
+ error (_("Requires an argument. Valid arguments are %s integer from -1 "
+ "to 255 or an RGB hex triplet in a format #RRGGBB"),
+ msg.c_str ());
+ }
+
+ const char *p = skip_to_space (*args);
+ size_t len = p - *args;
+
+ int nmatches = 0;
+ ui_file_style::basic_color match = ui_file_style::NONE;
+ for (int i = 0; ui_file_style::basic_color_enums[i]; ++i)
+ if (strncmp (*args, ui_file_style::basic_color_enums[i], len) == 0)
+ {
+ match = static_cast<ui_file_style::basic_color> (i - 1);
+ if (ui_file_style::basic_color_enums[i][len] == '\0')
+ {
+ nmatches = 1;
+ break; /* Exact match. */
+ }
+ else
+ nmatches++;
+ }
+
+ if (nmatches == 1)
+ {
+ *args += len;
+ return ui_file_style::color (match);
+ }
+
+ if (nmatches > 1)
+ error (_("Ambiguous item \"%.*s\"."), (int) len, *args);
+
+ if (**args != '#')
+ {
+ ULONGEST num = get_ulongest (args);
+ if (num > 255)
+ error (_("integer %s out of range"), pulongest (num));
+ return ui_file_style::color (color_space::XTERM_256COLOR,
+ static_cast<int> (num));
+ }
+
+ /* Try to parse #RRGGBB string. */
+ if (len != 7)
+ error_no_arg (_("invalid RGB hex triplet format"));
+
+ uint8_t r, g, b;
+ int scanned_chars = 0;
+ int parsed_args = sscanf (*args, "#%2" SCNx8 "%2" SCNx8 "%2" SCNx8 "%n",
+ &r, &g, &b, &scanned_chars);
+
+ if (parsed_args != 3 || scanned_chars != 7)
+ error_no_arg (_("invalid RGB hex triplet format"));
+
+ *args += len;
+ return ui_file_style::color (r, g, b);
+}
+
+/* See cli-decode.h. */
+
+ui_file_style::color
+parse_var_color (const char *arg)
+{
+ const char *end_arg = arg;
+ ui_file_style::color color = parse_cli_var_color (&end_arg);
+
+ int len = end_arg - arg;
+ const char *after = skip_spaces (end_arg);
+ if (*after != '\0')
+ error (_("Junk after item \"%.*s\": %s"), len, arg, after);
+
+ return color;
+}
diff --git a/gdb/cli/cli-decode.h b/gdb/cli/cli-decode.h
index 18db8822af3..7f1233c87ad 100644
--- a/gdb/cli/cli-decode.h
+++ b/gdb/cli/cli-decode.h
@@ -306,6 +306,27 @@ extern const char * const boolean_enums[];
/* The enums of auto-boolean commands. */
extern const char * const auto_boolean_enums[];
+/* Add the different possible completions of TEXT with color.
+
+ WORD points in the same buffer as TEXT, and completions should be
+ returned relative to this position. For example, suppose TEXT is "foo"
+ and we want to complete to "foobar". If WORD is "oo", return
+ "oobar"; if WORD is "baz/foo", return "baz/foobar". */
+
+extern void complete_on_color (completion_tracker &tracker,
+ const char *text, const char *word);
+
+/* Parse ARGS, an option to a var_color variable.
+ *
+ Either returns the parsed value on success or throws an error. ARGS may be
+ one of strings {none, black, red, green, yellow, blue, magenta,
+ cyan, white}, or color number from 0 to 255, or RGB hex triplet #RRGGBB.
+ */
+extern ui_file_style::color parse_cli_var_color (const char **args);
+
+/* Same as above but additionally check that there is no junk in the end. */
+extern ui_file_style::color parse_var_color (const char *arg);
+
/* Verify whether a given cmd_list_element is a user-defined command.
Return 1 if it is user-defined. Return 0 otherwise. */
diff --git a/gdb/cli/cli-option.c b/gdb/cli/cli-option.c
index b1794ad4b17..fbb2d35e6c3 100644
--- a/gdb/cli/cli-option.c
+++ b/gdb/cli/cli-option.c
@@ -46,6 +46,9 @@ union option_value
/* For var_string options. This is malloc-allocated. */
std::string *string;
+
+ /* For var_color options. */
+ ui_file_style::color color = ui_file_style::NONE;
};
/* Holds an options definition and its value. */
@@ -424,6 +427,35 @@ parse_option (gdb::array_view<const option_def_group> options_group,
val.enumeration = parse_cli_var_enum (args, match->enums);
return option_def_and_value {*match, match_ctx, val};
}
+ case var_color:
+ {
+ if (completion != nullptr)
+ {
+ const char *after_arg = skip_to_space (*args);
+ if (*after_arg == '\0')
+ {
+ complete_on_color (completion->tracker, *args, *args);
+
+ if (completion->tracker.have_completions ())
+ return {};
+ }
+ }
+
+ if (check_for_argument (args, "--"))
+ {
+ /* Treat e.g., "backtrace -entry-values --" as if there
+ was no argument after "-entry-values". This makes
+ parse_cli_var_color throw an error with a suggestion of
+ what are the valid options. */
+ args = nullptr;
+ }
+
+ option_value val;
+ ui_file_style::color color = parse_cli_var_color (args);
+ ui_file_style::color approx_color = color.approximate (colorsupport ());
+ val.color = approx_color;
+ return option_def_and_value {*match, match_ctx, val};
+ }
case var_string:
{
if (check_for_argument (args, "--"))
@@ -601,6 +633,10 @@ save_option_value_in_ctx (gdb::optional<option_def_and_value> &ov)
*ov->option.var_address.enumeration (ov->option, ov->ctx)
= ov->value->enumeration;
break;
+ case var_color:
+ *ov->option.var_address.color (ov->option, ov->ctx)
+ = ov->value->color;
+ break;
case var_string:
*ov->option.var_address.string (ov->option, ov->ctx)
= std::move (*ov->value->string);
@@ -677,6 +713,14 @@ get_val_type_str (const option_def &opt, std::string &buffer)
}
return buffer.c_str ();
}
+ case var_color:
+ {
+ buffer = "";
+ for (size_t i = 0; ui_file_style::basic_color_enums[i]; ++i)
+ buffer.append (ui_file_style::basic_color_enums[i]).append ("|");
+ buffer += "NUMBER|#RRGGBB";
+ return buffer.c_str ();
+ }
case var_string:
return "STRING";
default:
diff --git a/gdb/cli/cli-option.h b/gdb/cli/cli-option.h
index 26a8da3a5a4..d30c7024324 100644
--- a/gdb/cli/cli-option.h
+++ b/gdb/cli/cli-option.h
@@ -87,6 +87,7 @@ struct option_def
int *(*integer) (const option_def &, void *ctx);
const char **(*enumeration) (const option_def &, void *ctx);
std::string *(*string) (const option_def &, void *ctx);
+ ui_file_style::color *(*color) (const option_def &, void *ctx);
}
var_address;
@@ -282,6 +283,26 @@ struct string_option_def : option_def
}
};
+/* A var_color command line option. */
+
+template<typename Context>
+struct color_option_def : option_def
+{
+ color_option_def (const char *long_option_,
+ ui_file_style::color *(*get_var_address_cb_) (Context *),
+ show_value_ftype *show_cmd_cb_,
+ const char *set_doc_,
+ const char *show_doc_ = nullptr,
+ const char *help_doc_ = nullptr)
+ : option_def (long_option_, var_color,
+ (erased_get_var_address_ftype *) get_var_address_cb_,
+ show_cmd_cb_,
+ set_doc_, show_doc_, help_doc_)
+ {
+ var_address.color = detail::get_var_address<ui_file_style::color, Context>;
+ }
+};
+
/* A group of options that all share the same context pointer to pass
to the options' get-current-value callbacks. */
struct option_def_group
diff --git a/gdb/cli/cli-setshow.c b/gdb/cli/cli-setshow.c
index 139ebaf8323..3f6e9d0a0b5 100644
--- a/gdb/cli/cli-setshow.c
+++ b/gdb/cli/cli-setshow.c
@@ -454,6 +454,13 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c)
option_changed = c->var->set<const char *> (match);
}
break;
+ case var_color:
+ {
+ ui_file_style::color color = parse_var_color (arg);
+ ui_file_style::color approx_color = color.approximate (colorsupport ());
+ option_changed = c->var->set<ui_file_style::color> (approx_color);
+ }
+ break;
case var_zuinteger_unlimited:
option_changed = c->var->set<int>
(parse_cli_var_zuinteger_unlimited (&arg, true));
@@ -535,6 +542,14 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c)
gdb::observers::command_param_changed.notify
(name, c->var->get<const char *> ());
break;
+ case var_color:
+ {
+ const ui_file_style::color &color
+ = c->var->get<ui_file_style::color> ();
+ gdb::observers::command_param_changed.notify
+ (name, color.to_string ().c_str ());
+ }
+ break;
case var_boolean:
{
const char *opt = c->var->get<bool> () ? "on" : "off";
@@ -602,6 +617,12 @@ get_setshow_command_value_string (const setting &var)
stb.puts (value);
}
break;
+ case var_color:
+ {
+ const ui_file_style::color &value = var.get<ui_file_style::color> ();
+ stb.puts (value.to_string ().c_str ());
+ }
+ break;
case var_boolean:
stb.puts (var.get<bool> () ? "on" : "off");
break;
diff --git a/gdb/cli/cli-style.c b/gdb/cli/cli-style.c
index abf685561fa..3123a391763 100644
--- a/gdb/cli/cli-style.c
+++ b/gdb/cli/cli-style.c
@@ -43,20 +43,6 @@ bool source_styling = true;
bool disassembler_styling = true;
-/* Name of colors; must correspond to ui_file_style::basic_color. */
-static const char * const cli_colors[] = {
- "none",
- "black",
- "red",
- "green",
- "yellow",
- "blue",
- "magenta",
- "cyan",
- "white",
- nullptr
-};
-
/* Names of intensities; must correspond to
ui_file_style::intensity. */
static const char * const cli_intensities[] = {
@@ -132,8 +118,8 @@ cli_style_option::cli_style_option (const char *name,
ui_file_style::intensity intensity)
: changed (name),
m_name (name),
- m_foreground (cli_colors[fg - ui_file_style::NONE]),
- m_background (cli_colors[0]),
+ m_foreground (fg),
+ m_background (ui_file_style::NONE),
m_intensity (cli_intensities[intensity])
{
}
@@ -144,32 +130,17 @@ cli_style_option::cli_style_option (const char *name,
ui_file_style::intensity i)
: changed (name),
m_name (name),
- m_foreground (cli_colors[0]),
- m_background (cli_colors[0]),
+ m_foreground (ui_file_style::NONE),
+ m_background (ui_file_style::NONE),
m_intensity (cli_intensities[i])
{
}
-/* Return the color number corresponding to COLOR. */
-
-static int
-color_number (const char *color)
-{
- for (int i = 0; i < ARRAY_SIZE (cli_colors); ++i)
- {
- if (color == cli_colors[i])
- return i - 1;
- }
- gdb_assert_not_reached ("color not found");
-}
-
/* See cli-style.h. */
ui_file_style
cli_style_option::style () const
{
- int fg = color_number (m_foreground);
- int bg = color_number (m_background);
ui_file_style::intensity intensity = ui_file_style::NORMAL;
for (int i = 0; i < ARRAY_SIZE (cli_intensities); ++i)
@@ -181,7 +152,7 @@ cli_style_option::style () const
}
}
- return ui_file_style (fg, bg, intensity);
+ return ui_file_style (m_foreground, m_background, intensity);
}
/* See cli-style.h. */
@@ -254,9 +225,8 @@ cli_style_option::add_setshow_commands (enum command_class theclass,
set_show_commands commands;
- commands = add_setshow_enum_cmd
- ("foreground", theclass, cli_colors,
- &m_foreground,
+ commands = add_setshow_color_cmd
+ ("foreground", theclass, &m_foreground,
_("Set the foreground color for this property."),
_("Show the foreground color for this property."),
nullptr,
@@ -266,9 +236,8 @@ cli_style_option::add_setshow_commands (enum command_class theclass,
commands.set->set_context (this);
commands.show->set_context (this);
- commands = add_setshow_enum_cmd
- ("background", theclass, cli_colors,
- &m_background,
+ commands = add_setshow_color_cmd
+ ("background", theclass, &m_background,
_("Set the background color for this property."),
_("Show the background color for this property."),
nullptr,
diff --git a/gdb/cli/cli-style.h b/gdb/cli/cli-style.h
index 4090cf01333..4db86b00359 100644
--- a/gdb/cli/cli-style.h
+++ b/gdb/cli/cli-style.h
@@ -67,9 +67,9 @@ class cli_style_option
const char *m_name;
/* The foreground. */
- const char *m_foreground;
+ ui_file_style::color m_foreground;
/* The background. */
- const char *m_background;
+ ui_file_style::color m_background;
/* The intensity. */
const char *m_intensity;
diff --git a/gdb/command.h b/gdb/command.h
index d901da3c8cb..eb3b2cc20d6 100644
--- a/gdb/command.h
+++ b/gdb/command.h
@@ -119,7 +119,9 @@ enum var_types
/* Enumerated type. Can only have one of the specified values.
*VAR is a char pointer to the name of the element that we
find. */
- var_enum
+ var_enum,
+ /* Color type. *VAR is a ui_file_style::color structure. */
+ var_color
};
/* Return true if a setting of type VAR_TYPE is backed with type T.
@@ -179,6 +181,14 @@ inline bool var_type_uses<const char *> (var_types t)
return t == var_enum;
}
+/* Return true if a setting of type T is backed by an ui_file_style::color
+ variable. */
+template<>
+inline bool var_type_uses<ui_file_style::color> (var_types t)
+{
+ return t == var_color;
+}
+
template<bool is_scalar, typename T> struct setting_func_types_1;
template<typename T>
@@ -665,6 +675,20 @@ extern set_show_commands add_setshow_enum_cmd
setting_func_types<const char *>::get get_func, show_value_ftype *show_func,
cmd_list_element **set_list, cmd_list_element **show_list);
+extern set_show_commands add_setshow_color_cmd
+ (const char *name, command_class theclass, ui_file_style::color *var,
+ const char *set_doc, const char *show_doc, const char *help_doc,
+ cmd_func_ftype *set_func, show_value_ftype *show_func,
+ cmd_list_element **set_list, cmd_list_element **show_list);
+
+extern set_show_commands add_setshow_color_cmd
+ (const char *name, command_class theclass,
+ const char *set_doc, const char *show_doc, const char *help_doc,
+ setting_func_types<ui_file_style::color>::set set_func,
+ setting_func_types<ui_file_style::color>::get get_func,
+ show_value_ftype *show_func, cmd_list_element **set_list,
+ cmd_list_element **show_list);
+
extern set_show_commands add_setshow_auto_boolean_cmd
(const char *name, command_class theclass, auto_boolean *var,
const char *set_doc, const char *show_doc, const char *help_doc,
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 382df00ee7d..26c8f4c77c5 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -12638,6 +12638,15 @@ and @code{$_shell_exitsignal} according to the exit status of the last
launched command. These variables are set and used similarly to
the variables @code{$_exitcode} and @code{$_exitsignal}.
+@item $_colorsupport
+@vindex $_colorsupport@r{, convenience variable}
+Comma-separated list of color space names supported by terminal. Names could
+be any of @samp{monochrome}, @samp{ansi_8color}, @samp{aixterm_16color},
+@samp{xterm_256color}, @samp{rgb_24bit}. E.g. for plain linux terminal the
+value could be @samp{monochrome,ansi_8color} and for terminal with truecolor
+support it could be
+@samp{monochrome,ansi_8color,aixterm_16color,xterm_256color,rgb_24bit}.
+
@end table
@node Convenience Funs
@@ -26605,16 +26614,46 @@ For example, the style of file names can be controlled using the
@table @code
@item set style filename background @var{color}
-Set the background to @var{color}. Valid colors are @samp{none}
-(meaning the terminal's default color), @samp{black}, @samp{red},
-@samp{green}, @samp{yellow}, @samp{blue}, @samp{magenta}, @samp{cyan},
-and@samp{white}.
+Set the background to @var{color}. @var{color} can be @samp{none}
+(meaning the terminal's default color), a name of one of the eight standard
+colors of ISO/IEC 6429, index from 0 to 255 into terminal's color
+palette or a hexadecimal RGB triplet in @samp{#RRGGBB} format for
+24-bit TrueColor.
+
+Valid color names are @samp{black}, @samp{red}, @samp{green},
+@samp{yellow}, @samp{blue}, @samp{magenta}, @samp{cyan}, and
+@samp{white}.
+
+Integers 0 to 7 are the synonyms for the standard colors. Integers 8-15 are
+used for the so-called bright colors from the aixterm extended 16-color
+palette. Integers 16-255 are the indexes into xterm extended 256-color palette
+(usually 6x6x6 cube plus gray ramp). In general, 256-color palette is terminal
+dependent and sometimes can be changed with OSC 4 sequences, e.g.
+"\033]4;1;rgb:00/FF/00\033\\".
+
+It is the responsibility of the user to verify that the terminal supports
+the specified colors.
@item set style filename foreground @var{color}
-Set the foreground to @var{color}. Valid colors are @samp{none}
-(meaning the terminal's default color), @samp{black}, @samp{red},
-@samp{green}, @samp{yellow}, @samp{blue}, @samp{magenta}, @samp{cyan},
-and@samp{white}.
+Set the foreground to @var{color}. @var{color} can be @samp{none}
+(meaning the terminal's default color), a name of one of the eight standard
+colors of ISO/IEC 6429, index from 0 to 255 into terminal's color
+palette or a hexadecimal RGB triplet in @samp{#RRGGBB} format for
+24-bit TrueColor.
+
+Valid color names are @samp{black}, @samp{red}, @samp{green},
+@samp{yellow}, @samp{blue}, @samp{magenta}, @samp{cyan}, and
+@samp{white}.
+
+Integers 0 to 7 are the synonyms for the standard colors. Integers 8-15 are
+used for the so-called bright colors from the aixterm extended 16-color
+palette. Integers 16-255 are the indexes into xterm extended 256-color palette
+(usually 6x6x6 cube plus gray ramp). In general, 256-color palette is terminal
+dependent and sometimes can be changed with OSC 4 sequences, e.g.
+"\033]4;1;rgb:00/FF/00\033\\".
+
+It is the responsibility of the user to verify that the terminal supports
+the specified colors.
@item set style filename intensity @var{value}
Set the intensity to @var{value}. Valid intensities are @samp{normal}
diff --git a/gdb/doc/guile.texi b/gdb/doc/guile.texi
index 63916eed181..d906a0e7e84 100644
--- a/gdb/doc/guile.texi
+++ b/gdb/doc/guile.texi
@@ -155,6 +155,7 @@ from the Guile interactive prompt.
* I/O Ports in Guile:: GDB I/O ports
* Memory Ports in Guile:: Accessing memory through ports and bytevectors
* Iterators In Guile:: Basic iterator support
+* Colors In Guile:: Colorize output with Guile
@end menu
@node Basic Guile
@@ -408,6 +409,9 @@ as a symbol.
@item <gdb:value>
@xref{Values From Inferior In Guile}.
+
+@item <gdb:color>
+@xref{Colors In Guile}.
@end table
The following @value{GDBN} objects are managed internally so that the
@@ -2169,6 +2173,14 @@ The value is a filename. This is just like
@item PARAM_ENUM
The value is a string, which must be one of a collection of string
constants provided when the parameter is created.
+
+@item PARAM_COLOR
+The value is either a string or an unsigned integer. Integer from 0 to 255
+means index into terminal's color palette. String can be a hex RGB triplet in
+@samp{#RRGGBB} format or one of the following color names:
+@samp{none} (meaning the terminal's default color), @samp{black}, @samp{red},
+@samp{green}, @samp{yellow}, @samp{blue}, @samp{magenta}, @samp{cyan},
+or @samp{white}.
@end vtable
@node Progspaces In Guile
@@ -3780,6 +3792,96 @@ Run @var{iterator} until the result of @code{(pred element)} is true
and return that as the result. Otherwise return @code{#f}.
@end deffn
+@node Colors In Guile
+@subsubsection Colors In Guile
+
+@cindex guile colors
+@tindex <gdb:color>
+You can assign instance of @code{<gdb:color>} to the value of
+a @code{<gdb:parameter>} object created with @code{PARAM_COLOR}.
+
+@code{<gdb:color>} may refer to an index from color palette or contain
+components of a color from some colorspace.
+
+@deffn {Scheme Procedure} make-color @w{@r{[}value} @
+ @w{@r{[}#:color-space color-space@r{]}@r{]}}
+
+@var{value} is an integer index of a color in palette, tuple with color
+components or a string. String can be a hex RGB triplet in @samp{#RRGGBB}
+format or one of the following color names:
+@samp{none} (meaning the terminal's default color), @samp{black}, @samp{red},
+@samp{green}, @samp{yellow}, @samp{blue}, @samp{magenta}, @samp{cyan},
+or @samp{white}.
+
+@var{color-space} should be one of the @samp{COLORSPACE_} constants. This
+argument tells @value{GDBN} which color space @var{value} belongs.
+@end deffn
+
+@deffn {Scheme Procedure} color? object
+Return @code{#t} if @var{object} is a @code{<gdb:color>} object.
+Otherwise return @code{#f}.
+@end deffn
+
+@deffn {Scheme Procedure} color-none? color
+Return @code{#t} if @var{color} is terminal's default.
+Otherwise return @code{#f}.
+@end deffn
+
+@deffn {Scheme Procedure} color-indexed? color
+Return @code{#t} if @var{color} is indexed, i.e. belongs to some palette.
+Otherwise return @code{#f}.
+@end deffn
+
+@deffn {Scheme Procedure} color-direct? color
+Return @code{#t} if @var{color} is direct in the sense of ISO/IEC 8613-6.
+Otherwise return @code{#f}.
+@end deffn
+
+@deffn {Scheme Procedure} color-string color
+Return the textual representation of a @code{<gdb:color>} object.
+@end deffn
+
+@deffn {Scheme Procedure} color-colorspace color
+Return the color space of a @code{<gdb:color>} object.
+@end deffn
+
+@deffn {Scheme Procedure} color-index color
+Return index of the color of a @code{<gdb:color>} object in a palette.
+@end deffn
+
+@deffn {Scheme Procedure} color-components color
+Return components of the direct @code{<gdb:color>} object.
+@end deffn
+
+@deffn {Scheme Procedure} color-escape-sequence color is_foreground
+Return string to change terminal's color to this.
+
+@var{is_foreground} if this color should be applied to foreground or background
+@end deffn
+
+When color is initialized, its color space must be specified. The
+available color spaces are represented by constants defined in the @code{gdb}
+module:
+
+@vtable @code
+@item COLORSPACE_MONOCHROME
+Palette with only terminal's default color.
+
+@item COLORSPACE_ANSI_8COLOR
+Palette with eight standard colors of ISO/IEC 6429 "black", "red", "green", etc.
+
+@item COLORSPACE_AIXTERM_16COLOR
+Palette with 16 colors. First eight are standard colors of ISO/IEC 6429
+"black", "red", "green", etc. Next eight are their bright version.
+
+@item COLORSPACE_XTERM_256COLOR
+Palette with 256 colors. First 16 are from COLORSPACE_AIXTERM_16COLOR. Next
+216 colors are 6x6x6 RGB cube. And last 24 colors forms grayscale ramp.
+
+@item COLORSPACE_RGB_24BIT
+Direct 24-bit RGB colors.
+@end vtable
+
@node Guile Auto-loading
@subsection Guile Auto-loading
@cindex guile auto-loading
diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index eeb847aeaa8..617fccc9ebd 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -4651,6 +4651,11 @@ except the special value -1 should be interpreted to mean
@item gdb.PARAM_ENUM
The value is a string, which must be one of a collection string
constants provided when the parameter is created.
+
+@findex PARAM_COLOR
+@findex gdb.PARAM_COLOR
+@item gdb.PARAM_COLOR
+The value is @code{gdb.Color} instance.
@end table
@node Functions In Python
@@ -6241,6 +6246,96 @@ resolve this to the lazy string's character type, use the type's
writable.
@end defvar
+@node Colors In Python
+@subsection Python representation of colors
+
+@cindex colors in python
+@tindex gdb.Color
+You can assign instance of @code{Color} to the @code{value} of
+a @code{Parameter} instance created with @code{PARAM_COLOR}.
+
+@code{Color} may refer to an index from color palette or contain components
+of a color from some colorspace.
+
+@defun Color.__init__ (@r{[}@var{value} @r{[}, @var{color-space}@r{]}@r{]})
+
+@var{value} is @code{None} (meaning the terminal's default color),
+an integer index of a color in palette, tuple with color components
+or a string. String can be a hex RGB triplet in @samp{#RRGGBB} format
+or one of the following color names:
+@samp{none} (meaning the terminal's default color), @samp{black}, @samp{red},
+@samp{green}, @samp{yellow}, @samp{blue}, @samp{magenta}, @samp{cyan},
+or @samp{white}.
+
+@var{color-space} should be one of the @samp{COLORSPACE_} constants. This
+argument tells @value{GDBN} which color space @var{value} belongs.
+@end defun
+
+@defvar Color.is_none
+This atribute is boolean. If its value is @code{True} then color is terminal's
+default.
+@end defvar
+
+@defvar Color.is_indexed
+This atribute is boolean. If its value is @code{True} then color is indexed,
+i.e. belongs to some palette.
+@end defvar
+
+@defvar Color.is_direct
+This atribute is boolean. If its value is @code{True} then this object
+describes direct colour in the sense of ISO/IEC 8613-6.
+@end defvar
+
+@defvar Color.index
+This attribute exist if @code{is_indexed} is @code{True}. Its integer value is
+index of a color in a palette.
+@end defvar
+
+@defvar Color.components
+This attribute exist if @code{is_direct} is @code{True}. Its value is tuple
+with integer components of a color.
+@end defvar
+
+@defun Color.escape_sequence (@var{self}, @var{is_foreground})
+Returns string to change terminal's color to this.
+
+@var{is_foreground} if this color should be applied to foreground or background
+@end defun
+
+When color is initialized, its color space must be specified. The
+available color spaces are represented by constants defined in the @code{gdb}
+module:
+
+@table @code
+@findex COLORSPACE_MONOCHROME
+@findex gdb.COLORSPACE_MONOCHROME
+@item gdb.COLORSPACE_MONOCHROME
+Palette with only terminal's default color.
+
+@findex COLORSPACE_ANSI_8COLOR
+@findex gdb.COLORSPACE_ANSI_8COLOR
+@item gdb.COLORSPACE_ANSI_8COLOR
+Palette with eight standard colors of ISO/IEC 6429 "black", "red", "green", etc.
+
+@findex COLORSPACE_AIXTERM_16COLOR
+@findex gdb.COLORSPACE_AIXTERM_16COLOR
+@item gdb.COLORSPACE_AIXTERM_16COLOR
+Palette with 16 colors. First eight are standard colors of ISO/IEC 6429
+"black", "red", "green", etc. Next eight are their bright version.
+
+@findex COLORSPACE_XTERM_256COLOR
+@findex gdb.COLORSPACE_XTERM_256COLOR
+@item gdb.COLORSPACE_XTERM_256COLOR
+Palette with 256 colors. First 16 are from COLORSPACE_AIXTERM_16COLOR. Next
+216 colors are 6x6x6 RGB cube. And last 24 colors forms grayscale ramp.
+
+@findex COLORSPACE_RGB_24BIT
+@findex gdb.COLORSPACE_RGB_24BIT
+@item gdb.COLORSPACE_RGB_24BIT
+Direct 24-bit RGB colors.
+
+@end table
+
@node Architectures In Python
@subsubsection Python representation of architectures
@cindex Python architectures
diff --git a/gdb/guile/guile-internal.h b/gdb/guile/guile-internal.h
index 28e4889bfa9..1e4991a0186 100644
--- a/gdb/guile/guile-internal.h
+++ b/gdb/guile/guile-internal.h
@@ -446,6 +446,14 @@ extern int gdbscm_valid_command_class_p (int command_class);
extern char *gdbscm_canonicalize_command_name (const char *name,
int want_trailing_space);
+/* scm-color.c */
+
+extern SCM coscm_scm_from_color (const ui_file_style::color &color);
+
+extern int coscm_is_color (SCM scm);
+
+extern const ui_file_style::color & coscm_get_color (SCM color_scm);
+
/* scm-frame.c */
struct frame_smob;
@@ -624,6 +632,7 @@ extern void gdbscm_initialize_arches (void);
extern void gdbscm_initialize_auto_load (void);
extern void gdbscm_initialize_blocks (void);
extern void gdbscm_initialize_breakpoints (void);
+extern void gdbscm_initialize_colors (void);
extern void gdbscm_initialize_commands (void);
extern void gdbscm_initialize_disasm (void);
extern void gdbscm_initialize_exceptions (void);
diff --git a/gdb/guile/guile.c b/gdb/guile/guile.c
index e5565b627d9..90bb787c87d 100644
--- a/gdb/guile/guile.c
+++ b/gdb/guile/guile.c
@@ -593,6 +593,7 @@ initialize_gdb_module (void *data)
gdbscm_initialize_auto_load ();
gdbscm_initialize_blocks ();
gdbscm_initialize_breakpoints ();
+ gdbscm_initialize_colors ();
gdbscm_initialize_commands ();
gdbscm_initialize_disasm ();
gdbscm_initialize_frames ();
diff --git a/gdb/guile/scm-color.c b/gdb/guile/scm-color.c
new file mode 100644
index 00000000000..249111630af
--- /dev/null
+++ b/gdb/guile/scm-color.c
@@ -0,0 +1,445 @@
+/* GDB parameters implemented in Guile.
+
+ Copyright (C) 2008-2022 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "defs.h"
+#include "value.h"
+#include "charset.h"
+#include "gdbcmd.h"
+#include "cli/cli-decode.h"
+#include "completer.h"
+#include "language.h"
+#include "arch-utils.h"
+#include "guile-internal.h"
+
+/* A GDB color. */
+
+struct color_smob
+{
+ /* This always appears first. */
+ gdb_smob base;
+
+ /* Underlying value. */
+ ui_file_style::color color;
+};
+
+static const char color_smob_name[] = "gdb:color";
+
+/* The tag Guile knows the color smob by. */
+static scm_t_bits color_smob_tag;
+
+/* Keywords used by make-color. */
+static SCM colorspace_keyword;
+
+static const char *coscm_colorspace_name (color_space colorspace);
+
+/* Administrivia for color smobs. */
+
+static int
+coscm_print_color_smob (SCM self, SCM port, scm_print_state *pstate)
+{
+ const ui_file_style::color &color = coscm_get_color (self);
+
+ gdbscm_printf (port, "#<%s", color_smob_name);
+
+ gdbscm_printf (port, " %s", color.to_string ().c_str ());
+ gdbscm_printf (port, " %s", coscm_colorspace_name (color.colorspace ()));
+ scm_puts (">", port);
+
+ scm_remember_upto_here_1 (self);
+
+ /* Non-zero means success. */
+ return 1;
+}
+
+/* Create an empty (uninitialized) color. */
+
+static SCM
+coscm_make_color_smob (void)
+{
+ color_smob *c_smob = (color_smob *)
+ scm_gc_calloc (sizeof (color_smob), color_smob_name);
+ SCM c_scm;
+
+ c_smob->color = ui_file_style::color (ui_file_style::NONE);
+ c_scm = scm_new_smob (color_smob_tag, (scm_t_bits) c_smob);
+ gdbscm_init_gsmob (&c_smob->base);
+
+ return c_scm;
+}
+
+/* Return the <gdb:color> object that encapsulates COLOR. */
+
+SCM
+coscm_scm_from_color (const ui_file_style::color &color)
+{
+ SCM c_scm = coscm_make_color_smob ();
+ color_smob *c_smob = (color_smob *) SCM_SMOB_DATA (c_scm);
+ c_smob->color = color;
+ return c_scm;
+}
+
+/* Return the color field of color_smob. */
+
+const ui_file_style::color &
+coscm_get_color (SCM color_scm)
+{
+ SCM_ASSERT_TYPE (coscm_is_color (color_scm), color_scm, SCM_ARG1, FUNC_NAME,
+ _("<gdb:color>"));
+
+ color_smob *c_smob = (color_smob *) SCM_SMOB_DATA (color_scm);
+ return c_smob->color;
+
+}
+
+/* Returns non-zero if SCM is a <gdb:color> object. */
+
+int
+coscm_is_color (SCM scm)
+{
+ return SCM_SMOB_PREDICATE (color_smob_tag, scm);
+}
+
+/* (gdb:color? scm) -> boolean */
+
+static SCM
+gdbscm_color_p (SCM scm)
+{
+ return scm_from_bool (coscm_is_color (scm));
+}
+
+static const scheme_integer_constant colorspaces[] =
+{
+ { "COLORSPACE_MONOCHROME", (int) color_space::MONOCHROME },
+ { "COLORSPACE_ANSI_8COLOR", (int) color_space::ANSI_8COLOR },
+ { "COLORSPACE_AIXTERM_16COLOR", (int) color_space::AIXTERM_16COLOR },
+ { "COLORSPACE_XTERM_256COLOR", (int) color_space::XTERM_256COLOR },
+ { "COLORSPACE_RGB_24BIT", (int) color_space::RGB_24BIT },
+
+ END_INTEGER_CONSTANTS
+};
+
+/* Return non-zero if COLORSPACE is a valid color space. */
+
+static int
+coscm_valid_colorspace_p (int colorspace)
+{
+ for (int i = 0; colorspaces[i].name != NULL; ++i)
+ {
+ if (colorspaces[i].value == colorspace)
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Return COLORSPACE as a string. */
+
+static const char *
+coscm_colorspace_name (color_space colorspace)
+{
+ for (int i = 0; colorspaces[i].name != NULL; ++i)
+ {
+ if (colorspaces[i].value == static_cast<int> (colorspace))
+ return colorspaces[i].name;
+ }
+
+ gdb_assert_not_reached ("bad color space");
+}
+
+/* Free function for a color_smob. */
+static size_t
+coscm_free_color_smob (SCM self)
+{
+ (void) self;
+ return 0;
+}
+
+/* Color Scheme functions. */
+
+/* (make-color [value
+ [#:color-space colorspace]]) -> <gdb:color>
+
+ VALUE is the value of the color. It may be SCM_UNDEFINED, string, number
+ or list.
+
+ COLORSPACE is the color space of the VALUE. It should be one of the
+ COLORSPACE_* constants defined in the gdb module.
+
+ The result is the <gdb:color> Scheme object. */
+
+static SCM
+gdbscm_make_color (SCM value_scm, SCM rest)
+{
+ SCM colorspace_arg = SCM_UNDEFINED;
+ color_space colorspace = color_space::MONOCHROME;
+
+ scm_c_bind_keyword_arguments (FUNC_NAME, rest,
+ static_cast<scm_t_keyword_arguments_flags> (0),
+ colorspace_keyword, &colorspace_arg,
+ SCM_UNDEFINED);
+
+ if (!SCM_UNBNDP (colorspace_arg))
+ {
+ SCM_ASSERT_TYPE (scm_is_integer (colorspace_arg), colorspace_arg,
+ SCM_ARG2, FUNC_NAME, _("int"));
+ int colorspace_int = scm_to_int (colorspace_arg);
+ if (coscm_valid_colorspace_p (colorspace_int))
+ colorspace = static_cast<color_space> (colorspace_int);
+ else
+ gdbscm_out_of_range_error (FUNC_NAME, SCM_ARG2,
+ scm_from_int (colorspace_int),
+ _("invalid colorspace argument"));
+ }
+
+ ui_file_style::color color = ui_file_style::NONE;
+ gdbscm_gdb_exception exc {};
+
+ try
+ {
+ if (SCM_UNBNDP (value_scm) || scm_is_integer (value_scm))
+ {
+ int i = -1;
+ if (scm_is_integer (value_scm))
+ {
+ i = scm_to_int (value_scm);
+ if (i < 0)
+ gdbscm_out_of_range_error (FUNC_NAME, SCM_ARG1, value_scm,
+ _("negative color index"));
+ }
+
+ if (SCM_UNBNDP (colorspace_arg))
+ color = ui_file_style::color (i);
+ else
+ color = ui_file_style::color (colorspace, i);
+ }
+ else if (gdbscm_is_true (scm_list_p (value_scm)))
+ {
+ if (SCM_UNBNDP (colorspace_arg)
+ || colorspace != color_space::RGB_24BIT)
+ error (_("colorspace must be COLORSPACE_RGB_24BIT with "
+ "value of list type."));
+
+ if (scm_ilength (value_scm) != 3)
+ error (_("List value with RGB must be of size 3."));
+
+ uint8_t rgb[3] = {};
+ int i = 0;
+ for (; i < 3 && !scm_is_eq (value_scm, SCM_EOL); ++i)
+ {
+ SCM item = scm_car (value_scm);
+
+ SCM_ASSERT_TYPE (scm_is_integer (item), item, SCM_ARG1, FUNC_NAME,
+ _("int"));
+ int component = scm_to_int (item);
+ if (component < 0 || component > UINT8_MAX)
+ gdbscm_out_of_range_error (FUNC_NAME, SCM_ARG1, item,
+ _("invalid rgb component"));
+ rgb[i] = static_cast<uint8_t> (component);
+
+ value_scm = scm_cdr (value_scm);
+ }
+
+ gdb_assert (i == 3);
+
+ color = ui_file_style::color (rgb[0], rgb[1], rgb[2]);
+ }
+ else if (scm_is_string (value_scm))
+ {
+ SCM exception;
+
+ gdb::unique_xmalloc_ptr<char> string
+ = gdbscm_scm_to_host_string (value_scm, nullptr, &exception);
+ if (string == nullptr)
+ gdbscm_throw (exception);
+
+ color = parse_var_color (string.get ());
+
+ if (!SCM_UNBNDP (colorspace_arg) && colorspace != color.colorspace ())
+ error (_("colorspace doesn't match to value."));
+
+ }
+ else
+ scm_wrong_type_arg_msg (FUNC_NAME, SCM_ARG1, value_scm,
+ "integer, string or list");
+ }
+ catch (const gdb_exception &except)
+ {
+ exc = unpack (except);
+ }
+
+ GDBSCM_HANDLE_GDB_EXCEPTION (exc);
+
+ return coscm_scm_from_color (color);
+}
+
+/* (color-string <gdb:color>) -> value */
+
+static SCM
+gdbscm_color_string (SCM self)
+{
+ const ui_file_style::color &color = coscm_get_color (self);
+ std::string s = color.to_string ();
+ return gdbscm_scm_from_host_string (s.c_str (), s.size ());
+}
+
+/* (color-colorspace <gdb:color>) -> value */
+
+static SCM
+gdbscm_color_colorspace (SCM self)
+{
+ const ui_file_style::color &color = coscm_get_color (self);
+ return scm_from_int (static_cast<int> (color.colorspace ()));
+}
+
+/* (color-none? scm) -> boolean */
+
+static SCM
+gdbscm_color_none_p (SCM self)
+{
+ const ui_file_style::color &color = coscm_get_color (self);
+ return scm_from_bool (color.is_none ());
+}
+
+/* (color-indexed? scm) -> boolean */
+
+static SCM
+gdbscm_color_indexed_p (SCM self)
+{
+ const ui_file_style::color &color = coscm_get_color (self);
+ return scm_from_bool (color.is_indexed ());
+}
+
+/* (color-direct? scm) -> boolean */
+
+static SCM
+gdbscm_color_direct_p (SCM self)
+{
+ const ui_file_style::color &color = coscm_get_color (self);
+ return scm_from_bool (color.is_direct ());
+}
+
+/* (color-index <gdb:color>) -> value */
+
+static SCM
+gdbscm_color_index (SCM self)
+{
+ const ui_file_style::color &color = coscm_get_color (self);
+
+ if (!color.is_indexed ())
+ gdbscm_misc_error (FUNC_NAME, SCM_ARG1, self, "color is not indexed");
+ return scm_from_int (color.get_value ());
+}
+
+/* (color-components <gdb:color>) -> value */
+
+static SCM
+gdbscm_color_components (SCM self)
+{
+ const ui_file_style::color &color = coscm_get_color (self);
+
+ if (!color.is_direct ())
+ gdbscm_misc_error (FUNC_NAME, SCM_ARG1, self, "color is not direct");
+
+ uint8_t rgb[3] = {};
+ color.get_rgb (rgb);
+ SCM red = scm_from_uint8 (rgb[0]);
+ SCM green = scm_from_uint8 (rgb[1]);
+ SCM blue = scm_from_uint8 (rgb[2]);
+ return scm_list_3 (red, green, blue);
+}
+
+/* (color-escape-sequence <gdb:color> is_fg) -> value */
+
+static SCM
+gdbscm_color_escape_sequence (SCM self, SCM is_fg_scm)
+{
+ const ui_file_style::color &color = coscm_get_color (self);
+ SCM_ASSERT_TYPE (gdbscm_is_bool (is_fg_scm), is_fg_scm, SCM_ARG2, FUNC_NAME,
+ _("boolean"));
+ bool is_fg = gdbscm_is_true (is_fg_scm);
+ std::string s = color.to_ansi (is_fg);
+ return gdbscm_scm_from_host_string (s.c_str (), s.size ());
+}
+
+/* Initialize the Scheme color support. */
+
+static const scheme_function color_functions[] =
+{
+ { "make-color", 0, 1, 1, as_a_scm_t_subr (gdbscm_make_color),
+ "\
+Make a GDB color object.\n\
+\n\
+ Arguments: [value\n\
+ [#:color-space <colorspace>]]\n\
+ value: The name of the color. It may be string, number with color index\n\
+ or list with RGB components.\n\
+ colorspace: The color space of the color, one of COLORSPACE_*." },
+
+ { "color?", 1, 0, 0, as_a_scm_t_subr (gdbscm_color_p),
+ "\
+Return #t if the object is a <gdb:color> object." },
+
+ { "color-none?", 1, 0, 0, as_a_scm_t_subr (gdbscm_color_none_p),
+ "\
+Return #t if the <gdb:color> object has default color." },
+
+ { "color-indexed?", 1, 0, 0, as_a_scm_t_subr (gdbscm_color_indexed_p),
+ "\
+Return #t if the <gdb:color> object is from indexed color space." },
+
+ { "color-direct?", 1, 0, 0, as_a_scm_t_subr (gdbscm_color_direct_p),
+ "\
+Return #t if the <gdb:color> object has direct color (e.g. RGB, CMY, CMYK)." },
+
+ { "color-string", 1, 0, 0, as_a_scm_t_subr (gdbscm_color_string),
+ "\
+Return the textual representation of a <gdb:color> object." },
+
+ { "color-colorspace", 1, 0, 0, as_a_scm_t_subr (gdbscm_color_colorspace),
+ "\
+Return the color space of a <gdb:color> object." },
+
+ { "color-index", 1, 0, 0, as_a_scm_t_subr (gdbscm_color_index),
+ "\
+Return index of the color of a <gdb:color> object in a palette." },
+
+ { "color-components", 1, 0, 0, as_a_scm_t_subr (gdbscm_color_components),
+ "\
+Return components of the direct <gdb:color> object." },
+
+ { "color-escape-sequence", 2, 0, 0,
+ as_a_scm_t_subr (gdbscm_color_escape_sequence),
+ "\
+Return string to change terminal's color to this." },
+
+ END_FUNCTIONS
+};
+
+void
+gdbscm_initialize_colors (void)
+{
+ color_smob_tag = gdbscm_make_smob_type (color_smob_name, sizeof (color_smob));
+ scm_set_smob_free (color_smob_tag, coscm_free_color_smob);
+ scm_set_smob_print (color_smob_tag, coscm_print_color_smob);
+
+ gdbscm_define_integer_constants (colorspaces, 1);
+ gdbscm_define_functions (color_functions, 1);
+
+ colorspace_keyword = scm_from_latin1_keyword ("color-space");
+}
diff --git a/gdb/guile/scm-param.c b/gdb/guile/scm-param.c
index 54c8c27301a..60cc17432f0 100644
--- a/gdb/guile/scm-param.c
+++ b/gdb/guile/scm-param.c
@@ -48,6 +48,9 @@ union pascm_variable
/* Hold a string, for enums. */
const char *cstringval;
+
+ /* Hold a color. */
+ ui_file_style::color color;
};
/* A GDB parameter.
@@ -129,6 +132,8 @@ make_setting (param_smob *s)
return setting (s->type, s->value.stringval);
else if (var_type_uses<const char *> (s->type))
return setting (s->type, &s->value.cstringval);
+ else if (var_type_uses<ui_file_style::color> (s->type))
+ return setting (s->type, &s->value.color);
else
gdb_assert_not_reached ("unhandled var type");
}
@@ -190,10 +195,9 @@ static SCM
pascm_make_param_smob (void)
{
param_smob *p_smob = (param_smob *)
- scm_gc_malloc (sizeof (param_smob), param_smob_name);
+ scm_gc_calloc (sizeof (param_smob), param_smob_name);
SCM p_scm;
- memset (p_smob, 0, sizeof (*p_smob));
p_smob->cmd_class = no_class;
p_smob->type = var_boolean; /* ARI: var_boolean */
p_smob->set_func = SCM_BOOL_F;
@@ -466,6 +470,13 @@ add_setshow_generic (enum var_types param_type, enum command_class cmd_class,
set_list, show_list);
break;
+ case var_color:
+ commands = add_setshow_color_cmd (cmd_name, cmd_class, &self->value.color,
+ set_doc, show_doc, help_doc,
+ set_func, show_func,
+ set_list, show_list);
+ break;
+
default:
gdb_assert_not_reached ("bad param_type value");
}
@@ -545,6 +556,7 @@ static const scheme_integer_constant parameter_types[] =
{ "PARAM_OPTIONAL_FILENAME", var_optional_filename },
{ "PARAM_FILENAME", var_filename },
{ "PARAM_ENUM", var_enum },
+ { "PARAM_COLOR", var_color },
END_INTEGER_CONSTANTS
};
@@ -611,6 +623,11 @@ pascm_param_value (const setting &var, int arg_pos, const char *func_name)
return gdbscm_scm_from_host_string (str, strlen (str));
}
+ case var_color:
+ {
+ return coscm_scm_from_color (var.get<ui_file_style::color> ());
+ }
+
case var_boolean:
{
if (var.get<bool> ())
@@ -716,6 +733,12 @@ pascm_set_param_value_x (param_smob *p_smob,
break;
}
+ case var_color:
+ SCM_ASSERT_TYPE (coscm_is_color (value), value, arg_pos, func_name,
+ _("<gdb:color>"));
+ var.set<ui_file_style::color> (coscm_get_color (value));
+ break;
+
case var_boolean:
SCM_ASSERT_TYPE (gdbscm_is_bool (value), value, arg_pos, func_name,
_("boolean"));
@@ -961,6 +984,8 @@ gdbscm_make_parameter (SCM name_scm, SCM rest)
scm_set_smob_free (parameter_smob_tag, pascm_free_parameter_smob);
if (var_type_uses<std::string> (p_smob->type))
p_smob->value.stringval = new std::string;
+ else if (var_type_uses<ui_file_style::color> (p_smob->type))
+ p_smob->value.color = ui_file_style::NONE;
if (initial_value_arg_pos > 0)
{
diff --git a/gdb/python/py-color.c b/gdb/python/py-color.c
new file mode 100644
index 00000000000..fb816a06328
--- /dev/null
+++ b/gdb/python/py-color.c
@@ -0,0 +1,346 @@
+/* Python interface to ui_file_style::color objects.
+
+ Copyright (C) 2008-2022 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+
+#include "defs.h"
+#include "python-internal.h"
+#include "py-color.h"
+#include "cli/cli-decode.h"
+
+/* Colorspace constants and their values. */
+static struct {
+ const char *name;
+ color_space value;
+} colorspace_constants[] =
+{
+ { "COLORSPACE_MONOCHROME", color_space::MONOCHROME },
+ { "COLORSPACE_ANSI_8COLOR", color_space::ANSI_8COLOR },
+ { "COLORSPACE_AIXTERM_16COLOR", color_space::AIXTERM_16COLOR },
+ { "COLORSPACE_XTERM_256COLOR", color_space::XTERM_256COLOR },
+ { "COLORSPACE_RGB_24BIT", color_space::RGB_24BIT },
+};
+
+/* A color. */
+struct colorpy_object
+{
+ PyObject_HEAD
+
+ /* Underlying value. */
+ ui_file_style::color color;
+};
+
+extern PyTypeObject colorpy_object_type
+ CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("parmpy_object");
+
+/* See py-color.h. */
+gdbpy_ref<>
+create_color_object (const ui_file_style::color &color)
+{
+ gdbpy_ref<colorpy_object> color_obj (PyObject_New (colorpy_object,
+ &colorpy_object_type));
+
+ if (color_obj == NULL)
+ return NULL;
+
+ color_obj->color = color;
+ return gdbpy_ref<> ((PyObject *) color_obj.release ());
+}
+
+/* See py-color.h. */
+bool
+gdbpy_is_color (PyObject *obj)
+{
+ return PyObject_IsInstance (obj, (PyObject *)&colorpy_object_type);
+}
+
+/* See py-color.h. */
+const ui_file_style::color &
+gdbpy_get_color (PyObject *obj)
+{
+ gdb_assert (gdbpy_is_color (obj));
+ colorpy_object *self = (colorpy_object *) obj;
+ return self->color;
+}
+
+/* Get an attribute. */
+static PyObject *
+get_attr (PyObject *obj, PyObject *attr_name)
+{
+ if (! PyUnicode_Check (attr_name))
+ return PyObject_GenericGetAttr (obj, attr_name);
+
+ colorpy_object *self = (colorpy_object *) obj;
+ const ui_file_style::color &color = self->color;
+
+ if (! PyUnicode_CompareWithASCIIString (attr_name, "colorspace"))
+ {
+ int value = static_cast<int> (color.colorspace ());
+ return gdb_py_object_from_longest (value).release ();
+ }
+
+ if (! PyUnicode_CompareWithASCIIString (attr_name, "is_none"))
+ return color.is_none () ? Py_True : Py_False;
+
+ if (! PyUnicode_CompareWithASCIIString (attr_name, "is_indexed"))
+ return color.is_indexed () ? Py_True : Py_False;
+
+ if (! PyUnicode_CompareWithASCIIString (attr_name, "is_direct"))
+ return color.is_direct () ? Py_True : Py_False;
+
+ if (color.is_indexed ()
+ && ! PyUnicode_CompareWithASCIIString (attr_name, "index"))
+ return gdb_py_object_from_longest (color.get_value ()).release ();
+
+ if (color.is_direct ()
+ && ! PyUnicode_CompareWithASCIIString (attr_name, "components"))
+ {
+ uint8_t rgb[3];
+ color.get_rgb (rgb);
+
+ gdbpy_ref<> rgb_objects[3];
+ for (int i = 0; i < 3; ++i)
+ {
+ rgb_objects[i] = gdb_py_object_from_ulongest (rgb[i]);
+ if (rgb_objects[i] == nullptr)
+ return nullptr;
+ }
+
+ PyObject *comp = PyTuple_New (3);
+ if (!comp)
+ return nullptr;
+
+ for (int i = 0; i < 3; ++i)
+ PyTuple_SET_ITEM (comp, i, rgb_objects[i].release ());
+
+ return comp;
+ }
+
+ return PyObject_GenericGetAttr (obj, attr_name);
+}
+
+/* Implementation of Color.escape_sequence (self, is_fg) -> str. */
+
+static PyObject *
+colorpy_escape_sequence (PyObject *self, PyObject *is_fg_obj)
+{
+ if (!gdbpy_is_color (self))
+ {
+ PyErr_SetString (PyExc_RuntimeError,
+ _("Object is not gdb.Color."));
+ return nullptr;
+ }
+
+ if (! PyBool_Check (is_fg_obj))
+ {
+ PyErr_SetString (PyExc_RuntimeError,
+ _("A boolean argument is required."));
+ return nullptr;
+ }
+
+ bool is_fg = PyObject_IsTrue (is_fg_obj);
+ std::string s = gdbpy_get_color (self).to_ansi (is_fg);
+
+ return host_string_to_python_string (s.c_str ()).release ();
+}
+
+/* Object initializer; fills color with value.
+
+ Use: __init__(VALUE = None, COLORSPACE = None)
+
+ VALUE is a string, integer, RGB-tuple or None.
+
+ COLORSPACE is the color space index.
+
+ Returns -1 on error, with a python exception set. */
+
+static int
+colorpy_init (PyObject *self, PyObject *args, PyObject *kwds)
+{
+ colorpy_object *obj = (colorpy_object *) self;
+ PyObject *value_obj = nullptr;
+ PyObject *colorspace_obj = nullptr;
+ color_space colorspace = color_space::MONOCHROME;
+
+ if (! PyArg_ParseTuple (args, "|OO", &value_obj, &colorspace_obj))
+ return -1;
+
+ try
+ {
+ if (colorspace_obj)
+ {
+ if (PyLong_Check (colorspace_obj))
+ {
+ long colorspace_id = -1;
+ if (! gdb_py_int_as_long (colorspace_obj, &colorspace_id))
+ return -1;
+ if (colorspace_id < INT_MIN || colorspace_id > INT_MAX)
+ error (_("colorspace %ld is out of range."), colorspace_id);
+ colorspace = static_cast<color_space> (colorspace_id);
+ }
+ else if (colorspace_obj == Py_None)
+ colorspace_obj = nullptr;
+ else
+ error (_("colorspace must be None or integer"));
+ }
+
+ if (!value_obj || value_obj == Py_None)
+ obj->color = ui_file_style::color (colorspace, -1);
+ else if (PyLong_Check (value_obj))
+ {
+ long value = -1;
+ if (! gdb_py_int_as_long (value_obj, &value))
+ return -1;
+ if (value < 0 || value > INT_MAX)
+ error (_("value %ld is out of range."), value);
+ if (colorspace_obj)
+ obj->color = ui_file_style::color (colorspace, value);
+ else
+ obj->color = ui_file_style::color (value);
+ }
+ else if (PyTuple_Check (value_obj))
+ {
+ if (!colorspace_obj || colorspace != color_space::RGB_24BIT)
+ error (_("colorspace must be gdb.COLORSPACE_RGB_24BIT with "
+ "value of tuple type."));
+ Py_ssize_t tuple_size = PyTuple_Size (value_obj);
+ if (tuple_size != 3)
+ error (_("Tuple value with RGB must be of size 3."));
+ uint8_t rgb[3];
+ for (int i = 0; i < 3; ++i)
+ {
+ PyObject *item = PyTuple_GetItem (value_obj, i);
+ if (!PyLong_Check (item))
+ error (_("Item %d of an RGB tuple must be integer."), i);
+ long item_value = -1;
+ if (!gdb_py_int_as_long (item, &item_value))
+ return -1;
+ if (item_value < 0 || item_value > UINT8_MAX)
+ error (_("RGB item %ld is out of byte range."), item_value);
+ rgb[i] = static_cast<uint8_t> (item_value);
+ }
+
+ obj->color = ui_file_style::color (rgb[0], rgb[1], rgb[2]);
+ }
+ else if (PyUnicode_Check (value_obj))
+ {
+ gdb::unique_xmalloc_ptr<char>
+ str (python_string_to_host_string (value_obj));
+ if (!str)
+ return -1;
+ obj->color = parse_var_color (str.get());
+
+ if (colorspace_obj && colorspace != obj->color.colorspace ())
+ error (_("colorspace doesn't match to value."));
+ }
+ else
+ error (_("value must be one of None, integer, tuple or str."));
+ }
+ catch (const gdb_exception &except)
+ {
+ gdbpy_convert_exception (except);
+ return -1;
+ }
+
+ Py_INCREF (self);
+ return 0;
+}
+
+/* Deallocate function for a gdb.Color. */
+
+static void
+colorpy_dealloc (PyObject *)
+{
+}
+
+static PyObject *
+colorpy_str (PyObject *self)
+{
+ colorpy_object *obj = reinterpret_cast<colorpy_object *> (self);
+
+ return PyUnicode_FromString (obj->color.to_string ().c_str ());
+}
+
+/* Initialize the 'color' module. */
+int
+gdbpy_initialize_color (void)
+{
+ colorpy_object_type.tp_new = PyType_GenericNew;
+ if (PyType_Ready (&colorpy_object_type) < 0)
+ return -1;
+
+ for (auto & pair : colorspace_constants)
+ if (PyModule_AddIntConstant (gdb_module, pair.name,
+ static_cast<long> (pair.value)) < 0)
+ return -1;
+
+ return gdb_pymodule_addobject (gdb_module, "Color",
+ (PyObject *) &colorpy_object_type);
+}
+
+/* Color methods. */
+
+static PyMethodDef color_methods[] =
+{
+ { "escape_sequence", colorpy_escape_sequence, METH_O,
+ "escape_sequence (is_foreground) -> str.\n\
+Return the ANSI escape sequence for this color.\n\
+IS_FOREGROUND indicates whether this is a foreground or background color."},
+ {NULL}
+};
+
+PyTypeObject colorpy_object_type =
+{
+ PyVarObject_HEAD_INIT (NULL, 0)
+ "gdb.Color", /*tp_name*/
+ sizeof (colorpy_object), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ colorpy_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ colorpy_str, /*tp_str*/
+ get_attr, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "GDB color object", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ color_methods, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ colorpy_init, /* tp_init */
+ 0, /* tp_alloc */
+};
diff --git a/gdb/python/py-color.h b/gdb/python/py-color.h
new file mode 100644
index 00000000000..95726faa19a
--- /dev/null
+++ b/gdb/python/py-color.h
@@ -0,0 +1,35 @@
+/* Python interface to ui_file_style::color objects.
+
+ Copyright (C) 2009-2022 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef PYTHON_PY_COLOR_H
+#define PYTHON_PY_COLOR_H
+
+#include "python-internal.h"
+#include "ui-style.h"
+
+/* Create a new gdb.Color object from COLOR. */
+extern gdbpy_ref<> create_color_object (const ui_file_style::color &color);
+
+/* Check if OBJ is instance of a gdb.Color type. */
+extern bool gdbpy_is_color (PyObject *obj);
+
+/* Extracts value from OBJ object of gdb.Color type. */
+extern const ui_file_style::color & gdbpy_get_color (PyObject *obj);
+
+#endif /* PYTHON_PY_COLOR_H */
diff --git a/gdb/python/py-param.c b/gdb/python/py-param.c
index 5d509ba4658..265b638416f 100644
--- a/gdb/python/py-param.c
+++ b/gdb/python/py-param.c
@@ -27,6 +27,7 @@
#include "completer.h"
#include "language.h"
#include "arch-utils.h"
+#include "py-color.h"
/* Parameter constants and their values. */
static struct {
@@ -46,6 +47,7 @@ static struct {
{ "PARAM_ZUINTEGER", var_zuinteger },
{ "PARAM_ZUINTEGER_UNLIMITED", var_zuinteger_unlimited },
{ "PARAM_ENUM", var_enum },
+ { "PARAM_COLOR", var_color },
{ NULL, 0 }
};
@@ -70,6 +72,9 @@ union parmpy_variable
/* Hold a string, for enums. */
const char *cstringval;
+
+ /* Hold a color. */
+ ui_file_style::color color;
};
/* A GDB parameter. */
@@ -108,6 +113,8 @@ make_setting (parmpy_object *s)
return setting (s->type, s->value.stringval);
else if (var_type_uses<const char *> (s->type))
return setting (s->type, &s->value.cstringval);
+ else if (var_type_uses<ui_file_style::color> (s->type))
+ return setting (s->type, &s->value.color);
else
gdb_assert_not_reached ("unhandled var type");
}
@@ -199,6 +206,19 @@ set_parameter_value (parmpy_object *self, PyObject *value)
break;
}
+ case var_color:
+ {
+ if (gdbpy_is_color (value))
+ self->value.color = gdbpy_get_color (value);
+ else
+ {
+ PyErr_SetString (PyExc_RuntimeError,
+ _("color argument must be a gdb.Color object."));
+ return -1;
+ }
+ }
+ break;
+
case var_boolean:
if (! PyBool_Check (value))
{
@@ -637,6 +657,15 @@ add_setshow_generic (int parmclass, enum command_class cmdclass,
get_show_value, set_list, show_list);
break;
+ case var_color:
+ /* Initialize the value, just in case. */
+ self->value.color = ui_file_style::NONE;
+ commands = add_setshow_color_cmd (cmd_name.get (), cmdclass,
+ &self->value.color, set_doc,
+ show_doc, help_doc, get_set_value,
+ get_show_value, set_list, show_list);
+ break;
+
default:
gdb_assert_not_reached ("Unhandled parameter class.");
}
@@ -758,7 +787,8 @@ parmpy_init (PyObject *self, PyObject *args, PyObject *kwds)
&& parmclass != var_string && parmclass != var_string_noescape
&& parmclass != var_optional_filename && parmclass != var_filename
&& parmclass != var_zinteger && parmclass != var_zuinteger
- && parmclass != var_zuinteger_unlimited && parmclass != var_enum)
+ && parmclass != var_zuinteger_unlimited && parmclass != var_enum
+ && parmclass != var_color)
{
PyErr_SetString (PyExc_RuntimeError,
_("Invalid parameter class argument."));
@@ -779,7 +809,7 @@ parmpy_init (PyObject *self, PyObject *args, PyObject *kwds)
else
obj->enumeration = NULL;
obj->type = (enum var_types) parmclass;
- memset (&obj->value, 0, sizeof (obj->value));
+ obj->value = {}; /* zeros initialization */
if (var_type_uses<std::string> (obj->type))
obj->value.stringval = new std::string;
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index 08749d14200..f3877e04ffa 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -545,6 +545,8 @@ int gdbpy_initialize_micommands (void)
void gdbpy_finalize_micommands ();
int gdbpy_initialize_disasm ()
CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
+int gdbpy_initialize_color (void)
+ CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
PyMODINIT_FUNC gdbpy_events_mod_func ();
diff --git a/gdb/python/python.c b/gdb/python/python.c
index c7d4157b70c..0eb47cbd543 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -37,6 +37,7 @@
#include "run-on-main-thread.h"
#include "gdbsupport/selftest.h"
#include "observable.h"
+#include "py-color.h"
/* Declared constants and enum for python stack printing. */
static const char python_excp_none[] = "none";
@@ -484,6 +485,12 @@ gdbpy_parameter_value (const setting &var)
return host_string_to_python_string (str).release ();
}
+ case var_color:
+ {
+ const ui_file_style::color &color = var.get<ui_file_style::color> ();
+ return create_color_object (color).release ();
+ }
+
case var_boolean:
{
if (var.get<bool> ())
@@ -2148,6 +2155,7 @@ do_start_initialization ()
|| gdbpy_initialize_membuf () < 0
|| gdbpy_initialize_connection () < 0
|| gdbpy_initialize_tui () < 0
+ || gdbpy_initialize_color () < 0
|| gdbpy_initialize_micommands () < 0)
return false;
diff --git a/gdb/testsuite/gdb.base/style.exp b/gdb/testsuite/gdb.base/style.exp
index 2242c5bf743..775d1b45e12 100644
--- a/gdb/testsuite/gdb.base/style.exp
+++ b/gdb/testsuite/gdb.base/style.exp
@@ -68,10 +68,12 @@ proc clean_restart_and_disable { prefix args } {
proc run_style_tests { } {
global testfile srcfile hex binfile
global currently_disabled_style decimal hex
+ global env
- save_vars { env(TERM) } {
+ save_vars { env(TERM) env(COLORTERM) } {
# We need an ANSI-capable terminal to get the output.
- setenv TERM ansi
+ setenv TERM xterm-256color
+ setenv COLORTERM truecolor
# Restart GDB with the correct TERM variable setting, this
# means that GDB will enable styling.
@@ -298,6 +300,21 @@ proc run_style_tests { } {
set url [limited_style "http:.*html" file]
gdb_test "show version" "${vers}.*<$url>.*" \
"'show version' is styled"
+
+ if { $currently_disabled_style != "version" } {
+ # Check that colors in styling can be set as integer and as RGB hex
+ # triplet. Check that the version string is styled in the output of
+ # 'show version' according to the set colors.
+ gdb_test_no_output "set style version intensity normal"
+ gdb_test_no_output "set style version background 255"
+ gdb_test_no_output "set style version foreground #FED210"
+ gdb_test "show style version background" \
+ "The \033\\\[38;2;254;210;16;48;5;255;22;27m.*\".*version.*\".*style.*\033\\\[m background color is: 255" \
+ "Version's 256-color background style"
+ gdb_test "show style version foreground" \
+ "The \033\\\[38;2;254;210;16;48;5;255;22;27m.*\".*version.*\".*style.*\033\\\[m foreground color is: #FED210" \
+ "Version's TrueColor foreground style"
+ }
}
}
@@ -370,6 +387,188 @@ proc test_startup_version_string { } {
gdb_test "" "${vers}.*" "version is styled at startup"
}
+# Color support is disabled when TERM=dumb and COLORTERM="".
+# All colors are approximated to none when set.
+proc test_colorsupport_monochrome { } {
+ with_test_prefix "colorsupport_monochrome" {
+ global env
+ save_vars { env(TERM) env(COLORTERM) } {
+ setenv TERM dumb
+ setenv COLORTERM ""
+ gdb_exit
+ gdb_start
+ gdb_test "print \$_colorsupport" \
+ "\\\$1 = \"monochrome\"" \
+ "color support is monochrome"
+ gdb_test_no_output "set style enabled off"
+ gdb_test_no_output "set style filename foreground none"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: none" \
+ "none without approximation"
+ gdb_test_no_output "set style filename foreground blue"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: none" \
+ "blue approximated to none"
+ }
+ }
+}
+
+# Color support is limited by 8 colors when TERM=ansi and COLORTERM="".
+# All colors are approximated to basic colors when set.
+proc test_colorsupport_8color { } {
+ with_test_prefix "colorsupport_8color" {
+ global env
+ save_vars { env(TERM) env(COLORTERM) } {
+ setenv TERM ansi
+ setenv COLORTERM ""
+ gdb_exit
+ gdb_start
+ gdb_test "print \$_colorsupport" \
+ "\\\$1 = \"monochrome,ansi_8color\"" \
+ "color support is 8 color"
+ gdb_test_no_output "set style enabled off"
+ gdb_test_no_output "set style filename foreground none"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: none" \
+ "none without approximation"
+ gdb_test_no_output "set style filename foreground yellow"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: yellow" \
+ "yellow without approximation"
+ gdb_test_no_output "set style filename foreground 9"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: red" \
+ "9 approximated to red"
+ gdb_test_no_output "set style filename foreground 118"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: green" \
+ "118 approximated to green"
+ gdb_test_no_output "set style filename foreground #000ABC"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: blue" \
+ "#000ABC approximated to blue"
+ }
+ }
+}
+
+# Color support is limited by 256 colors when TERM=xterm-256color and
+# COLORTERM="". All colors are approximated by 256 color palette when set.
+proc test_colorsupport_256color { } {
+ with_test_prefix "colorsupport_256color" {
+ global env
+ save_vars { env(TERM) env(COLORTERM) } {
+ setenv TERM xterm-256color
+ setenv COLORTERM ""
+ gdb_exit
+ gdb_start
+ gdb_test "print \$_colorsupport" \
+ "\\\$1 = \"monochrome,ansi_8color,aixterm_16color,xterm_256color\"" \
+ "color support is 256 color"
+ gdb_test_no_output "set style enabled off"
+ gdb_test_no_output "set style filename foreground none"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: none" \
+ "none without approximation"
+ gdb_test_no_output "set style filename foreground red"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: red" \
+ "red without approximation"
+ gdb_test_no_output "set style filename foreground 9"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: 9" \
+ "9 without approximation"
+ gdb_test_no_output "set style filename foreground 118"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: 118" \
+ "118 without approximation"
+ gdb_test_no_output "set style filename foreground #CD00CD"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: 5" \
+ "#CD00CD approximated to 5"
+ gdb_test_no_output "set style filename foreground #FFAF12"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: 214" \
+ "#FFAF12 approximated to 214"
+ }
+ }
+}
+
+# Color support is limited by 16777216 colors when TERM=xterm-256color and
+# COLORTERM="truecolor". No approximation needed.
+proc test_colorsupport_truecolor { } {
+ with_test_prefix "colorsupport_truecolor" {
+ global env
+ save_vars { env(TERM) env(COLORTERM) } {
+ setenv TERM xterm-256color
+ setenv COLORTERM truecolor
+ gdb_exit
+ gdb_start
+ gdb_test "print \$_colorsupport" \
+ "\\\$1 = \"monochrome,ansi_8color,aixterm_16color,xterm_256color,rgb_24bit\"" \
+ "color support is truecolor"
+ gdb_test_no_output "set style enabled off"
+ gdb_test_no_output "set style filename foreground none"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: none" \
+ "none without approximation"
+ gdb_test_no_output "set style filename foreground red"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: red" \
+ "red without approximation"
+ gdb_test_no_output "set style filename foreground 9"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: 9" \
+ "9 without approximation"
+ gdb_test_no_output "set style filename foreground 118"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: 118" \
+ "118 without approximation"
+ gdb_test_no_output "set style filename foreground #CD00CD"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: #CD00CD" \
+ "#CD00CD without approximation"
+ }
+ }
+}
+
+# Color support is limited by 16777216 colors when TERM=dumb and
+# COLORTERM=24bit. No approximation needed. Basic colors replaced with RGB.
+proc test_colorsupport_truecolor_only { } {
+ with_test_prefix "colorsupport_truecolor_only" {
+ global env
+ save_vars { env(TERM) env(COLORTERM) } {
+ setenv TERM dumb
+ setenv COLORTERM truecolor
+ gdb_exit
+ gdb_start
+ gdb_test "print \$_colorsupport" \
+ "\\\$1 = \"monochrome,rgb_24bit\"" \
+ "color support is truecolor only"
+ gdb_test_no_output "set style enabled off"
+ gdb_test_no_output "set style filename foreground none"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: none" \
+ "none without approximation"
+ gdb_test_no_output "set style filename foreground red"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: #DE382B" \
+ "red replaced by #DE382B"
+ gdb_test_no_output "set style filename foreground 9"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: #FF0000" \
+ "9 replaced by #FF0000"
+ gdb_test_no_output "set style filename foreground 118"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: #87FF00" \
+ "118 replaced by #87FF00"
+ gdb_test_no_output "set style filename foreground #CD00CD"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: #CD00CD" \
+ "#CD00CD without approximation"
+ }
+ }
+}
+
# Check to see if the Python styling of disassembler output is
# expected or not, this styling requires Python support in GDB, and
# the Python pygments module to be available.
@@ -402,3 +601,9 @@ if { $python_disassembly_styling } {
# Finally, check the styling of the version string during startup.
test_startup_version_string
+
+test_colorsupport_monochrome
+test_colorsupport_8color
+test_colorsupport_256color
+test_colorsupport_truecolor
+test_colorsupport_truecolor_only
diff --git a/gdb/testsuite/gdb.guile/scm-color.exp b/gdb/testsuite/gdb.guile/scm-color.exp
new file mode 100644
index 00000000000..e6e9dd4838c
--- /dev/null
+++ b/gdb/testsuite/gdb.guile/scm-color.exp
@@ -0,0 +1,114 @@
+# Copyright (C) 2010-2022 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# This file is part of the GDB testsuite.
+# It tests GDB parameter support in Guile.
+
+load_lib gdb-guile.exp
+
+# Start with a fresh gdb.
+gdb_exit
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+
+# Skip all tests if Guile scripting is not enabled.
+if { [skip_guile_tests] } { continue }
+
+gdb_install_guile_utils
+gdb_install_guile_module
+
+gdb_test_no_output [concat "guile (define (print_color_attrs c) " \
+ "(display (color-string c)) (display \" \") " \
+ "(display (color-colorspace c)) (display \" \") " \
+ "(display (color-none? c)) (display \" \") " \
+ "(display (color-indexed? c)) (display \" \") " \
+ "(display (color-direct? c)) (newline))"] \
+ "print_color_attrs helper"
+
+gdb_test "guile (print_color_attrs (make-color))" \
+ "none 0 #t #f #f" \
+ "print attrs of a color without params"
+
+gdb_test_no_output "guile (define c (make-color \"green\"))" \
+ "create color from basic name string"
+gdb_test "guile (print_color_attrs c)" "green 1 #f #t #f" \
+ "print attrs of a basic color name"
+gdb_test "guile (print (color-index c))" "2" \
+ "print index of a basic color name"
+
+gdb_test_no_output "guile (define c (make-color 2))" \
+ "create color from basic index"
+gdb_test "guile (print_color_attrs c)" "green 1 #f #t #f" \
+ "print attrs of a basic color"
+gdb_test "guile (print (color-index c))" "2" \
+ "print index of a basic color"
+
+gdb_test_no_output "guile (define c (make-color 14))" \
+ "create color from integer 14"
+gdb_test "guile (print_color_attrs c)" "14 2 #f #t #f" \
+ "print attrs of an color 14"
+gdb_test "guile (print (color-index c))" "14" \
+ "print index of color 14"
+
+gdb_test_no_output "guile (define c (make-color 2 #:color-space COLORSPACE_ANSI_8COLOR))" \
+ "create color from basic index and ansi colorspace"
+gdb_test "guile (print_color_attrs c)" "green 1 #f #t #f" \
+ "print attrs of a basic color with ansi colorspace"
+gdb_test "guile (print (color-index c))" "2" \
+ "print index of a basic color with ansi colorspace"
+
+gdb_test_no_output "guile (define c (make-color 2 #:color-space COLORSPACE_XTERM_256COLOR))" \
+ "create color from basic index and xterm256 colorspace"
+gdb_test "guile (print_color_attrs c)" "2 3 #f #t #f" \
+ "print attrs of a basic color with xterm256 colorspace"
+gdb_test "guile (print (color-index c))" "2" \
+ "print index of a basic color with xterm256 colorspace"
+
+gdb_test_no_output "guile (define c (make-color '(171 205 239) #:color-space COLORSPACE_RGB_24BIT))" \
+ "create color from rgb components"
+gdb_test "guile (print_color_attrs c)" "#ABCDEF 4 #f #f #t" \
+ "print attrs of an RGB color"
+gdb_test "guile (print (color-components c))" "\\(171 205 239\\)" \
+ "print components of an RGB color"
+
+gdb_test_no_output "guile (define c (make-color \"none\"))" \
+ "create color from string none"
+gdb_test "guile (print_color_attrs c)" "none 0 #t #f #f" \
+ "print attrs of a color none"
+
+gdb_test_no_output "guile (define c (make-color \"254\"))" \
+ "create color from string 254"
+gdb_test "guile (print_color_attrs c)" "254 3 #f #t #f" \
+ "print attrs of an color 254"
+gdb_test "guile (print (color-index c))" "254" \
+ "print index of color 254"
+
+gdb_test_no_output "guile (define c_none (make-color \"none\"))" \
+ "save default color"
+gdb_test_no_output "guile (define c_red (make-color \"red\"))" \
+ "save blue color"
+gdb_test_no_output "guile (define c_green (make-color \"green\"))" \
+ "save yellow color"
+gdb_test [concat "guile " \
+ "(display (color-escape-sequence c_red #t)) " \
+ "(display (color-escape-sequence c_green #f)) " \
+ "(display \"red on green\") " \
+ "(display (color-escape-sequence c_none #f)) " \
+ "(display \" red on default\") " \
+ "(display (color-escape-sequence c_none #t)) " \
+ "(newline)"] \
+ "\033\\\[31m\033\\\[42mred on green\033\\\[49m red on default\033\\\[39m" \
+ "escape sequences"
+
diff --git a/gdb/testsuite/gdb.guile/scm-parameter.exp b/gdb/testsuite/gdb.guile/scm-parameter.exp
index cf6f2834373..6ea540d16fd 100644
--- a/gdb/testsuite/gdb.guile/scm-parameter.exp
+++ b/gdb/testsuite/gdb.guile/scm-parameter.exp
@@ -206,3 +206,46 @@ with_test_prefix "previously-ambiguous" {
gdb_test "help set print s" "This command is not documented." "set help"
gdb_test "help set print" "set print s -- This command is not documented.*" "general help"
}
+
+# Test a color parameter.
+
+save_vars { env(TERM) env(COLORTERM) } {
+ # This enables 256 colors support and disables colors approximation.
+ setenv TERM xterm-256color
+ setenv COLORTERM truecolor
+
+ # Start with a fresh gdb.
+ gdb_exit
+ gdb_start
+ gdb_reinitialize_dir $srcdir/$subdir
+
+ gdb_install_guile_utils
+ gdb_install_guile_module
+
+ # We use "." here instead of ":" so that this works on win32 too.
+ set escaped_directory [string_to_regexp "$srcdir/$subdir"]
+
+ gdb_test_multiline "color gdb parameter" \
+ "guile" "" \
+ "(define test-color-param" "" \
+ " (make-parameter \"print test-color-param\"" "" \
+ " #:command-class COMMAND_DATA" "" \
+ " #:parameter-type PARAM_COLOR" "" \
+ " #:doc \"When set, test param does something useful. When disabled, does nothing.\"" "" \
+ " #:show-doc \"Show the state of the test-color-param.\"" "" \
+ " #:set-doc \"Set the state of the test-color-param.\"" "" \
+ " #:show-func (lambda (self value)" "" \
+ " (format #f \"The state of the test-color-param is ~a.\" value))" "" \
+ " #:initial-value (make-color \"green\")))" "" \
+ "(register-parameter! test-color-param)" "" \
+ "end"
+
+ with_test_prefix "test-color-param" {
+ gdb_test "guile (print (parameter-value test-color-param))" "= #<gdb:color green COLORSPACE_ANSI_8COLOR>" "color parameter value (green)"
+ gdb_test "show print test-color-param" "The state of the test-color-param is green." "show initial value"
+ gdb_test_no_output "set print test-color-param 255"
+ gdb_test "show print test-color-param" "The state of the test-color-param is 255." "show new value"
+ gdb_test "guile (print (parameter-value test-color-param))" "= #<gdb:color 255 COLORSPACE_XTERM_256COLOR>" "color parameter value (255)"
+ gdb_test "set print test-color-param 256" "integer 256 out of range.*" "set invalid color parameter"
+ }
+}
diff --git a/gdb/testsuite/gdb.python/py-color.exp b/gdb/testsuite/gdb.python/py-color.exp
new file mode 100644
index 00000000000..5764342d89d
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-color.exp
@@ -0,0 +1,101 @@
+# Copyright (C) 2010-2022 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# This file is part of the GDB testsuite.
+# It tests gdb.parameter and gdb.Parameter.
+
+load_lib gdb-python.exp
+
+# Start with a fresh gdb.
+clean_restart
+
+# Skip all tests if Python scripting is not enabled.
+if { [skip_python_tests] } { continue }
+
+gdb_test_no_output "python print_color_attrs = lambda c: print (c, c.colorspace, c.is_none, c.is_indexed, c.is_direct)" \
+ "print_color_attrs helper"
+
+gdb_test_no_output "python c = gdb.Color ()" \
+ "create color without params"
+gdb_test "python print_color_attrs (c)" "none 0 True False False" \
+ "print attrs of a color without params"
+
+gdb_test_no_output "python c = gdb.Color ('green')" \
+ "create color from basic name string"
+gdb_test "python print_color_attrs (c)" "green 1 False True False" \
+ "print attrs of a basic color name"
+gdb_test "python print (c.index)" "2" \
+ "print index of a basic color name"
+
+gdb_test_no_output "python c = gdb.Color (2)" \
+ "create color from basic index"
+gdb_test "python print_color_attrs (c)" "green 1 False True False" \
+ "print attrs of a basic color"
+gdb_test "python print (c.index)" "2" \
+ "print index of a basic color"
+
+gdb_test_no_output "python c = gdb.Color (14)" \
+ "create color from integer 14"
+gdb_test "python print_color_attrs (c)" "14 2 False True False" \
+ "print attrs of an color 14"
+gdb_test "python print (c.index)" "14" \
+ "print index of color 14"
+
+gdb_test_no_output "python c = gdb.Color (2, gdb.COLORSPACE_ANSI_8COLOR)" \
+ "create color from basic index and ansi colorspace"
+gdb_test "python print_color_attrs (c)" "green 1 False True False" \
+ "print attrs of a basic color with ansi colorspace"
+gdb_test "python print (c.index)" "2" \
+ "print index of a basic color with ansi colorspace"
+
+gdb_test_no_output "python c = gdb.Color (2, gdb.COLORSPACE_XTERM_256COLOR)" \
+ "create color from basic index and xterm256 colorspace"
+gdb_test "python print_color_attrs (c)" "2 3 False True False" \
+ "print attrs of a basic color with xterm256 colorspace"
+gdb_test "python print (c.index)" "2" \
+ "print index of a basic color with xterm256 colorspace"
+
+gdb_test_no_output "python c = gdb.Color ((171, 205, 239), gdb.COLORSPACE_RGB_24BIT)" \
+ "create color from rgb components"
+gdb_test "python print_color_attrs (c)" "#ABCDEF 4 False False True" \
+ "print attrs of an RGB color"
+gdb_test "python print (c.components)" "\\(171, 205, 239\\)" \
+ "print components of an RGB color"
+
+gdb_test_no_output "python c = gdb.Color ('none')" \
+ "create color from string none"
+gdb_test "python print_color_attrs (c)" "none 0 True False False" \
+ "print attrs of a color none"
+
+gdb_test_no_output "python c = gdb.Color ('254')" \
+ "create color from string 254"
+gdb_test "python print_color_attrs (c)" "254 3 False True False" \
+ "print attrs of an color 254"
+gdb_test "python print (c.index)" "254" \
+ "print index of color 254"
+
+gdb_test_no_output "python c_none = gdb.Color ('none')" \
+ "save default color"
+gdb_test_no_output "python c_red = gdb.Color ('red')" \
+ "save blue color"
+gdb_test_no_output "python c_green = gdb.Color ('green')" \
+ "save yellow color"
+gdb_test [concat "python print (c_red.escape_sequence (True) + " \
+ "c_green.escape_sequence (False) + 'red on green' + " \
+ "c_none.escape_sequence (False) + ' red on default' + " \
+ "c_none.escape_sequence (True))"] \
+ "\033\\\[31m\033\\\[42mred on green\033\\\[49m red on default\033\\\[39m" \
+ "escape sequences"
+
diff --git a/gdb/testsuite/gdb.python/py-parameter.exp b/gdb/testsuite/gdb.python/py-parameter.exp
index d6db6ac3bb1..9254380dbcb 100644
--- a/gdb/testsuite/gdb.python/py-parameter.exp
+++ b/gdb/testsuite/gdb.python/py-parameter.exp
@@ -177,6 +177,58 @@ proc_with_prefix test_enum_parameter { } {
"Undefined item: \"three\".*" "set invalid enum parameter"
}
+# Test an color parameter.
+proc_with_prefix test_color_parameter { } {
+ global env
+ save_vars { env(TERM) env(COLORTERM) } {
+ # This enables 256 colors support and disables colors approximation.
+ setenv TERM xterm-256color
+ setenv COLORTERM truecolor
+
+ clean_restart
+
+ gdb_test_multiline "color gdb parameter" \
+ "python" "" \
+ "class TestColorParam (gdb.Parameter):" "" \
+ " \"\"\"When set, test param does something useful. When disabled, does nothing.\"\"\"" "" \
+ " show_doc = \"Show the state of the color\"" ""\
+ " set_doc = \"Set the state of the color\"" "" \
+ " def get_show_string (self, pvalue):" ""\
+ " return \"The state of the color is \" + str(pvalue)" ""\
+ " def get_set_string (self):" ""\
+ " return \"The state of the color has been set to \" + str(self.value)" ""\
+ " def __init__ (self, name):" "" \
+ " super (TestColorParam, self).__init__ (name, gdb.COMMAND_DATA, gdb.PARAM_COLOR)" "" \
+ " self.value = gdb.Color(\"green\")" "" \
+ "test_color_param = TestColorParam ('print test-color-param')" ""\
+ "end"
+
+ gdb_test "python print (test_color_param.value)" "green" \
+ "test color parameter value is green"
+ gdb_test "show print test-color-param" \
+ "The state of the color is green.*" \
+ "show parameter is initial value"
+ gdb_test "set print test-color-param 255" \
+ "The state of the color has been set to 255" "set color to 255"
+ gdb_test "show print test-color-param" \
+ "The state of the color is 255.*" "show parameter is new value"
+ gdb_test "python print (test_color_param.value)" "255" \
+ "test color parameter value is 255"
+ gdb_test_no_output "python test_color_param.value = gdb.Color(254)" \
+ "assign test_color_param.value to 254"
+ gdb_test "python print (test_color_param.value)" "254" \
+ "test color parameter value is integer"
+ gdb_test_no_output "python test_color_param.value = gdb.Color('#FED210')" \
+ "assign test_color_param.value to #FED210"
+ gdb_test "python print (test_color_param.value.components)" "\\(254, 210, 16\\)" \
+ "test color parameter components from RGB hex tripple value"
+ gdb_test "set print test-color-param 256" \
+ "integer 256 out of range.*" "set invalid color parameter"
+ gdb_test "python test_color_param.value = gdb.Color(256)" \
+ ".*gdb.error: Palette color index 256 is out of range.*" "set invalid color value"
+ }
+}
+
# Test a file parameter.
proc_with_prefix test_file_parameter { } {
clean_restart
@@ -391,6 +443,7 @@ test_directories
test_data_directory
test_boolean_parameter
test_enum_parameter
+test_color_parameter
test_file_parameter
test_undocumented_parameter
test_really_undocumented_parameter
diff --git a/gdb/testsuite/lib/gdb-utils.exp b/gdb/testsuite/lib/gdb-utils.exp
index ffdfb75557c..e7bfbfbd29c 100644
--- a/gdb/testsuite/lib/gdb-utils.exp
+++ b/gdb/testsuite/lib/gdb-utils.exp
@@ -59,16 +59,20 @@ proc string_list_to_regexp { args } {
# "function", "variable", or "address".
proc style {str style} {
+ set fg 39
+ set bg 49
+ set intensity 22
+ set reverse 27
switch -exact -- $style {
- title { set style 1 }
- file { set style 32 }
- function { set style 33 }
- highlight { set style 31 }
- variable { set style 36 }
- address { set style 34 }
- metadata { set style 2 }
- version { set style "35;1" }
+ title { set intensity 1 }
+ file { set fg 32 }
+ function { set fg 33 }
+ highlight { set fg 31 }
+ variable { set fg 36 }
+ address { set fg 34 }
+ metadata { set intensity 2 }
+ version { set fg 35; set intensity 1 }
none { return $str }
}
- return "\033\\\[${style}m${str}\033\\\[m"
+ return "\033\\\[${fg};${bg};${intensity};${reverse}m${str}\033\\\[m"
}
diff --git a/gdb/top.c b/gdb/top.c
index 60835acd5e5..b9d153d3933 100644
--- a/gdb/top.c
+++ b/gdb/top.c
@@ -2242,6 +2242,17 @@ init_gdb_version_vars (void)
set_internalvar_integer (minor_version_var, vminor + (vrevision > 0));
}
+static void
+init_colorsupport_var (void)
+{
+ const std::vector<color_space>& cs = colorsupport ();
+ std::string s;
+ for (color_space c : cs)
+ s.append (s.empty () ? "" : ",").append (color_space_name (c));
+ struct internalvar *colorsupport_var = create_internalvar ("_colorsupport");
+ set_internalvar_string (colorsupport_var, s.c_str ());
+}
+
static void
init_main (void)
{
@@ -2457,6 +2468,8 @@ gdb_init ()
/* Create $_gdb_major and $_gdb_minor convenience variables. */
init_gdb_version_vars ();
+ /* Create $_colorsupport convenience variable. */
+ init_colorsupport_var ();
}
void _initialize_top ();
diff --git a/gdb/ui-style.c b/gdb/ui-style.c
index f1a5b8c4101..f41f87ac67a 100644
--- a/gdb/ui-style.c
+++ b/gdb/ui-style.c
@@ -18,6 +18,7 @@
#include "defs.h"
#include "ui-style.h"
+#include "gdb_curses.h"
#include "gdbsupport/gdb_regex.h"
/* A regular expression that is used for matching ANSI terminal escape
@@ -47,91 +48,229 @@ static const char ansi_regex_text[] =
static regex_t ansi_regex;
-/* This maps bright colors to RGB triples. The index is the bright
- color index, starting with bright black. The values come from
- xterm. */
-
-static const uint8_t bright_colors[][3] = {
- { 127, 127, 127 }, /* Black. */
- { 255, 0, 0 }, /* Red. */
- { 0, 255, 0 }, /* Green. */
- { 255, 255, 0 }, /* Yellow. */
- { 92, 92, 255 }, /* Blue. */
- { 255, 0, 255 }, /* Magenta. */
- { 0, 255, 255 }, /* Cyan. */
- { 255, 255, 255 } /* White. */
+/* This maps 8-color palette to RGB triples. The values come from
+ plain linux terminal. */
+
+static const uint8_t palette_8colors[][3] = {
+ { 1, 1, 1 }, /* Black. */
+ { 222, 56, 43 }, /* Red. */
+ { 57, 181, 74 }, /* Green. */
+ { 255, 199, 6 }, /* Yellow. */
+ { 0, 111, 184 }, /* Blue. */
+ { 118, 38, 113 }, /* Magenta. */
+ { 44, 181, 233 }, /* Cyan. */
+ { 204, 204, 204 }, /* White. */
};
+/* This maps 16-color palette to RGB triples. The values come from xterm. */
+
+static const uint8_t palette_16colors[][3] = {
+ { 0, 0, 0 }, /* Black. */
+ { 205, 0, 0 }, /* Red. */
+ { 0, 205, 0 }, /* Green. */
+ { 205, 205, 0 }, /* Yellow. */
+ { 0, 0, 238 }, /* Blue. */
+ { 205, 0, 205 }, /* Magenta. */
+ { 0, 205, 205 }, /* Cyan. */
+ { 229, 229, 229 }, /* White. */
+ { 127, 127, 127 }, /* Bright Black. */
+ { 255, 0, 0 }, /* Bright Red. */
+ { 0, 255, 0 }, /* Bright Green. */
+ { 255, 255, 0 }, /* Bright Yellow. */
+ { 92, 92, 255 }, /* Bright Blue. */
+ { 255, 0, 255 }, /* Bright Magenta. */
+ { 0, 255, 255 }, /* Bright Cyan. */
+ { 255, 255, 255 } /* Bright White. */
+};
+
+/* See ui-style.h. */
+/* Must correspond to ui_file_style::basic_color. */
+const std::vector<const char *> ui_file_style::basic_color_enums = {
+ "none",
+ "black",
+ "red",
+ "green",
+ "yellow",
+ "blue",
+ "magenta",
+ "cyan",
+ "white",
+ nullptr
+};
+
+/* Returns text representation of a basic COLOR. */
+
+static const char *
+basic_color_name (int color)
+{
+ int pos = color - ui_file_style::NONE;
+ if (0 <= pos && pos < ui_file_style::basic_color_enums.size ())
+ if (const char *s = ui_file_style::basic_color_enums[pos])
+ return s;
+ error (_("Basic color %d has no name."), color);
+}
+
/* See ui-style.h. */
bool
ui_file_style::color::append_ansi (bool is_fg, std::string *str) const
{
- if (m_simple)
- {
- if (m_value >= BLACK && m_value <= WHITE)
- str->append (std::to_string (m_value + (is_fg ? 30 : 40)));
- else if (m_value > WHITE && m_value <= WHITE + 8)
- str->append (std::to_string (m_value - WHITE + (is_fg ? 90 : 100)));
- else if (m_value != -1)
- {
- str->append (is_fg ? "38;5;" : "48;5;");
- str->append (std::to_string (m_value));
- }
- else
- return false;
- }
- else
+ if (m_color_space == color_space::MONOCHROME)
+ str->append (is_fg ? "39" : "49");
+ else if (is_basic ())
+ str->append (std::to_string (m_value + (is_fg ? 30 : 40)));
+ else if (m_color_space == color_space::AIXTERM_16COLOR)
+ str->append (std::to_string (m_value - WHITE - 1 + (is_fg ? 90 : 100)));
+ else if (m_color_space == color_space::XTERM_256COLOR)
+ str->append (is_fg ? "38;5;" : "48;5;").append (std::to_string (m_value));
+ else if (m_color_space == color_space::RGB_24BIT)
{
str->append (is_fg ? "38;2;" : "48;2;");
str->append (std::to_string (m_red)
+ ";" + std::to_string (m_green)
+ ";" + std::to_string (m_blue));
}
+ else
+ return false;
+
return true;
}
/* See ui-style.h. */
+std::string
+ui_file_style::color::to_ansi (bool is_fg) const
+{
+ std::string s = "\033[";
+ if (!append_ansi (is_fg, &s))
+ return {};
+ s.push_back ('m');
+ return s;
+}
-void
-ui_file_style::color::get_rgb (uint8_t *rgb) const
+/* See ui-style.h. */
+
+std::string
+ui_file_style::color::to_string () const
{
- if (m_simple)
+ if (m_color_space == color_space::RGB_24BIT)
{
- /* Can't call this for a basic color or NONE -- those will end
- up in the assert below. */
- if (m_value >= 8 && m_value <= 15)
- memcpy (rgb, bright_colors[m_value - 8], 3 * sizeof (uint8_t));
- else if (m_value >= 16 && m_value <= 231)
- {
- int value = m_value;
- value -= 16;
- /* This obscure formula seems to be what terminals actually
- do. */
- int component = value / 36;
- rgb[0] = component == 0 ? 0 : (55 + component * 40);
- value %= 36;
- component = value / 6;
- rgb[1] = component == 0 ? 0 : (55 + component * 40);
- value %= 6;
- rgb[2] = value == 0 ? 0 : (55 + value * 40);
- }
- else if (m_value >= 232)
- {
- uint8_t v = (m_value - 232) * 10 + 8;
- rgb[0] = v;
- rgb[1] = v;
- rgb[2] = v;
- }
- else
- gdb_assert_not_reached ("get_rgb called on invalid color");
+ char s[64];
+ snprintf (s, sizeof s, "#%02X%02X%02X", m_red, m_green, m_blue);
+ return s;
}
+ else if (is_none () || is_basic ())
+ return basic_color_name (m_value);
else
+ return std::to_string (get_value ());
+}
+
+/* See ui-style.h. */
+
+void
+ui_file_style::color::get_rgb (uint8_t *rgb) const
+{
+ if (m_color_space == color_space::RGB_24BIT)
{
rgb[0] = m_red;
rgb[1] = m_green;
rgb[2] = m_blue;
}
+ else if (m_color_space == color_space::ANSI_8COLOR
+ && 0 <= m_value && m_value <= 7)
+ memcpy (rgb, palette_8colors[m_value], 3 * sizeof (uint8_t));
+ else if (m_color_space == color_space::AIXTERM_16COLOR
+ && 0 <= m_value && m_value <= 15)
+ memcpy (rgb, palette_16colors[m_value], 3 * sizeof (uint8_t));
+ else if (m_color_space != color_space::XTERM_256COLOR)
+ gdb_assert_not_reached ("get_rgb called on invalid color");
+ else if (0 <= m_value && m_value <= 15)
+ memcpy (rgb, palette_16colors[m_value], 3 * sizeof (uint8_t));
+ else if (m_value >= 16 && m_value <= 231)
+ {
+ int value = m_value;
+ value -= 16;
+ /* This obscure formula seems to be what terminals actually
+ do. */
+ int component = value / 36;
+ rgb[0] = component == 0 ? 0 : (55 + component * 40);
+ value %= 36;
+ component = value / 6;
+ rgb[1] = component == 0 ? 0 : (55 + component * 40);
+ value %= 6;
+ rgb[2] = value == 0 ? 0 : (55 + value * 40);
+ }
+ else if (232 <= m_value && m_value <= 255)
+ {
+ uint8_t v = (m_value - 232) * 10 + 8;
+ rgb[0] = v;
+ rgb[1] = v;
+ rgb[2] = v;
+ }
+ else
+ gdb_assert_not_reached ("get_rgb called on invalid color");
+}
+
+/* See ui-style.h. */
+
+ui_file_style::color
+ui_file_style::color::approximate (const std::vector<color_space> &spaces) const
+{
+ if (spaces.empty () || is_none ())
+ return NONE;
+
+ color_space target_space = color_space::MONOCHROME;
+ for (color_space sp : spaces)
+ if (sp == m_color_space)
+ return *this;
+ else if (sp > target_space)
+ target_space = sp;
+
+ if (target_space == color_space::RGB_24BIT)
+ {
+ uint8_t rgb[3];
+ get_rgb (rgb);
+ return color (rgb[0], rgb[1], rgb[2]);
+ }
+
+ int target_size = 0;
+ switch (target_space)
+ {
+ case color_space::ANSI_8COLOR:
+ target_size = 8;
+ break;
+ case color_space::AIXTERM_16COLOR:
+ target_size = 16;
+ break;
+ case color_space::XTERM_256COLOR:
+ target_size = 256;
+ break;
+ }
+
+ if (is_simple() && m_value < target_size)
+ return color (target_space, m_value);
+
+ color result = NONE;
+ int best_distance = std::numeric_limits<int>::max ();
+ uint8_t rgb[3];
+ get_rgb (rgb);
+
+ for (int i = 0; i < target_size; ++i)
+ {
+ uint8_t c_rgb[3];
+ color c (target_space, i);
+ c.get_rgb (c_rgb);
+ int d_red = std::abs (rgb[0] - c_rgb[0]);
+ int d_green = std::abs (rgb[1] - c_rgb[1]);
+ int d_blue = std::abs (rgb[2] - c_rgb[2]);
+ int dist = d_red * d_red + d_green * d_green + d_blue * d_blue;
+ if (dist < best_distance)
+ {
+ best_distance = dist;
+ result = c;
+ }
+ }
+
+ return result;
}
/* See ui-style.h. */
@@ -140,26 +279,23 @@ std::string
ui_file_style::to_ansi () const
{
std::string result ("\033[");
- bool need_semi = m_foreground.append_ansi (true, &result);
- if (!m_background.is_none ())
+ if (!is_default ())
{
+ bool need_semi = m_foreground.append_ansi (true, &result);
if (need_semi)
result.push_back (';');
- m_background.append_ansi (false, &result);
- need_semi = true;
- }
- if (m_intensity != NORMAL)
- {
+ need_semi = m_background.append_ansi (false, &result);
if (need_semi)
result.push_back (';');
- result.append (std::to_string (m_intensity));
- need_semi = true;
- }
- if (m_reverse)
- {
- if (need_semi)
- result.push_back (';');
- result.push_back ('7');
+ if (m_intensity == NORMAL)
+ result.append ("22");
+ else
+ result.append (std::to_string (m_intensity));
+ result.push_back (';');
+ if (m_reverse)
+ result.push_back ('7');
+ else
+ result.append ("27");
}
result.push_back ('m');
return result;
@@ -316,9 +452,11 @@ ui_file_style::parse (const char *buf, size_t *n_read)
case 35:
case 36:
case 37:
+ m_foreground = color (value - 30);
+ break;
/* Note: not 38. */
case 39:
- m_foreground = color (value - 30);
+ m_foreground = NONE;
break;
case 40:
@@ -329,9 +467,11 @@ ui_file_style::parse (const char *buf, size_t *n_read)
case 45:
case 46:
case 47:
+ m_background = color (value - 40);
+ break;
/* Note: not 48. */
case 49:
- m_background = color (value - 40);
+ m_background = NONE;
break;
case 90:
@@ -413,3 +553,44 @@ _initialize_ui_style ()
error. */
gdb_assert (code == 0);
}
+
+/* See ui-style.h. */
+
+const std::vector<color_space> &
+colorsupport ()
+{
+ static const std::vector<color_space> value = []
+ {
+ std::vector<color_space> result = {color_space::MONOCHROME};
+
+ int colors = tgetnum ((char *)"Co");
+ if (colors >= 8)
+ result.push_back (color_space::ANSI_8COLOR);
+ if (colors >= 16)
+ result.push_back (color_space::AIXTERM_16COLOR);
+ if (colors >= 256)
+ result.push_back (color_space::XTERM_256COLOR);
+
+ const char *colorterm = getenv ("COLORTERM");
+ if (colorterm && (!strcmp (colorterm, "truecolor")
+ || !strcmp (colorterm, "24bit")))
+ result.push_back (color_space::RGB_24BIT);
+
+ return result;
+ } ();
+ return value;
+}
+
+const char *
+color_space_name (color_space c)
+{
+ switch (c)
+ {
+ case color_space::MONOCHROME: return "monochrome";
+ case color_space::ANSI_8COLOR: return "ansi_8color";
+ case color_space::AIXTERM_16COLOR: return "aixterm_16color";
+ case color_space::XTERM_256COLOR: return "xterm_256color";
+ case color_space::RGB_24BIT: return "rgb_24bit";
+ }
+ error (_("Color space %d has no name."), static_cast<int> (c));
+}
diff --git a/gdb/ui-style.h b/gdb/ui-style.h
index fe1b2af611d..a481baf8ad5 100644
--- a/gdb/ui-style.h
+++ b/gdb/ui-style.h
@@ -19,6 +19,26 @@
#ifndef UI_STYLE_H
#define UI_STYLE_H
+/* One of the color spaces that usually supported by terminals. */
+enum class color_space
+{
+ MONOCHROME, // one default terminal color
+ ANSI_8COLOR, // foreground colors \e[30m ... \e[37m,
+ // background colors \e[40m ... \e[47m
+ AIXTERM_16COLOR, // foreground colors \e[30m ... \e[37m, \e[90m ... \e[97m
+ // background colors \e[40m ... \e[47m, \e[100m ... \e107m
+ XTERM_256COLOR, // foreground colors \e[38;5;0m ... \e[38;5;255m
+ // background colors \e[48;5;0m ... \e[48;5;255m
+ RGB_24BIT // foreground colors \e[38;2;0;0;0m ... \e[38;2;255;255;255m
+ // background colors \e[48;2;0;0;0m ... \e[48;2;255;255;255m
+};
+
+/* Color spaces supported by terminal. */
+extern const std::vector<color_space> & colorsupport ();
+
+/* Textual representation of C. */
+extern const char * color_space_name (color_space c);
+
/* Styles that can be applied to a ui_file. */
struct ui_file_style
{
@@ -43,20 +63,61 @@ struct ui_file_style
public:
color (basic_color c)
- : m_simple (true),
+ : m_color_space (c == NONE ? color_space::MONOCHROME
+ : color_space::ANSI_8COLOR),
m_value (c)
{
}
color (int c)
- : m_simple (true),
+ : m_value (c)
+ {
+ if (c < -1 || c > 255)
+ error (_("Palette color index %d is out of range."), c);
+ if (c == -1)
+ m_color_space = color_space::MONOCHROME;
+ else if (c <= 7)
+ m_color_space = color_space::ANSI_8COLOR;
+ else if (c <= 15)
+ m_color_space = color_space::AIXTERM_16COLOR;
+ else
+ m_color_space = color_space::XTERM_256COLOR;
+ }
+
+ color (color_space cs, int c)
+ : m_color_space (cs),
m_value (c)
{
- gdb_assert (c >= -1 && c <= 255);
+ if (c < -1 || c > 255)
+ error (_("Palette color index %d is out of range."), c);
+
+ std::pair<int, int> range;
+ switch (cs)
+ {
+ case color_space::MONOCHROME:
+ range = {-1, -1};
+ break;
+ case color_space::ANSI_8COLOR:
+ range = {0, 7};
+ break;
+ case color_space::AIXTERM_16COLOR:
+ range = {0, 15};
+ break;
+ case color_space::XTERM_256COLOR:
+ range = {0, 255};
+ break;
+ default:
+ error (_("Color space %d is incompatible with indexed colors."),
+ static_cast<int> (cs));
+ }
+
+ if (c < range.first || c > range.second)
+ error (_("Color %d is out of range [%d, %d] of color space %d."),
+ c, range.first, range.second, static_cast<int> (cs));
}
color (uint8_t r, uint8_t g, uint8_t b)
- : m_simple (false),
+ : m_color_space (color_space::RGB_24BIT),
m_red (r),
m_green (g),
m_blue (b)
@@ -65,19 +126,24 @@ struct ui_file_style
bool operator== (const color &other) const
{
- if (m_simple != other.m_simple)
+ if (m_color_space != other.m_color_space)
return false;
- if (m_simple)
+ if (is_simple ())
return m_value == other.m_value;
return (m_red == other.m_red && m_green == other.m_green
&& m_blue == other.m_blue);
}
+ bool operator!= (const color &other) const
+ {
+ return ! (*this == other);
+ }
+
bool operator< (const color &other) const
{
- if (m_simple != other.m_simple)
- return m_simple < other.m_simple;
- if (m_simple)
+ if (m_color_space != other.m_color_space)
+ return m_color_space < other.m_color_space;
+ if (is_simple ())
return m_value < other.m_value;
if (m_red < other.m_red)
return true;
@@ -91,23 +157,54 @@ struct ui_file_style
return false;
}
+ color_space colorspace () const
+ {
+ return m_color_space;
+ }
+
/* Return true if this is the "NONE" color, false otherwise. */
bool is_none () const
{
- return m_simple && m_value == NONE;
+ return m_color_space == color_space::MONOCHROME && m_value == NONE;
}
/* Return true if this is one of the basic colors, false
otherwise. */
bool is_basic () const
{
- return m_simple && m_value >= BLACK && m_value <= WHITE;
+ if (m_color_space == color_space::ANSI_8COLOR
+ || m_color_space == color_space::AIXTERM_16COLOR)
+ return BLACK <= m_value && m_value <= WHITE;
+ else
+ return false;
+ }
+
+ /* Return true if this is one of the colors, stored as int, false
+ otherwise. */
+ bool is_simple () const
+ {
+ return m_color_space != color_space::RGB_24BIT;
+ }
+
+ /* Return true if this is one of the indexed colors, false
+ otherwise. */
+ bool is_indexed () const
+ {
+ return m_color_space != color_space::RGB_24BIT
+ && m_color_space != color_space::MONOCHROME;
}
- /* Return the value of a basic color. */
+ /* Return true if this is one of the direct colors (RGB, CMY, CMYK), false
+ otherwise. */
+ bool is_direct () const
+ {
+ return m_color_space == color_space::RGB_24BIT;
+ }
+
+ /* Return the value of a simple color. */
int get_value () const
{
- gdb_assert (is_basic ());
+ gdb_assert (is_simple ());
return m_value;
}
@@ -119,13 +216,23 @@ struct ui_file_style
/* Append the ANSI terminal escape sequence for this color to STR.
IS_FG indicates whether this is a foreground or background
color. Returns true if any characters were written; returns
- false otherwise (which can only happen for the "NONE"
- color). */
+ false otherwise. */
bool append_ansi (bool is_fg, std::string *str) const;
+ /* Return the ANSI escape sequence for this color.
+ IS_FG indicates whether this is a foreground or background color. */
+ std::string to_ansi (bool is_fg) const;
+
+ /* Returns text representation of this object.
+ It is "none", name of a basic color, number or a #RRGGBB hex triplet. */
+ std::string to_string () const;
+
+ /* Approximates THIS color by closest one from SPACES. */
+ color approximate (const std::vector<color_space> &spaces) const;
+
private:
- bool m_simple;
+ color_space m_color_space;
union
{
int m_value;
@@ -235,6 +342,9 @@ struct ui_file_style
return this;
}
+ /* nullptr-terminated list of names corresponding to enum basic_color. */
+ static const std::vector<const char *> basic_color_enums;
+
private:
color m_foreground = NONE;
diff --git a/gdb/unittests/style-selftests.c b/gdb/unittests/style-selftests.c
index 8ad803594c3..34b0d80adc6 100644
--- a/gdb/unittests/style-selftests.c
+++ b/gdb/unittests/style-selftests.c
@@ -59,7 +59,7 @@ run_tests ()
SELF_CHECK (style.get_background ().is_none ());
SELF_CHECK (style.get_intensity () == ui_file_style::NORMAL);
SELF_CHECK (style.is_reverse ());
- SELF_CHECK (style.to_ansi () == "\033[7m");
+ SELF_CHECK (style.to_ansi () == "\033[39;49;22;7m");
style = ui_file_style ();
SELF_CHECK (style.parse ("\033[32;1m", &n_read));
@@ -69,7 +69,7 @@ run_tests ()
SELF_CHECK (style.get_background ().is_none ());
SELF_CHECK (style.get_intensity () == ui_file_style::BOLD);
SELF_CHECK (!style.is_reverse ());
- SELF_CHECK (style.to_ansi () == "\033[32;1m");
+ SELF_CHECK (style.to_ansi () == "\033[32;49;1;27m");
style = ui_file_style ();
SELF_CHECK (style.parse ("\033[38;5;112;48;5;249m", &n_read));
@@ -82,7 +82,7 @@ run_tests ()
CHECK_RGB (0xb2, 0xb2, 0xb2);
SELF_CHECK (style.get_intensity () == ui_file_style::NORMAL);
SELF_CHECK (!style.is_reverse ());
- SELF_CHECK (style.to_ansi () == "\033[38;5;112;48;5;249m");
+ SELF_CHECK (style.to_ansi () == "\033[38;5;112;48;5;249;22;27m");
style = ui_file_style ();
SELF_CHECK (style.parse ("\033[38;2;83;84;85;48;2;0;1;254;2;7m", &n_read));
--
2.34.1
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v4] Add an option with a color type.
2022-10-16 11:40 [PATCH v4] Add an option with a color type Andrei Pikas
@ 2022-10-16 13:45 ` Eli Zaretskii via Gdb-patches
2022-10-16 16:03 ` [PATCH v5] " Andrei Pikas
0 siblings, 1 reply; 33+ messages in thread
From: Eli Zaretskii via Gdb-patches @ 2022-10-16 13:45 UTC (permalink / raw)
To: Andrei Pikas; +Cc: gdb, gdb-patches
> From: Andrei Pikas <gdb@mail.api.win>
> Date: Sun, 16 Oct 2022 14:40:36 +0300
> Cc: Andrei Pikas <gdb@mail.api.win>
>
> 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.
Thanks.
> diff --git a/gdb/NEWS b/gdb/NEWS
> index d2efe2a0a58..00173c09227 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
The NEWS part is OK.
> +@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
Please follow "E.g." either by a comma or by @:, so that TeX wouldn't
mistakenly decide that the sentence ends there, and typeset it
accordingly.
> @table @code
> @item set style filename background @var{color}
> -Set the background to @var{color}. Valid colors are @samp{none}
> -(meaning the terminal's default color), @samp{black}, @samp{red},
> -@samp{green}, @samp{yellow}, @samp{blue}, @samp{magenta}, @samp{cyan},
> -and@samp{white}.
> +Set the background to @var{color}. @var{color} can be @samp{none}
> +(meaning the terminal's default color), a name of one of the eight standard
> +colors of ISO/IEC 6429, index from 0 to 255 into terminal's color
> +palette or a hexadecimal RGB triplet in @samp{#RRGGBB} format for
> +24-bit TrueColor.
> +
> +Valid color names are @samp{black}, @samp{red}, @samp{green},
> +@samp{yellow}, @samp{blue}, @samp{magenta}, @samp{cyan}, and
> +@samp{white}.
> +
> +Integers 0 to 7 are the synonyms for the standard colors. Integers 8-15 are
> +used for the so-called bright colors from the aixterm extended 16-color
> +palette. Integers 16-255 are the indexes into xterm extended 256-color palette
> +(usually 6x6x6 cube plus gray ramp). In general, 256-color palette is terminal
> +dependent and sometimes can be changed with OSC 4 sequences, e.g.
> +"\033]4;1;rgb:00/FF/00\033\\".
> +
> +It is the responsibility of the user to verify that the terminal supports
> +the specified colors.
>
> @item set style filename foreground @var{color}
> -Set the foreground to @var{color}. Valid colors are @samp{none}
> -(meaning the terminal's default color), @samp{black}, @samp{red},
> -@samp{green}, @samp{yellow}, @samp{blue}, @samp{magenta}, @samp{cyan},
> -and@samp{white}.
> +Set the foreground to @var{color}. @var{color} can be @samp{none}
> +(meaning the terminal's default color), a name of one of the eight standard
> +colors of ISO/IEC 6429, index from 0 to 255 into terminal's color
> +palette or a hexadecimal RGB triplet in @samp{#RRGGBB} format for
> +24-bit TrueColor.
> +
> +Valid color names are @samp{black}, @samp{red}, @samp{green},
> +@samp{yellow}, @samp{blue}, @samp{magenta}, @samp{cyan}, and
> +@samp{white}.
> +
> +Integers 0 to 7 are the synonyms for the standard colors. Integers 8-15 are
> +used for the so-called bright colors from the aixterm extended 16-color
> +palette. Integers 16-255 are the indexes into xterm extended 256-color palette
> +(usually 6x6x6 cube plus gray ramp). In general, 256-color palette is terminal
> +dependent and sometimes can be changed with OSC 4 sequences, e.g.
> +"\033]4;1;rgb:00/FF/00\033\\".
> +
> +It is the responsibility of the user to verify that the terminal supports
> +the specified colors.
This includes the same text twice very close to one another. I
suggest to leave only the first part, and for "set style filename
foreground" just say that the COLOR can be given in the same ways as
for the background.
> +@deffn {Scheme Procedure} color-escape-sequence color is_foreground
> +Return string to change terminal's color to this.
> +
> +@var{is_foreground} if this color should be applied to foreground or background
The last sentence reads strangely and seems incomplete: did you
perhaps forgot some words there?
> +@defun Color.escape_sequence (@var{self}, @var{is_foreground})
> +Returns string to change terminal's color to this.
> +
> +@var{is_foreground} if this color should be applied to foreground or background
> +@end defun
Same here.
> +@findex COLORSPACE_XTERM_256COLOR
> +@findex gdb.COLORSPACE_XTERM_256COLOR
> +@item gdb.COLORSPACE_XTERM_256COLOR
> +Palette with 256 colors. First 16 are from COLORSPACE_AIXTERM_16COLOR. Next
> +216 colors are 6x6x6 RGB cube. And last 24 colors forms grayscale ramp.
^^^^^
"form", in plural.
Thanks.
^ permalink raw reply [flat|nested] 33+ messages in thread
* [PATCH v5] Add an option with a color type.
2022-10-16 13:45 ` Eli Zaretskii via Gdb-patches
@ 2022-10-16 16:03 ` Andrei Pikas
2022-10-16 16:22 ` Eli Zaretskii via Gdb-patches
0 siblings, 1 reply; 33+ messages in thread
From: Andrei Pikas @ 2022-10-16 16:03 UTC (permalink / raw)
To: gdb-patches; +Cc: Andrei Pikas
Colors can be specified as "none" for terminal's default color, as a name of
one of the eight standard colors of ISO/IEC 6429 "black", "red", "green", etc.,
as an RGB hexadecimal tripplet #RRGGBB for 24-bit TrueColor, or as an
integer from 0 to 255. Integers 0 to 7 are the synonyms for the standard
colors. Integers 8-15 are used for the so-called bright colors from the
aixterm extended 16-color palette. Integers 16-255 are the indexes into xterm
extended 256-color palette (usually 6x6x6 cube plus gray ramp). In
general, 256-color palette is terminal dependent and sometimes can be
changed with OSC 4 sequences, e.g. "\033]4;1;rgb:00/FF/00\033\\".
It is the responsibility of the user to verify that the terminal supports
the specified colors.
PATCH v5 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
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..88dda2fe7f8 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.
+
+@var{is_foreground} 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..47450c2ae1e 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.
+
+@var{is_foreground} 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));
--
2.34.1
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v5] Add an option with a color type.
2022-10-16 16:03 ` [PATCH v5] " Andrei Pikas
@ 2022-10-16 16:22 ` Eli Zaretskii via Gdb-patches
2022-10-16 16:28 ` [PATCH v6] " Andrei Pikas
0 siblings, 1 reply; 33+ messages in thread
From: Eli Zaretskii via Gdb-patches @ 2022-10-16 16:22 UTC (permalink / raw)
To: Andrei Pikas; +Cc: gdb, gdb-patches
> From: Andrei Pikas <gdb@mail.api.win>
> Cc: eliz@gnu.org,
> Andrei Pikas <gdb@mail.api.win>
> Date: Sun, 16 Oct 2022 19:03:54 +0300
>
> +@deffn {Scheme Procedure} color-escape-sequence color is_foreground
> +Return string to change terminal's color to this.
> +
> +@var{is_foreground} If @var{is_foreground} is @code{#t}, then the returned
^^^^^^^^^^^^^^^^^^^^
I believe the underlined part should be deleted.
> +sequence will change foreground color. Otherwise, the returned sequence will
^^
Two spaces between sentences, please.
> +@defun Color.escape_sequence (@var{self}, @var{is_foreground})
> +Returns string to change terminal's color to this.
> +
> +@var{is_foreground} 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
Same here.
The documentation parts are okay when these nits are fixed.
Thanks.
^ permalink raw reply [flat|nested] 33+ messages in thread
* [PATCH v6] Add an option with a color type.
2022-10-16 16:22 ` Eli Zaretskii via Gdb-patches
@ 2022-10-16 16:28 ` Andrei Pikas
2022-10-16 16:45 ` Eli Zaretskii via Gdb-patches
` (2 more replies)
0 siblings, 3 replies; 33+ messages in thread
From: Andrei Pikas @ 2022-10-16 16:28 UTC (permalink / raw)
To: gdb-patches; +Cc: Andrei Pikas
Colors can be specified as "none" for terminal's default color, as a name of
one of the eight standard colors of ISO/IEC 6429 "black", "red", "green", etc.,
as an RGB hexadecimal tripplet #RRGGBB for 24-bit TrueColor, or as an
integer from 0 to 255. Integers 0 to 7 are the synonyms for the standard
colors. Integers 8-15 are used for the so-called bright colors from the
aixterm extended 16-color palette. Integers 16-255 are the indexes into xterm
extended 256-color palette (usually 6x6x6 cube plus gray ramp). In
general, 256-color palette is terminal dependent and sometimes can be
changed with OSC 4 sequences, e.g. "\033]4;1;rgb:00/FF/00\033\\".
It is the responsibility of the user to verify that the terminal supports
the specified colors.
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
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));
--
2.34.1
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v6] Add an option with a color type.
2022-10-16 16:28 ` [PATCH v6] " Andrei Pikas
@ 2022-10-16 16:45 ` Eli Zaretskii via Gdb-patches
2024-04-19 19:33 ` Tom Tromey
2024-09-13 20:16 ` Tom Tromey
2 siblings, 0 replies; 33+ messages in thread
From: Eli Zaretskii via Gdb-patches @ 2022-10-16 16:45 UTC (permalink / raw)
To: Andrei Pikas; +Cc: gdb, gdb-patches
> 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.
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v6] Add an option with a color type.
2022-10-16 16:28 ` [PATCH v6] " Andrei Pikas
2022-10-16 16:45 ` Eli Zaretskii via Gdb-patches
@ 2024-04-19 19:33 ` Tom Tromey
2024-04-19 19:52 ` Andrei Pikas
2024-09-13 20:16 ` Tom Tromey
2 siblings, 1 reply; 33+ messages in thread
From: Tom Tromey @ 2024-04-19 19:33 UTC (permalink / raw)
To: Andrei Pikas; +Cc: gdb-patches
>>>>> "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
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v6] Add an option with a color type.
2024-04-19 19:33 ` Tom Tromey
@ 2024-04-19 19:52 ` Andrei Pikas
2024-04-19 20:19 ` Tom Tromey
0 siblings, 1 reply; 33+ messages in thread
From: Andrei Pikas @ 2024-04-19 19:52 UTC (permalink / raw)
To: Tom Tromey; +Cc: gdb-patches
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
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v6] Add an option with a color type.
2024-04-19 19:52 ` Andrei Pikas
@ 2024-04-19 20:19 ` Tom Tromey
2024-04-20 18:24 ` Tom Tromey
0 siblings, 1 reply; 33+ messages in thread
From: Tom Tromey @ 2024-04-19 20:19 UTC (permalink / raw)
To: Andrei Pikas; +Cc: Tom Tromey, gdb-patches
>>>>> 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
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v6] Add an option with a color type.
2024-04-19 20:19 ` Tom Tromey
@ 2024-04-20 18:24 ` Tom Tromey
2024-04-20 18:32 ` Andrei Pikas
2024-05-11 15:17 ` Andrei Pikas
0 siblings, 2 replies; 33+ messages in thread
From: Tom Tromey @ 2024-04-20 18:24 UTC (permalink / raw)
To: Tom Tromey; +Cc: Andrei Pikas, gdb-patches
>>>>> "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
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v6] Add an option with a color type.
2024-04-20 18:24 ` Tom Tromey
@ 2024-04-20 18:32 ` Andrei Pikas
2024-05-11 15:17 ` Andrei Pikas
1 sibling, 0 replies; 33+ messages in thread
From: Andrei Pikas @ 2024-04-20 18:32 UTC (permalink / raw)
To: Tom Tromey; +Cc: gdb-patches
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
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v6] Add an option with a color type.
2024-04-20 18:24 ` Tom Tromey
2024-04-20 18:32 ` Andrei Pikas
@ 2024-05-11 15:17 ` Andrei Pikas
2024-05-13 19:02 ` Tom Tromey
1 sibling, 1 reply; 33+ messages in thread
From: Andrei Pikas @ 2024-05-11 15:17 UTC (permalink / raw)
To: Tom Tromey; +Cc: gdb-patches
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
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v6] Add an option with a color type.
2024-05-11 15:17 ` Andrei Pikas
@ 2024-05-13 19:02 ` Tom Tromey
2024-05-21 9:00 ` Andrei Pikas
0 siblings, 1 reply; 33+ messages in thread
From: Tom Tromey @ 2024-05-13 19:02 UTC (permalink / raw)
To: Andrei Pikas; +Cc: Tom Tromey, gdb-patches
> 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
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v6] Add an option with a color type.
2024-05-13 19:02 ` Tom Tromey
@ 2024-05-21 9:00 ` Andrei Pikas
0 siblings, 0 replies; 33+ messages in thread
From: Andrei Pikas @ 2024-05-21 9:00 UTC (permalink / raw)
To: Tom Tromey; +Cc: gdb-patches
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
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v6] Add an option with a color type.
2022-10-16 16:28 ` [PATCH v6] " Andrei Pikas
2022-10-16 16:45 ` Eli Zaretskii via Gdb-patches
2024-04-19 19:33 ` Tom Tromey
@ 2024-09-13 20:16 ` Tom Tromey
2024-09-14 18:06 ` Andrei Pikas
2024-09-14 19:04 ` [PATCH v7] " Andrei Pikas
2 siblings, 2 replies; 33+ messages in thread
From: Tom Tromey @ 2024-09-13 20:16 UTC (permalink / raw)
To: Andrei Pikas; +Cc: gdb-patches
>>>>> 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
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v6] Add an option with a color type.
2024-09-13 20:16 ` Tom Tromey
@ 2024-09-14 18:06 ` Andrei Pikas
2024-09-14 19:38 ` Tom Tromey
2024-09-14 19:04 ` [PATCH v7] " Andrei Pikas
1 sibling, 1 reply; 33+ messages in thread
From: Andrei Pikas @ 2024-09-14 18:06 UTC (permalink / raw)
To: Tom Tromey; +Cc: gdb-patches
[-- Attachment #1: Type: text/plain, Size: 488 bytes --]
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.
[-- Attachment #2: Type: text/html, Size: 960 bytes --]
^ permalink raw reply [flat|nested] 33+ messages in thread
* [PATCH v7] Add an option with a color type.
2024-09-13 20:16 ` Tom Tromey
2024-09-14 18:06 ` Andrei Pikas
@ 2024-09-14 19:04 ` Andrei Pikas
2024-09-15 5:37 ` Eli Zaretskii
2024-10-04 17:55 ` Tom Tromey
1 sibling, 2 replies; 33+ messages in thread
From: Andrei Pikas @ 2024-09-14 19:04 UTC (permalink / raw)
To: tom; +Cc: gdb-patches, Andrei Pikas
Colors can be specified as "none" for terminal's default color, as a name of
one of the eight standard colors of ISO/IEC 6429 "black", "red", "green", etc.,
as an RGB hexadecimal tripplet #RRGGBB for 24-bit TrueColor, or as an
integer from 0 to 255. Integers 0 to 7 are the synonyms for the standard
colors. Integers 8-15 are used for the so-called bright colors from the
aixterm extended 16-color palette. Integers 16-255 are the indexes into xterm
extended 256-color palette (usually 6x6x6 cube plus gray ramp). In
general, 256-color palette is terminal dependent and sometimes can be
changed with OSC 4 sequences, e.g. "\033]4;1;rgb:00/FF/00\033\\".
It is the responsibility of the user to verify that the terminal supports
the specified colors.
PATCH v5 changes: documentation fixed.
PATCH v6 changes: documentation fixed.
PATCH v7 changes: rebase onto master and fixes after review.
---
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 | 443 ++++++++++++++++++++++
gdb/guile/scm-param.c | 33 +-
gdb/python/py-color.c | 347 +++++++++++++++++
gdb/python/py-color.h | 35 ++
gdb/python/py-param.c | 38 +-
gdb/python/python.c | 7 +
gdb/testsuite/gdb.base/style.exp | 197 ++++++++++
gdb/testsuite/gdb.guile/scm-color.exp | 110 ++++++
gdb/testsuite/gdb.guile/scm-parameter.exp | 47 +++
gdb/testsuite/gdb.python/py-color.exp | 100 +++++
gdb/testsuite/gdb.python/py-parameter.exp | 53 +++
gdb/testsuite/lib/gdb-utils.exp | 22 +-
gdb/testsuite/lib/gdb.exp | 3 +-
gdb/top.c | 14 +
gdb/ui-style.c | 331 ++++++++++++----
gdb/ui-style.h | 142 ++++++-
gdb/unittests/style-selftests.c | 6 +-
33 files changed, 2414 insertions(+), 161 deletions(-)
create mode 100644 gdb/guile/scm-color.c
create mode 100644 gdb/python/py-color.c
create mode 100644 gdb/python/py-color.h
create mode 100644 gdb/testsuite/gdb.guile/scm-color.exp
create mode 100644 gdb/testsuite/gdb.python/py-color.exp
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index bcf1ee45a70..502b4b6f9e5 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -359,6 +359,7 @@ SUBDIR_GUILE_SRCS = \
guile/scm-block.c \
guile/scm-breakpoint.c \
guile/scm-cmd.c \
+ guile/scm-color.c \
guile/scm-disasm.c \
guile/scm-exception.c \
guile/scm-frame.c \
@@ -395,6 +396,7 @@ SUBDIR_PYTHON_SRCS = \
python/py-bpevent.c \
python/py-breakpoint.c \
python/py-cmd.c \
+ python/py-color.c \
python/py-connection.c \
python/py-continueevent.c \
python/py-dap.c \
diff --git a/gdb/NEWS b/gdb/NEWS
index cfc9cb05f77..4accb394cc9 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -37,11 +37,40 @@
history has been reached. It also specifies that the forward execution can
continue, and the recording will also continue.
+* "set style" commands now supports numeric format for basic colors
+ from 0 to 255 and #RRGGBB format for TrueColor.
+
+* New built-in convenience variable $_colorsupport provides comma-separated
+ list of color space names supported by terminal. It is handy for
+ conditionally using styling colors based on terminal features. For example:
+
+ (gdb) if $_regex ($_colorsupport, ".*(^|,)rgb_24bit($|,).*")
+ >set style filename background #FACADE
+ >else
+ >if $_regex ($_colorsupport, ".*(^|,)xterm_256color($|,).*")
+ >set style filename background 224
+ >else
+ >set style filename background red
+ >end
+ >end
+
* Python API
** Added gdb.record.clear. Clears the trace data of the current recording.
This forces re-decoding of the trace for successive commands.
+ ** New class gdb.Color for dealing with colors.
+
+ ** New constant gdb.PARAM_COLOR represents color type of a
+ gdb.Parameter.value. Parameter's value is gdb.Color instance.
+
+* Guile API
+
+ ** New type <gdb:color> for dealing with colors.
+
+ ** New constant PARAM_COLOR represents color type of a value
+ of a <gdb:parameter> object. Parameter's value is <gdb::color> instance.
+
* Debugger Adapter Protocol changes
** The "scopes" request will now return a scope holding global
diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c
index fd8e27735f0..deec2a77cf3 100644
--- a/gdb/cli/cli-cmds.c
+++ b/gdb/cli/cli-cmds.c
@@ -2387,6 +2387,12 @@ value_from_setting (const setting &var, struct gdbarch *gdbarch)
return current_language->value_string (gdbarch, value, len);
}
+ case var_color:
+ {
+ std::string s = var.get<ui_file_style::color> ().to_string ();
+ return value_cstring (s.c_str (), s.size (),
+ builtin_type (gdbarch)->builtin_char);
+ }
default:
gdb_assert_not_reached ("bad var_type");
}
@@ -2434,6 +2440,7 @@ str_value_from_setting (const setting &var, struct gdbarch *gdbarch)
case var_pinteger:
case var_boolean:
case var_auto_boolean:
+ case var_color:
{
std::string cmd_val = get_setshow_command_value_string (var);
diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
index 163012a6bec..f86152480a0 100644
--- a/gdb/cli/cli-decode.c
+++ b/gdb/cli/cli-decode.c
@@ -23,6 +23,7 @@
#include "cli/cli-cmds.h"
#include "cli/cli-decode.h"
#include "cli/cli-style.h"
+#include "cli/cli-utils.h"
#include <optional>
/* Prototypes for local functions. */
@@ -739,6 +740,87 @@ add_setshow_enum_cmd (const char *name, command_class theclass,
return cmds;
}
+/* See cli-decode.h. */
+
+void
+complete_on_color (completion_tracker &tracker,
+ const char *text, const char *word)
+{
+ complete_on_enum (tracker, ui_file_style::basic_color_enums.data (),
+ text, word);
+ if (*text == '\0')
+ {
+ /* Convenience to let the user know what the option
+ can accept. Note there's no common prefix between
+ the strings on purpose, so that complete_on_enum doesn't do
+ a partial match. */
+ tracker.add_completion (make_unique_xstrdup ("NUMBER"));
+ tracker.add_completion (make_unique_xstrdup ("#RRGGBB"));
+ }
+}
+
+/* Completer used in color commands. */
+
+static void
+color_completer (struct cmd_list_element *ignore,
+ completion_tracker &tracker,
+ const char *text, const char *word)
+{
+ complete_on_color (tracker, text, word);
+}
+
+
+/* Add element named NAME to command list LIST (the list for set or
+ some sublist thereof). CLASS is as in add_cmd. VAR is address
+ of the variable which will contain the color. */
+
+set_show_commands
+add_setshow_color_cmd (const char *name,
+ enum command_class theclass,
+ ui_file_style::color *var,
+ const char *set_doc,
+ const char *show_doc,
+ const char *help_doc,
+ cmd_func_ftype *set_func,
+ show_value_ftype *show_func,
+ struct cmd_list_element **set_list,
+ struct cmd_list_element **show_list)
+{
+ set_show_commands commands = add_setshow_cmd_full<ui_file_style::color>
+ (name, theclass, var_color, var,
+ set_doc, show_doc, help_doc,
+ nullptr, nullptr, set_func, show_func,
+ set_list, show_list);
+
+ set_cmd_completer (commands.set, color_completer);
+
+ return commands;
+}
+
+/* Same as above but using a getter and a setter function instead of a pointer
+ to a global storage buffer. */
+
+set_show_commands
+add_setshow_color_cmd (const char *name, command_class theclass,
+ const char *set_doc, const char *show_doc,
+ const char *help_doc,
+ setting_func_types<ui_file_style::color>::set set_func,
+ setting_func_types<ui_file_style::color>::get get_func,
+ show_value_ftype *show_func,
+ cmd_list_element **set_list,
+ cmd_list_element **show_list)
+{
+ auto cmds = add_setshow_cmd_full<ui_file_style::color>
+ (name, theclass, var_color, nullptr,
+ set_doc, show_doc, help_doc,
+ set_func, get_func, nullptr, show_func,
+ set_list, show_list);
+
+ set_cmd_completer (cmds.set, color_completer);
+
+ return cmds;
+}
+
/* See cli-decode.h. */
const char * const auto_boolean_enums[] = { "on", "off", "auto", NULL };
@@ -2749,3 +2831,95 @@ cli_user_command_p (struct cmd_list_element *cmd)
{
return cmd->theclass == class_user && cmd->func == do_simple_func;
}
+
+/* See cli-decode.h. */
+
+ui_file_style::color
+parse_cli_var_color (const char **args)
+{
+ /* Do a "set" command. ARG is nullptr if no argument, or the
+ text of the argument. */
+
+ if (args == nullptr || *args == nullptr || **args == '\0')
+ {
+ std::string msg;
+
+ for (size_t i = 0; ui_file_style::basic_color_enums[i]; ++i)
+ {
+ msg.append ("\"");
+ msg.append (ui_file_style::basic_color_enums[i]);
+ msg.append ("\", ");
+ }
+
+ error (_("Requires an argument. Valid arguments are %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 7365c3f0157..5918d3b9dbe 100644
--- a/gdb/cli/cli-decode.h
+++ b/gdb/cli/cli-decode.h
@@ -306,6 +306,27 @@ extern const char * const boolean_enums[];
/* The enums of auto-boolean commands. */
extern const char * const auto_boolean_enums[];
+/* Add the different possible completions of TEXT with color.
+
+ WORD points in the same buffer as TEXT, and completions should be
+ returned relative to this position. For example, suppose TEXT is "foo"
+ and we want to complete to "foobar". If WORD is "oo", return
+ "oobar"; if WORD is "baz/foo", return "baz/foobar". */
+
+extern void complete_on_color (completion_tracker &tracker,
+ const char *text, const char *word);
+
+/* Parse ARGS, an option to a var_color variable.
+ *
+ Either returns the parsed value on success or throws an error. ARGS may be
+ one of strings {none, black, red, green, yellow, blue, magenta,
+ cyan, white}, or color number from 0 to 255, or RGB hex triplet #RRGGBB.
+ */
+extern ui_file_style::color parse_cli_var_color (const char **args);
+
+/* Same as above but additionally check that there is no junk in the end. */
+extern ui_file_style::color parse_var_color (const char *arg);
+
/* Verify whether a given cmd_list_element is a user-defined command.
Return 1 if it is user-defined. Return 0 otherwise. */
diff --git a/gdb/cli/cli-option.c b/gdb/cli/cli-option.c
index 05539285c80..3a165ef6d2c 100644
--- a/gdb/cli/cli-option.c
+++ b/gdb/cli/cli-option.c
@@ -45,6 +45,9 @@ union option_value
/* For var_string options. This is malloc-allocated. */
std::string *string;
+
+ /* For var_color options. */
+ ui_file_style::color color = ui_file_style::NONE;
};
/* Holds an options definition and its value. */
@@ -433,6 +436,35 @@ parse_option (gdb::array_view<const option_def_group> options_group,
val.enumeration = parse_cli_var_enum (args, match->enums);
return option_def_and_value {*match, match_ctx, val};
}
+ case var_color:
+ {
+ if (completion != nullptr)
+ {
+ const char *after_arg = skip_to_space (*args);
+ if (*after_arg == '\0')
+ {
+ complete_on_color (completion->tracker, *args, *args);
+
+ if (completion->tracker.have_completions ())
+ return {};
+ }
+ }
+
+ if (check_for_argument (args, "--"))
+ {
+ /* Treat e.g., "backtrace -entry-values --" as if there
+ was no argument after "-entry-values". This makes
+ parse_cli_var_color throw an error with a suggestion of
+ what are the valid options. */
+ args = nullptr;
+ }
+
+ option_value val;
+ ui_file_style::color color = parse_cli_var_color (args);
+ ui_file_style::color approx_color = color.approximate (colorsupport ());
+ val.color = approx_color;
+ return option_def_and_value {*match, match_ctx, val};
+ }
case var_string:
{
if (check_for_argument (args, "--"))
@@ -611,6 +643,10 @@ save_option_value_in_ctx (std::optional<option_def_and_value> &ov)
*ov->option.var_address.enumeration (ov->option, ov->ctx)
= ov->value->enumeration;
break;
+ case var_color:
+ *ov->option.var_address.color (ov->option, ov->ctx)
+ = ov->value->color;
+ break;
case var_string:
*ov->option.var_address.string (ov->option, ov->ctx)
= std::move (*ov->value->string);
@@ -699,6 +735,14 @@ get_val_type_str (const option_def &opt, std::string &buffer)
}
return buffer.c_str ();
}
+ case var_color:
+ {
+ buffer = "";
+ for (size_t i = 0; ui_file_style::basic_color_enums[i]; ++i)
+ buffer.append (ui_file_style::basic_color_enums[i]).append ("|");
+ buffer += "NUMBER|#RRGGBB";
+ return buffer.c_str ();
+ }
case var_string:
return "STRING";
default:
diff --git a/gdb/cli/cli-option.h b/gdb/cli/cli-option.h
index bbe281d9721..2fc354ef1a2 100644
--- a/gdb/cli/cli-option.h
+++ b/gdb/cli/cli-option.h
@@ -91,6 +91,7 @@ struct option_def
int *(*integer) (const option_def &, void *ctx);
const char **(*enumeration) (const option_def &, void *ctx);
std::string *(*string) (const option_def &, void *ctx);
+ ui_file_style::color *(*color) (const option_def &, void *ctx);
}
var_address;
@@ -308,6 +309,26 @@ struct string_option_def : option_def
}
};
+/* A var_color command line option. */
+
+template<typename Context>
+struct color_option_def : option_def
+{
+ color_option_def (const char *long_option_,
+ ui_file_style::color *(*get_var_address_cb_) (Context *),
+ show_value_ftype *show_cmd_cb_,
+ const char *set_doc_,
+ const char *show_doc_ = nullptr,
+ const char *help_doc_ = nullptr)
+ : option_def (long_option_, var_color,
+ (erased_get_var_address_ftype *) get_var_address_cb_,
+ show_cmd_cb_,
+ set_doc_, show_doc_, help_doc_)
+ {
+ var_address.color = detail::get_var_address<ui_file_style::color, Context>;
+ }
+};
+
/* A group of options that all share the same context pointer to pass
to the options' get-current-value callbacks. */
struct option_def_group
diff --git a/gdb/cli/cli-setshow.c b/gdb/cli/cli-setshow.c
index bcc793b3148..a8f22c9bde6 100644
--- a/gdb/cli/cli-setshow.c
+++ b/gdb/cli/cli-setshow.c
@@ -443,6 +443,13 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c)
option_changed = c->var->set<const char *> (match);
}
break;
+ case var_color:
+ {
+ ui_file_style::color color = parse_var_color (arg);
+ ui_file_style::color approx_color = color.approximate (colorsupport ());
+ option_changed = c->var->set<ui_file_style::color> (approx_color);
+ }
+ break;
default:
error (_("gdb internal error: bad var_type in do_setshow_command"));
}
@@ -520,6 +527,14 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c)
interps_notify_param_changed
(name, c->var->get<const char *> ());
break;
+ case var_color:
+ {
+ const ui_file_style::color &color
+ = c->var->get<ui_file_style::color> ();
+ interps_notify_param_changed
+ (name, color.to_string ().c_str ());
+ }
+ break;
case var_boolean:
{
const char *opt = c->var->get<bool> () ? "on" : "off";
@@ -585,6 +600,12 @@ get_setshow_command_value_string (const setting &var)
stb.puts (value);
}
break;
+ case var_color:
+ {
+ const ui_file_style::color &value = var.get<ui_file_style::color> ();
+ stb.puts (value.to_string ().c_str ());
+ }
+ break;
case var_boolean:
stb.puts (var.get<bool> () ? "on" : "off");
break;
diff --git a/gdb/cli/cli-style.c b/gdb/cli/cli-style.c
index 5928998e9ca..36c1ead58f0 100644
--- a/gdb/cli/cli-style.c
+++ b/gdb/cli/cli-style.c
@@ -42,20 +42,6 @@ bool source_styling = true;
bool disassembler_styling = true;
-/* Name of colors; must correspond to ui_file_style::basic_color. */
-static const char * const cli_colors[] = {
- "none",
- "black",
- "red",
- "green",
- "yellow",
- "blue",
- "magenta",
- "cyan",
- "white",
- nullptr
-};
-
/* Names of intensities; must correspond to
ui_file_style::intensity. */
static const char * const cli_intensities[] = {
@@ -131,8 +117,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])
{
}
@@ -143,32 +129,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)
@@ -180,7 +151,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. */
@@ -253,9 +224,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,
@@ -265,9 +235,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 1663b4ee53c..66ce35cf2e2 100644
--- a/gdb/cli/cli-style.h
+++ b/gdb/cli/cli-style.h
@@ -67,9 +67,9 @@ class cli_style_option
const char *m_name;
/* The foreground. */
- const char *m_foreground;
+ ui_file_style::color m_foreground;
/* The background. */
- const char *m_background;
+ ui_file_style::color m_background;
/* The intensity. */
const char *m_intensity;
diff --git a/gdb/command.h b/gdb/command.h
index 0ceaf3e2a6d..715fcd8096f 100644
--- a/gdb/command.h
+++ b/gdb/command.h
@@ -111,7 +111,9 @@ enum var_types
/* Enumerated type. Can only have one of the specified values.
*VAR is a char pointer to the name of the element that we
find. */
- var_enum
+ var_enum,
+ /* Color type. *VAR is a ui_file_style::color structure. */
+ var_color
};
/* A structure describing an extra literal accepted and shown in place
@@ -185,6 +187,14 @@ inline bool var_type_uses<const char *> (var_types t)
return t == var_enum;
}
+/* Return true if a setting of type T is backed by an ui_file_style::color
+ variable. */
+template<>
+inline bool var_type_uses<ui_file_style::color> (var_types t)
+{
+ return t == var_color;
+}
+
template<bool is_scalar, typename T> struct setting_func_types_1;
template<typename T>
@@ -681,6 +691,20 @@ extern set_show_commands add_setshow_enum_cmd
setting_func_types<const char *>::get get_func, show_value_ftype *show_func,
cmd_list_element **set_list, cmd_list_element **show_list);
+extern set_show_commands add_setshow_color_cmd
+ (const char *name, command_class theclass, ui_file_style::color *var,
+ const char *set_doc, const char *show_doc, const char *help_doc,
+ cmd_func_ftype *set_func, show_value_ftype *show_func,
+ cmd_list_element **set_list, cmd_list_element **show_list);
+
+extern set_show_commands add_setshow_color_cmd
+ (const char *name, command_class theclass,
+ const char *set_doc, const char *show_doc, const char *help_doc,
+ setting_func_types<ui_file_style::color>::set set_func,
+ setting_func_types<ui_file_style::color>::get get_func,
+ show_value_ftype *show_func, cmd_list_element **set_list,
+ cmd_list_element **show_list);
+
extern set_show_commands add_setshow_auto_boolean_cmd
(const char *name, command_class theclass, auto_boolean *var,
const char *set_doc, const char *show_doc, const char *help_doc,
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 77a4021b36a..7de0cce344d 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -12990,6 +12990,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
@@ -27917,16 +27926,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 a66e8bfe16b..0cb01663d6e 100644
--- a/gdb/doc/guile.texi
+++ b/gdb/doc/guile.texi
@@ -155,6 +155,7 @@ from the Guile interactive prompt.
* I/O Ports in Guile:: GDB I/O ports
* Memory Ports in Guile:: Accessing memory through ports and bytevectors
* Iterators In Guile:: Basic iterator support
+* Colors In Guile:: Colorize output with Guile
@end menu
@node Basic Guile
@@ -408,6 +409,9 @@ as a symbol.
@item <gdb:value>
@xref{Values From Inferior In Guile}.
+
+@item <gdb:color>
+@xref{Colors In Guile}.
@end table
The following @value{GDBN} objects are managed internally so that the
@@ -2184,6 +2188,14 @@ The value is a filename. This is just like
@item PARAM_ENUM
The value is a string, which must be one of a collection of string
constants provided when the parameter is created.
+
+@item PARAM_COLOR
+The value is either a string or an unsigned integer. Integer from 0 to 255
+means index into terminal's color palette. String can be a hex RGB triplet in
+@samp{#RRGGBB} format or one of the following color names:
+@samp{none} (meaning the terminal's default color), @samp{black}, @samp{red},
+@samp{green}, @samp{yellow}, @samp{blue}, @samp{magenta}, @samp{cyan},
+or @samp{white}.
@end vtable
@node Progspaces In Guile
@@ -3820,6 +3832,98 @@ Run @var{iterator} until the result of @code{(pred element)} is true
and return that as the result. Otherwise return @code{#f}.
@end deffn
+@node Colors In Guile
+@subsubsection Colors In Guile
+
+@cindex guile colors
+@tindex <gdb:color>
+You can assign instance of @code{<gdb:color>} to the value of
+a @code{<gdb:parameter>} object created with @code{PARAM_COLOR}.
+
+@code{<gdb:color>} may refer to an index from color palette or contain
+components of a color from some colorspace.
+
+@deffn {Scheme Procedure} make-color @w{@r{[}value} @
+ @w{@r{[}#:color-space color-space@r{]}@r{]}}
+
+@var{value} is an integer index of a color in palette, tuple with color
+components or a string. String can be a hex RGB triplet in @samp{#RRGGBB}
+format or one of the following color names:
+@samp{none} (meaning the terminal's default color), @samp{black}, @samp{red},
+@samp{green}, @samp{yellow}, @samp{blue}, @samp{magenta}, @samp{cyan},
+or @samp{white}.
+
+@var{color-space} should be one of the @samp{COLORSPACE_} constants. This
+argument tells @value{GDBN} which color space @var{value} belongs.
+@end deffn
+
+@deffn {Scheme Procedure} color? object
+Return @code{#t} if @var{object} is a @code{<gdb:color>} object.
+Otherwise return @code{#f}.
+@end deffn
+
+@deffn {Scheme Procedure} color-none? color
+Return @code{#t} if @var{color} is terminal's default.
+Otherwise return @code{#f}.
+@end deffn
+
+@deffn {Scheme Procedure} color-indexed? color
+Return @code{#t} if @var{color} is indexed, i.e. belongs to some palette.
+Otherwise return @code{#f}.
+@end deffn
+
+@deffn {Scheme Procedure} color-direct? color
+Return @code{#t} if @var{color} is direct in the sense of ISO/IEC 8613-6.
+Otherwise return @code{#f}.
+@end deffn
+
+@deffn {Scheme Procedure} color-string color
+Return the textual representation of a @code{<gdb:color>} object.
+@end deffn
+
+@deffn {Scheme Procedure} color-colorspace color
+Return the color space of a @code{<gdb:color>} object.
+@end deffn
+
+@deffn {Scheme Procedure} color-index color
+Return index of the color of a @code{<gdb:color>} object in a palette.
+@end deffn
+
+@deffn {Scheme Procedure} color-components color
+Return components of the direct @code{<gdb:color>} object.
+@end deffn
+
+@deffn {Scheme Procedure} color-escape-sequence color is_foreground
+Return string to change terminal's color to this.
+
+If @var{is_foreground} is @code{#t}, then the returned sequence will change
+foreground color. Otherwise, the returned sequence will change background
+color.
+@end deffn
+
+When color is initialized, its color space must be specified. The
+available color spaces are represented by constants defined in the @code{gdb}
+module:
+
+@vtable @code
+@item COLORSPACE_MONOCHROME
+Palette with only terminal's default color.
+
+@item COLORSPACE_ANSI_8COLOR
+Palette with eight standard colors of ISO/IEC 6429 "black", "red", "green", etc.
+
+@item COLORSPACE_AIXTERM_16COLOR
+Palette with 16 colors. First eight are standard colors of ISO/IEC 6429
+"black", "red", "green", etc. Next eight are their bright version.
+
+@item COLORSPACE_XTERM_256COLOR
+Palette with 256 colors. First 16 are from COLORSPACE_AIXTERM_16COLOR. Next
+216 colors are 6x6x6 RGB cube. And last 24 colors form grayscale ramp.
+
+@item COLORSPACE_RGB_24BIT
+Direct 24-bit RGB colors.
+@end vtable
+
@node Guile Auto-loading
@subsection Guile Auto-loading
@cindex guile auto-loading
diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index 22f0e6c6d0a..61c179a088d 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -5230,6 +5230,11 @@ except the special value -1 is returned for the setting of ``unlimited''.
@item gdb.PARAM_ENUM
The value is a string, which must be one of a collection string
constants provided when the parameter is created.
+
+@findex PARAM_COLOR
+@findex gdb.PARAM_COLOR
+@item gdb.PARAM_COLOR
+The value is @code{gdb.Color} instance.
@end table
@node Functions In Python
@@ -6999,6 +7004,98 @@ resolve this to the lazy string's character type, use the type's
writable.
@end defvar
+@node Colors In Python
+@subsection Python representation of colors
+
+@cindex colors in python
+@tindex gdb.Color
+You can assign instance of @code{Color} to the @code{value} of
+a @code{Parameter} instance created with @code{PARAM_COLOR}.
+
+@code{Color} may refer to an index from color palette or contain components
+of a color from some colorspace.
+
+@defun Color.__init__ (@r{[}@var{value} @r{[}, @var{color-space}@r{]}@r{]})
+
+@var{value} is @code{None} (meaning the terminal's default color),
+an integer index of a color in palette, tuple with color components
+or a string. String can be a hex RGB triplet in @samp{#RRGGBB} format
+or one of the following color names:
+@samp{none} (meaning the terminal's default color), @samp{black}, @samp{red},
+@samp{green}, @samp{yellow}, @samp{blue}, @samp{magenta}, @samp{cyan},
+or @samp{white}.
+
+@var{color-space} should be one of the @samp{COLORSPACE_} constants. This
+argument tells @value{GDBN} which color space @var{value} belongs.
+@end defun
+
+@defvar Color.is_none
+This atribute is boolean. If its value is @code{True} then color is terminal's
+default.
+@end defvar
+
+@defvar Color.is_indexed
+This atribute is boolean. If its value is @code{True} then color is indexed,
+i.e. belongs to some palette.
+@end defvar
+
+@defvar Color.is_direct
+This atribute is boolean. If its value is @code{True} then this object
+describes direct colour in the sense of ISO/IEC 8613-6.
+@end defvar
+
+@defvar Color.index
+This attribute exist if @code{is_indexed} is @code{True}. Its integer value is
+index of a color in a palette.
+@end defvar
+
+@defvar Color.components
+This attribute exist if @code{is_direct} is @code{True}. Its value is tuple
+with integer components of a color.
+@end defvar
+
+@defun Color.escape_sequence (@var{self}, @var{is_foreground})
+Returns string to change terminal's color to this.
+
+If @var{is_foreground} is @code{True}, then the returned sequence will change
+foreground color. Otherwise, the returned sequence will change background
+color.
+@end defun
+
+When color is initialized, its color space must be specified. The
+available color spaces are represented by constants defined in the @code{gdb}
+module:
+
+@table @code
+@findex COLORSPACE_MONOCHROME
+@findex gdb.COLORSPACE_MONOCHROME
+@item gdb.COLORSPACE_MONOCHROME
+Palette with only terminal's default color.
+
+@findex COLORSPACE_ANSI_8COLOR
+@findex gdb.COLORSPACE_ANSI_8COLOR
+@item gdb.COLORSPACE_ANSI_8COLOR
+Palette with eight standard colors of ISO/IEC 6429 "black", "red", "green", etc.
+
+@findex COLORSPACE_AIXTERM_16COLOR
+@findex gdb.COLORSPACE_AIXTERM_16COLOR
+@item gdb.COLORSPACE_AIXTERM_16COLOR
+Palette with 16 colors. First eight are standard colors of ISO/IEC 6429
+"black", "red", "green", etc. Next eight are their bright version.
+
+@findex COLORSPACE_XTERM_256COLOR
+@findex gdb.COLORSPACE_XTERM_256COLOR
+@item gdb.COLORSPACE_XTERM_256COLOR
+Palette with 256 colors. First 16 are from COLORSPACE_AIXTERM_16COLOR. Next
+216 colors are 6x6x6 RGB cube. And last 24 colors form grayscale ramp.
+
+@findex COLORSPACE_RGB_24BIT
+@findex gdb.COLORSPACE_RGB_24BIT
+@item gdb.COLORSPACE_RGB_24BIT
+Direct 24-bit RGB colors.
+
+@end table
+
@node Architectures In Python
@subsubsection Python representation of architectures
@cindex Python architectures
diff --git a/gdb/guile/guile-internal.h b/gdb/guile/guile-internal.h
index dea78845088..b137713d133 100644
--- a/gdb/guile/guile-internal.h
+++ b/gdb/guile/guile-internal.h
@@ -439,6 +439,14 @@ extern int gdbscm_valid_command_class_p (int command_class);
extern char *gdbscm_canonicalize_command_name (const char *name,
int want_trailing_space);
+/* scm-color.c */
+
+extern SCM coscm_scm_from_color (const ui_file_style::color &color);
+
+extern int coscm_is_color (SCM scm);
+
+extern const ui_file_style::color & coscm_get_color (SCM color_scm);
+
/* scm-frame.c */
struct frame_smob;
@@ -617,6 +625,7 @@ extern void gdbscm_initialize_arches (void);
extern void gdbscm_initialize_auto_load (void);
extern void gdbscm_initialize_blocks (void);
extern void gdbscm_initialize_breakpoints (void);
+extern void gdbscm_initialize_colors (void);
extern void gdbscm_initialize_commands (void);
extern void gdbscm_initialize_disasm (void);
extern void gdbscm_initialize_exceptions (void);
diff --git a/gdb/guile/guile.c b/gdb/guile/guile.c
index 432093b6aea..5358b8330a3 100644
--- a/gdb/guile/guile.c
+++ b/gdb/guile/guile.c
@@ -594,6 +594,7 @@ initialize_gdb_module (void *data)
gdbscm_initialize_auto_load ();
gdbscm_initialize_blocks ();
gdbscm_initialize_breakpoints ();
+ gdbscm_initialize_colors ();
gdbscm_initialize_commands ();
gdbscm_initialize_disasm ();
gdbscm_initialize_frames ();
diff --git a/gdb/guile/scm-color.c b/gdb/guile/scm-color.c
new file mode 100644
index 00000000000..d0ba07342f8
--- /dev/null
+++ b/gdb/guile/scm-color.c
@@ -0,0 +1,443 @@
+/* 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 "value.h"
+#include "charset.h"
+#include "cli/cli-decode.h"
+#include "completer.h"
+#include "language.h"
+#include "arch-utils.h"
+#include "guile-internal.h"
+
+/* A GDB color. */
+
+struct color_smob
+{
+ /* This always appears first. */
+ gdb_smob base;
+
+ /* Underlying value. */
+ ui_file_style::color color;
+};
+
+static const char color_smob_name[] = "gdb:color";
+
+/* The tag Guile knows the color smob by. */
+static scm_t_bits color_smob_tag;
+
+/* Keywords used by make-color. */
+static SCM colorspace_keyword;
+
+static const char *coscm_colorspace_name (color_space colorspace);
+
+/* Administrivia for color smobs. */
+
+static int
+coscm_print_color_smob (SCM self, SCM port, scm_print_state *pstate)
+{
+ const ui_file_style::color &color = coscm_get_color (self);
+
+ gdbscm_printf (port, "#<%s", color_smob_name);
+
+ gdbscm_printf (port, " %s", color.to_string ().c_str ());
+ gdbscm_printf (port, " %s", coscm_colorspace_name (color.colorspace ()));
+ scm_puts (">", port);
+
+ scm_remember_upto_here_1 (self);
+
+ /* Non-zero means success. */
+ return 1;
+}
+
+/* Create an empty (uninitialized) color. */
+
+static SCM
+coscm_make_color_smob (void)
+{
+ color_smob *c_smob = (color_smob *)
+ scm_gc_calloc (sizeof (color_smob), color_smob_name);
+ SCM c_scm;
+
+ c_smob->color = ui_file_style::color (ui_file_style::NONE);
+ c_scm = scm_new_smob (color_smob_tag, (scm_t_bits) c_smob);
+ gdbscm_init_gsmob (&c_smob->base);
+
+ return c_scm;
+}
+
+/* Return the <gdb:color> object that encapsulates COLOR. */
+
+SCM
+coscm_scm_from_color (const ui_file_style::color &color)
+{
+ SCM c_scm = coscm_make_color_smob ();
+ color_smob *c_smob = (color_smob *) SCM_SMOB_DATA (c_scm);
+ c_smob->color = color;
+ return c_scm;
+}
+
+/* Return the color field of color_smob. */
+
+const ui_file_style::color &
+coscm_get_color (SCM color_scm)
+{
+ SCM_ASSERT_TYPE (coscm_is_color (color_scm), color_scm, SCM_ARG1, FUNC_NAME,
+ _("<gdb:color>"));
+
+ color_smob *c_smob = (color_smob *) SCM_SMOB_DATA (color_scm);
+ return c_smob->color;
+
+}
+
+/* Returns non-zero if SCM is a <gdb:color> object. */
+
+int
+coscm_is_color (SCM scm)
+{
+ return SCM_SMOB_PREDICATE (color_smob_tag, scm);
+}
+
+/* (gdb:color? scm) -> boolean */
+
+static SCM
+gdbscm_color_p (SCM scm)
+{
+ return scm_from_bool (coscm_is_color (scm));
+}
+
+static const scheme_integer_constant colorspaces[] =
+{
+ { "COLORSPACE_MONOCHROME", (int) color_space::MONOCHROME },
+ { "COLORSPACE_ANSI_8COLOR", (int) color_space::ANSI_8COLOR },
+ { "COLORSPACE_AIXTERM_16COLOR", (int) color_space::AIXTERM_16COLOR },
+ { "COLORSPACE_XTERM_256COLOR", (int) color_space::XTERM_256COLOR },
+ { "COLORSPACE_RGB_24BIT", (int) color_space::RGB_24BIT },
+
+ END_INTEGER_CONSTANTS
+};
+
+/* Return non-zero if COLORSPACE is a valid color space. */
+
+static int
+coscm_valid_colorspace_p (int colorspace)
+{
+ for (int i = 0; colorspaces[i].name != nullptr; ++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 != nullptr; ++i)
+ {
+ if (colorspaces[i].value == static_cast<int> (colorspace))
+ return colorspaces[i].name;
+ }
+
+ gdb_assert_not_reached ("bad color space");
+}
+
+/* Free function for a color_smob. */
+static size_t
+coscm_free_color_smob (SCM self)
+{
+ (void) self;
+ return 0;
+}
+
+/* Color Scheme functions. */
+
+/* (make-color [value
+ [#:color-space colorspace]]) -> <gdb:color>
+
+ VALUE is the value of the color. It may be SCM_UNDEFINED, string, number
+ or list.
+
+ COLORSPACE is the color space of the VALUE. It should be one of the
+ COLORSPACE_* constants defined in the gdb module.
+
+ The result is the <gdb:color> Scheme object. */
+
+static SCM
+gdbscm_make_color (SCM value_scm, SCM rest)
+{
+ SCM colorspace_arg = SCM_UNDEFINED;
+ color_space colorspace = color_space::MONOCHROME;
+
+ scm_c_bind_keyword_arguments (FUNC_NAME, rest,
+ static_cast<scm_t_keyword_arguments_flags> (0),
+ colorspace_keyword, &colorspace_arg,
+ SCM_UNDEFINED);
+
+ if (!SCM_UNBNDP (colorspace_arg))
+ {
+ SCM_ASSERT_TYPE (scm_is_integer (colorspace_arg), colorspace_arg,
+ SCM_ARG2, FUNC_NAME, _("int"));
+ int colorspace_int = scm_to_int (colorspace_arg);
+ if (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 3a1e1583f54..749c5ea1f7d 100644
--- a/gdb/guile/scm-param.c
+++ b/gdb/guile/scm-param.c
@@ -47,6 +47,9 @@ union pascm_variable
/* Hold a string, for enums. */
const char *cstringval;
+
+ /* Hold a color. */
+ ui_file_style::color color;
};
/* A GDB parameter.
@@ -130,6 +133,7 @@ enum scm_param_types
param_optional_filename,
param_filename,
param_enum,
+ param_color,
};
/* Translation from Guile parameters to GDB variable types. Keep in the
@@ -155,7 +159,8 @@ param_to_var[] =
{ var_string_noescape },
{ var_optional_filename },
{ var_filename },
- { var_enum }
+ { var_enum },
+ { var_color }
};
/* Wraps a setting around an existing param_smob. This abstraction
@@ -179,6 +184,8 @@ make_setting (param_smob *s)
return setting (type, s->value.stringval);
else if (var_type_uses<const char *> (type))
return setting (type, &s->value.cstringval);
+ else if (var_type_uses<ui_file_style::color> (s->type))
+ return setting (s->type, &s->value.color);
else
gdb_assert_not_reached ("unhandled var type");
}
@@ -239,10 +246,9 @@ static SCM
pascm_make_param_smob (void)
{
param_smob *p_smob = (param_smob *)
- scm_gc_malloc (sizeof (param_smob), param_smob_name);
+ scm_gc_calloc (sizeof (param_smob), param_smob_name);
SCM p_scm;
- memset (p_smob, 0, sizeof (*p_smob));
p_smob->cmd_class = no_class;
p_smob->type = var_boolean; /* ARI: var_boolean */
p_smob->set_func = SCM_BOOL_F;
@@ -511,6 +517,13 @@ add_setshow_generic (enum var_types param_type,
set_list, show_list);
break;
+ case var_color:
+ commands = add_setshow_color_cmd (cmd_name, cmd_class, &self->value.color,
+ set_doc, show_doc, help_doc,
+ set_func, show_func,
+ set_list, show_list);
+ break;
+
default:
gdb_assert_not_reached ("bad param_type value");
}
@@ -588,6 +601,7 @@ static const scheme_integer_constant parameter_types[] =
{ "PARAM_OPTIONAL_FILENAME", param_optional_filename },
{ "PARAM_FILENAME", param_filename },
{ "PARAM_ENUM", param_enum },
+ { "PARAM_COLOR", param_color },
END_INTEGER_CONSTANTS
};
@@ -650,6 +664,11 @@ pascm_param_value (const setting &var, int arg_pos, const char *func_name)
return gdbscm_scm_from_host_string (str, strlen (str));
}
+ case var_color:
+ {
+ return coscm_scm_from_color (var.get<ui_file_style::color> ());
+ }
+
case var_boolean:
{
if (var.get<bool> ())
@@ -764,6 +783,12 @@ pascm_set_param_value_x (param_smob *p_smob,
break;
}
+ case var_color:
+ SCM_ASSERT_TYPE (coscm_is_color (value), value, arg_pos, func_name,
+ _("<gdb:color>"));
+ var.set<ui_file_style::color> (coscm_get_color (value));
+ break;
+
case var_boolean:
SCM_ASSERT_TYPE (gdbscm_is_bool (value), value, arg_pos, func_name,
_("boolean"));
@@ -1050,6 +1075,8 @@ gdbscm_make_parameter (SCM name_scm, SCM rest)
scm_set_smob_free (parameter_smob_tag, pascm_free_parameter_smob);
if (var_type_uses<std::string> (p_smob->type))
p_smob->value.stringval = new std::string;
+ else if (var_type_uses<ui_file_style::color> (p_smob->type))
+ p_smob->value.color = ui_file_style::NONE;
if (initial_value_arg_pos > 0)
{
diff --git a/gdb/python/py-color.c b/gdb/python/py-color.c
new file mode 100644
index 00000000000..1d72cc3e7d1
--- /dev/null
+++ b/gdb/python/py-color.c
@@ -0,0 +1,347 @@
+/* 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 "python-internal.h"
+#include "py-color.h"
+#include "cli/cli-decode.h"
+
+/* Colorspace constants and their values. */
+static struct {
+ const char *name;
+ color_space value;
+} colorspace_constants[] =
+{
+ { "COLORSPACE_MONOCHROME", color_space::MONOCHROME },
+ { "COLORSPACE_ANSI_8COLOR", color_space::ANSI_8COLOR },
+ { "COLORSPACE_AIXTERM_16COLOR", color_space::AIXTERM_16COLOR },
+ { "COLORSPACE_XTERM_256COLOR", color_space::XTERM_256COLOR },
+ { "COLORSPACE_RGB_24BIT", color_space::RGB_24BIT },
+};
+
+/* A color. */
+struct colorpy_object
+{
+ PyObject_HEAD
+
+ /* Underlying value. */
+ ui_file_style::color color;
+};
+
+extern PyTypeObject colorpy_object_type;
+
+/* See py-color.h. */
+gdbpy_ref<>
+create_color_object (const ui_file_style::color &color)
+{
+ gdbpy_ref<colorpy_object> color_obj (PyObject_New (colorpy_object,
+ &colorpy_object_type));
+
+ if (color_obj == nullptr)
+ return nullptr;
+
+ color_obj->color = color;
+ return gdbpy_ref<> ((PyObject *) color_obj.release ());
+}
+
+/* See py-color.h. */
+bool
+gdbpy_is_color (PyObject *obj)
+{
+ return PyObject_IsInstance (obj, (PyObject *)&colorpy_object_type);
+}
+
+/* See py-color.h. */
+const ui_file_style::color &
+gdbpy_get_color (PyObject *obj)
+{
+ gdb_assert (gdbpy_is_color (obj));
+ colorpy_object *self = (colorpy_object *) obj;
+ return self->color;
+}
+
+/* Get an attribute. */
+static PyObject *
+get_attr (PyObject *obj, PyObject *attr_name)
+{
+ if (! PyUnicode_Check (attr_name))
+ return PyObject_GenericGetAttr (obj, attr_name);
+
+ colorpy_object *self = (colorpy_object *) obj;
+ const ui_file_style::color &color = self->color;
+
+ if (! PyUnicode_CompareWithASCIIString (attr_name, "colorspace"))
+ {
+ int value = static_cast<int> (color.colorspace ());
+ return gdb_py_object_from_longest (value).release ();
+ }
+
+ if (! PyUnicode_CompareWithASCIIString (attr_name, "is_none"))
+ return PyBool_FromLong (color.is_none ());
+
+ if (! PyUnicode_CompareWithASCIIString (attr_name, "is_indexed"))
+ return PyBool_FromLong (color.is_indexed ());
+
+ if (! PyUnicode_CompareWithASCIIString (attr_name, "is_direct"))
+ return PyBool_FromLong (color.is_direct ());
+
+ if (color.is_indexed ()
+ && ! PyUnicode_CompareWithASCIIString (attr_name, "index"))
+ return gdb_py_object_from_longest (color.get_value ()).release ();
+
+ if (color.is_direct ()
+ && ! PyUnicode_CompareWithASCIIString (attr_name, "components"))
+ {
+ uint8_t rgb[3];
+ color.get_rgb (rgb);
+
+ gdbpy_ref<> rgb_objects[3];
+ for (int i = 0; i < 3; ++i)
+ {
+ rgb_objects[i] = gdb_py_object_from_ulongest (rgb[i]);
+ if (rgb_objects[i] == nullptr)
+ return nullptr;
+ }
+
+ PyObject *comp = PyTuple_New (3);
+ if (comp == nullptr)
+ return nullptr;
+
+ for (int i = 0; i < 3; ++i)
+ PyTuple_SET_ITEM (comp, i, rgb_objects[i].release ());
+
+ return comp;
+ }
+
+ return PyObject_GenericGetAttr (obj, attr_name);
+}
+
+/* Implementation of Color.escape_sequence (self, is_fg) -> str. */
+
+static PyObject *
+colorpy_escape_sequence (PyObject *self, PyObject *is_fg_obj)
+{
+ if (!gdbpy_is_color (self))
+ {
+ PyErr_SetString (PyExc_RuntimeError,
+ _("Object is not gdb.Color."));
+ return nullptr;
+ }
+
+ if (! PyBool_Check (is_fg_obj))
+ {
+ PyErr_SetString (PyExc_RuntimeError,
+ _("A boolean argument is required."));
+ return nullptr;
+ }
+
+ bool is_fg = 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 == nullptr || value_obj == Py_None)
+ obj->color = ui_file_style::color (colorspace, -1);
+ else if (PyLong_Check (value_obj))
+ {
+ long value = -1;
+ if (! gdb_py_int_as_long (value_obj, &value))
+ return -1;
+ if (value < 0 || value > INT_MAX)
+ error (_("value %ld is out of range."), value);
+ if (colorspace_obj)
+ obj->color = ui_file_style::color (colorspace, value);
+ else
+ obj->color = ui_file_style::color (value);
+ }
+ else if (PyTuple_Check (value_obj))
+ {
+ if (colorspace_obj == nullptr || colorspace != color_space::RGB_24BIT)
+ error (_("colorspace must be gdb.COLORSPACE_RGB_24BIT with "
+ "value of tuple type."));
+ Py_ssize_t tuple_size = PyTuple_Size (value_obj);
+ if (tuple_size != 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 != nullptr
+ && 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. */
+static 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."},
+ {nullptr}
+};
+
+PyTypeObject colorpy_object_type =
+{
+ PyVarObject_HEAD_INIT (nullptr, 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 */
+};
+
+GDBPY_INITIALIZE_FILE (gdbpy_initialize_color);
diff --git a/gdb/python/py-color.h b/gdb/python/py-color.h
new file mode 100644
index 00000000000..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 e7032f7758c..7f1188b08e1 100644
--- a/gdb/python/py-param.c
+++ b/gdb/python/py-param.c
@@ -26,6 +26,7 @@
#include "completer.h"
#include "language.h"
#include "arch-utils.h"
+#include "py-color.h"
/* Python parameter types as in PARM_CONSTANTS below. */
@@ -43,6 +44,7 @@ enum py_param_types
param_zuinteger,
param_zuinteger_unlimited,
param_enum,
+ param_color,
};
/* Translation from Python parameters to GDB variable types. Keep in the
@@ -69,7 +71,8 @@ param_to_var[] =
{ var_integer },
{ var_uinteger },
{ var_pinteger, pinteger_unlimited_literals },
- { var_enum }
+ { var_enum },
+ { var_color }
};
/* Parameter constants and their values. */
@@ -90,6 +93,7 @@ static struct {
{ "PARAM_ZUINTEGER", param_zuinteger },
{ "PARAM_ZUINTEGER_UNLIMITED", param_zuinteger_unlimited },
{ "PARAM_ENUM", param_enum },
+ { "PARAM_COLOR", param_color },
{ NULL, 0 }
};
@@ -114,6 +118,9 @@ union parmpy_variable
/* Hold a string, for enums. */
const char *cstringval;
+
+ /* Hold a color. */
+ ui_file_style::color color;
};
/* A GDB parameter. */
@@ -157,6 +164,8 @@ make_setting (parmpy_object *s)
return setting (type, s->value.stringval);
else if (var_type_uses<const char *> (type))
return setting (type, &s->value.cstringval);
+ else if (var_type_uses<ui_file_style::color> (s->type))
+ return setting (s->type, &s->value.color);
else
gdb_assert_not_reached ("unhandled var type");
}
@@ -248,6 +257,19 @@ set_parameter_value (parmpy_object *self, PyObject *value)
break;
}
+ case var_color:
+ {
+ if (gdbpy_is_color (value))
+ self->value.color = gdbpy_get_color (value);
+ else
+ {
+ PyErr_SetString (PyExc_RuntimeError,
+ _("color argument must be a gdb.Color object."));
+ return -1;
+ }
+ }
+ break;
+
case var_boolean:
if (! PyBool_Check (value))
{
@@ -707,6 +729,15 @@ add_setshow_generic (enum var_types type, const literal_def *extra_literals,
get_show_value, set_list, show_list);
break;
+ case var_color:
+ /* Initialize the value, just in case. */
+ self->value.color = ui_file_style::NONE;
+ commands = add_setshow_color_cmd (cmd_name.get (), cmdclass,
+ &self->value.color, set_doc,
+ show_doc, help_doc, get_set_value,
+ get_show_value, set_list, show_list);
+ break;
+
default:
gdb_assert_not_reached ("Unhandled parameter class.");
}
@@ -830,7 +861,8 @@ parmpy_init (PyObject *self, PyObject *args, PyObject *kwds)
&& parmclass != param_string && parmclass != param_string_noescape
&& parmclass != param_optional_filename && parmclass != param_filename
&& parmclass != param_zinteger && parmclass != param_zuinteger
- && parmclass != param_zuinteger_unlimited && parmclass != param_enum)
+ && parmclass != param_zuinteger_unlimited && parmclass != param_enum
+ && parmclass != param_color)
{
PyErr_SetString (PyExc_RuntimeError,
_("Invalid parameter class argument."));
@@ -854,7 +886,7 @@ parmpy_init (PyObject *self, PyObject *args, PyObject *kwds)
extra_literals = param_to_var[parmclass].extra_literals;
obj->type = type;
obj->extra_literals = extra_literals;
- memset (&obj->value, 0, sizeof (obj->value));
+ obj->value = {}; /* zeros initialization */
if (var_type_uses<std::string> (obj->type))
obj->value.stringval = new std::string;
diff --git a/gdb/python/python.c b/gdb/python/python.c
index a2ce1f6545a..856bc867a4b 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -35,6 +35,7 @@
#include "location.h"
#include "run-on-main-thread.h"
#include "observable.h"
+#include "py-color.h"
#if GDB_SELF_TEST
#include "gdbsupport/selftest.h"
@@ -506,6 +507,12 @@ gdbpy_parameter_value (const setting &var)
return host_string_to_python_string (str).release ();
}
+ case var_color:
+ {
+ const ui_file_style::color &color = var.get<ui_file_style::color> ();
+ return create_color_object (color).release ();
+ }
+
case var_boolean:
{
if (var.get<bool> ())
diff --git a/gdb/testsuite/gdb.base/style.exp b/gdb/testsuite/gdb.base/style.exp
index aff654537ac..adcfd968b6b 100644
--- a/gdb/testsuite/gdb.base/style.exp
+++ b/gdb/testsuite/gdb.base/style.exp
@@ -70,6 +70,9 @@ proc run_style_tests { } {
global currently_disabled_style decimal hex
with_ansi_styling_terminal {
+ setenv TERM xterm-256color
+ setenv COLORTERM truecolor
+
# Restart GDB with the correct TERM variable setting, this
# means that GDB will enable styling.
clean_restart_and_disable "restart 1" ${binfile}
@@ -309,6 +312,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"
+ }
}
}
@@ -485,6 +503,179 @@ proc test_startup_version_string { } {
gdb_test "" "${vers}.*" "version is styled at startup"
}
+# Color support is disabled when TERM=dumb and COLORTERM="".
+# All colors are approximated to none when set.
+proc test_colorsupport_monochrome { } {
+ with_test_prefix "colorsupport_monochrome" {
+ with_ansi_styling_terminal {
+ setenv TERM dumb
+ gdb_exit
+ gdb_start
+ gdb_test "print \$_colorsupport" \
+ "\\\$1 = \"monochrome\"" \
+ "color support is monochrome"
+ gdb_test_no_output "set style enabled off"
+ gdb_test_no_output "set style filename foreground none"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: none" \
+ "none without approximation"
+ gdb_test_no_output "set style filename foreground blue"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: none" \
+ "blue approximated to none"
+ }
+ }
+}
+
+# Color support is limited by 8 colors when TERM=ansi and COLORTERM="".
+# All colors are approximated to basic colors when set.
+proc test_colorsupport_8color { } {
+ with_test_prefix "colorsupport_8color" {
+ with_ansi_styling_terminal {
+ gdb_exit
+ gdb_start
+ gdb_test "print \$_colorsupport" \
+ "\033\\\[.*m\\\$1\033\\\[m = \"monochrome,ansi_8color\"" \
+ "color support is 8 color"
+ gdb_test_no_output "set style enabled off"
+ gdb_test_no_output "set style filename foreground none"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: none" \
+ "none without approximation"
+ gdb_test_no_output "set style filename foreground yellow"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: yellow" \
+ "yellow without approximation"
+ gdb_test_no_output "set style filename foreground 9"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: red" \
+ "9 approximated to red"
+ gdb_test_no_output "set style filename foreground 118"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: green" \
+ "118 approximated to green"
+ gdb_test_no_output "set style filename foreground #000ABC"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: blue" \
+ "#000ABC approximated to blue"
+ }
+ }
+}
+
+# Color support is limited by 256 colors when TERM=xterm-256color and
+# COLORTERM="". All colors are approximated by 256 color palette when set.
+proc test_colorsupport_256color { } {
+ with_test_prefix "colorsupport_256color" {
+ with_ansi_styling_terminal {
+ setenv TERM xterm-256color
+ gdb_exit
+ gdb_start
+ gdb_test "print \$_colorsupport" \
+ "\033\\\[.*m\\\$1\033\\\[m = \"monochrome,ansi_8color,aixterm_16color,xterm_256color\"" \
+ "color support is 256 color"
+ gdb_test_no_output "set style enabled off"
+ gdb_test_no_output "set style filename foreground none"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: none" \
+ "none without approximation"
+ gdb_test_no_output "set style filename foreground red"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: red" \
+ "red without approximation"
+ gdb_test_no_output "set style filename foreground 9"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: 9" \
+ "9 without approximation"
+ gdb_test_no_output "set style filename foreground 118"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: 118" \
+ "118 without approximation"
+ gdb_test_no_output "set style filename foreground #CD00CD"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: 5" \
+ "#CD00CD approximated to 5"
+ gdb_test_no_output "set style filename foreground #FFAF12"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: 214" \
+ "#FFAF12 approximated to 214"
+ }
+ }
+}
+
+# Color support is limited by 16777216 colors when TERM=xterm-256color and
+# COLORTERM="truecolor". No approximation needed.
+proc test_colorsupport_truecolor { } {
+ with_test_prefix "colorsupport_truecolor" {
+ with_ansi_styling_terminal {
+ setenv TERM xterm-256color
+ setenv COLORTERM truecolor
+ gdb_exit
+ gdb_start
+ gdb_test "print \$_colorsupport" \
+ "\033\\\[.*m\\\$1\033\\\[m = \"monochrome,ansi_8color,aixterm_16color,xterm_256color,rgb_24bit\"" \
+ "color support is truecolor"
+ gdb_test_no_output "set style enabled off"
+ gdb_test_no_output "set style filename foreground none"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: none" \
+ "none without approximation"
+ gdb_test_no_output "set style filename foreground red"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: red" \
+ "red without approximation"
+ gdb_test_no_output "set style filename foreground 9"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: 9" \
+ "9 without approximation"
+ gdb_test_no_output "set style filename foreground 118"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: 118" \
+ "118 without approximation"
+ gdb_test_no_output "set style filename foreground #CD00CD"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: #CD00CD" \
+ "#CD00CD without approximation"
+ }
+ }
+}
+
+# Color support is limited by 16777216 colors when TERM=dumb and
+# COLORTERM=24bit. No approximation needed. Basic colors replaced with RGB.
+proc test_colorsupport_truecolor_only { } {
+ with_test_prefix "colorsupport_truecolor_only" {
+ with_ansi_styling_terminal {
+ setenv TERM dumb
+ setenv COLORTERM truecolor
+ gdb_exit
+ gdb_start
+ gdb_test "print \$_colorsupport" \
+ "\\\$1 = \"monochrome,rgb_24bit\"" \
+ "color support is truecolor only"
+ gdb_test_no_output "set style enabled off"
+ gdb_test_no_output "set style filename foreground none"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: none" \
+ "none without approximation"
+ gdb_test_no_output "set style filename foreground red"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: #DE382B" \
+ "red replaced by #DE382B"
+ gdb_test_no_output "set style filename foreground 9"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: #FF0000" \
+ "9 replaced by #FF0000"
+ gdb_test_no_output "set style filename foreground 118"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: #87FF00" \
+ "118 replaced by #87FF00"
+ gdb_test_no_output "set style filename foreground #CD00CD"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: #CD00CD" \
+ "#CD00CD without approximation"
+ }
+ }
+}
+
# Check to see if the Python styling of disassembler output is
# expected or not, this styling requires Python support in GDB, and
# the Python pygments module to be available.
@@ -519,3 +710,9 @@ test_disassembler_error_handling
# Finally, check the styling of the version string during startup.
test_startup_version_string
+
+test_colorsupport_monochrome
+test_colorsupport_8color
+test_colorsupport_256color
+test_colorsupport_truecolor
+test_colorsupport_truecolor_only
diff --git a/gdb/testsuite/gdb.guile/scm-color.exp b/gdb/testsuite/gdb.guile/scm-color.exp
new file mode 100644
index 00000000000..e5773650f2f
--- /dev/null
+++ b/gdb/testsuite/gdb.guile/scm-color.exp
@@ -0,0 +1,110 @@
+# 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
+
+require allow_guile_tests
+
+clean_restart
+
+gdb_install_guile_utils
+gdb_install_guile_module
+
+gdb_test_no_output [concat "guile (define (print_color_attrs c) " \
+ "(display (color-string c)) (display \" \") " \
+ "(display (color-colorspace c)) (display \" \") " \
+ "(display (color-none? c)) (display \" \") " \
+ "(display (color-indexed? c)) (display \" \") " \
+ "(display (color-direct? c)) (newline))"] \
+ "print_color_attrs helper"
+
+gdb_test "guile (print_color_attrs (make-color))" \
+ "none 0 #t #f #f" \
+ "print attrs of a color without params"
+
+gdb_test_no_output "guile (define c (make-color \"green\"))" \
+ "create color from basic name string"
+gdb_test "guile (print_color_attrs c)" "green 1 #f #t #f" \
+ "print attrs of a basic color name"
+gdb_test "guile (print (color-index c))" "2" \
+ "print index of a basic color name"
+
+gdb_test_no_output "guile (define c (make-color 2))" \
+ "create color from basic index"
+gdb_test "guile (print_color_attrs c)" "green 1 #f #t #f" \
+ "print attrs of a basic color"
+gdb_test "guile (print (color-index c))" "2" \
+ "print index of a basic color"
+
+gdb_test_no_output "guile (define c (make-color 14))" \
+ "create color from integer 14"
+gdb_test "guile (print_color_attrs c)" "14 2 #f #t #f" \
+ "print attrs of an color 14"
+gdb_test "guile (print (color-index c))" "14" \
+ "print index of color 14"
+
+gdb_test_no_output "guile (define c (make-color 2 #:color-space COLORSPACE_ANSI_8COLOR))" \
+ "create color from basic index and ansi colorspace"
+gdb_test "guile (print_color_attrs c)" "green 1 #f #t #f" \
+ "print attrs of a basic color with ansi colorspace"
+gdb_test "guile (print (color-index c))" "2" \
+ "print index of a basic color with ansi colorspace"
+
+gdb_test_no_output "guile (define c (make-color 2 #:color-space COLORSPACE_XTERM_256COLOR))" \
+ "create color from basic index and xterm256 colorspace"
+gdb_test "guile (print_color_attrs c)" "2 3 #f #t #f" \
+ "print attrs of a basic color with xterm256 colorspace"
+gdb_test "guile (print (color-index c))" "2" \
+ "print index of a basic color with xterm256 colorspace"
+
+gdb_test_no_output "guile (define c (make-color '(171 205 239) #:color-space COLORSPACE_RGB_24BIT))" \
+ "create color from rgb components"
+gdb_test "guile (print_color_attrs c)" "#ABCDEF 4 #f #f #t" \
+ "print attrs of an RGB color"
+gdb_test "guile (print (color-components c))" "\\(171 205 239\\)" \
+ "print components of an RGB color"
+
+gdb_test_no_output "guile (define c (make-color \"none\"))" \
+ "create color from string none"
+gdb_test "guile (print_color_attrs c)" "none 0 #t #f #f" \
+ "print attrs of a color none"
+
+gdb_test_no_output "guile (define c (make-color \"254\"))" \
+ "create color from string 254"
+gdb_test "guile (print_color_attrs c)" "254 3 #f #t #f" \
+ "print attrs of an color 254"
+gdb_test "guile (print (color-index c))" "254" \
+ "print index of color 254"
+
+gdb_test_no_output "guile (define c_none (make-color \"none\"))" \
+ "save default color"
+gdb_test_no_output "guile (define c_red (make-color \"red\"))" \
+ "save blue color"
+gdb_test_no_output "guile (define c_green (make-color \"green\"))" \
+ "save yellow color"
+gdb_test [concat "guile " \
+ "(display (color-escape-sequence c_red #t)) " \
+ "(display (color-escape-sequence c_green #f)) " \
+ "(display \"red on green\") " \
+ "(display (color-escape-sequence c_none #f)) " \
+ "(display \" red on default\") " \
+ "(display (color-escape-sequence c_none #t)) " \
+ "(newline)"] \
+ "\033\\\[31m\033\\\[42mred on green\033\\\[49m red on default\033\\\[39m" \
+ "escape sequences"
+
diff --git a/gdb/testsuite/gdb.guile/scm-parameter.exp b/gdb/testsuite/gdb.guile/scm-parameter.exp
index d10e9d24065..eabd17980e7 100644
--- a/gdb/testsuite/gdb.guile/scm-parameter.exp
+++ b/gdb/testsuite/gdb.guile/scm-parameter.exp
@@ -389,3 +389,50 @@ with_test_prefix "previously-ambiguous" {
}
rename scm_param_test_maybe_no_output ""
+
+# Test a color parameter.
+
+with_ansi_styling_terminal {
+ # This enables 256 colors support and disables colors approximation.
+ setenv TERM xterm-256color
+ setenv COLORTERM truecolor
+
+ # Start with a fresh gdb.
+ gdb_exit
+ gdb_start
+ gdb_reinitialize_dir $srcdir/$subdir
+
+ gdb_install_guile_utils
+ gdb_install_guile_module
+
+ # We use "." here instead of ":" so that this works on win32 too.
+ set escaped_directory [string_to_regexp "$srcdir/$subdir"]
+
+ gdb_test_multiline "color gdb parameter" \
+ "guile" "" \
+ "(define test-color-param" "" \
+ " (make-parameter \"print test-color-param\"" "" \
+ " #:command-class COMMAND_DATA" "" \
+ " #:parameter-type PARAM_COLOR" "" \
+ " #:doc \"When set, test param does something useful. When disabled, does nothing.\"" "" \
+ " #:show-doc \"Show the state of the test-color-param.\"" "" \
+ " #:set-doc \"Set the state of the test-color-param.\"" "" \
+ " #:show-func (lambda (self value)" "" \
+ " (format #f \"The state of the test-color-param is ~a.\" value))" "" \
+ " #:initial-value (make-color \"green\")))" "" \
+ "(register-parameter! test-color-param)" "" \
+ "end"
+
+ with_test_prefix "test-color-param" {
+ with_test_prefix "initial-value" {
+ gdb_test "guile (print (parameter-value test-color-param))" "= #<gdb:color green COLORSPACE_ANSI_8COLOR>" "color parameter value (green)"
+ gdb_test "show print test-color-param" "The state of the test-color-param is green." "show initial value"
+ gdb_test_no_output "set print test-color-param 255"
+ }
+ with_test_prefix "new-value" {
+ gdb_test "show print test-color-param" "The state of the test-color-param is 255." "show new value"
+ gdb_test "guile (print (parameter-value test-color-param))" "= #<gdb:color 255 COLORSPACE_XTERM_256COLOR>" "color parameter value (255)"
+ gdb_test "set print test-color-param 256" "integer 256 out of range.*" "set invalid color parameter"
+ }
+ }
+}
diff --git a/gdb/testsuite/gdb.python/py-color.exp b/gdb/testsuite/gdb.python/py-color.exp
new file mode 100644
index 00000000000..8387fbfe71a
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-color.exp
@@ -0,0 +1,100 @@
+# 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
+
+require allow_python_tests
+
+# Start with a fresh gdb.
+clean_restart
+
+gdb_test_no_output "python print_color_attrs = lambda c: print (c, c.colorspace, c.is_none, c.is_indexed, c.is_direct)" \
+ "print_color_attrs helper"
+
+gdb_test_no_output "python c = gdb.Color ()" \
+ "create color without params"
+gdb_test "python print_color_attrs (c)" "none 0 True False False" \
+ "print attrs of a color without params"
+
+gdb_test_no_output "python c = gdb.Color ('green')" \
+ "create color from basic name string"
+gdb_test "python print_color_attrs (c)" "green 1 False True False" \
+ "print attrs of a basic color name"
+gdb_test "python print (c.index)" "2" \
+ "print index of a basic color name"
+
+gdb_test_no_output "python c = gdb.Color (2)" \
+ "create color from basic index"
+gdb_test "python print_color_attrs (c)" "green 1 False True False" \
+ "print attrs of a basic color"
+gdb_test "python print (c.index)" "2" \
+ "print index of a basic color"
+
+gdb_test_no_output "python c = gdb.Color (14)" \
+ "create color from integer 14"
+gdb_test "python print_color_attrs (c)" "14 2 False True False" \
+ "print attrs of an color 14"
+gdb_test "python print (c.index)" "14" \
+ "print index of color 14"
+
+gdb_test_no_output "python c = gdb.Color (2, gdb.COLORSPACE_ANSI_8COLOR)" \
+ "create color from basic index and ansi colorspace"
+gdb_test "python print_color_attrs (c)" "green 1 False True False" \
+ "print attrs of a basic color with ansi colorspace"
+gdb_test "python print (c.index)" "2" \
+ "print index of a basic color with ansi colorspace"
+
+gdb_test_no_output "python c = gdb.Color (2, gdb.COLORSPACE_XTERM_256COLOR)" \
+ "create color from basic index and xterm256 colorspace"
+gdb_test "python print_color_attrs (c)" "2 3 False True False" \
+ "print attrs of a basic color with xterm256 colorspace"
+gdb_test "python print (c.index)" "2" \
+ "print index of a basic color with xterm256 colorspace"
+
+gdb_test_no_output "python c = gdb.Color ((171, 205, 239), gdb.COLORSPACE_RGB_24BIT)" \
+ "create color from rgb components"
+gdb_test "python print_color_attrs (c)" "#ABCDEF 4 False False True" \
+ "print attrs of an RGB color"
+gdb_test "python print (c.components)" "\\(171, 205, 239\\)" \
+ "print components of an RGB color"
+
+gdb_test_no_output "python c = gdb.Color ('none')" \
+ "create color from string none"
+gdb_test "python print_color_attrs (c)" "none 0 True False False" \
+ "print attrs of a color none"
+
+gdb_test_no_output "python c = gdb.Color ('254')" \
+ "create color from string 254"
+gdb_test "python print_color_attrs (c)" "254 3 False True False" \
+ "print attrs of an color 254"
+gdb_test "python print (c.index)" "254" \
+ "print index of color 254"
+
+gdb_test_no_output "python c_none = gdb.Color ('none')" \
+ "save default color"
+gdb_test_no_output "python c_red = gdb.Color ('red')" \
+ "save blue color"
+gdb_test_no_output "python c_green = gdb.Color ('green')" \
+ "save yellow color"
+gdb_test [concat "python print (c_red.escape_sequence (True) + " \
+ "c_green.escape_sequence (False) + 'red on green' + " \
+ "c_none.escape_sequence (False) + ' red on default' + " \
+ "c_none.escape_sequence (True))"] \
+ "\033\\\[31m\033\\\[42mred on green\033\\\[49m red on default\033\\\[39m" \
+ "escape sequences"
+
diff --git a/gdb/testsuite/gdb.python/py-parameter.exp b/gdb/testsuite/gdb.python/py-parameter.exp
index de524f49ad6..6e6a6007198 100644
--- a/gdb/testsuite/gdb.python/py-parameter.exp
+++ b/gdb/testsuite/gdb.python/py-parameter.exp
@@ -185,6 +185,58 @@ proc_with_prefix test_enum_parameter { } {
"Undefined item: \"three\".*" "set invalid enum parameter"
}
+# Test an color parameter.
+proc_with_prefix test_color_parameter { } {
+ global env
+ 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)" \
+ ".*Error occurred in Python: Palette color index 256 is out of range.*" "set invalid color value"
+ }
+}
+
# Test a file parameter.
proc_with_prefix test_file_parameter { } {
clean_restart
@@ -623,6 +675,7 @@ test_directories
test_data_directory
test_boolean_parameter
test_enum_parameter
+test_color_parameter
test_file_parameter
test_undocumented_parameter
test_really_undocumented_parameter
diff --git a/gdb/testsuite/lib/gdb-utils.exp b/gdb/testsuite/lib/gdb-utils.exp
index 41989da3ed2..ea02bff27e4 100644
--- a/gdb/testsuite/lib/gdb-utils.exp
+++ b/gdb/testsuite/lib/gdb-utils.exp
@@ -67,18 +67,22 @@ 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"
}
# gdb_get_bp_addr num
diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index c993f48fd34..1c579dc0d47 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -10061,10 +10061,11 @@ proc with_override { name override body } {
# Run BODY after setting the TERM environment variable to 'ansi', and
# unsetting the NO_COLOR environment variable.
proc with_ansi_styling_terminal { body } {
- save_vars { ::env(TERM) ::env(NO_COLOR) } {
+ save_vars { ::env(TERM) ::env(NO_COLOR) ::env(COLORTERM) } {
# Set environment variables to allow styling.
setenv TERM ansi
unset -nocomplain ::env(NO_COLOR)
+ unset -nocomplain ::env(COLORTERM)
set code [catch {uplevel 1 $body} result]
}
diff --git a/gdb/top.c b/gdb/top.c
index d6bf1d448d5..1460c9cad6c 100644
--- a/gdb/top.c
+++ b/gdb/top.c
@@ -2122,6 +2122,17 @@ show_startup_quiet (struct ui_file *file, int from_tty,
value);
}
+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)
{
@@ -2326,6 +2337,9 @@ gdb_init ()
during startup. */
set_language (language_c);
expected_language = current_language; /* Don't warn about the change. */
+
+ /* Create $_colorsupport convenience variable. */
+ init_colorsupport_var ();
}
void _initialize_top ();
diff --git a/gdb/ui-style.c b/gdb/ui-style.c
index 952102e30c3..ae54ca8634e 100644
--- a/gdb/ui-style.c
+++ b/gdb/ui-style.c
@@ -17,6 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include "ui-style.h"
+#include "gdb_curses.h"
#include "gdbsupport/gdb_regex.h"
/* A regular expression that is used for matching ANSI terminal escape
@@ -46,91 +47,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. */
@@ -139,26 +278,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;
@@ -315,9 +451,11 @@ ui_file_style::parse (const char *buf, size_t *n_read)
case 35:
case 36:
case 37:
+ m_foreground = color (value - 30);
+ break;
/* Note: not 38. */
case 39:
- m_foreground = color (value - 30);
+ m_foreground = NONE;
break;
case 40:
@@ -328,9 +466,11 @@ ui_file_style::parse (const char *buf, size_t *n_read)
case 45:
case 46:
case 47:
+ m_background = color (value - 40);
+ break;
/* Note: not 48. */
case 49:
- m_background = color (value - 40);
+ m_background = NONE;
break;
case 90:
@@ -412,3 +552,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 1b7b5fafb9d..a13618dfce5 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 4dc3c842c47..72bc878582a 100644
--- a/gdb/unittests/style-selftests.c
+++ b/gdb/unittests/style-selftests.c
@@ -58,7 +58,7 @@ run_tests ()
SELF_CHECK (style.get_background ().is_none ());
SELF_CHECK (style.get_intensity () == ui_file_style::NORMAL);
SELF_CHECK (style.is_reverse ());
- SELF_CHECK (style.to_ansi () == "\033[7m");
+ SELF_CHECK (style.to_ansi () == "\033[39;49;22;7m");
style = ui_file_style ();
SELF_CHECK (style.parse ("\033[32;1m", &n_read));
@@ -68,7 +68,7 @@ run_tests ()
SELF_CHECK (style.get_background ().is_none ());
SELF_CHECK (style.get_intensity () == ui_file_style::BOLD);
SELF_CHECK (!style.is_reverse ());
- SELF_CHECK (style.to_ansi () == "\033[32;1m");
+ SELF_CHECK (style.to_ansi () == "\033[32;49;1;27m");
style = ui_file_style ();
SELF_CHECK (style.parse ("\033[38;5;112;48;5;249m", &n_read));
@@ -81,7 +81,7 @@ run_tests ()
CHECK_RGB (0xb2, 0xb2, 0xb2);
SELF_CHECK (style.get_intensity () == ui_file_style::NORMAL);
SELF_CHECK (!style.is_reverse ());
- SELF_CHECK (style.to_ansi () == "\033[38;5;112;48;5;249m");
+ SELF_CHECK (style.to_ansi () == "\033[38;5;112;48;5;249;22;27m");
style = ui_file_style ();
SELF_CHECK (style.parse ("\033[38;2;83;84;85;48;2;0;1;254;2;7m", &n_read));
--
2.34.1
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v6] Add an option with a color type.
2024-09-14 18:06 ` Andrei Pikas
@ 2024-09-14 19:38 ` Tom Tromey
2024-09-14 19:43 ` Andrei Pikas
0 siblings, 1 reply; 33+ messages in thread
From: Tom Tromey @ 2024-09-14 19:38 UTC (permalink / raw)
To: Andrei Pikas; +Cc: Tom Tromey, gdb-patches
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
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v6] Add an option with a color type.
2024-09-14 19:38 ` Tom Tromey
@ 2024-09-14 19:43 ` Andrei Pikas
0 siblings, 0 replies; 33+ messages in thread
From: Andrei Pikas @ 2024-09-14 19:43 UTC (permalink / raw)
To: Tom Tromey; +Cc: gdb-patches
[-- Attachment #1: Type: text/plain, Size: 136 bytes --]
> 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.
[-- Attachment #2: Type: text/html, Size: 383 bytes --]
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v7] Add an option with a color type.
2024-09-14 19:04 ` [PATCH v7] " Andrei Pikas
@ 2024-09-15 5:37 ` Eli Zaretskii
2024-10-05 19:11 ` Andrei Pikas
2024-10-04 17:55 ` Tom Tromey
1 sibling, 1 reply; 33+ messages in thread
From: Eli Zaretskii @ 2024-09-15 5:37 UTC (permalink / raw)
To: Andrei Pikas; +Cc: tom, gdb-patches, gdb
> From: Andrei Pikas <gdb@mail.api.win>
> Cc: gdb-patches@sourceware.org,
> Andrei Pikas <gdb@mail.api.win>
> Date: Sat, 14 Sep 2024 22:04:52 +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.
> PATCH v7 changes: rebase onto master and fixes after review.
> ---
> 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 | 443 ++++++++++++++++++++++
> gdb/guile/scm-param.c | 33 +-
> gdb/python/py-color.c | 347 +++++++++++++++++
> gdb/python/py-color.h | 35 ++
> gdb/python/py-param.c | 38 +-
> gdb/python/python.c | 7 +
> gdb/testsuite/gdb.base/style.exp | 197 ++++++++++
> gdb/testsuite/gdb.guile/scm-color.exp | 110 ++++++
> gdb/testsuite/gdb.guile/scm-parameter.exp | 47 +++
> gdb/testsuite/gdb.python/py-color.exp | 100 +++++
> gdb/testsuite/gdb.python/py-parameter.exp | 53 +++
> gdb/testsuite/lib/gdb-utils.exp | 22 +-
> gdb/testsuite/lib/gdb.exp | 3 +-
> gdb/top.c | 14 +
> gdb/ui-style.c | 331 ++++++++++++----
> gdb/ui-style.h | 142 ++++++-
> gdb/unittests/style-selftests.c | 6 +-
> 33 files changed, 2414 insertions(+), 161 deletions(-)
> create mode 100644 gdb/guile/scm-color.c
> create mode 100644 gdb/python/py-color.c
> create mode 100644 gdb/python/py-color.h
> create mode 100644 gdb/testsuite/gdb.guile/scm-color.exp
> create mode 100644 gdb/testsuite/gdb.python/py-color.exp
Thanks.
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -37,11 +37,40 @@
> history has been reached. It also specifies that the forward execution can
> continue, and the recording will also continue.
>
> +* "set style" commands now supports numeric format for basic colors
> + from 0 to 255 and #RRGGBB format for TrueColor.
> +
> +* New built-in convenience variable $_colorsupport provides comma-separated
> + list of color space names supported by terminal. 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
> +
I'd prefer to have a plain-English explanation of "color space" here,
instead of trying to explain it via script which uses the variable.
> --- a/gdb/cli/cli-style.c
> +++ b/gdb/cli/cli-style.c
> @@ -42,20 +42,6 @@ bool source_styling = true;
>
> bool disassembler_styling = true;
>
> -/* Name of colors; must correspond to ui_file_style::basic_color. */
> -static const char * const cli_colors[] = {
> - "none",
> - "black",
> - "red",
> - "green",
> - "yellow",
> - "blue",
> - "magenta",
> - "cyan",
> - "white",
> - nullptr
> -};
I don't understand the effect of this and related changes, and the log
message says nothing about this. Could you please explain why you
remove these names and their uses, and what will be used in their
stead? And why?
Regardless, were these changes tested in the MinGW port of GDB? It
emulates Posix terminal handling of colors via SGR escape sequences,
and I wonder whether these changes might somehow break styling support
in the MinGW port.
> +@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}.
What does this return for the MS-Windows terminal? aixterm_16color?
IOW, will any 16-color terminal return aixterm_16color? I think this
should be documented, and perhaps we should remove the "aix" part from
the name (since it is not necessarily specific to AIX).
Also, please add a "@cindex color space" entry before the above text,
as I believe this is where we describe this term. For the same
reason, I think the first use of "color spaces" should have the @dfn
markup.
> -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.
Should the RGB specs always use 2 hex characters per component,
or can one use RRRGGGBBB etc. as well? This should be documented.
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v7] Add an option with a color type.
2024-09-14 19:04 ` [PATCH v7] " Andrei Pikas
2024-09-15 5:37 ` Eli Zaretskii
@ 2024-10-04 17:55 ` Tom Tromey
2024-10-05 17:55 ` Andrei Pikas
2024-10-05 19:27 ` [PATCH v8] " Andrei Pikas
1 sibling, 2 replies; 33+ messages in thread
From: Tom Tromey @ 2024-10-04 17:55 UTC (permalink / raw)
To: Andrei Pikas; +Cc: tom, gdb-patches
>>>>> "Andrei" == Andrei Pikas <gdb@mail.api.win> writes:
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\\".
Thank you for the patch. I'm sorry about the long delays on this; gdb
has an ongoing review bandwidth problem. Anyway, I like this a lot and
I would like to see it get in.
I have a fair number of notes here but nothing really major.
Andrei> + case var_color:
Andrei> + {
Andrei> + std::string s = var.get<ui_file_style::color> ().to_string ();
Andrei> + return value_cstring (s.c_str (), s.size (),
Andrei> + builtin_type (gdbarch)->builtin_char);
Other code in this function uses:
return current_language->value_string (gdbarch, value, len);
Andrei> + if (*text == '\0')
Andrei> + {
Andrei> + /* Convenience to let the user know what the option
Andrei> + can accept. Note there's no common prefix between
Andrei> + the strings on purpose, so that complete_on_enum doesn't do
Andrei> + a partial match. */
Andrei> + tracker.add_completion (make_unique_xstrdup ("NUMBER"));
Andrei> + tracker.add_completion (make_unique_xstrdup ("#RRGGBB"));
Andrei> + }
This is a clever idea and I somewhat wish readline had a more robust
completion API to show this kind of info "inline".
Andrei> +ui_file_style::color
Andrei> +parse_cli_var_color (const char **args)
Andrei> +{
Andrei> + /* Do a "set" command. ARG is nullptr if no argument, or the
Andrei> + text of the argument. */
Andrei> +
Andrei> + if (args == nullptr || *args == nullptr || **args == '\0')
Andrei> + {
Andrei> + std::string msg;
Andrei> +
Andrei> + for (size_t i = 0; ui_file_style::basic_color_enums[i]; ++i)
Andrei> + {
Andrei> + msg.append ("\"");
Andrei> + msg.append (ui_file_style::basic_color_enums[i]);
Andrei> + msg.append ("\", ");
I think this ends up appending an extra comma. Perhaps the
comma-addition should be done at the star of the loop, if "i > 0".
Andrei> diff --git a/gdb/cli/cli-option.c b/gdb/cli/cli-option.c
Andrei> index 05539285c80..3a165ef6d2c 100644
Andrei> --- a/gdb/cli/cli-option.c
Andrei> +++ b/gdb/cli/cli-option.c
Andrei> @@ -45,6 +45,9 @@ union option_value
Andrei> /* For var_string options. This is malloc-allocated. */
Andrei> std::string *string;
Andrei> +
Andrei> + /* For var_color options. */
Andrei> + ui_file_style::color color = ui_file_style::NONE;
I didn't even know inline initializers were possible in a union.
Anyway I think it's probably better to leave this off.
Andrei> +@node Colors In Python
Andrei> +@subsection Python representation of colors
This new node doesn't seem to be referenced from a menu anywhere. I
think it has to be added to the appropriate spot in the menu in the
"Python API" node.
Andrei> diff --git a/gdb/python/py-color.c b/gdb/python/py-color.c
Andrei> new file mode 100644
Andrei> index 00000000000..1d72cc3e7d1
Andrei> --- /dev/null
Andrei> +++ b/gdb/python/py-color.c
Andrei> @@ -0,0 +1,347 @@
Andrei> +/* Python interface to ui_file_style::color objects.
Andrei> +
Andrei> + Copyright (C) 2008-2022 Free Software Foundation, Inc.
I suspect this should just be 2024.
Andrei> +/* See py-color.h. */
Andrei> +bool
Andrei> +gdbpy_is_color (PyObject *obj)
Andrei> +{
Andrei> + return PyObject_IsInstance (obj, (PyObject *)&colorpy_object_type);
gdb style is a space after the closing paren of a cast.
Andrei> +static PyObject *
Andrei> +colorpy_escape_sequence (PyObject *self, PyObject *is_fg_obj)
Andrei> +{
...
Andrei> + bool is_fg = PyObject_IsTrue (is_fg_obj);
In theory, this can fail, returning -1.
In practice, the code already calls PyBool_Check, so I think just a
direct comparison against Py_True would be fine.
Andrei> +static int
Andrei> +colorpy_init (PyObject *self, PyObject *args, PyObject *kwds)
...
Andrei> + if (colorspace_obj)
Andrei> + {
Andrei> + if (PyLong_Check (colorspace_obj))
Andrei> + {
Andrei> + long colorspace_id = -1;
Andrei> + if (! gdb_py_int_as_long (colorspace_obj, &colorspace_id))
Andrei> + return -1;
Andrei> + if (colorspace_id < INT_MIN || colorspace_id > INT_MAX)
Andrei> + error (_("colorspace %ld is out of range."), colorspace_id);
It seems like the range check here should use the colorspace enum values.
Andrei> + Py_ssize_t tuple_size = PyTuple_Size (value_obj);
Andrei> + if (tuple_size != 3)
Andrei> + error (_("Tuple value with RGB must be of size 3."));
If PyTuple_Size returns -1, this should early-return.
Andrei> + if (!str)
Andrei> + return -1;
gdb uses comparisons like 'str == nullptr' here.
Andrei> + if (colorspace_obj != nullptr
Andrei> + && colorspace != obj->color.colorspace ())
Andrei> + error (_("colorspace doesn't match to value."));
I would s/to/the in this error message.
Andrei> + catch (const gdb_exception &except)
Andrei> + {
Andrei> + gdbpy_convert_exception (except);
Andrei> + return -1;
Andrei> + }
A newish change landed and now this is written
return gdbpy_handle_exception (-1, except);
Andrei> +/* Deallocate function for a gdb.Color. */
Andrei> +
Andrei> +static void
Andrei> +colorpy_dealloc (PyObject *)
Andrei> +{
Andrei> +}
I wonder if this is necessary.
If so maybe a comment explaining why would be good.
Andrei> +/* Initialize the 'color' module. */
Andrei> +static int
Andrei> +gdbpy_initialize_color (void)
Andrei> +{
Andrei> + colorpy_object_type.tp_new = PyType_GenericNew;
Andrei> + if (PyType_Ready (&colorpy_object_type) < 0)
Andrei> + return -1;
A recent change means that this should now call gdbpy_type_ready
instead. This also removes the need to call gdb_pymodule_addobject, so
this call can be lowered to the end of this function.
Andrei> diff --git a/gdb/python/py-color.h b/gdb/python/py-color.h
...
Andrei> +/* Extracts value from OBJ object of gdb.Color type. */
Andrei> +extern const ui_file_style::color & gdbpy_get_color (PyObject *obj);
No space after the "&".
Andrei> - memset (&obj->value, 0, sizeof (obj->value));
Andrei> + obj->value = {}; /* zeros initialization */
Was this needed?
Andrei> diff --git a/gdb/testsuite/gdb.guile/scm-color.exp b/gdb/testsuite/gdb.guile/scm-color.exp
Andrei> new file mode 100644
Andrei> index 00000000000..e5773650f2f
Andrei> --- /dev/null
Andrei> +++ b/gdb/testsuite/gdb.guile/scm-color.exp
Andrei> @@ -0,0 +1,110 @@
Andrei> +# Copyright (C) 2010-2022 Free Software Foundation, Inc.
Another date to change. I'd suggest looking at all of them.
Andrei> diff --git a/gdb/testsuite/gdb.python/py-parameter.exp b/gdb/testsuite/gdb.python/py-parameter.exp
Andrei> index de524f49ad6..6e6a6007198 100644
Andrei> --- a/gdb/testsuite/gdb.python/py-parameter.exp
Andrei> +++ b/gdb/testsuite/gdb.python/py-parameter.exp
Andrei> @@ -185,6 +185,58 @@ proc_with_prefix test_enum_parameter { } {
Andrei> "Undefined item: \"three\".*" "set invalid enum parameter"
Andrei> }
Andrei> +# Test an color parameter.
Andrei> +proc_with_prefix test_color_parameter { } {
Andrei> + global env
Andrei> + save_vars { env(TERM) env(COLORTERM) } {
This should probably use with_ansi_styling_terminal to avoid having
NO_COLOR set?
Andrei> +static void
Andrei> +init_colorsupport_var (void)
You should use "()" instead of "(void)". The latter is a leftover
C-ism.
Andrei> +{
Andrei> + const std::vector<color_space>& cs = colorsupport ();
Also move the "&" after the space.
Andrei> +/* See ui-style.h. */
Andrei> +/* Must correspond to ui_file_style::basic_color. */
Andrei> +const std::vector<const char *> ui_file_style::basic_color_enums = {
Andrei> + "none",
Andrei> + "black",
Andrei> + "red",
Andrei> + "green",
Andrei> + "yellow",
Andrei> + "blue",
Andrei> + "magenta",
Andrei> + "cyan",
Andrei> + "white",
Andrei> + nullptr
Andrei> +};
I guess the nullptr is needed since although it is a vector now, it's
still passed to some null-expecting API? Like maybe completion?
I wonder if a vector can be constexpr.
Andrei> bool
Andrei> ui_file_style::color::append_ansi (bool is_fg, std::string *str) const
Andrei> {
...
Andrei> + else
Andrei> + return false;
I wonder when this can happen and if it could somehow be made
impossible.
Andrei> +const std::vector<color_space> &
Andrei> +colorsupport ()
Andrei> +{
Andrei> + static const std::vector<color_space> value = []
I didn't realize you could omit the '()' from a lambda.
Andrei> + {
Andrei> + std::vector<color_space> result = {color_space::MONOCHROME};
Andrei> +
Andrei> + int colors = tgetnum ((char *)"Co");
Is the cast really needed?
Andrei> + const char *colorterm = getenv ("COLORTERM");
Andrei> + if (colorterm && (!strcmp (colorterm, "truecolor")
'colorterm != nullptr'
Andrei> +const char *
Andrei> +color_space_name (color_space c)
Andrei> +{
Andrei> + switch (c)
Andrei> + {
Andrei> + case color_space::MONOCHROME: return "monochrome";
Andrei> + case color_space::ANSI_8COLOR: return "ansi_8color";
Andrei> + case color_space::AIXTERM_16COLOR: return "aixterm_16color";
Andrei> + case color_space::XTERM_256COLOR: return "xterm_256color";
Andrei> + case color_space::RGB_24BIT: return "rgb_24bit";
Andrei> + }
Andrei> + error (_("Color space %d has no name."), static_cast<int> (c));
Probably an assert-not-reached is more appropriate here?
Andrei> +}
Andrei> diff --git a/gdb/ui-style.h b/gdb/ui-style.h
Andrei> index 1b7b5fafb9d..a13618dfce5 100644
Andrei> --- a/gdb/ui-style.h
Andrei> +++ b/gdb/ui-style.h
Andrei> @@ -19,6 +19,26 @@
Andrei> #ifndef UI_STYLE_H
Andrei> #define UI_STYLE_H
Andrei> +/* One of the color spaces that usually supported by terminals. */
Andrei> +enum class color_space
Andrei> +{
Andrei> + MONOCHROME, // one default terminal color
Andrei> + ANSI_8COLOR, // foreground colors \e[30m ... \e[37m,
Andrei> + // background colors \e[40m ... \e[47m
Andrei> + AIXTERM_16COLOR, // foreground colors \e[30m ... \e[37m, \e[90m ... \e[97m
Andrei> + // background colors \e[40m ... \e[47m, \e[100m ... \e107m
Andrei> + XTERM_256COLOR, // foreground colors \e[38;5;0m ... \e[38;5;255m
Andrei> + // background colors \e[48;5;0m ... \e[48;5;255m
Andrei> + RGB_24BIT // foreground colors \e[38;2;0;0;0m ... \e[38;2;255;255;255m
Andrei> + // background colors \e[48;2;0;0;0m ... \e[48;2;255;255;255m
gdb doesn't use "//" comments. Also normally gdb doesn't use trailing
comments on the line; instead I'd put them before each constant they
describe.
Tom
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v7] Add an option with a color type.
2024-10-04 17:55 ` Tom Tromey
@ 2024-10-05 17:55 ` Andrei Pikas
2024-10-05 19:27 ` [PATCH v8] " Andrei Pikas
1 sibling, 0 replies; 33+ messages in thread
From: Andrei Pikas @ 2024-10-05 17:55 UTC (permalink / raw)
To: Tom Tromey; +Cc: gdb-patches
Hello.
> Andrei> + if (*text == '\0')
> Andrei> + {
> Andrei> + /* Convenience to let the user know what the option
> Andrei> + can accept. Note there's no common prefix between
> Andrei> + the strings on purpose, so that complete_on_enum doesn't do
> Andrei> + a partial match. */
> Andrei> + tracker.add_completion (make_unique_xstrdup ("NUMBER"));
> Andrei> + tracker.add_completion (make_unique_xstrdup ("#RRGGBB"));
> Andrei> + }
>
> This is a clever idea and I somewhat wish readline had a more robust
> completion API to show this kind of info "inline".
I didn't understand what I have to do about this.
> Andrei> +ui_file_style::color
> Andrei> +parse_cli_var_color (const char **args)
> Andrei> +{
> Andrei> + /* Do a "set" command. ARG is nullptr if no argument, or the
> Andrei> + text of the argument. */
> Andrei> +
> Andrei> + if (args == nullptr || *args == nullptr || **args == '\0')
> Andrei> + {
> Andrei> + std::string msg;
> Andrei> +
> Andrei> + for (size_t i = 0; ui_file_style::basic_color_enums[i]; ++i)
> Andrei> + {
> Andrei> + msg.append ("\"");
> Andrei> + msg.append (ui_file_style::basic_color_enums[i]);
> Andrei> + msg.append ("\", ");
>
> I think this ends up appending an extra comma. Perhaps the
> comma-addition should be done at the star of the loop, if "i > 0".
Yes, comma will be appended after the last color name.
But this is expected comma before the word integer.
The whole string will be
Requires an argument. Valid arguments are "none", "black", "red",
"green", "yellow", "blue", "magenta", "cyan", "white", integer from -1
to 255 or an RGB hex triplet in a format #RRGGBB
I will remove the extra space before integer in the next version of the
patch.
> Andrei> diff --git a/gdb/cli/cli-option.c b/gdb/cli/cli-option.c
> Andrei> index 05539285c80..3a165ef6d2c 100644
> Andrei> --- a/gdb/cli/cli-option.c
> Andrei> +++ b/gdb/cli/cli-option.c
> Andrei> @@ -45,6 +45,9 @@ union option_value
>
> Andrei> /* For var_string options. This is malloc-allocated. */
> Andrei> std::string *string;
> Andrei> +
> Andrei> + /* For var_color options. */
> Andrei> + ui_file_style::color color = ui_file_style::NONE;
>
> I didn't even know inline initializers were possible in a union.
> Anyway I think it's probably better to leave this off.
There is a compilation error without this.
> Andrei> - memset (&obj->value, 0, sizeof (obj->value));
> Andrei> + obj->value = {}; /* zeros initialization */
>
> Was this needed?
Yes. parmpy_variable is not std::is_trivial now because of the
ui_file_style::color. So memset from gdbsupport/poison.h fails on this type.
I will add explicit call to ~color() into parmpy_dealloc in the next
version of the patch. Though this destructor is trivial at the moment.
> Andrei> +/* See ui-style.h. */
> Andrei> +/* Must correspond to ui_file_style::basic_color. */
> Andrei> +const std::vector<const char *> ui_file_style::basic_color_enums = {
> Andrei> + "none",
> Andrei> + "black",
> Andrei> + "red",
> Andrei> + "green",
> Andrei> + "yellow",
> Andrei> + "blue",
> Andrei> + "magenta",
> Andrei> + "cyan",
> Andrei> + "white",
> Andrei> + nullptr
> Andrei> +};
>
> I guess the nullptr is needed since although it is a vector now, it's
> still passed to some null-expecting API? Like maybe completion?
>
> I wonder if a vector can be constexpr.
Yes, it's passed to complete_on_enum. Vector can't be constexpr.
> Andrei> bool
> Andrei> ui_file_style::color::append_ansi (bool is_fg, std::string *str) const
> Andrei> {
> ...
> Andrei> + else
> Andrei> + return false;
>
> I wonder when this can happen and if it could somehow be made
> impossible.
No, this should no happen after all the checks in the constructors.
I will add gdb_assert_not_reached here and change return type to void in
the next version of the patch.
On 04/10/2024 20:55, Tom Tromey wrote:
>>>>>> "Andrei" == Andrei Pikas<gdb@mail.api.win> writes:
> 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\\".
>
> Thank you for the patch. I'm sorry about the long delays on this; gdb
> has an ongoing review bandwidth problem. Anyway, I like this a lot and
> I would like to see it get in.
>
> I have a fair number of notes here but nothing really major.
>
> Andrei> + case var_color:
> Andrei> + {
> Andrei> + std::string s = var.get<ui_file_style::color> ().to_string ();
> Andrei> + return value_cstring (s.c_str (), s.size (),
> Andrei> + builtin_type (gdbarch)->builtin_char);
>
> Other code in this function uses:
>
> return current_language->value_string (gdbarch, value, len);
>
> Andrei> + if (*text == '\0')
> Andrei> + {
> Andrei> + /* Convenience to let the user know what the option
> Andrei> + can accept. Note there's no common prefix between
> Andrei> + the strings on purpose, so that complete_on_enum doesn't do
> Andrei> + a partial match. */
> Andrei> + tracker.add_completion (make_unique_xstrdup ("NUMBER"));
> Andrei> + tracker.add_completion (make_unique_xstrdup ("#RRGGBB"));
> Andrei> + }
>
> This is a clever idea and I somewhat wish readline had a more robust
> completion API to show this kind of info "inline".
>
> Andrei> +ui_file_style::color
> Andrei> +parse_cli_var_color (const char **args)
> Andrei> +{
> Andrei> + /* Do a "set" command. ARG is nullptr if no argument, or the
> Andrei> + text of the argument. */
> Andrei> +
> Andrei> + if (args == nullptr || *args == nullptr || **args == '\0')
> Andrei> + {
> Andrei> + std::string msg;
> Andrei> +
> Andrei> + for (size_t i = 0; ui_file_style::basic_color_enums[i]; ++i)
> Andrei> + {
> Andrei> + msg.append ("\"");
> Andrei> + msg.append (ui_file_style::basic_color_enums[i]);
> Andrei> + msg.append ("\", ");
>
> I think this ends up appending an extra comma. Perhaps the
> comma-addition should be done at the star of the loop, if "i > 0".
>
> Andrei> diff --git a/gdb/cli/cli-option.c b/gdb/cli/cli-option.c
> Andrei> index 05539285c80..3a165ef6d2c 100644
> Andrei> --- a/gdb/cli/cli-option.c
> Andrei> +++ b/gdb/cli/cli-option.c
> Andrei> @@ -45,6 +45,9 @@ union option_value
>
> Andrei> /* For var_string options. This is malloc-allocated. */
> Andrei> std::string *string;
> Andrei> +
> Andrei> + /* For var_color options. */
> Andrei> + ui_file_style::color color = ui_file_style::NONE;
>
> I didn't even know inline initializers were possible in a union.
> Anyway I think it's probably better to leave this off.
>
> Andrei> +@node Colors In Python
> Andrei> +@subsection Python representation of colors
>
> This new node doesn't seem to be referenced from a menu anywhere. I
> think it has to be added to the appropriate spot in the menu in the
> "Python API" node.
>
> Andrei> diff --git a/gdb/python/py-color.c b/gdb/python/py-color.c
> Andrei> new file mode 100644
> Andrei> index 00000000000..1d72cc3e7d1
> Andrei> --- /dev/null
> Andrei> +++ b/gdb/python/py-color.c
> Andrei> @@ -0,0 +1,347 @@
> Andrei> +/* Python interface to ui_file_style::color objects.
> Andrei> +
> Andrei> + Copyright (C) 2008-2022 Free Software Foundation, Inc.
>
> I suspect this should just be 2024.
>
> Andrei> +/* See py-color.h. */
> Andrei> +bool
> Andrei> +gdbpy_is_color (PyObject *obj)
> Andrei> +{
> Andrei> + return PyObject_IsInstance (obj, (PyObject *)&colorpy_object_type);
>
> gdb style is a space after the closing paren of a cast.
>
> Andrei> +static PyObject *
> Andrei> +colorpy_escape_sequence (PyObject *self, PyObject *is_fg_obj)
> Andrei> +{
> ...
> Andrei> + bool is_fg = PyObject_IsTrue (is_fg_obj);
>
> In theory, this can fail, returning -1.
>
> In practice, the code already calls PyBool_Check, so I think just a
> direct comparison against Py_True would be fine.
>
> Andrei> +static int
> Andrei> +colorpy_init (PyObject *self, PyObject *args, PyObject *kwds)
> ...
> Andrei> + if (colorspace_obj)
> Andrei> + {
> Andrei> + if (PyLong_Check (colorspace_obj))
> Andrei> + {
> Andrei> + long colorspace_id = -1;
> Andrei> + if (! gdb_py_int_as_long (colorspace_obj, &colorspace_id))
> Andrei> + return -1;
> Andrei> + if (colorspace_id < INT_MIN || colorspace_id > INT_MAX)
> Andrei> + error (_("colorspace %ld is out of range."), colorspace_id);
>
> It seems like the range check here should use the colorspace enum values.
>
> Andrei> + Py_ssize_t tuple_size = PyTuple_Size (value_obj);
> Andrei> + if (tuple_size != 3)
> Andrei> + error (_("Tuple value with RGB must be of size 3."));
>
> If PyTuple_Size returns -1, this should early-return.
>
> Andrei> + if (!str)
> Andrei> + return -1;
>
> gdb uses comparisons like 'str == nullptr' here.
>
> Andrei> + if (colorspace_obj != nullptr
> Andrei> + && colorspace != obj->color.colorspace ())
> Andrei> + error (_("colorspace doesn't match to value."));
>
> I would s/to/the in this error message.
>
> Andrei> + catch (const gdb_exception &except)
> Andrei> + {
> Andrei> + gdbpy_convert_exception (except);
> Andrei> + return -1;
> Andrei> + }
>
> A newish change landed and now this is written
>
> return gdbpy_handle_exception (-1, except);
>
> Andrei> +/* Deallocate function for a gdb.Color. */
> Andrei> +
> Andrei> +static void
> Andrei> +colorpy_dealloc (PyObject *)
> Andrei> +{
> Andrei> +}
>
> I wonder if this is necessary.
> If so maybe a comment explaining why would be good.
>
> Andrei> +/* Initialize the 'color' module. */
> Andrei> +static int
> Andrei> +gdbpy_initialize_color (void)
> Andrei> +{
> Andrei> + colorpy_object_type.tp_new = PyType_GenericNew;
> Andrei> + if (PyType_Ready (&colorpy_object_type) < 0)
> Andrei> + return -1;
>
> A recent change means that this should now call gdbpy_type_ready
> instead. This also removes the need to call gdb_pymodule_addobject, so
> this call can be lowered to the end of this function.
>
> Andrei> diff --git a/gdb/python/py-color.h b/gdb/python/py-color.h
> ...
> Andrei> +/* Extracts value from OBJ object of gdb.Color type. */
> Andrei> +extern const ui_file_style::color & gdbpy_get_color (PyObject *obj);
>
> No space after the "&".
>
> Andrei> - memset (&obj->value, 0, sizeof (obj->value));
> Andrei> + obj->value = {}; /* zeros initialization */
>
> Was this needed?
>
> Andrei> diff --git a/gdb/testsuite/gdb.guile/scm-color.exp b/gdb/testsuite/gdb.guile/scm-color.exp
> Andrei> new file mode 100644
> Andrei> index 00000000000..e5773650f2f
> Andrei> --- /dev/null
> Andrei> +++ b/gdb/testsuite/gdb.guile/scm-color.exp
> Andrei> @@ -0,0 +1,110 @@
> Andrei> +# Copyright (C) 2010-2022 Free Software Foundation, Inc.
>
> Another date to change. I'd suggest looking at all of them.
>
> Andrei> diff --git a/gdb/testsuite/gdb.python/py-parameter.exp b/gdb/testsuite/gdb.python/py-parameter.exp
> Andrei> index de524f49ad6..6e6a6007198 100644
> Andrei> --- a/gdb/testsuite/gdb.python/py-parameter.exp
> Andrei> +++ b/gdb/testsuite/gdb.python/py-parameter.exp
> Andrei> @@ -185,6 +185,58 @@ proc_with_prefix test_enum_parameter { } {
> Andrei> "Undefined item: \"three\".*" "set invalid enum parameter"
> Andrei> }
>
> Andrei> +# Test an color parameter.
> Andrei> +proc_with_prefix test_color_parameter { } {
> Andrei> + global env
> Andrei> + save_vars { env(TERM) env(COLORTERM) } {
>
> This should probably use with_ansi_styling_terminal to avoid having
> NO_COLOR set?
>
> Andrei> +static void
> Andrei> +init_colorsupport_var (void)
>
> You should use "()" instead of "(void)". The latter is a leftover
> C-ism.
>
> Andrei> +{
> Andrei> + const std::vector<color_space>& cs = colorsupport ();
>
> Also move the "&" after the space.
>
> Andrei> +/* See ui-style.h. */
> Andrei> +/* Must correspond to ui_file_style::basic_color. */
> Andrei> +const std::vector<const char *> ui_file_style::basic_color_enums = {
> Andrei> + "none",
> Andrei> + "black",
> Andrei> + "red",
> Andrei> + "green",
> Andrei> + "yellow",
> Andrei> + "blue",
> Andrei> + "magenta",
> Andrei> + "cyan",
> Andrei> + "white",
> Andrei> + nullptr
> Andrei> +};
>
> I guess the nullptr is needed since although it is a vector now, it's
> still passed to some null-expecting API? Like maybe completion?
>
> I wonder if a vector can be constexpr.
>
> Andrei> bool
> Andrei> ui_file_style::color::append_ansi (bool is_fg, std::string *str) const
> Andrei> {
> ...
> Andrei> + else
> Andrei> + return false;
>
> I wonder when this can happen and if it could somehow be made
> impossible.
>
> Andrei> +const std::vector<color_space> &
> Andrei> +colorsupport ()
> Andrei> +{
> Andrei> + static const std::vector<color_space> value = []
>
> I didn't realize you could omit the '()' from a lambda.
>
> Andrei> + {
> Andrei> + std::vector<color_space> result = {color_space::MONOCHROME};
> Andrei> +
> Andrei> + int colors = tgetnum ((char *)"Co");
>
> Is the cast really needed?
>
> Andrei> + const char *colorterm = getenv ("COLORTERM");
> Andrei> + if (colorterm && (!strcmp (colorterm, "truecolor")
>
> 'colorterm != nullptr'
>
> Andrei> +const char *
> Andrei> +color_space_name (color_space c)
> Andrei> +{
> Andrei> + switch (c)
> Andrei> + {
> Andrei> + case color_space::MONOCHROME: return "monochrome";
> Andrei> + case color_space::ANSI_8COLOR: return "ansi_8color";
> Andrei> + case color_space::AIXTERM_16COLOR: return "aixterm_16color";
> Andrei> + case color_space::XTERM_256COLOR: return "xterm_256color";
> Andrei> + case color_space::RGB_24BIT: return "rgb_24bit";
> Andrei> + }
> Andrei> + error (_("Color space %d has no name."), static_cast<int> (c));
>
> Probably an assert-not-reached is more appropriate here?
>
> Andrei> +}
> Andrei> diff --git a/gdb/ui-style.h b/gdb/ui-style.h
> Andrei> index 1b7b5fafb9d..a13618dfce5 100644
> Andrei> --- a/gdb/ui-style.h
> Andrei> +++ b/gdb/ui-style.h
> Andrei> @@ -19,6 +19,26 @@
> Andrei> #ifndef UI_STYLE_H
> Andrei> #define UI_STYLE_H
>
> Andrei> +/* One of the color spaces that usually supported by terminals. */
> Andrei> +enum class color_space
> Andrei> +{
> Andrei> + MONOCHROME, // one default terminal color
> Andrei> + ANSI_8COLOR, // foreground colors \e[30m ... \e[37m,
> Andrei> + // background colors \e[40m ... \e[47m
> Andrei> + AIXTERM_16COLOR, // foreground colors \e[30m ... \e[37m, \e[90m ... \e[97m
> Andrei> + // background colors \e[40m ... \e[47m, \e[100m ... \e107m
> Andrei> + XTERM_256COLOR, // foreground colors \e[38;5;0m ... \e[38;5;255m
> Andrei> + // background colors \e[48;5;0m ... \e[48;5;255m
> Andrei> + RGB_24BIT // foreground colors \e[38;2;0;0;0m ... \e[38;2;255;255;255m
> Andrei> + // background colors \e[48;2;0;0;0m ... \e[48;2;255;255;255m
>
> gdb doesn't use "//" comments. Also normally gdb doesn't use trailing
> comments on the line; instead I'd put them before each constant they
> describe.
>
> Tom
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v7] Add an option with a color type.
2024-09-15 5:37 ` Eli Zaretskii
@ 2024-10-05 19:11 ` Andrei Pikas
2024-10-05 19:40 ` Eli Zaretskii
0 siblings, 1 reply; 33+ messages in thread
From: Andrei Pikas @ 2024-10-05 19:11 UTC (permalink / raw)
To: Eli Zaretskii; +Cc: tom, gdb-patches
Hello.
> --- a/gdb/cli/cli-style.c
> +++ b/gdb/cli/cli-style.c
> @@ -42,20 +42,6 @@ bool source_styling = true;
>
> bool disassembler_styling = true;
>
> -/* Name of colors; must correspond to ui_file_style::basic_color. */
> -static const char * const cli_colors[] = {
> - "none",
> - "black",
> - "red",
> - "green",
> - "yellow",
> - "blue",
> - "magenta",
> - "cyan",
> - "white",
> - nullptr
> -};
> I don't understand the effect of this and related changes, and the log
> message says nothing about this. Could you please explain why you
> remove these names and their uses, and what will be used in their
> stead? And why?
They not removed. They moved to ui-style.c under the name
ui_file_style::basic_color_enums and became public to other files.
> Regardless, were these changes tested in the MinGW port of GDB? It
> emulates Posix terminal handling of colors via SGR escape sequences,
> and I wonder whether these changes might somehow break styling support
> in the MinGW port.
No, I can't build gdb with MinGW.
The same escape sequences \033[30m ... \033[37m will be used
for the basic eight colors as before. This patch only adds the possibility
for the user to setup another colors in addition to the basic. But they
are not used by default, so the styling will not be broken.
> +@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}.
> What does this return for the MS-Windows terminal? aixterm_16color?
> IOW, will any 16-color terminal return aixterm_16color? I think this
> should be documented, and perhaps we should remove the "aix" part from
> the name (since it is not necessarily specific to AIX).
tgetnum("Co") returns 8 in msys and 256 in cmd.exe because
TERM=xterm in msys and TERM is unset in cmd.exe.
When I set TERM=xterm or set TERM=ansi it returns 8.
monochrome color space is enabled always.
ansi_8color is enabled when tgetnum("Co") returns >= 8.
aixterm_16color is enabled when tgetnum("Co") returns >= 16.
xterm_256color is enabled when tgetnum("Co") returns >= 256.
rgb_24bit is enabled when COLORTERM=truecolor or 24bit.
I will document that it depends on "Co" termcap, TERM and COLORTERM
in the next patch version.
I don't think that "aix" part should be removed. Because there is no
standard
for the escape sequences \033[90m ... \033[97m. They where first used in the
aixterm and it is the only way to refer them unambiguously. Any other
terminal
in theory may invent it's own 16 colors and escape sequences for them.
On 15/09/2024 08:37, Eli Zaretskii wrote:
>> From: Andrei Pikas <gdb@mail.api.win>
>> Cc: gdb-patches@sourceware.org,
>> Andrei Pikas <gdb@mail.api.win>
>> Date: Sat, 14 Sep 2024 22:04:52 +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.
>> PATCH v7 changes: rebase onto master and fixes after review.
>> ---
>> 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 | 443 ++++++++++++++++++++++
>> gdb/guile/scm-param.c | 33 +-
>> gdb/python/py-color.c | 347 +++++++++++++++++
>> gdb/python/py-color.h | 35 ++
>> gdb/python/py-param.c | 38 +-
>> gdb/python/python.c | 7 +
>> gdb/testsuite/gdb.base/style.exp | 197 ++++++++++
>> gdb/testsuite/gdb.guile/scm-color.exp | 110 ++++++
>> gdb/testsuite/gdb.guile/scm-parameter.exp | 47 +++
>> gdb/testsuite/gdb.python/py-color.exp | 100 +++++
>> gdb/testsuite/gdb.python/py-parameter.exp | 53 +++
>> gdb/testsuite/lib/gdb-utils.exp | 22 +-
>> gdb/testsuite/lib/gdb.exp | 3 +-
>> gdb/top.c | 14 +
>> gdb/ui-style.c | 331 ++++++++++++----
>> gdb/ui-style.h | 142 ++++++-
>> gdb/unittests/style-selftests.c | 6 +-
>> 33 files changed, 2414 insertions(+), 161 deletions(-)
>> create mode 100644 gdb/guile/scm-color.c
>> create mode 100644 gdb/python/py-color.c
>> create mode 100644 gdb/python/py-color.h
>> create mode 100644 gdb/testsuite/gdb.guile/scm-color.exp
>> create mode 100644 gdb/testsuite/gdb.python/py-color.exp
> Thanks.
>
>> --- a/gdb/NEWS
>> +++ b/gdb/NEWS
>> @@ -37,11 +37,40 @@
>> history has been reached. It also specifies that the forward execution can
>> continue, and the recording will also continue.
>>
>> +* "set style" commands now supports numeric format for basic colors
>> + from 0 to 255 and #RRGGBB format for TrueColor.
>> +
>> +* New built-in convenience variable $_colorsupport provides comma-separated
>> + list of color space names supported by terminal. 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
>> +
> I'd prefer to have a plain-English explanation of "color space" here,
> instead of trying to explain it via script which uses the variable.
>
>> --- a/gdb/cli/cli-style.c
>> +++ b/gdb/cli/cli-style.c
>> @@ -42,20 +42,6 @@ bool source_styling = true;
>>
>> bool disassembler_styling = true;
>>
>> -/* Name of colors; must correspond to ui_file_style::basic_color. */
>> -static const char * const cli_colors[] = {
>> - "none",
>> - "black",
>> - "red",
>> - "green",
>> - "yellow",
>> - "blue",
>> - "magenta",
>> - "cyan",
>> - "white",
>> - nullptr
>> -};
> I don't understand the effect of this and related changes, and the log
> message says nothing about this. Could you please explain why you
> remove these names and their uses, and what will be used in their
> stead? And why?
>
> Regardless, were these changes tested in the MinGW port of GDB? It
> emulates Posix terminal handling of colors via SGR escape sequences,
> and I wonder whether these changes might somehow break styling support
> in the MinGW port.
>
>> +@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}.
> What does this return for the MS-Windows terminal? aixterm_16color?
> IOW, will any 16-color terminal return aixterm_16color? I think this
> should be documented, and perhaps we should remove the "aix" part from
> the name (since it is not necessarily specific to AIX).
>
> Also, please add a "@cindex color space" entry before the above text,
> as I believe this is where we describe this term. For the same
> reason, I think the first use of "color spaces" should have the @dfn
> markup.
>
>> -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.
> Should the RGB specs always use 2 hex characters per component,
> or can one use RRRGGGBBB etc. as well? This should be documented.
^ permalink raw reply [flat|nested] 33+ messages in thread
* [PATCH v8] Add an option with a color type.
2024-10-04 17:55 ` Tom Tromey
2024-10-05 17:55 ` Andrei Pikas
@ 2024-10-05 19:27 ` Andrei Pikas
2024-10-06 5:40 ` Eli Zaretskii
` (2 more replies)
1 sibling, 3 replies; 33+ messages in thread
From: Andrei Pikas @ 2024-10-05 19:27 UTC (permalink / raw)
To: tom; +Cc: gdb-patches, Andrei Pikas
Colors can be specified as "none" for terminal's default color, as a name of
one of the eight standard colors of ISO/IEC 6429 "black", "red", "green", etc.,
as an RGB hexadecimal tripplet #RRGGBB for 24-bit TrueColor, or as an
integer from 0 to 255. Integers 0 to 7 are the synonyms for the standard
colors. Integers 8-15 are used for the so-called bright colors from the
aixterm extended 16-color palette. Integers 16-255 are the indexes into xterm
extended 256-color palette (usually 6x6x6 cube plus gray ramp). In
general, 256-color palette is terminal dependent and sometimes can be
changed with OSC 4 sequences, e.g. "\033]4;1;rgb:00/FF/00\033\\".
It is the responsibility of the user to verify that the terminal supports
the specified colors.
PATCH v5 changes: documentation fixed.
PATCH v6 changes: documentation fixed.
PATCH v7 changes: rebase onto master and fixes after review.
PATCH v8 changes: fixes after review.
---
gdb/Makefile.in | 2 +
gdb/NEWS | 31 ++
gdb/cli/cli-cmds.c | 6 +
gdb/cli/cli-decode.c | 174 +++++++++
gdb/cli/cli-decode.h | 21 ++
gdb/cli/cli-option.c | 44 +++
gdb/cli/cli-option.h | 21 ++
gdb/cli/cli-setshow.c | 21 ++
gdb/cli/cli-style.c | 49 +--
gdb/cli/cli-style.h | 4 +-
gdb/command.h | 26 +-
gdb/doc/gdb.texinfo | 46 ++-
gdb/doc/guile.texi | 104 ++++++
gdb/doc/python.texi | 98 +++++
gdb/guile/guile-internal.h | 9 +
gdb/guile/guile.c | 1 +
gdb/guile/scm-color.c | 427 ++++++++++++++++++++++
gdb/guile/scm-param.c | 33 +-
gdb/python/py-color.c | 336 +++++++++++++++++
gdb/python/py-color.h | 35 ++
gdb/python/py-param.c | 40 +-
gdb/python/python.c | 7 +
gdb/testsuite/gdb.base/default.exp | 8 +-
gdb/testsuite/gdb.base/style.exp | 197 ++++++++++
gdb/testsuite/gdb.guile/scm-color.exp | 110 ++++++
gdb/testsuite/gdb.guile/scm-parameter.exp | 47 +++
gdb/testsuite/gdb.python/py-color.exp | 100 +++++
gdb/testsuite/gdb.python/py-parameter.exp | 53 +++
gdb/testsuite/lib/gdb-utils.exp | 24 +-
gdb/testsuite/lib/gdb.exp | 3 +-
gdb/top.c | 14 +
gdb/ui-style.c | 353 ++++++++++++++----
gdb/ui-style.h | 157 +++++++-
gdb/unittests/style-selftests.c | 6 +-
34 files changed, 2436 insertions(+), 171 deletions(-)
create mode 100644 gdb/guile/scm-color.c
create mode 100644 gdb/python/py-color.c
create mode 100644 gdb/python/py-color.h
create mode 100644 gdb/testsuite/gdb.guile/scm-color.exp
create mode 100644 gdb/testsuite/gdb.python/py-color.exp
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index bcf1ee45a70..502b4b6f9e5 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -359,6 +359,7 @@ SUBDIR_GUILE_SRCS = \
guile/scm-block.c \
guile/scm-breakpoint.c \
guile/scm-cmd.c \
+ guile/scm-color.c \
guile/scm-disasm.c \
guile/scm-exception.c \
guile/scm-frame.c \
@@ -395,6 +396,7 @@ SUBDIR_PYTHON_SRCS = \
python/py-bpevent.c \
python/py-breakpoint.c \
python/py-cmd.c \
+ python/py-color.c \
python/py-connection.c \
python/py-continueevent.c \
python/py-dap.c \
diff --git a/gdb/NEWS b/gdb/NEWS
index 3a0c8d1049f..f0b17000401 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -49,11 +49,42 @@
history has been reached. It also specifies that the forward execution can
continue, and the recording will also continue.
+* "set style" commands now supports numeric format for basic colors
+ from 0 to 255 and #RRGGBB format for TrueColor.
+
+* New built-in convenience variable $_colorsupport provides comma-separated
+ list of color space names supported by terminal. Each color space name is one
+ of monochrome, ansi_8color, aixterm_16color, xterm_256color or rgb_24bit.
+ It is handy for conditionally using styling colors based on terminal features.
+ For example:
+
+ (gdb) if $_regex ($_colorsupport, ".*(^|,)rgb_24bit($|,).*")
+ >set style filename background #FACADE
+ >else
+ >if $_regex ($_colorsupport, ".*(^|,)xterm_256color($|,).*")
+ >set style filename background 224
+ >else
+ >set style filename background red
+ >end
+ >end
+
* Python API
** Added gdb.record.clear. Clears the trace data of the current recording.
This forces re-decoding of the trace for successive commands.
+ ** New class gdb.Color for dealing with colors.
+
+ ** New constant gdb.PARAM_COLOR represents color type of a
+ gdb.Parameter.value. Parameter's value is gdb.Color instance.
+
+* Guile API
+
+ ** New type <gdb:color> for dealing with colors.
+
+ ** New constant PARAM_COLOR represents color type of a value
+ of a <gdb:parameter> object. Parameter's value is <gdb::color> instance.
+
* Debugger Adapter Protocol changes
** The "scopes" request will now return a scope holding global
diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c
index ea2e156a00c..12d5ac5fe45 100644
--- a/gdb/cli/cli-cmds.c
+++ b/gdb/cli/cli-cmds.c
@@ -2390,6 +2390,11 @@ value_from_setting (const setting &var, struct gdbarch *gdbarch)
return current_language->value_string (gdbarch, value, len);
}
+ case var_color:
+ {
+ std::string s = var.get<ui_file_style::color> ().to_string ();
+ return current_language->value_string (gdbarch, s.c_str (), s.size ());
+ }
default:
gdb_assert_not_reached ("bad var_type");
}
@@ -2437,6 +2442,7 @@ str_value_from_setting (const setting &var, struct gdbarch *gdbarch)
case var_pinteger:
case var_boolean:
case var_auto_boolean:
+ case var_color:
{
std::string cmd_val = get_setshow_command_value_string (var);
diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
index 163012a6bec..527a96ed3e4 100644
--- a/gdb/cli/cli-decode.c
+++ b/gdb/cli/cli-decode.c
@@ -23,6 +23,7 @@
#include "cli/cli-cmds.h"
#include "cli/cli-decode.h"
#include "cli/cli-style.h"
+#include "cli/cli-utils.h"
#include <optional>
/* Prototypes for local functions. */
@@ -739,6 +740,87 @@ add_setshow_enum_cmd (const char *name, command_class theclass,
return cmds;
}
+/* See cli-decode.h. */
+
+void
+complete_on_color (completion_tracker &tracker,
+ const char *text, const char *word)
+{
+ complete_on_enum (tracker, ui_file_style::basic_color_enums.data (),
+ text, word);
+ if (*text == '\0')
+ {
+ /* Convenience to let the user know what the option
+ can accept. Note there's no common prefix between
+ the strings on purpose, so that complete_on_enum doesn't do
+ a partial match. */
+ tracker.add_completion (make_unique_xstrdup ("NUMBER"));
+ tracker.add_completion (make_unique_xstrdup ("#RRGGBB"));
+ }
+}
+
+/* Completer used in color commands. */
+
+static void
+color_completer (struct cmd_list_element *ignore,
+ completion_tracker &tracker,
+ const char *text, const char *word)
+{
+ complete_on_color (tracker, text, word);
+}
+
+
+/* Add element named NAME to command list LIST (the list for set or
+ some sublist thereof). CLASS is as in add_cmd. VAR is address
+ of the variable which will contain the color. */
+
+set_show_commands
+add_setshow_color_cmd (const char *name,
+ enum command_class theclass,
+ ui_file_style::color *var,
+ const char *set_doc,
+ const char *show_doc,
+ const char *help_doc,
+ cmd_func_ftype *set_func,
+ show_value_ftype *show_func,
+ struct cmd_list_element **set_list,
+ struct cmd_list_element **show_list)
+{
+ set_show_commands commands = add_setshow_cmd_full<ui_file_style::color>
+ (name, theclass, var_color, var,
+ set_doc, show_doc, help_doc,
+ nullptr, nullptr, set_func, show_func,
+ set_list, show_list);
+
+ set_cmd_completer (commands.set, color_completer);
+
+ return commands;
+}
+
+/* Same as above but using a getter and a setter function instead of a pointer
+ to a global storage buffer. */
+
+set_show_commands
+add_setshow_color_cmd (const char *name, command_class theclass,
+ const char *set_doc, const char *show_doc,
+ const char *help_doc,
+ setting_func_types<ui_file_style::color>::set set_func,
+ setting_func_types<ui_file_style::color>::get get_func,
+ show_value_ftype *show_func,
+ cmd_list_element **set_list,
+ cmd_list_element **show_list)
+{
+ auto cmds = add_setshow_cmd_full<ui_file_style::color>
+ (name, theclass, var_color, nullptr,
+ set_doc, show_doc, help_doc,
+ set_func, get_func, nullptr, show_func,
+ set_list, show_list);
+
+ set_cmd_completer (cmds.set, color_completer);
+
+ return cmds;
+}
+
/* See cli-decode.h. */
const char * const auto_boolean_enums[] = { "on", "off", "auto", NULL };
@@ -2749,3 +2831,95 @@ cli_user_command_p (struct cmd_list_element *cmd)
{
return cmd->theclass == class_user && cmd->func == do_simple_func;
}
+
+/* See cli-decode.h. */
+
+ui_file_style::color
+parse_cli_var_color (const char **args)
+{
+ /* Do a "set" command. ARG is nullptr if no argument, or the
+ text of the argument. */
+
+ if (args == nullptr || *args == nullptr || **args == '\0')
+ {
+ std::string msg;
+
+ for (size_t i = 0; ui_file_style::basic_color_enums[i]; ++i)
+ {
+ msg.append ("\"");
+ msg.append (ui_file_style::basic_color_enums[i]);
+ msg.append ("\", ");
+ }
+
+ error (_("Requires an argument. Valid arguments are %sinteger from -1 "
+ "to 255 or an RGB hex triplet in a format #RRGGBB"),
+ msg.c_str ());
+ }
+
+ const char *p = skip_to_space (*args);
+ size_t len = p - *args;
+
+ int nmatches = 0;
+ ui_file_style::basic_color match = ui_file_style::NONE;
+ for (int i = 0; ui_file_style::basic_color_enums[i]; ++i)
+ if (strncmp (*args, ui_file_style::basic_color_enums[i], len) == 0)
+ {
+ match = static_cast<ui_file_style::basic_color> (i - 1);
+ if (ui_file_style::basic_color_enums[i][len] == '\0')
+ {
+ nmatches = 1;
+ break; /* Exact match. */
+ }
+ else
+ nmatches++;
+ }
+
+ if (nmatches == 1)
+ {
+ *args += len;
+ return ui_file_style::color (match);
+ }
+
+ if (nmatches > 1)
+ error (_("Ambiguous item \"%.*s\"."), (int) len, *args);
+
+ if (**args != '#')
+ {
+ ULONGEST num = get_ulongest (args);
+ if (num > 255)
+ error (_("integer %s out of range"), pulongest (num));
+ return ui_file_style::color (color_space::XTERM_256COLOR,
+ static_cast<int> (num));
+ }
+
+ /* Try to parse #RRGGBB string. */
+ if (len != 7)
+ error_no_arg (_("invalid RGB hex triplet format"));
+
+ uint8_t r, g, b;
+ int scanned_chars = 0;
+ int parsed_args = sscanf (*args, "#%2" SCNx8 "%2" SCNx8 "%2" SCNx8 "%n",
+ &r, &g, &b, &scanned_chars);
+
+ if (parsed_args != 3 || scanned_chars != 7)
+ error_no_arg (_("invalid RGB hex triplet format"));
+
+ *args += len;
+ return ui_file_style::color (r, g, b);
+}
+
+/* See cli-decode.h. */
+
+ui_file_style::color
+parse_var_color (const char *arg)
+{
+ const char *end_arg = arg;
+ ui_file_style::color color = parse_cli_var_color (&end_arg);
+
+ int len = end_arg - arg;
+ const char *after = skip_spaces (end_arg);
+ if (*after != '\0')
+ error (_("Junk after item \"%.*s\": %s"), len, arg, after);
+
+ return color;
+}
diff --git a/gdb/cli/cli-decode.h b/gdb/cli/cli-decode.h
index 7365c3f0157..5918d3b9dbe 100644
--- a/gdb/cli/cli-decode.h
+++ b/gdb/cli/cli-decode.h
@@ -306,6 +306,27 @@ extern const char * const boolean_enums[];
/* The enums of auto-boolean commands. */
extern const char * const auto_boolean_enums[];
+/* Add the different possible completions of TEXT with color.
+
+ WORD points in the same buffer as TEXT, and completions should be
+ returned relative to this position. For example, suppose TEXT is "foo"
+ and we want to complete to "foobar". If WORD is "oo", return
+ "oobar"; if WORD is "baz/foo", return "baz/foobar". */
+
+extern void complete_on_color (completion_tracker &tracker,
+ const char *text, const char *word);
+
+/* Parse ARGS, an option to a var_color variable.
+ *
+ Either returns the parsed value on success or throws an error. ARGS may be
+ one of strings {none, black, red, green, yellow, blue, magenta,
+ cyan, white}, or color number from 0 to 255, or RGB hex triplet #RRGGBB.
+ */
+extern ui_file_style::color parse_cli_var_color (const char **args);
+
+/* Same as above but additionally check that there is no junk in the end. */
+extern ui_file_style::color parse_var_color (const char *arg);
+
/* Verify whether a given cmd_list_element is a user-defined command.
Return 1 if it is user-defined. Return 0 otherwise. */
diff --git a/gdb/cli/cli-option.c b/gdb/cli/cli-option.c
index 05539285c80..3a165ef6d2c 100644
--- a/gdb/cli/cli-option.c
+++ b/gdb/cli/cli-option.c
@@ -45,6 +45,9 @@ union option_value
/* For var_string options. This is malloc-allocated. */
std::string *string;
+
+ /* For var_color options. */
+ ui_file_style::color color = ui_file_style::NONE;
};
/* Holds an options definition and its value. */
@@ -433,6 +436,35 @@ parse_option (gdb::array_view<const option_def_group> options_group,
val.enumeration = parse_cli_var_enum (args, match->enums);
return option_def_and_value {*match, match_ctx, val};
}
+ case var_color:
+ {
+ if (completion != nullptr)
+ {
+ const char *after_arg = skip_to_space (*args);
+ if (*after_arg == '\0')
+ {
+ complete_on_color (completion->tracker, *args, *args);
+
+ if (completion->tracker.have_completions ())
+ return {};
+ }
+ }
+
+ if (check_for_argument (args, "--"))
+ {
+ /* Treat e.g., "backtrace -entry-values --" as if there
+ was no argument after "-entry-values". This makes
+ parse_cli_var_color throw an error with a suggestion of
+ what are the valid options. */
+ args = nullptr;
+ }
+
+ option_value val;
+ ui_file_style::color color = parse_cli_var_color (args);
+ ui_file_style::color approx_color = color.approximate (colorsupport ());
+ val.color = approx_color;
+ return option_def_and_value {*match, match_ctx, val};
+ }
case var_string:
{
if (check_for_argument (args, "--"))
@@ -611,6 +643,10 @@ save_option_value_in_ctx (std::optional<option_def_and_value> &ov)
*ov->option.var_address.enumeration (ov->option, ov->ctx)
= ov->value->enumeration;
break;
+ case var_color:
+ *ov->option.var_address.color (ov->option, ov->ctx)
+ = ov->value->color;
+ break;
case var_string:
*ov->option.var_address.string (ov->option, ov->ctx)
= std::move (*ov->value->string);
@@ -699,6 +735,14 @@ get_val_type_str (const option_def &opt, std::string &buffer)
}
return buffer.c_str ();
}
+ case var_color:
+ {
+ buffer = "";
+ for (size_t i = 0; ui_file_style::basic_color_enums[i]; ++i)
+ buffer.append (ui_file_style::basic_color_enums[i]).append ("|");
+ buffer += "NUMBER|#RRGGBB";
+ return buffer.c_str ();
+ }
case var_string:
return "STRING";
default:
diff --git a/gdb/cli/cli-option.h b/gdb/cli/cli-option.h
index bbe281d9721..2fc354ef1a2 100644
--- a/gdb/cli/cli-option.h
+++ b/gdb/cli/cli-option.h
@@ -91,6 +91,7 @@ struct option_def
int *(*integer) (const option_def &, void *ctx);
const char **(*enumeration) (const option_def &, void *ctx);
std::string *(*string) (const option_def &, void *ctx);
+ ui_file_style::color *(*color) (const option_def &, void *ctx);
}
var_address;
@@ -308,6 +309,26 @@ struct string_option_def : option_def
}
};
+/* A var_color command line option. */
+
+template<typename Context>
+struct color_option_def : option_def
+{
+ color_option_def (const char *long_option_,
+ ui_file_style::color *(*get_var_address_cb_) (Context *),
+ show_value_ftype *show_cmd_cb_,
+ const char *set_doc_,
+ const char *show_doc_ = nullptr,
+ const char *help_doc_ = nullptr)
+ : option_def (long_option_, var_color,
+ (erased_get_var_address_ftype *) get_var_address_cb_,
+ show_cmd_cb_,
+ set_doc_, show_doc_, help_doc_)
+ {
+ var_address.color = detail::get_var_address<ui_file_style::color, Context>;
+ }
+};
+
/* A group of options that all share the same context pointer to pass
to the options' get-current-value callbacks. */
struct option_def_group
diff --git a/gdb/cli/cli-setshow.c b/gdb/cli/cli-setshow.c
index bcc793b3148..a8f22c9bde6 100644
--- a/gdb/cli/cli-setshow.c
+++ b/gdb/cli/cli-setshow.c
@@ -443,6 +443,13 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c)
option_changed = c->var->set<const char *> (match);
}
break;
+ case var_color:
+ {
+ ui_file_style::color color = parse_var_color (arg);
+ ui_file_style::color approx_color = color.approximate (colorsupport ());
+ option_changed = c->var->set<ui_file_style::color> (approx_color);
+ }
+ break;
default:
error (_("gdb internal error: bad var_type in do_setshow_command"));
}
@@ -520,6 +527,14 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c)
interps_notify_param_changed
(name, c->var->get<const char *> ());
break;
+ case var_color:
+ {
+ const ui_file_style::color &color
+ = c->var->get<ui_file_style::color> ();
+ interps_notify_param_changed
+ (name, color.to_string ().c_str ());
+ }
+ break;
case var_boolean:
{
const char *opt = c->var->get<bool> () ? "on" : "off";
@@ -585,6 +600,12 @@ get_setshow_command_value_string (const setting &var)
stb.puts (value);
}
break;
+ case var_color:
+ {
+ const ui_file_style::color &value = var.get<ui_file_style::color> ();
+ stb.puts (value.to_string ().c_str ());
+ }
+ break;
case var_boolean:
stb.puts (var.get<bool> () ? "on" : "off");
break;
diff --git a/gdb/cli/cli-style.c b/gdb/cli/cli-style.c
index 36a8bd9c526..8a47daadcf9 100644
--- a/gdb/cli/cli-style.c
+++ b/gdb/cli/cli-style.c
@@ -42,20 +42,6 @@ bool source_styling = true;
bool disassembler_styling = true;
-/* Name of colors; must correspond to ui_file_style::basic_color. */
-static const char * const cli_colors[] = {
- "none",
- "black",
- "red",
- "green",
- "yellow",
- "blue",
- "magenta",
- "cyan",
- "white",
- nullptr
-};
-
/* Names of intensities; must correspond to
ui_file_style::intensity. */
static const char * const cli_intensities[] = {
@@ -135,8 +121,8 @@ cli_style_option::cli_style_option (const char *name,
ui_file_style::intensity intensity)
: changed (name),
m_name (name),
- m_foreground (cli_colors[fg - ui_file_style::NONE]),
- m_background (cli_colors[0]),
+ m_foreground (fg),
+ m_background (ui_file_style::NONE),
m_intensity (cli_intensities[intensity])
{
}
@@ -147,32 +133,17 @@ cli_style_option::cli_style_option (const char *name,
ui_file_style::intensity i)
: changed (name),
m_name (name),
- m_foreground (cli_colors[0]),
- m_background (cli_colors[0]),
+ m_foreground (ui_file_style::NONE),
+ m_background (ui_file_style::NONE),
m_intensity (cli_intensities[i])
{
}
-/* Return the color number corresponding to COLOR. */
-
-static int
-color_number (const char *color)
-{
- for (int i = 0; i < ARRAY_SIZE (cli_colors); ++i)
- {
- if (color == cli_colors[i])
- return i - 1;
- }
- gdb_assert_not_reached ("color not found");
-}
-
/* See cli-style.h. */
ui_file_style
cli_style_option::style () const
{
- int fg = color_number (m_foreground);
- int bg = color_number (m_background);
ui_file_style::intensity intensity = ui_file_style::NORMAL;
for (int i = 0; i < ARRAY_SIZE (cli_intensities); ++i)
@@ -184,7 +155,7 @@ cli_style_option::style () const
}
}
- return ui_file_style (fg, bg, intensity);
+ return ui_file_style (m_foreground, m_background, intensity);
}
/* See cli-style.h. */
@@ -257,9 +228,8 @@ cli_style_option::add_setshow_commands (enum command_class theclass,
set_show_commands commands;
- commands = add_setshow_enum_cmd
- ("foreground", theclass, cli_colors,
- &m_foreground,
+ commands = add_setshow_color_cmd
+ ("foreground", theclass, &m_foreground,
_("Set the foreground color for this property."),
_("Show the foreground color for this property."),
nullptr,
@@ -269,9 +239,8 @@ cli_style_option::add_setshow_commands (enum command_class theclass,
commands.set->set_context (this);
commands.show->set_context (this);
- commands = add_setshow_enum_cmd
- ("background", theclass, cli_colors,
- &m_background,
+ commands = add_setshow_color_cmd
+ ("background", theclass, &m_background,
_("Set the background color for this property."),
_("Show the background color for this property."),
nullptr,
diff --git a/gdb/cli/cli-style.h b/gdb/cli/cli-style.h
index 5052b867cfa..5af299f1c6f 100644
--- a/gdb/cli/cli-style.h
+++ b/gdb/cli/cli-style.h
@@ -67,9 +67,9 @@ class cli_style_option
const char *m_name;
/* The foreground. */
- const char *m_foreground;
+ ui_file_style::color m_foreground;
/* The background. */
- const char *m_background;
+ ui_file_style::color m_background;
/* The intensity. */
const char *m_intensity;
diff --git a/gdb/command.h b/gdb/command.h
index 0ceaf3e2a6d..715fcd8096f 100644
--- a/gdb/command.h
+++ b/gdb/command.h
@@ -111,7 +111,9 @@ enum var_types
/* Enumerated type. Can only have one of the specified values.
*VAR is a char pointer to the name of the element that we
find. */
- var_enum
+ var_enum,
+ /* Color type. *VAR is a ui_file_style::color structure. */
+ var_color
};
/* A structure describing an extra literal accepted and shown in place
@@ -185,6 +187,14 @@ inline bool var_type_uses<const char *> (var_types t)
return t == var_enum;
}
+/* Return true if a setting of type T is backed by an ui_file_style::color
+ variable. */
+template<>
+inline bool var_type_uses<ui_file_style::color> (var_types t)
+{
+ return t == var_color;
+}
+
template<bool is_scalar, typename T> struct setting_func_types_1;
template<typename T>
@@ -681,6 +691,20 @@ extern set_show_commands add_setshow_enum_cmd
setting_func_types<const char *>::get get_func, show_value_ftype *show_func,
cmd_list_element **set_list, cmd_list_element **show_list);
+extern set_show_commands add_setshow_color_cmd
+ (const char *name, command_class theclass, ui_file_style::color *var,
+ const char *set_doc, const char *show_doc, const char *help_doc,
+ cmd_func_ftype *set_func, show_value_ftype *show_func,
+ cmd_list_element **set_list, cmd_list_element **show_list);
+
+extern set_show_commands add_setshow_color_cmd
+ (const char *name, command_class theclass,
+ const char *set_doc, const char *show_doc, const char *help_doc,
+ setting_func_types<ui_file_style::color>::set set_func,
+ setting_func_types<ui_file_style::color>::get get_func,
+ show_value_ftype *show_func, cmd_list_element **set_list,
+ cmd_list_element **show_list);
+
extern set_show_commands add_setshow_auto_boolean_cmd
(const char *name, command_class theclass, auto_boolean *var,
const char *set_doc, const char *show_doc, const char *help_doc,
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index cc1b69c6978..e49a0425e59 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -12973,6 +12973,20 @@ and @code{$_shell_exitsignal} according to the exit status of the last
launched command. These variables are set and used similarly to
the variables @code{$_exitcode} and @code{$_exitsignal}.
+@item $_colorsupport
+@vindex $_colorsupport@r{, convenience variable}
+@cindex color space
+Comma-separated list of @dfn{color space} names supported by terminal. Names
+could be any of @samp{monochrome}, @samp{ansi_8color}, @samp{aixterm_16color},
+@samp{xterm_256color}, @samp{rgb_24bit}. E.g., for plain linux terminal the
+value could be @samp{monochrome,ansi_8color} and for terminal with truecolor
+support it could be
+@samp{monochrome,ansi_8color,aixterm_16color,xterm_256color,rgb_24bit}.
+@samp{rgb_24bit} availability is determined by the @env{COLORTERM} environment
+variable which may be @samp{truecolor} or @samp{24bit}. Other color spaces are
+determined by the "Co" termcap which in turn depends on the @env{TERM}
+environment variable.
+
@end table
@node Convenience Funs
@@ -27815,16 +27829,32 @@ For example, the style of file names can be controlled using the
@table @code
@item set style filename background @var{color}
-Set the background to @var{color}. Valid colors are @samp{none}
-(meaning the terminal's default color), @samp{black}, @samp{red},
-@samp{green}, @samp{yellow}, @samp{blue}, @samp{magenta}, @samp{cyan},
-and@samp{white}.
+Set the background to @var{color}. @var{color} can be @samp{none}
+(meaning the terminal's default color), a name of one of the eight standard
+colors of ISO/IEC 6429, index from 0 to 255 into terminal's color
+palette or a hexadecimal RGB triplet in @samp{#RRGGBB} format for
+24-bit TrueColor.
+
+Valid color names are @samp{black}, @samp{red}, @samp{green},
+@samp{yellow}, @samp{blue}, @samp{magenta}, @samp{cyan}, and
+@samp{white}.
+
+Integers 0 to 7 are the synonyms for the standard colors. Integers 8-15 are
+used for the so-called bright colors from the aixterm extended 16-color
+palette. Integers 16-255 are the indexes into xterm extended 256-color palette
+(usually 6x6x6 cube plus gray ramp). In general, 256-color palette is terminal
+dependent and sometimes can be changed with OSC 4 sequences, e.g.
+"\033]4;1;rgb:00/FF/00\033\\". A hexadecimal 24-bit TrueColor is specified in
+the format @samp{#RRGGBB} where RR, GG and BB are the 2-digit hexadecimal
+integers specifiing the intensity of the red, green and blue color components,
+respectively.
+
+It is the responsibility of the user to verify that the terminal supports
+the specified colors.
@item set style filename foreground @var{color}
-Set the foreground to @var{color}. Valid colors are @samp{none}
-(meaning the terminal's default color), @samp{black}, @samp{red},
-@samp{green}, @samp{yellow}, @samp{blue}, @samp{magenta}, @samp{cyan},
-and@samp{white}.
+Set the foreground to @var{color}. @var{color} can be given in the same ways
+as for the background.
@item set style filename intensity @var{value}
Set the intensity to @var{value}. Valid intensities are @samp{normal}
diff --git a/gdb/doc/guile.texi b/gdb/doc/guile.texi
index a66e8bfe16b..0cb01663d6e 100644
--- a/gdb/doc/guile.texi
+++ b/gdb/doc/guile.texi
@@ -155,6 +155,7 @@ from the Guile interactive prompt.
* I/O Ports in Guile:: GDB I/O ports
* Memory Ports in Guile:: Accessing memory through ports and bytevectors
* Iterators In Guile:: Basic iterator support
+* Colors In Guile:: Colorize output with Guile
@end menu
@node Basic Guile
@@ -408,6 +409,9 @@ as a symbol.
@item <gdb:value>
@xref{Values From Inferior In Guile}.
+
+@item <gdb:color>
+@xref{Colors In Guile}.
@end table
The following @value{GDBN} objects are managed internally so that the
@@ -2184,6 +2188,14 @@ The value is a filename. This is just like
@item PARAM_ENUM
The value is a string, which must be one of a collection of string
constants provided when the parameter is created.
+
+@item PARAM_COLOR
+The value is either a string or an unsigned integer. Integer from 0 to 255
+means index into terminal's color palette. String can be a hex RGB triplet in
+@samp{#RRGGBB} format or one of the following color names:
+@samp{none} (meaning the terminal's default color), @samp{black}, @samp{red},
+@samp{green}, @samp{yellow}, @samp{blue}, @samp{magenta}, @samp{cyan},
+or @samp{white}.
@end vtable
@node Progspaces In Guile
@@ -3820,6 +3832,98 @@ Run @var{iterator} until the result of @code{(pred element)} is true
and return that as the result. Otherwise return @code{#f}.
@end deffn
+@node Colors In Guile
+@subsubsection Colors In Guile
+
+@cindex guile colors
+@tindex <gdb:color>
+You can assign instance of @code{<gdb:color>} to the value of
+a @code{<gdb:parameter>} object created with @code{PARAM_COLOR}.
+
+@code{<gdb:color>} may refer to an index from color palette or contain
+components of a color from some colorspace.
+
+@deffn {Scheme Procedure} make-color @w{@r{[}value} @
+ @w{@r{[}#:color-space color-space@r{]}@r{]}}
+
+@var{value} is an integer index of a color in palette, tuple with color
+components or a string. String can be a hex RGB triplet in @samp{#RRGGBB}
+format or one of the following color names:
+@samp{none} (meaning the terminal's default color), @samp{black}, @samp{red},
+@samp{green}, @samp{yellow}, @samp{blue}, @samp{magenta}, @samp{cyan},
+or @samp{white}.
+
+@var{color-space} should be one of the @samp{COLORSPACE_} constants. This
+argument tells @value{GDBN} which color space @var{value} belongs.
+@end deffn
+
+@deffn {Scheme Procedure} color? object
+Return @code{#t} if @var{object} is a @code{<gdb:color>} object.
+Otherwise return @code{#f}.
+@end deffn
+
+@deffn {Scheme Procedure} color-none? color
+Return @code{#t} if @var{color} is terminal's default.
+Otherwise return @code{#f}.
+@end deffn
+
+@deffn {Scheme Procedure} color-indexed? color
+Return @code{#t} if @var{color} is indexed, i.e. belongs to some palette.
+Otherwise return @code{#f}.
+@end deffn
+
+@deffn {Scheme Procedure} color-direct? color
+Return @code{#t} if @var{color} is direct in the sense of ISO/IEC 8613-6.
+Otherwise return @code{#f}.
+@end deffn
+
+@deffn {Scheme Procedure} color-string color
+Return the textual representation of a @code{<gdb:color>} object.
+@end deffn
+
+@deffn {Scheme Procedure} color-colorspace color
+Return the color space of a @code{<gdb:color>} object.
+@end deffn
+
+@deffn {Scheme Procedure} color-index color
+Return index of the color of a @code{<gdb:color>} object in a palette.
+@end deffn
+
+@deffn {Scheme Procedure} color-components color
+Return components of the direct @code{<gdb:color>} object.
+@end deffn
+
+@deffn {Scheme Procedure} color-escape-sequence color is_foreground
+Return string to change terminal's color to this.
+
+If @var{is_foreground} is @code{#t}, then the returned sequence will change
+foreground color. Otherwise, the returned sequence will change background
+color.
+@end deffn
+
+When color is initialized, its color space must be specified. The
+available color spaces are represented by constants defined in the @code{gdb}
+module:
+
+@vtable @code
+@item COLORSPACE_MONOCHROME
+Palette with only terminal's default color.
+
+@item COLORSPACE_ANSI_8COLOR
+Palette with eight standard colors of ISO/IEC 6429 "black", "red", "green", etc.
+
+@item COLORSPACE_AIXTERM_16COLOR
+Palette with 16 colors. First eight are standard colors of ISO/IEC 6429
+"black", "red", "green", etc. Next eight are their bright version.
+
+@item COLORSPACE_XTERM_256COLOR
+Palette with 256 colors. First 16 are from COLORSPACE_AIXTERM_16COLOR. Next
+216 colors are 6x6x6 RGB cube. And last 24 colors form grayscale ramp.
+
+@item COLORSPACE_RGB_24BIT
+Direct 24-bit RGB colors.
+@end vtable
+
@node Guile Auto-loading
@subsection Guile Auto-loading
@cindex guile auto-loading
diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index 22f0e6c6d0a..e27bc68bf20 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -225,6 +225,7 @@ optional arguments while skipping others. Example:
* Finish Breakpoints in Python:: Setting Breakpoints on function return
using Python.
* Lazy Strings In Python:: Python representation of lazy strings.
+* Colors In Python:: Python representation of colors.
* Architectures In Python:: Python representation of architectures.
* Registers In Python:: Python representation of registers.
* Connections In Python:: Python representation of connections.
@@ -5230,6 +5231,11 @@ except the special value -1 is returned for the setting of ``unlimited''.
@item gdb.PARAM_ENUM
The value is a string, which must be one of a collection string
constants provided when the parameter is created.
+
+@findex PARAM_COLOR
+@findex gdb.PARAM_COLOR
+@item gdb.PARAM_COLOR
+The value is @code{gdb.Color} instance.
@end table
@node Functions In Python
@@ -6999,6 +7005,98 @@ resolve this to the lazy string's character type, use the type's
writable.
@end defvar
+@node Colors In Python
+@subsection Python representation of colors
+
+@cindex colors in python
+@tindex gdb.Color
+You can assign instance of @code{Color} to the @code{value} of
+a @code{Parameter} instance created with @code{PARAM_COLOR}.
+
+@code{Color} may refer to an index from color palette or contain components
+of a color from some colorspace.
+
+@defun Color.__init__ (@r{[}@var{value} @r{[}, @var{color-space}@r{]}@r{]})
+
+@var{value} is @code{None} (meaning the terminal's default color),
+an integer index of a color in palette, tuple with color components
+or a string. String can be a hex RGB triplet in @samp{#RRGGBB} format
+or one of the following color names:
+@samp{none} (meaning the terminal's default color), @samp{black}, @samp{red},
+@samp{green}, @samp{yellow}, @samp{blue}, @samp{magenta}, @samp{cyan},
+or @samp{white}.
+
+@var{color-space} should be one of the @samp{COLORSPACE_} constants. This
+argument tells @value{GDBN} which color space @var{value} belongs.
+@end defun
+
+@defvar Color.is_none
+This atribute is boolean. If its value is @code{True} then color is terminal's
+default.
+@end defvar
+
+@defvar Color.is_indexed
+This atribute is boolean. If its value is @code{True} then color is indexed,
+i.e. belongs to some palette.
+@end defvar
+
+@defvar Color.is_direct
+This atribute is boolean. If its value is @code{True} then this object
+describes direct colour in the sense of ISO/IEC 8613-6.
+@end defvar
+
+@defvar Color.index
+This attribute exist if @code{is_indexed} is @code{True}. Its integer value is
+index of a color in a palette.
+@end defvar
+
+@defvar Color.components
+This attribute exist if @code{is_direct} is @code{True}. Its value is tuple
+with integer components of a color.
+@end defvar
+
+@defun Color.escape_sequence (@var{self}, @var{is_foreground})
+Returns string to change terminal's color to this.
+
+If @var{is_foreground} is @code{True}, then the returned sequence will change
+foreground color. Otherwise, the returned sequence will change background
+color.
+@end defun
+
+When color is initialized, its color space must be specified. The
+available color spaces are represented by constants defined in the @code{gdb}
+module:
+
+@table @code
+@findex COLORSPACE_MONOCHROME
+@findex gdb.COLORSPACE_MONOCHROME
+@item gdb.COLORSPACE_MONOCHROME
+Palette with only terminal's default color.
+
+@findex COLORSPACE_ANSI_8COLOR
+@findex gdb.COLORSPACE_ANSI_8COLOR
+@item gdb.COLORSPACE_ANSI_8COLOR
+Palette with eight standard colors of ISO/IEC 6429 "black", "red", "green", etc.
+
+@findex COLORSPACE_AIXTERM_16COLOR
+@findex gdb.COLORSPACE_AIXTERM_16COLOR
+@item gdb.COLORSPACE_AIXTERM_16COLOR
+Palette with 16 colors. First eight are standard colors of ISO/IEC 6429
+"black", "red", "green", etc. Next eight are their bright version.
+
+@findex COLORSPACE_XTERM_256COLOR
+@findex gdb.COLORSPACE_XTERM_256COLOR
+@item gdb.COLORSPACE_XTERM_256COLOR
+Palette with 256 colors. First 16 are from COLORSPACE_AIXTERM_16COLOR. Next
+216 colors are 6x6x6 RGB cube. And last 24 colors form grayscale ramp.
+
+@findex COLORSPACE_RGB_24BIT
+@findex gdb.COLORSPACE_RGB_24BIT
+@item gdb.COLORSPACE_RGB_24BIT
+Direct 24-bit RGB colors.
+
+@end table
+
@node Architectures In Python
@subsubsection Python representation of architectures
@cindex Python architectures
diff --git a/gdb/guile/guile-internal.h b/gdb/guile/guile-internal.h
index dea78845088..b137713d133 100644
--- a/gdb/guile/guile-internal.h
+++ b/gdb/guile/guile-internal.h
@@ -439,6 +439,14 @@ extern int gdbscm_valid_command_class_p (int command_class);
extern char *gdbscm_canonicalize_command_name (const char *name,
int want_trailing_space);
+/* scm-color.c */
+
+extern SCM coscm_scm_from_color (const ui_file_style::color &color);
+
+extern int coscm_is_color (SCM scm);
+
+extern const ui_file_style::color & coscm_get_color (SCM color_scm);
+
/* scm-frame.c */
struct frame_smob;
@@ -617,6 +625,7 @@ extern void gdbscm_initialize_arches (void);
extern void gdbscm_initialize_auto_load (void);
extern void gdbscm_initialize_blocks (void);
extern void gdbscm_initialize_breakpoints (void);
+extern void gdbscm_initialize_colors (void);
extern void gdbscm_initialize_commands (void);
extern void gdbscm_initialize_disasm (void);
extern void gdbscm_initialize_exceptions (void);
diff --git a/gdb/guile/guile.c b/gdb/guile/guile.c
index 432093b6aea..5358b8330a3 100644
--- a/gdb/guile/guile.c
+++ b/gdb/guile/guile.c
@@ -594,6 +594,7 @@ initialize_gdb_module (void *data)
gdbscm_initialize_auto_load ();
gdbscm_initialize_blocks ();
gdbscm_initialize_breakpoints ();
+ gdbscm_initialize_colors ();
gdbscm_initialize_commands ();
gdbscm_initialize_disasm ();
gdbscm_initialize_frames ();
diff --git a/gdb/guile/scm-color.c b/gdb/guile/scm-color.c
new file mode 100644
index 00000000000..6ebe252f125
--- /dev/null
+++ b/gdb/guile/scm-color.c
@@ -0,0 +1,427 @@
+/* GDB parameters implemented in Guile.
+
+ Copyright (C) 2008-2024 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "value.h"
+#include "charset.h"
+#include "cli/cli-decode.h"
+#include "completer.h"
+#include "language.h"
+#include "arch-utils.h"
+#include "guile-internal.h"
+
+/* A GDB color. */
+
+struct color_smob
+{
+ /* This always appears first. */
+ gdb_smob base;
+
+ /* Underlying value. */
+ ui_file_style::color color;
+};
+
+static const char color_smob_name[] = "gdb:color";
+
+/* The tag Guile knows the color smob by. */
+static scm_t_bits color_smob_tag;
+
+/* Keywords used by make-color. */
+static SCM colorspace_keyword;
+
+static const char *coscm_colorspace_name (color_space colorspace);
+
+/* Administrivia for color smobs. */
+
+static int
+coscm_print_color_smob (SCM self, SCM port, scm_print_state *pstate)
+{
+ const ui_file_style::color &color = coscm_get_color (self);
+
+ gdbscm_printf (port, "#<%s", color_smob_name);
+
+ gdbscm_printf (port, " %s", color.to_string ().c_str ());
+ gdbscm_printf (port, " %s", coscm_colorspace_name (color.colorspace ()));
+ scm_puts (">", port);
+
+ scm_remember_upto_here_1 (self);
+
+ /* Non-zero means success. */
+ return 1;
+}
+
+/* Create an empty (uninitialized) color. */
+
+static SCM
+coscm_make_color_smob (void)
+{
+ color_smob *c_smob = (color_smob *)
+ scm_gc_calloc (sizeof (color_smob), color_smob_name);
+ SCM c_scm;
+
+ c_smob->color = ui_file_style::color (ui_file_style::NONE);
+ c_scm = scm_new_smob (color_smob_tag, (scm_t_bits) c_smob);
+ gdbscm_init_gsmob (&c_smob->base);
+
+ return c_scm;
+}
+
+/* Return the <gdb:color> object that encapsulates COLOR. */
+
+SCM
+coscm_scm_from_color (const ui_file_style::color &color)
+{
+ SCM c_scm = coscm_make_color_smob ();
+ color_smob *c_smob = (color_smob *) SCM_SMOB_DATA (c_scm);
+ c_smob->color = color;
+ return c_scm;
+}
+
+/* Return the color field of color_smob. */
+
+const ui_file_style::color &
+coscm_get_color (SCM color_scm)
+{
+ SCM_ASSERT_TYPE (coscm_is_color (color_scm), color_scm, SCM_ARG1, FUNC_NAME,
+ _("<gdb:color>"));
+
+ color_smob *c_smob = (color_smob *) SCM_SMOB_DATA (color_scm);
+ return c_smob->color;
+
+}
+
+/* Returns non-zero if SCM is a <gdb:color> object. */
+
+int
+coscm_is_color (SCM scm)
+{
+ return SCM_SMOB_PREDICATE (color_smob_tag, scm);
+}
+
+/* (gdb:color? scm) -> boolean */
+
+static SCM
+gdbscm_color_p (SCM scm)
+{
+ return scm_from_bool (coscm_is_color (scm));
+}
+
+static const scheme_integer_constant colorspaces[] =
+{
+ { "COLORSPACE_MONOCHROME", (int) color_space::MONOCHROME },
+ { "COLORSPACE_ANSI_8COLOR", (int) color_space::ANSI_8COLOR },
+ { "COLORSPACE_AIXTERM_16COLOR", (int) color_space::AIXTERM_16COLOR },
+ { "COLORSPACE_XTERM_256COLOR", (int) color_space::XTERM_256COLOR },
+ { "COLORSPACE_RGB_24BIT", (int) color_space::RGB_24BIT },
+
+ END_INTEGER_CONSTANTS
+};
+
+/* Return COLORSPACE as a string. */
+
+static const char *
+coscm_colorspace_name (color_space colorspace)
+{
+ for (int i = 0; colorspaces[i].name != nullptr; ++i)
+ {
+ if (colorspaces[i].value == static_cast<int> (colorspace))
+ return colorspaces[i].name;
+ }
+
+ gdb_assert_not_reached ("bad color space");
+}
+
+/* Free function for a color_smob. */
+static size_t
+coscm_free_color_smob (SCM self)
+{
+ (void) self;
+ return 0;
+}
+
+/* Color Scheme functions. */
+
+/* (make-color [value
+ [#:color-space colorspace]]) -> <gdb:color>
+
+ VALUE is the value of the color. It may be SCM_UNDEFINED, string, number
+ or list.
+
+ COLORSPACE is the color space of the VALUE. It should be one of the
+ COLORSPACE_* constants defined in the gdb module.
+
+ The result is the <gdb:color> Scheme object. */
+
+static SCM
+gdbscm_make_color (SCM value_scm, SCM rest)
+{
+ SCM colorspace_arg = SCM_UNDEFINED;
+ color_space colorspace = color_space::MONOCHROME;
+
+ scm_c_bind_keyword_arguments (FUNC_NAME, rest,
+ static_cast<scm_t_keyword_arguments_flags> (0),
+ colorspace_keyword, &colorspace_arg,
+ SCM_UNDEFINED);
+
+ if (!SCM_UNBNDP (colorspace_arg))
+ {
+ SCM_ASSERT_TYPE (scm_is_integer (colorspace_arg), colorspace_arg,
+ SCM_ARG2, FUNC_NAME, _("int"));
+ int colorspace_int = scm_to_int (colorspace_arg);
+ if (!color_space_safe_cast (&colorspace, colorspace_int))
+ gdbscm_out_of_range_error (FUNC_NAME, SCM_ARG2,
+ scm_from_int (colorspace_int),
+ _("invalid colorspace argument"));
+ }
+
+ ui_file_style::color color = ui_file_style::NONE;
+ gdbscm_gdb_exception exc {};
+
+ try
+ {
+ if (SCM_UNBNDP (value_scm) || scm_is_integer (value_scm))
+ {
+ int i = -1;
+ if (scm_is_integer (value_scm))
+ {
+ i = scm_to_int (value_scm);
+ if (i < 0)
+ gdbscm_out_of_range_error (FUNC_NAME, SCM_ARG1, value_scm,
+ _("negative color index"));
+ }
+
+ if (SCM_UNBNDP (colorspace_arg))
+ color = ui_file_style::color (i);
+ else
+ color = ui_file_style::color (colorspace, i);
+ }
+ else if (gdbscm_is_true (scm_list_p (value_scm)))
+ {
+ if (SCM_UNBNDP (colorspace_arg)
+ || colorspace != color_space::RGB_24BIT)
+ error (_("colorspace must be COLORSPACE_RGB_24BIT with "
+ "value of list type."));
+
+ if (scm_ilength (value_scm) != 3)
+ error (_("List value with RGB must be of size 3."));
+
+ uint8_t rgb[3] = {};
+ int i = 0;
+ for (; i < 3 && !scm_is_eq (value_scm, SCM_EOL); ++i)
+ {
+ SCM item = scm_car (value_scm);
+
+ SCM_ASSERT_TYPE (scm_is_integer (item), item, SCM_ARG1, FUNC_NAME,
+ _("int"));
+ int component = scm_to_int (item);
+ if (component < 0 || component > UINT8_MAX)
+ gdbscm_out_of_range_error (FUNC_NAME, SCM_ARG1, item,
+ _("invalid rgb component"));
+ rgb[i] = static_cast<uint8_t> (component);
+
+ value_scm = scm_cdr (value_scm);
+ }
+
+ gdb_assert (i == 3);
+
+ color = ui_file_style::color (rgb[0], rgb[1], rgb[2]);
+ }
+ else if (scm_is_string (value_scm))
+ {
+ SCM exception;
+
+ gdb::unique_xmalloc_ptr<char> string
+ = gdbscm_scm_to_host_string (value_scm, nullptr, &exception);
+ if (string == nullptr)
+ gdbscm_throw (exception);
+
+ color = parse_var_color (string.get ());
+
+ if (!SCM_UNBNDP (colorspace_arg) && colorspace != color.colorspace ())
+ error (_("colorspace doesn't match to the value."));
+
+ }
+ else
+ scm_wrong_type_arg_msg (FUNC_NAME, SCM_ARG1, value_scm,
+ "integer, string or list");
+ }
+ catch (const gdb_exception &except)
+ {
+ exc = unpack (except);
+ }
+
+ GDBSCM_HANDLE_GDB_EXCEPTION (exc);
+
+ return coscm_scm_from_color (color);
+}
+
+/* (color-string <gdb:color>) -> value */
+
+static SCM
+gdbscm_color_string (SCM self)
+{
+ const ui_file_style::color &color = coscm_get_color (self);
+ std::string s = color.to_string ();
+ return gdbscm_scm_from_host_string (s.c_str (), s.size ());
+}
+
+/* (color-colorspace <gdb:color>) -> value */
+
+static SCM
+gdbscm_color_colorspace (SCM self)
+{
+ const ui_file_style::color &color = coscm_get_color (self);
+ return scm_from_int (static_cast<int> (color.colorspace ()));
+}
+
+/* (color-none? scm) -> boolean */
+
+static SCM
+gdbscm_color_none_p (SCM self)
+{
+ const ui_file_style::color &color = coscm_get_color (self);
+ return scm_from_bool (color.is_none ());
+}
+
+/* (color-indexed? scm) -> boolean */
+
+static SCM
+gdbscm_color_indexed_p (SCM self)
+{
+ const ui_file_style::color &color = coscm_get_color (self);
+ return scm_from_bool (color.is_indexed ());
+}
+
+/* (color-direct? scm) -> boolean */
+
+static SCM
+gdbscm_color_direct_p (SCM self)
+{
+ const ui_file_style::color &color = coscm_get_color (self);
+ return scm_from_bool (color.is_direct ());
+}
+
+/* (color-index <gdb:color>) -> value */
+
+static SCM
+gdbscm_color_index (SCM self)
+{
+ const ui_file_style::color &color = coscm_get_color (self);
+
+ if (!color.is_indexed ())
+ gdbscm_misc_error (FUNC_NAME, SCM_ARG1, self, "color is not indexed");
+ return scm_from_int (color.get_value ());
+}
+
+/* (color-components <gdb:color>) -> value */
+
+static SCM
+gdbscm_color_components (SCM self)
+{
+ const ui_file_style::color &color = coscm_get_color (self);
+
+ if (!color.is_direct ())
+ gdbscm_misc_error (FUNC_NAME, SCM_ARG1, self, "color is not direct");
+
+ uint8_t rgb[3] = {};
+ color.get_rgb (rgb);
+ SCM red = scm_from_uint8 (rgb[0]);
+ SCM green = scm_from_uint8 (rgb[1]);
+ SCM blue = scm_from_uint8 (rgb[2]);
+ return scm_list_3 (red, green, blue);
+}
+
+/* (color-escape-sequence <gdb:color> is_fg) -> value */
+
+static SCM
+gdbscm_color_escape_sequence (SCM self, SCM is_fg_scm)
+{
+ const ui_file_style::color &color = coscm_get_color (self);
+ SCM_ASSERT_TYPE (gdbscm_is_bool (is_fg_scm), is_fg_scm, SCM_ARG2, FUNC_NAME,
+ _("boolean"));
+ bool is_fg = gdbscm_is_true (is_fg_scm);
+ std::string s = color.to_ansi (is_fg);
+ return gdbscm_scm_from_host_string (s.c_str (), s.size ());
+}
+
+/* Initialize the Scheme color support. */
+
+static const scheme_function color_functions[] =
+{
+ { "make-color", 0, 1, 1, as_a_scm_t_subr (gdbscm_make_color),
+ "\
+Make a GDB color object.\n\
+\n\
+ Arguments: [value\n\
+ [#:color-space <colorspace>]]\n\
+ value: The name of the color. It may be string, number with color index\n\
+ or list with RGB components.\n\
+ colorspace: The color space of the color, one of COLORSPACE_*." },
+
+ { "color?", 1, 0, 0, as_a_scm_t_subr (gdbscm_color_p),
+ "\
+Return #t if the object is a <gdb:color> object." },
+
+ { "color-none?", 1, 0, 0, as_a_scm_t_subr (gdbscm_color_none_p),
+ "\
+Return #t if the <gdb:color> object has default color." },
+
+ { "color-indexed?", 1, 0, 0, as_a_scm_t_subr (gdbscm_color_indexed_p),
+ "\
+Return #t if the <gdb:color> object is from indexed color space." },
+
+ { "color-direct?", 1, 0, 0, as_a_scm_t_subr (gdbscm_color_direct_p),
+ "\
+Return #t if the <gdb:color> object has direct color (e.g. RGB, CMY, CMYK)." },
+
+ { "color-string", 1, 0, 0, as_a_scm_t_subr (gdbscm_color_string),
+ "\
+Return the textual representation of a <gdb:color> object." },
+
+ { "color-colorspace", 1, 0, 0, as_a_scm_t_subr (gdbscm_color_colorspace),
+ "\
+Return the color space of a <gdb:color> object." },
+
+ { "color-index", 1, 0, 0, as_a_scm_t_subr (gdbscm_color_index),
+ "\
+Return index of the color of a <gdb:color> object in a palette." },
+
+ { "color-components", 1, 0, 0, as_a_scm_t_subr (gdbscm_color_components),
+ "\
+Return components of the direct <gdb:color> object." },
+
+ { "color-escape-sequence", 2, 0, 0,
+ as_a_scm_t_subr (gdbscm_color_escape_sequence),
+ "\
+Return string to change terminal's color to this." },
+
+ END_FUNCTIONS
+};
+
+void
+gdbscm_initialize_colors (void)
+{
+ color_smob_tag = gdbscm_make_smob_type (color_smob_name, sizeof (color_smob));
+ scm_set_smob_free (color_smob_tag, coscm_free_color_smob);
+ scm_set_smob_print (color_smob_tag, coscm_print_color_smob);
+
+ gdbscm_define_integer_constants (colorspaces, 1);
+ gdbscm_define_functions (color_functions, 1);
+
+ colorspace_keyword = scm_from_latin1_keyword ("color-space");
+}
diff --git a/gdb/guile/scm-param.c b/gdb/guile/scm-param.c
index 3a1e1583f54..749c5ea1f7d 100644
--- a/gdb/guile/scm-param.c
+++ b/gdb/guile/scm-param.c
@@ -47,6 +47,9 @@ union pascm_variable
/* Hold a string, for enums. */
const char *cstringval;
+
+ /* Hold a color. */
+ ui_file_style::color color;
};
/* A GDB parameter.
@@ -130,6 +133,7 @@ enum scm_param_types
param_optional_filename,
param_filename,
param_enum,
+ param_color,
};
/* Translation from Guile parameters to GDB variable types. Keep in the
@@ -155,7 +159,8 @@ param_to_var[] =
{ var_string_noescape },
{ var_optional_filename },
{ var_filename },
- { var_enum }
+ { var_enum },
+ { var_color }
};
/* Wraps a setting around an existing param_smob. This abstraction
@@ -179,6 +184,8 @@ make_setting (param_smob *s)
return setting (type, s->value.stringval);
else if (var_type_uses<const char *> (type))
return setting (type, &s->value.cstringval);
+ else if (var_type_uses<ui_file_style::color> (s->type))
+ return setting (s->type, &s->value.color);
else
gdb_assert_not_reached ("unhandled var type");
}
@@ -239,10 +246,9 @@ static SCM
pascm_make_param_smob (void)
{
param_smob *p_smob = (param_smob *)
- scm_gc_malloc (sizeof (param_smob), param_smob_name);
+ scm_gc_calloc (sizeof (param_smob), param_smob_name);
SCM p_scm;
- memset (p_smob, 0, sizeof (*p_smob));
p_smob->cmd_class = no_class;
p_smob->type = var_boolean; /* ARI: var_boolean */
p_smob->set_func = SCM_BOOL_F;
@@ -511,6 +517,13 @@ add_setshow_generic (enum var_types param_type,
set_list, show_list);
break;
+ case var_color:
+ commands = add_setshow_color_cmd (cmd_name, cmd_class, &self->value.color,
+ set_doc, show_doc, help_doc,
+ set_func, show_func,
+ set_list, show_list);
+ break;
+
default:
gdb_assert_not_reached ("bad param_type value");
}
@@ -588,6 +601,7 @@ static const scheme_integer_constant parameter_types[] =
{ "PARAM_OPTIONAL_FILENAME", param_optional_filename },
{ "PARAM_FILENAME", param_filename },
{ "PARAM_ENUM", param_enum },
+ { "PARAM_COLOR", param_color },
END_INTEGER_CONSTANTS
};
@@ -650,6 +664,11 @@ pascm_param_value (const setting &var, int arg_pos, const char *func_name)
return gdbscm_scm_from_host_string (str, strlen (str));
}
+ case var_color:
+ {
+ return coscm_scm_from_color (var.get<ui_file_style::color> ());
+ }
+
case var_boolean:
{
if (var.get<bool> ())
@@ -764,6 +783,12 @@ pascm_set_param_value_x (param_smob *p_smob,
break;
}
+ case var_color:
+ SCM_ASSERT_TYPE (coscm_is_color (value), value, arg_pos, func_name,
+ _("<gdb:color>"));
+ var.set<ui_file_style::color> (coscm_get_color (value));
+ break;
+
case var_boolean:
SCM_ASSERT_TYPE (gdbscm_is_bool (value), value, arg_pos, func_name,
_("boolean"));
@@ -1050,6 +1075,8 @@ gdbscm_make_parameter (SCM name_scm, SCM rest)
scm_set_smob_free (parameter_smob_tag, pascm_free_parameter_smob);
if (var_type_uses<std::string> (p_smob->type))
p_smob->value.stringval = new std::string;
+ else if (var_type_uses<ui_file_style::color> (p_smob->type))
+ p_smob->value.color = ui_file_style::NONE;
if (initial_value_arg_pos > 0)
{
diff --git a/gdb/python/py-color.c b/gdb/python/py-color.c
new file mode 100644
index 00000000000..3a90b620d5f
--- /dev/null
+++ b/gdb/python/py-color.c
@@ -0,0 +1,336 @@
+/* Python interface to ui_file_style::color objects.
+
+ Copyright (C) 2008-2024 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+
+#include "python-internal.h"
+#include "py-color.h"
+#include "cli/cli-decode.h"
+
+/* Colorspace constants and their values. */
+static struct {
+ const char *name;
+ color_space value;
+} colorspace_constants[] =
+{
+ { "COLORSPACE_MONOCHROME", color_space::MONOCHROME },
+ { "COLORSPACE_ANSI_8COLOR", color_space::ANSI_8COLOR },
+ { "COLORSPACE_AIXTERM_16COLOR", color_space::AIXTERM_16COLOR },
+ { "COLORSPACE_XTERM_256COLOR", color_space::XTERM_256COLOR },
+ { "COLORSPACE_RGB_24BIT", color_space::RGB_24BIT },
+};
+
+/* A color. */
+struct colorpy_object
+{
+ PyObject_HEAD
+
+ /* Underlying value. */
+ ui_file_style::color color;
+};
+
+extern PyTypeObject colorpy_object_type;
+
+/* See py-color.h. */
+gdbpy_ref<>
+create_color_object (const ui_file_style::color &color)
+{
+ gdbpy_ref<colorpy_object> color_obj (PyObject_New (colorpy_object,
+ &colorpy_object_type));
+
+ if (color_obj == nullptr)
+ return nullptr;
+
+ color_obj->color = color;
+ return gdbpy_ref<> ((PyObject *) color_obj.release ());
+}
+
+/* See py-color.h. */
+bool
+gdbpy_is_color (PyObject *obj)
+{
+ return PyObject_IsInstance (obj, (PyObject *) &colorpy_object_type);
+}
+
+/* See py-color.h. */
+const ui_file_style::color &
+gdbpy_get_color (PyObject *obj)
+{
+ gdb_assert (gdbpy_is_color (obj));
+ colorpy_object *self = (colorpy_object *) obj;
+ return self->color;
+}
+
+/* Get an attribute. */
+static PyObject *
+get_attr (PyObject *obj, PyObject *attr_name)
+{
+ if (! PyUnicode_Check (attr_name))
+ return PyObject_GenericGetAttr (obj, attr_name);
+
+ colorpy_object *self = (colorpy_object *) obj;
+ const ui_file_style::color &color = self->color;
+
+ if (! PyUnicode_CompareWithASCIIString (attr_name, "colorspace"))
+ {
+ int value = static_cast<int> (color.colorspace ());
+ return gdb_py_object_from_longest (value).release ();
+ }
+
+ if (! PyUnicode_CompareWithASCIIString (attr_name, "is_none"))
+ return PyBool_FromLong (color.is_none ());
+
+ if (! PyUnicode_CompareWithASCIIString (attr_name, "is_indexed"))
+ return PyBool_FromLong (color.is_indexed ());
+
+ if (! PyUnicode_CompareWithASCIIString (attr_name, "is_direct"))
+ return PyBool_FromLong (color.is_direct ());
+
+ if (color.is_indexed ()
+ && ! PyUnicode_CompareWithASCIIString (attr_name, "index"))
+ return gdb_py_object_from_longest (color.get_value ()).release ();
+
+ if (color.is_direct ()
+ && ! PyUnicode_CompareWithASCIIString (attr_name, "components"))
+ {
+ uint8_t rgb[3];
+ color.get_rgb (rgb);
+
+ gdbpy_ref<> rgb_objects[3];
+ for (int i = 0; i < 3; ++i)
+ {
+ rgb_objects[i] = gdb_py_object_from_ulongest (rgb[i]);
+ if (rgb_objects[i] == nullptr)
+ return nullptr;
+ }
+
+ PyObject *comp = PyTuple_New (3);
+ if (comp == nullptr)
+ return nullptr;
+
+ for (int i = 0; i < 3; ++i)
+ PyTuple_SET_ITEM (comp, i, rgb_objects[i].release ());
+
+ return comp;
+ }
+
+ return PyObject_GenericGetAttr (obj, attr_name);
+}
+
+/* Implementation of Color.escape_sequence (self, is_fg) -> str. */
+
+static PyObject *
+colorpy_escape_sequence (PyObject *self, PyObject *is_fg_obj)
+{
+ if (!gdbpy_is_color (self))
+ {
+ PyErr_SetString (PyExc_RuntimeError,
+ _("Object is not gdb.Color."));
+ return nullptr;
+ }
+
+ if (! PyBool_Check (is_fg_obj))
+ {
+ PyErr_SetString (PyExc_RuntimeError,
+ _("A boolean argument is required."));
+ return nullptr;
+ }
+
+ bool is_fg = is_fg_obj == Py_True;
+ std::string s = gdbpy_get_color (self).to_ansi (is_fg);
+
+ return host_string_to_python_string (s.c_str ()).release ();
+}
+
+/* Object initializer; fills color with value.
+
+ Use: __init__(VALUE = None, COLORSPACE = None)
+
+ VALUE is a string, integer, RGB-tuple or None.
+
+ COLORSPACE is the color space index.
+
+ Returns -1 on error, with a python exception set. */
+
+static int
+colorpy_init (PyObject *self, PyObject *args, PyObject *kwds)
+{
+ colorpy_object *obj = (colorpy_object *) self;
+ PyObject *value_obj = nullptr;
+ PyObject *colorspace_obj = nullptr;
+ color_space colorspace = color_space::MONOCHROME;
+
+ if (! PyArg_ParseTuple (args, "|OO", &value_obj, &colorspace_obj))
+ return -1;
+
+ try
+ {
+ if (colorspace_obj)
+ {
+ if (PyLong_Check (colorspace_obj))
+ {
+ long colorspace_id = -1;
+ if (! gdb_py_int_as_long (colorspace_obj, &colorspace_id))
+ return -1;
+ if (!color_space_safe_cast (&colorspace, colorspace_id))
+ error (_("colorspace %ld is out of range."), colorspace_id);
+ }
+ else if (colorspace_obj == Py_None)
+ colorspace_obj = nullptr;
+ else
+ error (_("colorspace must be None or integer"));
+ }
+
+ if (value_obj == nullptr || value_obj == Py_None)
+ obj->color = ui_file_style::color (colorspace, -1);
+ else if (PyLong_Check (value_obj))
+ {
+ long value = -1;
+ if (! gdb_py_int_as_long (value_obj, &value))
+ return -1;
+ if (value < 0 || value > INT_MAX)
+ error (_("value %ld is out of range."), value);
+ if (colorspace_obj)
+ obj->color = ui_file_style::color (colorspace, value);
+ else
+ obj->color = ui_file_style::color (value);
+ }
+ else if (PyTuple_Check (value_obj))
+ {
+ if (colorspace_obj == nullptr || colorspace != color_space::RGB_24BIT)
+ error (_("colorspace must be gdb.COLORSPACE_RGB_24BIT with "
+ "value of tuple type."));
+ Py_ssize_t tuple_size = PyTuple_Size (value_obj);
+ if (tuple_size < 0)
+ return -1;
+ if (tuple_size != 3)
+ error (_("Tuple value with RGB must be of size 3."));
+ uint8_t rgb[3];
+ for (int i = 0; i < 3; ++i)
+ {
+ PyObject *item = PyTuple_GetItem (value_obj, i);
+ if (!PyLong_Check (item))
+ error (_("Item %d of an RGB tuple must be integer."), i);
+ long item_value = -1;
+ if (!gdb_py_int_as_long (item, &item_value))
+ return -1;
+ if (item_value < 0 || item_value > UINT8_MAX)
+ error (_("RGB item %ld is out of byte range."), item_value);
+ rgb[i] = static_cast<uint8_t> (item_value);
+ }
+
+ obj->color = ui_file_style::color (rgb[0], rgb[1], rgb[2]);
+ }
+ else if (PyUnicode_Check (value_obj))
+ {
+ gdb::unique_xmalloc_ptr<char>
+ str (python_string_to_host_string (value_obj));
+ if (str == nullptr)
+ return -1;
+ obj->color = parse_var_color (str.get());
+
+ if (colorspace_obj != nullptr
+ && colorspace != obj->color.colorspace ())
+ error (_("colorspace doesn't match to the value."));
+ }
+ else
+ error (_("value must be one of None, integer, tuple or str."));
+ }
+ catch (const gdb_exception &except)
+ {
+ return gdbpy_handle_gdb_exception (-1, except);
+ }
+
+ Py_INCREF (self);
+ return 0;
+}
+
+static PyObject *
+colorpy_str (PyObject *self)
+{
+ colorpy_object *obj = reinterpret_cast<colorpy_object *> (self);
+
+ return PyUnicode_FromString (obj->color.to_string ().c_str ());
+}
+
+/* Initialize the 'color' module. */
+static int
+gdbpy_initialize_color (void)
+{
+ for (auto & pair : colorspace_constants)
+ if (PyModule_AddIntConstant (gdb_module, pair.name,
+ static_cast<long> (pair.value)) < 0)
+ return -1;
+
+ colorpy_object_type.tp_new = PyType_GenericNew;
+ return gdbpy_type_ready (&colorpy_object_type, gdb_module);
+}
+
+/* Color methods. */
+
+static PyMethodDef color_methods[] =
+{
+ { "escape_sequence", colorpy_escape_sequence, METH_O,
+ "escape_sequence (is_foreground) -> str.\n\
+Return the ANSI escape sequence for this color.\n\
+IS_FOREGROUND indicates whether this is a foreground or background color."},
+ {nullptr}
+};
+
+PyTypeObject colorpy_object_type =
+{
+ PyVarObject_HEAD_INIT (nullptr, 0)
+ "gdb.Color", /*tp_name*/
+ sizeof (colorpy_object), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ 0, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ colorpy_str, /*tp_str*/
+ get_attr, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "GDB color object", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ color_methods, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ colorpy_init, /* tp_init */
+ 0, /* tp_alloc */
+};
+
+GDBPY_INITIALIZE_FILE (gdbpy_initialize_color);
diff --git a/gdb/python/py-color.h b/gdb/python/py-color.h
new file mode 100644
index 00000000000..a778d5ba06e
--- /dev/null
+++ b/gdb/python/py-color.h
@@ -0,0 +1,35 @@
+/* Python interface to ui_file_style::color objects.
+
+ Copyright (C) 2009-2024 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef PYTHON_PY_COLOR_H
+#define PYTHON_PY_COLOR_H
+
+#include "python-internal.h"
+#include "ui-style.h"
+
+/* Create a new gdb.Color object from COLOR. */
+extern gdbpy_ref<> create_color_object (const ui_file_style::color &color);
+
+/* Check if OBJ is instance of a gdb.Color type. */
+extern bool gdbpy_is_color (PyObject *obj);
+
+/* Extracts value from OBJ object of gdb.Color type. */
+extern const ui_file_style::color &gdbpy_get_color (PyObject *obj);
+
+#endif /* PYTHON_PY_COLOR_H */
diff --git a/gdb/python/py-param.c b/gdb/python/py-param.c
index 9741782d3f1..0e79d2199f6 100644
--- a/gdb/python/py-param.c
+++ b/gdb/python/py-param.c
@@ -26,6 +26,7 @@
#include "completer.h"
#include "language.h"
#include "arch-utils.h"
+#include "py-color.h"
/* Python parameter types as in PARM_CONSTANTS below. */
@@ -43,6 +44,7 @@ enum py_param_types
param_zuinteger,
param_zuinteger_unlimited,
param_enum,
+ param_color,
};
/* Translation from Python parameters to GDB variable types. Keep in the
@@ -69,7 +71,8 @@ param_to_var[] =
{ var_integer },
{ var_uinteger },
{ var_pinteger, pinteger_unlimited_literals },
- { var_enum }
+ { var_enum },
+ { var_color }
};
/* Parameter constants and their values. */
@@ -90,6 +93,7 @@ static struct {
{ "PARAM_ZUINTEGER", param_zuinteger },
{ "PARAM_ZUINTEGER_UNLIMITED", param_zuinteger_unlimited },
{ "PARAM_ENUM", param_enum },
+ { "PARAM_COLOR", param_color },
{ NULL, 0 }
};
@@ -114,6 +118,9 @@ union parmpy_variable
/* Hold a string, for enums. */
const char *cstringval;
+
+ /* Hold a color. */
+ ui_file_style::color color;
};
/* A GDB parameter. */
@@ -157,6 +164,8 @@ make_setting (parmpy_object *s)
return setting (type, s->value.stringval);
else if (var_type_uses<const char *> (type))
return setting (type, &s->value.cstringval);
+ else if (var_type_uses<ui_file_style::color> (s->type))
+ return setting (s->type, &s->value.color);
else
gdb_assert_not_reached ("unhandled var type");
}
@@ -248,6 +257,19 @@ set_parameter_value (parmpy_object *self, PyObject *value)
break;
}
+ case var_color:
+ {
+ if (gdbpy_is_color (value))
+ self->value.color = gdbpy_get_color (value);
+ else
+ {
+ PyErr_SetString (PyExc_RuntimeError,
+ _("color argument must be a gdb.Color object."));
+ return -1;
+ }
+ }
+ break;
+
case var_boolean:
if (! PyBool_Check (value))
{
@@ -707,6 +729,15 @@ add_setshow_generic (enum var_types type, const literal_def *extra_literals,
get_show_value, set_list, show_list);
break;
+ case var_color:
+ /* Initialize the value, just in case. */
+ self->value.color = ui_file_style::NONE;
+ commands = add_setshow_color_cmd (cmd_name.get (), cmdclass,
+ &self->value.color, set_doc,
+ show_doc, help_doc, get_set_value,
+ get_show_value, set_list, show_list);
+ break;
+
default:
gdb_assert_not_reached ("Unhandled parameter class.");
}
@@ -830,7 +861,8 @@ parmpy_init (PyObject *self, PyObject *args, PyObject *kwds)
&& parmclass != param_string && parmclass != param_string_noescape
&& parmclass != param_optional_filename && parmclass != param_filename
&& parmclass != param_zinteger && parmclass != param_zuinteger
- && parmclass != param_zuinteger_unlimited && parmclass != param_enum)
+ && parmclass != param_zuinteger_unlimited && parmclass != param_enum
+ && parmclass != param_color)
{
PyErr_SetString (PyExc_RuntimeError,
_("Invalid parameter class argument."));
@@ -854,7 +886,7 @@ parmpy_init (PyObject *self, PyObject *args, PyObject *kwds)
extra_literals = param_to_var[parmclass].extra_literals;
obj->type = type;
obj->extra_literals = extra_literals;
- memset (&obj->value, 0, sizeof (obj->value));
+ obj->value = {}; /* zeros initialization */
if (var_type_uses<std::string> (obj->type))
obj->value.stringval = new std::string;
@@ -900,6 +932,8 @@ parmpy_dealloc (PyObject *obj)
if (var_type_uses<std::string> (parm_obj->type))
delete parm_obj->value.stringval;
+ else if (var_type_uses<ui_file_style::color> (parm_obj->type))
+ parm_obj->value.color.~color();
}
/* Initialize the 'parameters' module. */
diff --git a/gdb/python/python.c b/gdb/python/python.c
index cc06526ffcf..aed7b8c0ae1 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -35,6 +35,7 @@
#include "location.h"
#include "run-on-main-thread.h"
#include "observable.h"
+#include "py-color.h"
#if GDB_SELF_TEST
#include "gdbsupport/selftest.h"
@@ -506,6 +507,12 @@ gdbpy_parameter_value (const setting &var)
return host_string_to_python_string (str).release ();
}
+ case var_color:
+ {
+ const ui_file_style::color &color = var.get<ui_file_style::color> ();
+ return create_color_object (color).release ();
+ }
+
case var_boolean:
{
if (var.get<bool> ())
diff --git a/gdb/testsuite/gdb.base/default.exp b/gdb/testsuite/gdb.base/default.exp
index 628f0705b7f..a5fbebbbea5 100644
--- a/gdb/testsuite/gdb.base/default.exp
+++ b/gdb/testsuite/gdb.base/default.exp
@@ -15,8 +15,11 @@
# Start with a fresh gdb
-gdb_exit
-gdb_start
+with_ansi_styling_terminal {
+ setenv TERM dumb
+ gdb_exit
+ gdb_start
+}
set prev_timeout $timeout
set timeout 60
@@ -684,6 +687,7 @@ set show_conv_list \
{$_probe_arg10 = <error: No frame selected>} \
{$_probe_arg11 = <error: No frame selected>} \
{$_cimag = <internal function _cimag>} \
+ {$_colorsupport = "monochrome"} \
{$_creal = <internal function _creal>} \
{$_isvoid = <internal function _isvoid>} \
{$_shell = <internal function _shell>} \
diff --git a/gdb/testsuite/gdb.base/style.exp b/gdb/testsuite/gdb.base/style.exp
index d29b2381923..14f6a0a8597 100644
--- a/gdb/testsuite/gdb.base/style.exp
+++ b/gdb/testsuite/gdb.base/style.exp
@@ -70,6 +70,9 @@ proc run_style_tests { } {
global currently_disabled_style decimal hex
with_ansi_styling_terminal {
+ setenv TERM xterm-256color
+ setenv COLORTERM truecolor
+
# Restart GDB with the correct TERM variable setting, this
# means that GDB will enable styling.
clean_restart_and_disable "restart 1" ${binfile}
@@ -310,6 +313,21 @@ proc run_style_tests { } {
set url [limited_style "http:.*html" file]
gdb_test "show version" "${vers}.*<$url>.*" \
"'show version' is styled"
+
+ if { $currently_disabled_style != "version" } {
+ # Check that colors in styling can be set as integer and as RGB hex
+ # triplet. Check that the version string is styled in the output of
+ # 'show version' according to the set colors.
+ gdb_test_no_output "set style version intensity normal"
+ gdb_test_no_output "set style version background 255"
+ gdb_test_no_output "set style version foreground #FED210"
+ gdb_test "show style version background" \
+ "The \033\\\[38;2;254;210;16;48;5;255;22;27m.*\".*version.*\".*style.*\033\\\[m background color is: 255" \
+ "Version's 256-color background style"
+ gdb_test "show style version foreground" \
+ "The \033\\\[38;2;254;210;16;48;5;255;22;27m.*\".*version.*\".*style.*\033\\\[m foreground color is: #FED210" \
+ "Version's TrueColor foreground style"
+ }
}
}
@@ -486,6 +504,179 @@ proc test_startup_version_string { } {
gdb_test "" "${vers}.*" "version is styled at startup"
}
+# Color support is disabled when TERM=dumb and COLORTERM="".
+# All colors are approximated to none when set.
+proc test_colorsupport_monochrome { } {
+ with_test_prefix "colorsupport_monochrome" {
+ with_ansi_styling_terminal {
+ setenv TERM dumb
+ gdb_exit
+ gdb_start
+ gdb_test "print \$_colorsupport" \
+ "\\\$1 = \"monochrome\"" \
+ "color support is monochrome"
+ gdb_test_no_output "set style enabled off"
+ gdb_test_no_output "set style filename foreground none"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: none" \
+ "none without approximation"
+ gdb_test_no_output "set style filename foreground blue"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: none" \
+ "blue approximated to none"
+ }
+ }
+}
+
+# Color support is limited by 8 colors when TERM=ansi and COLORTERM="".
+# All colors are approximated to basic colors when set.
+proc test_colorsupport_8color { } {
+ with_test_prefix "colorsupport_8color" {
+ with_ansi_styling_terminal {
+ gdb_exit
+ gdb_start
+ gdb_test "print \$_colorsupport" \
+ "\033\\\[.*m\\\$1\033\\\[m = \"monochrome,ansi_8color\"" \
+ "color support is 8 color"
+ gdb_test_no_output "set style enabled off"
+ gdb_test_no_output "set style filename foreground none"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: none" \
+ "none without approximation"
+ gdb_test_no_output "set style filename foreground yellow"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: yellow" \
+ "yellow without approximation"
+ gdb_test_no_output "set style filename foreground 9"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: red" \
+ "9 approximated to red"
+ gdb_test_no_output "set style filename foreground 118"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: green" \
+ "118 approximated to green"
+ gdb_test_no_output "set style filename foreground #000ABC"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: blue" \
+ "#000ABC approximated to blue"
+ }
+ }
+}
+
+# Color support is limited by 256 colors when TERM=xterm-256color and
+# COLORTERM="". All colors are approximated by 256 color palette when set.
+proc test_colorsupport_256color { } {
+ with_test_prefix "colorsupport_256color" {
+ with_ansi_styling_terminal {
+ setenv TERM xterm-256color
+ gdb_exit
+ gdb_start
+ gdb_test "print \$_colorsupport" \
+ "\033\\\[.*m\\\$1\033\\\[m = \"monochrome,ansi_8color,aixterm_16color,xterm_256color\"" \
+ "color support is 256 color"
+ gdb_test_no_output "set style enabled off"
+ gdb_test_no_output "set style filename foreground none"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: none" \
+ "none without approximation"
+ gdb_test_no_output "set style filename foreground red"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: red" \
+ "red without approximation"
+ gdb_test_no_output "set style filename foreground 9"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: 9" \
+ "9 without approximation"
+ gdb_test_no_output "set style filename foreground 118"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: 118" \
+ "118 without approximation"
+ gdb_test_no_output "set style filename foreground #CD00CD"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: 5" \
+ "#CD00CD approximated to 5"
+ gdb_test_no_output "set style filename foreground #FFAF12"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: 214" \
+ "#FFAF12 approximated to 214"
+ }
+ }
+}
+
+# Color support is limited by 16777216 colors when TERM=xterm-256color and
+# COLORTERM="truecolor". No approximation needed.
+proc test_colorsupport_truecolor { } {
+ with_test_prefix "colorsupport_truecolor" {
+ with_ansi_styling_terminal {
+ setenv TERM xterm-256color
+ setenv COLORTERM truecolor
+ gdb_exit
+ gdb_start
+ gdb_test "print \$_colorsupport" \
+ "\033\\\[.*m\\\$1\033\\\[m = \"monochrome,ansi_8color,aixterm_16color,xterm_256color,rgb_24bit\"" \
+ "color support is truecolor"
+ gdb_test_no_output "set style enabled off"
+ gdb_test_no_output "set style filename foreground none"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: none" \
+ "none without approximation"
+ gdb_test_no_output "set style filename foreground red"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: red" \
+ "red without approximation"
+ gdb_test_no_output "set style filename foreground 9"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: 9" \
+ "9 without approximation"
+ gdb_test_no_output "set style filename foreground 118"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: 118" \
+ "118 without approximation"
+ gdb_test_no_output "set style filename foreground #CD00CD"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: #CD00CD" \
+ "#CD00CD without approximation"
+ }
+ }
+}
+
+# Color support is limited by 16777216 colors when TERM=dumb and
+# COLORTERM=24bit. No approximation needed. Basic colors replaced with RGB.
+proc test_colorsupport_truecolor_only { } {
+ with_test_prefix "colorsupport_truecolor_only" {
+ with_ansi_styling_terminal {
+ setenv TERM dumb
+ setenv COLORTERM truecolor
+ gdb_exit
+ gdb_start
+ gdb_test "print \$_colorsupport" \
+ "\\\$1 = \"monochrome,rgb_24bit\"" \
+ "color support is truecolor only"
+ gdb_test_no_output "set style enabled off"
+ gdb_test_no_output "set style filename foreground none"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: none" \
+ "none without approximation"
+ gdb_test_no_output "set style filename foreground red"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: #DE382B" \
+ "red replaced by #DE382B"
+ gdb_test_no_output "set style filename foreground 9"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: #FF0000" \
+ "9 replaced by #FF0000"
+ gdb_test_no_output "set style filename foreground 118"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: #87FF00" \
+ "118 replaced by #87FF00"
+ gdb_test_no_output "set style filename foreground #CD00CD"
+ gdb_test "show style filename foreground" \
+ "The \"filename\" style foreground color is: #CD00CD" \
+ "#CD00CD without approximation"
+ }
+ }
+}
+
# Check to see if the Python styling of disassembler output is
# expected or not, this styling requires Python support in GDB, and
# the Python pygments module to be available.
@@ -520,3 +711,9 @@ test_disassembler_error_handling
# Finally, check the styling of the version string during startup.
test_startup_version_string
+
+test_colorsupport_monochrome
+test_colorsupport_8color
+test_colorsupport_256color
+test_colorsupport_truecolor
+test_colorsupport_truecolor_only
diff --git a/gdb/testsuite/gdb.guile/scm-color.exp b/gdb/testsuite/gdb.guile/scm-color.exp
new file mode 100644
index 00000000000..9fd7f38b495
--- /dev/null
+++ b/gdb/testsuite/gdb.guile/scm-color.exp
@@ -0,0 +1,110 @@
+# Copyright (C) 2010-2024 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# This file is part of the GDB testsuite.
+# It tests GDB parameter support in Guile.
+
+load_lib gdb-guile.exp
+
+require allow_guile_tests
+
+clean_restart
+
+gdb_install_guile_utils
+gdb_install_guile_module
+
+gdb_test_no_output [concat "guile (define (print_color_attrs c) " \
+ "(display (color-string c)) (display \" \") " \
+ "(display (color-colorspace c)) (display \" \") " \
+ "(display (color-none? c)) (display \" \") " \
+ "(display (color-indexed? c)) (display \" \") " \
+ "(display (color-direct? c)) (newline))"] \
+ "print_color_attrs helper"
+
+gdb_test "guile (print_color_attrs (make-color))" \
+ "none 0 #t #f #f" \
+ "print attrs of a color without params"
+
+gdb_test_no_output "guile (define c (make-color \"green\"))" \
+ "create color from basic name string"
+gdb_test "guile (print_color_attrs c)" "green 1 #f #t #f" \
+ "print attrs of a basic color name"
+gdb_test "guile (print (color-index c))" "2" \
+ "print index of a basic color name"
+
+gdb_test_no_output "guile (define c (make-color 2))" \
+ "create color from basic index"
+gdb_test "guile (print_color_attrs c)" "green 1 #f #t #f" \
+ "print attrs of a basic color"
+gdb_test "guile (print (color-index c))" "2" \
+ "print index of a basic color"
+
+gdb_test_no_output "guile (define c (make-color 14))" \
+ "create color from integer 14"
+gdb_test "guile (print_color_attrs c)" "14 2 #f #t #f" \
+ "print attrs of an color 14"
+gdb_test "guile (print (color-index c))" "14" \
+ "print index of color 14"
+
+gdb_test_no_output "guile (define c (make-color 2 #:color-space COLORSPACE_ANSI_8COLOR))" \
+ "create color from basic index and ansi colorspace"
+gdb_test "guile (print_color_attrs c)" "green 1 #f #t #f" \
+ "print attrs of a basic color with ansi colorspace"
+gdb_test "guile (print (color-index c))" "2" \
+ "print index of a basic color with ansi colorspace"
+
+gdb_test_no_output "guile (define c (make-color 2 #:color-space COLORSPACE_XTERM_256COLOR))" \
+ "create color from basic index and xterm256 colorspace"
+gdb_test "guile (print_color_attrs c)" "2 3 #f #t #f" \
+ "print attrs of a basic color with xterm256 colorspace"
+gdb_test "guile (print (color-index c))" "2" \
+ "print index of a basic color with xterm256 colorspace"
+
+gdb_test_no_output "guile (define c (make-color '(171 205 239) #:color-space COLORSPACE_RGB_24BIT))" \
+ "create color from rgb components"
+gdb_test "guile (print_color_attrs c)" "#ABCDEF 4 #f #f #t" \
+ "print attrs of an RGB color"
+gdb_test "guile (print (color-components c))" "\\(171 205 239\\)" \
+ "print components of an RGB color"
+
+gdb_test_no_output "guile (define c (make-color \"none\"))" \
+ "create color from string none"
+gdb_test "guile (print_color_attrs c)" "none 0 #t #f #f" \
+ "print attrs of a color none"
+
+gdb_test_no_output "guile (define c (make-color \"254\"))" \
+ "create color from string 254"
+gdb_test "guile (print_color_attrs c)" "254 3 #f #t #f" \
+ "print attrs of an color 254"
+gdb_test "guile (print (color-index c))" "254" \
+ "print index of color 254"
+
+gdb_test_no_output "guile (define c_none (make-color \"none\"))" \
+ "save default color"
+gdb_test_no_output "guile (define c_red (make-color \"red\"))" \
+ "save blue color"
+gdb_test_no_output "guile (define c_green (make-color \"green\"))" \
+ "save yellow color"
+gdb_test [concat "guile " \
+ "(display (color-escape-sequence c_red #t)) " \
+ "(display (color-escape-sequence c_green #f)) " \
+ "(display \"red on green\") " \
+ "(display (color-escape-sequence c_none #f)) " \
+ "(display \" red on default\") " \
+ "(display (color-escape-sequence c_none #t)) " \
+ "(newline)"] \
+ "\033\\\[31m\033\\\[42mred on green\033\\\[49m red on default\033\\\[39m" \
+ "escape sequences"
+
diff --git a/gdb/testsuite/gdb.guile/scm-parameter.exp b/gdb/testsuite/gdb.guile/scm-parameter.exp
index d10e9d24065..eabd17980e7 100644
--- a/gdb/testsuite/gdb.guile/scm-parameter.exp
+++ b/gdb/testsuite/gdb.guile/scm-parameter.exp
@@ -389,3 +389,50 @@ with_test_prefix "previously-ambiguous" {
}
rename scm_param_test_maybe_no_output ""
+
+# Test a color parameter.
+
+with_ansi_styling_terminal {
+ # This enables 256 colors support and disables colors approximation.
+ setenv TERM xterm-256color
+ setenv COLORTERM truecolor
+
+ # Start with a fresh gdb.
+ gdb_exit
+ gdb_start
+ gdb_reinitialize_dir $srcdir/$subdir
+
+ gdb_install_guile_utils
+ gdb_install_guile_module
+
+ # We use "." here instead of ":" so that this works on win32 too.
+ set escaped_directory [string_to_regexp "$srcdir/$subdir"]
+
+ gdb_test_multiline "color gdb parameter" \
+ "guile" "" \
+ "(define test-color-param" "" \
+ " (make-parameter \"print test-color-param\"" "" \
+ " #:command-class COMMAND_DATA" "" \
+ " #:parameter-type PARAM_COLOR" "" \
+ " #:doc \"When set, test param does something useful. When disabled, does nothing.\"" "" \
+ " #:show-doc \"Show the state of the test-color-param.\"" "" \
+ " #:set-doc \"Set the state of the test-color-param.\"" "" \
+ " #:show-func (lambda (self value)" "" \
+ " (format #f \"The state of the test-color-param is ~a.\" value))" "" \
+ " #:initial-value (make-color \"green\")))" "" \
+ "(register-parameter! test-color-param)" "" \
+ "end"
+
+ with_test_prefix "test-color-param" {
+ with_test_prefix "initial-value" {
+ gdb_test "guile (print (parameter-value test-color-param))" "= #<gdb:color green COLORSPACE_ANSI_8COLOR>" "color parameter value (green)"
+ gdb_test "show print test-color-param" "The state of the test-color-param is green." "show initial value"
+ gdb_test_no_output "set print test-color-param 255"
+ }
+ with_test_prefix "new-value" {
+ gdb_test "show print test-color-param" "The state of the test-color-param is 255." "show new value"
+ gdb_test "guile (print (parameter-value test-color-param))" "= #<gdb:color 255 COLORSPACE_XTERM_256COLOR>" "color parameter value (255)"
+ gdb_test "set print test-color-param 256" "integer 256 out of range.*" "set invalid color parameter"
+ }
+ }
+}
diff --git a/gdb/testsuite/gdb.python/py-color.exp b/gdb/testsuite/gdb.python/py-color.exp
new file mode 100644
index 00000000000..eb62d7f84a2
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-color.exp
@@ -0,0 +1,100 @@
+# Copyright (C) 2010-2024 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# This file is part of the GDB testsuite.
+# It tests gdb.parameter and gdb.Parameter.
+
+load_lib gdb-python.exp
+
+require allow_python_tests
+
+# Start with a fresh gdb.
+clean_restart
+
+gdb_test_no_output "python print_color_attrs = lambda c: print (c, c.colorspace, c.is_none, c.is_indexed, c.is_direct)" \
+ "print_color_attrs helper"
+
+gdb_test_no_output "python c = gdb.Color ()" \
+ "create color without params"
+gdb_test "python print_color_attrs (c)" "none 0 True False False" \
+ "print attrs of a color without params"
+
+gdb_test_no_output "python c = gdb.Color ('green')" \
+ "create color from basic name string"
+gdb_test "python print_color_attrs (c)" "green 1 False True False" \
+ "print attrs of a basic color name"
+gdb_test "python print (c.index)" "2" \
+ "print index of a basic color name"
+
+gdb_test_no_output "python c = gdb.Color (2)" \
+ "create color from basic index"
+gdb_test "python print_color_attrs (c)" "green 1 False True False" \
+ "print attrs of a basic color"
+gdb_test "python print (c.index)" "2" \
+ "print index of a basic color"
+
+gdb_test_no_output "python c = gdb.Color (14)" \
+ "create color from integer 14"
+gdb_test "python print_color_attrs (c)" "14 2 False True False" \
+ "print attrs of an color 14"
+gdb_test "python print (c.index)" "14" \
+ "print index of color 14"
+
+gdb_test_no_output "python c = gdb.Color (2, gdb.COLORSPACE_ANSI_8COLOR)" \
+ "create color from basic index and ansi colorspace"
+gdb_test "python print_color_attrs (c)" "green 1 False True False" \
+ "print attrs of a basic color with ansi colorspace"
+gdb_test "python print (c.index)" "2" \
+ "print index of a basic color with ansi colorspace"
+
+gdb_test_no_output "python c = gdb.Color (2, gdb.COLORSPACE_XTERM_256COLOR)" \
+ "create color from basic index and xterm256 colorspace"
+gdb_test "python print_color_attrs (c)" "2 3 False True False" \
+ "print attrs of a basic color with xterm256 colorspace"
+gdb_test "python print (c.index)" "2" \
+ "print index of a basic color with xterm256 colorspace"
+
+gdb_test_no_output "python c = gdb.Color ((171, 205, 239), gdb.COLORSPACE_RGB_24BIT)" \
+ "create color from rgb components"
+gdb_test "python print_color_attrs (c)" "#ABCDEF 4 False False True" \
+ "print attrs of an RGB color"
+gdb_test "python print (c.components)" "\\(171, 205, 239\\)" \
+ "print components of an RGB color"
+
+gdb_test_no_output "python c = gdb.Color ('none')" \
+ "create color from string none"
+gdb_test "python print_color_attrs (c)" "none 0 True False False" \
+ "print attrs of a color none"
+
+gdb_test_no_output "python c = gdb.Color ('254')" \
+ "create color from string 254"
+gdb_test "python print_color_attrs (c)" "254 3 False True False" \
+ "print attrs of an color 254"
+gdb_test "python print (c.index)" "254" \
+ "print index of color 254"
+
+gdb_test_no_output "python c_none = gdb.Color ('none')" \
+ "save default color"
+gdb_test_no_output "python c_red = gdb.Color ('red')" \
+ "save blue color"
+gdb_test_no_output "python c_green = gdb.Color ('green')" \
+ "save yellow color"
+gdb_test [concat "python print (c_red.escape_sequence (True) + " \
+ "c_green.escape_sequence (False) + 'red on green' + " \
+ "c_none.escape_sequence (False) + ' red on default' + " \
+ "c_none.escape_sequence (True))"] \
+ "\033\\\[31m\033\\\[42mred on green\033\\\[49m red on default\033\\\[39m" \
+ "escape sequences"
+
diff --git a/gdb/testsuite/gdb.python/py-parameter.exp b/gdb/testsuite/gdb.python/py-parameter.exp
index de524f49ad6..74e4178bf35 100644
--- a/gdb/testsuite/gdb.python/py-parameter.exp
+++ b/gdb/testsuite/gdb.python/py-parameter.exp
@@ -185,6 +185,58 @@ proc_with_prefix test_enum_parameter { } {
"Undefined item: \"three\".*" "set invalid enum parameter"
}
+# Test an color parameter.
+proc_with_prefix test_color_parameter { } {
+ global env
+ with_ansi_styling_terminal {
+ # This enables 256 colors support and disables colors approximation.
+ setenv TERM xterm-256color
+ setenv COLORTERM truecolor
+
+ clean_restart
+
+ gdb_test_multiline "color gdb parameter" \
+ "python" "" \
+ "class TestColorParam (gdb.Parameter):" "" \
+ " \"\"\"When set, test param does something useful. When disabled, does nothing.\"\"\"" "" \
+ " show_doc = \"Show the state of the color\"" ""\
+ " set_doc = \"Set the state of the color\"" "" \
+ " def get_show_string (self, pvalue):" ""\
+ " return \"The state of the color is \" + str(pvalue)" ""\
+ " def get_set_string (self):" ""\
+ " return \"The state of the color has been set to \" + str(self.value)" ""\
+ " def __init__ (self, name):" "" \
+ " super (TestColorParam, self).__init__ (name, gdb.COMMAND_DATA, gdb.PARAM_COLOR)" "" \
+ " self.value = gdb.Color(\"green\")" "" \
+ "test_color_param = TestColorParam ('print test-color-param')" ""\
+ "end"
+
+ gdb_test "python print (test_color_param.value)" "green" \
+ "test color parameter value is green"
+ gdb_test "show print test-color-param" \
+ "The state of the color is green.*" \
+ "show parameter is initial value"
+ gdb_test "set print test-color-param 255" \
+ "The state of the color has been set to 255" "set color to 255"
+ gdb_test "show print test-color-param" \
+ "The state of the color is 255.*" "show parameter is new value"
+ gdb_test "python print (test_color_param.value)" "255" \
+ "test color parameter value is 255"
+ gdb_test_no_output "python test_color_param.value = gdb.Color(254)" \
+ "assign test_color_param.value to 254"
+ gdb_test "python print (test_color_param.value)" "254" \
+ "test color parameter value is integer"
+ gdb_test_no_output "python test_color_param.value = gdb.Color('#FED210')" \
+ "assign test_color_param.value to #FED210"
+ gdb_test "python print (test_color_param.value.components)" "\\(254, 210, 16\\)" \
+ "test color parameter components from RGB hex tripple value"
+ gdb_test "set print test-color-param 256" \
+ "integer 256 out of range.*" "set invalid color parameter"
+ gdb_test "python test_color_param.value = gdb.Color(256)" \
+ ".*Error occurred in Python: Palette color index 256 is out of range.*" "set invalid color value"
+ }
+}
+
# Test a file parameter.
proc_with_prefix test_file_parameter { } {
clean_restart
@@ -623,6 +675,7 @@ test_directories
test_data_directory
test_boolean_parameter
test_enum_parameter
+test_color_parameter
test_file_parameter
test_undocumented_parameter
test_really_undocumented_parameter
diff --git a/gdb/testsuite/lib/gdb-utils.exp b/gdb/testsuite/lib/gdb-utils.exp
index a1fdf7381a1..52e73a3d9fd 100644
--- a/gdb/testsuite/lib/gdb-utils.exp
+++ b/gdb/testsuite/lib/gdb-utils.exp
@@ -67,19 +67,23 @@ proc string_list_to_regexp { args } {
# "function", "variable", "address", etc.
proc style {str style} {
+ set fg 39
+ set bg 49
+ set intensity 22
+ set reverse 27
switch -exact -- $style {
- title { set style 1 }
- file { set style 32 }
- function { set style 33 }
- highlight { set style 31 }
- variable { set style 36 }
- address { set style 34 }
- metadata { set style 2 }
- version { set style "35;1" }
- line-number { set style 2 }
+ title { set intensity 1 }
+ file { set fg 32 }
+ function { set fg 33 }
+ highlight { set fg 31 }
+ variable { set fg 36 }
+ address { set fg 34 }
+ metadata { set intensity 2 }
+ version { set fg 35; set intensity 1 }
+ line-number { set intensity 2 }
none { return $str }
}
- return "\033\\\[${style}m${str}\033\\\[m"
+ return "\033\\\[${fg};${bg};${intensity};${reverse}m${str}\033\\\[m"
}
# gdb_get_bp_addr num
diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index 738cd2fe9c6..f7dcec8a597 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -10126,10 +10126,11 @@ proc with_override { name override body } {
# Run BODY after setting the TERM environment variable to 'ansi', and
# unsetting the NO_COLOR environment variable.
proc with_ansi_styling_terminal { body } {
- save_vars { ::env(TERM) ::env(NO_COLOR) } {
+ save_vars { ::env(TERM) ::env(NO_COLOR) ::env(COLORTERM) } {
# Set environment variables to allow styling.
setenv TERM ansi
unset -nocomplain ::env(NO_COLOR)
+ unset -nocomplain ::env(COLORTERM)
set code [catch {uplevel 1 $body} result]
}
diff --git a/gdb/top.c b/gdb/top.c
index eae54aae1ff..0352997dc93 100644
--- a/gdb/top.c
+++ b/gdb/top.c
@@ -2112,6 +2112,17 @@ show_startup_quiet (struct ui_file *file, int from_tty,
value);
}
+static void
+init_colorsupport_var ()
+{
+ const std::vector<color_space> &cs = colorsupport ();
+ std::string s;
+ for (color_space c : cs)
+ s.append (s.empty () ? "" : ",").append (color_space_name (c));
+ struct internalvar *colorsupport_var = create_internalvar ("_colorsupport");
+ set_internalvar_string (colorsupport_var, s.c_str ());
+}
+
static void
init_main (void)
{
@@ -2316,6 +2327,9 @@ gdb_init ()
during startup. */
set_language (language_c);
expected_language = current_language; /* Don't warn about the change. */
+
+ /* Create $_colorsupport convenience variable. */
+ init_colorsupport_var ();
}
void _initialize_top ();
diff --git a/gdb/ui-style.c b/gdb/ui-style.c
index 952102e30c3..f0a8e81d637 100644
--- a/gdb/ui-style.c
+++ b/gdb/ui-style.c
@@ -17,6 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include "ui-style.h"
+#include "gdb_curses.h"
#include "gdbsupport/gdb_regex.h"
/* A regular expression that is used for matching ANSI terminal escape
@@ -46,119 +47,250 @@ static const char ansi_regex_text[] =
static regex_t ansi_regex;
-/* This maps bright colors to RGB triples. The index is the bright
- color index, starting with bright black. The values come from
- xterm. */
-
-static const uint8_t bright_colors[][3] = {
- { 127, 127, 127 }, /* Black. */
- { 255, 0, 0 }, /* Red. */
- { 0, 255, 0 }, /* Green. */
- { 255, 255, 0 }, /* Yellow. */
- { 92, 92, 255 }, /* Blue. */
- { 255, 0, 255 }, /* Magenta. */
- { 0, 255, 255 }, /* Cyan. */
- { 255, 255, 255 } /* White. */
+/* This maps 8-color palette to RGB triples. The values come from
+ plain linux terminal. */
+
+static const uint8_t palette_8colors[][3] = {
+ { 1, 1, 1 }, /* Black. */
+ { 222, 56, 43 }, /* Red. */
+ { 57, 181, 74 }, /* Green. */
+ { 255, 199, 6 }, /* Yellow. */
+ { 0, 111, 184 }, /* Blue. */
+ { 118, 38, 113 }, /* Magenta. */
+ { 44, 181, 233 }, /* Cyan. */
+ { 204, 204, 204 }, /* White. */
+};
+
+/* This maps 16-color palette to RGB triples. The values come from xterm. */
+
+static const uint8_t palette_16colors[][3] = {
+ { 0, 0, 0 }, /* Black. */
+ { 205, 0, 0 }, /* Red. */
+ { 0, 205, 0 }, /* Green. */
+ { 205, 205, 0 }, /* Yellow. */
+ { 0, 0, 238 }, /* Blue. */
+ { 205, 0, 205 }, /* Magenta. */
+ { 0, 205, 205 }, /* Cyan. */
+ { 229, 229, 229 }, /* White. */
+ { 127, 127, 127 }, /* Bright Black. */
+ { 255, 0, 0 }, /* Bright Red. */
+ { 0, 255, 0 }, /* Bright Green. */
+ { 255, 255, 0 }, /* Bright Yellow. */
+ { 92, 92, 255 }, /* Bright Blue. */
+ { 255, 0, 255 }, /* Bright Magenta. */
+ { 0, 255, 255 }, /* Bright Cyan. */
+ { 255, 255, 255 } /* Bright White. */
};
/* See ui-style.h. */
+/* Must correspond to ui_file_style::basic_color. */
+const std::vector<const char *> ui_file_style::basic_color_enums = {
+ "none",
+ "black",
+ "red",
+ "green",
+ "yellow",
+ "blue",
+ "magenta",
+ "cyan",
+ "white",
+ nullptr
+};
-bool
+/* Returns text representation of a basic COLOR. */
+
+static const char *
+basic_color_name (int color)
+{
+ int pos = color - ui_file_style::NONE;
+ if (0 <= pos && pos < ui_file_style::basic_color_enums.size ())
+ if (const char *s = ui_file_style::basic_color_enums[pos])
+ return s;
+ error (_("Basic color %d has no name."), color);
+}
+
+/* See ui-style.h. */
+
+void
ui_file_style::color::append_ansi (bool is_fg, std::string *str) const
{
- if (m_simple)
- {
- if (m_value >= BLACK && m_value <= WHITE)
- str->append (std::to_string (m_value + (is_fg ? 30 : 40)));
- else if (m_value > WHITE && m_value <= WHITE + 8)
- str->append (std::to_string (m_value - WHITE + (is_fg ? 90 : 100)));
- else if (m_value != -1)
- {
- str->append (is_fg ? "38;5;" : "48;5;");
- str->append (std::to_string (m_value));
- }
- else
- return false;
- }
- else
+ if (m_color_space == color_space::MONOCHROME)
+ str->append (is_fg ? "39" : "49");
+ else if (is_basic ())
+ str->append (std::to_string (m_value + (is_fg ? 30 : 40)));
+ else if (m_color_space == color_space::AIXTERM_16COLOR)
+ str->append (std::to_string (m_value - WHITE - 1 + (is_fg ? 90 : 100)));
+ else if (m_color_space == color_space::XTERM_256COLOR)
+ str->append (is_fg ? "38;5;" : "48;5;").append (std::to_string (m_value));
+ else if (m_color_space == color_space::RGB_24BIT)
{
+ // See ISO/IEC 8613-6 (or ITU T.416) 13.1.8 Select Graphic Rendition (SGR)
str->append (is_fg ? "38;2;" : "48;2;");
str->append (std::to_string (m_red)
+ ";" + std::to_string (m_green)
+ ";" + std::to_string (m_blue));
}
- return true;
+ else
+ gdb_assert_not_reached ("no valid ansi representation of the color");
}
/* See ui-style.h. */
+std::string
+ui_file_style::color::to_ansi (bool is_fg) const
+{
+ std::string s = "\033[";
+ append_ansi (is_fg, &s);
+ s.push_back ('m');
+ return s;
+}
-void
-ui_file_style::color::get_rgb (uint8_t *rgb) const
+/* See ui-style.h. */
+
+std::string
+ui_file_style::color::to_string () const
{
- if (m_simple)
+ if (m_color_space == color_space::RGB_24BIT)
{
- /* Can't call this for a basic color or NONE -- those will end
- up in the assert below. */
- if (m_value >= 8 && m_value <= 15)
- memcpy (rgb, bright_colors[m_value - 8], 3 * sizeof (uint8_t));
- else if (m_value >= 16 && m_value <= 231)
- {
- int value = m_value;
- value -= 16;
- /* This obscure formula seems to be what terminals actually
- do. */
- int component = value / 36;
- rgb[0] = component == 0 ? 0 : (55 + component * 40);
- value %= 36;
- component = value / 6;
- rgb[1] = component == 0 ? 0 : (55 + component * 40);
- value %= 6;
- rgb[2] = value == 0 ? 0 : (55 + value * 40);
- }
- else if (m_value >= 232)
- {
- uint8_t v = (m_value - 232) * 10 + 8;
- rgb[0] = v;
- rgb[1] = v;
- rgb[2] = v;
- }
- else
- gdb_assert_not_reached ("get_rgb called on invalid color");
+ char s[64];
+ snprintf (s, sizeof s, "#%02X%02X%02X", m_red, m_green, m_blue);
+ return s;
}
+ else if (is_none () || is_basic ())
+ return basic_color_name (m_value);
else
+ return std::to_string (get_value ());
+}
+
+/* See ui-style.h. */
+
+void
+ui_file_style::color::get_rgb (uint8_t *rgb) const
+{
+ if (m_color_space == color_space::RGB_24BIT)
{
rgb[0] = m_red;
rgb[1] = m_green;
rgb[2] = m_blue;
}
+ else if (m_color_space == color_space::ANSI_8COLOR
+ && 0 <= m_value && m_value <= 7)
+ memcpy (rgb, palette_8colors[m_value], 3 * sizeof (uint8_t));
+ else if (m_color_space == color_space::AIXTERM_16COLOR
+ && 0 <= m_value && m_value <= 15)
+ memcpy (rgb, palette_16colors[m_value], 3 * sizeof (uint8_t));
+ else if (m_color_space != color_space::XTERM_256COLOR)
+ gdb_assert_not_reached ("get_rgb called on invalid color");
+ else if (0 <= m_value && m_value <= 15)
+ memcpy (rgb, palette_16colors[m_value], 3 * sizeof (uint8_t));
+ else if (m_value >= 16 && m_value <= 231)
+ {
+ int value = m_value;
+ value -= 16;
+ /* This obscure formula seems to be what terminals actually
+ do. */
+ int component = value / 36;
+ rgb[0] = component == 0 ? 0 : (55 + component * 40);
+ value %= 36;
+ component = value / 6;
+ rgb[1] = component == 0 ? 0 : (55 + component * 40);
+ value %= 6;
+ rgb[2] = value == 0 ? 0 : (55 + value * 40);
+ }
+ else if (232 <= m_value && m_value <= 255)
+ {
+ uint8_t v = (m_value - 232) * 10 + 8;
+ rgb[0] = v;
+ rgb[1] = v;
+ rgb[2] = v;
+ }
+ else
+ gdb_assert_not_reached ("get_rgb called on invalid color");
}
/* See ui-style.h. */
-std::string
-ui_file_style::to_ansi () const
+ui_file_style::color
+ui_file_style::color::approximate (const std::vector<color_space> &spaces) const
{
- std::string result ("\033[");
- bool need_semi = m_foreground.append_ansi (true, &result);
- if (!m_background.is_none ())
+ if (spaces.empty () || is_none ())
+ return NONE;
+
+ color_space target_space = color_space::MONOCHROME;
+ for (color_space sp : spaces)
+ if (sp == m_color_space)
+ return *this;
+ else if (sp > target_space)
+ target_space = sp;
+
+ if (target_space == color_space::RGB_24BIT)
{
- if (need_semi)
- result.push_back (';');
- m_background.append_ansi (false, &result);
- need_semi = true;
+ uint8_t rgb[3];
+ get_rgb (rgb);
+ return color (rgb[0], rgb[1], rgb[2]);
+ }
+
+ int target_size = 0;
+ switch (target_space)
+ {
+ case color_space::ANSI_8COLOR:
+ target_size = 8;
+ break;
+ case color_space::AIXTERM_16COLOR:
+ target_size = 16;
+ break;
+ case color_space::XTERM_256COLOR:
+ target_size = 256;
+ break;
}
- if (m_intensity != NORMAL)
+
+ if (is_simple() && m_value < target_size)
+ return color (target_space, m_value);
+
+ color result = NONE;
+ int best_distance = std::numeric_limits<int>::max ();
+ uint8_t rgb[3];
+ get_rgb (rgb);
+
+ for (int i = 0; i < target_size; ++i)
{
- if (need_semi)
- result.push_back (';');
- result.append (std::to_string (m_intensity));
- need_semi = true;
+ uint8_t c_rgb[3];
+ color c (target_space, i);
+ c.get_rgb (c_rgb);
+ int d_red = std::abs (rgb[0] - c_rgb[0]);
+ int d_green = std::abs (rgb[1] - c_rgb[1]);
+ int d_blue = std::abs (rgb[2] - c_rgb[2]);
+ int dist = d_red * d_red + d_green * d_green + d_blue * d_blue;
+ if (dist < best_distance)
+ {
+ best_distance = dist;
+ result = c;
+ }
}
- if (m_reverse)
+
+ return result;
+}
+
+/* See ui-style.h. */
+
+std::string
+ui_file_style::to_ansi () const
+{
+ std::string result ("\033[");
+ if (!is_default ())
{
- if (need_semi)
- result.push_back (';');
- result.push_back ('7');
+ m_foreground.append_ansi (true, &result);
+ result.push_back (';');
+ m_background.append_ansi (false, &result);
+ result.push_back (';');
+ if (m_intensity == NORMAL)
+ result.append ("22");
+ else
+ result.append (std::to_string (m_intensity));
+ result.push_back (';');
+ if (m_reverse)
+ result.push_back ('7');
+ else
+ result.append ("27");
}
result.push_back ('m');
return result;
@@ -315,9 +447,11 @@ ui_file_style::parse (const char *buf, size_t *n_read)
case 35:
case 36:
case 37:
+ m_foreground = color (value - 30);
+ break;
/* Note: not 38. */
case 39:
- m_foreground = color (value - 30);
+ m_foreground = NONE;
break;
case 40:
@@ -328,9 +462,11 @@ ui_file_style::parse (const char *buf, size_t *n_read)
case 45:
case 46:
case 47:
+ m_background = color (value - 40);
+ break;
/* Note: not 48. */
case 49:
- m_background = color (value - 40);
+ m_background = NONE;
break;
case 90:
@@ -412,3 +548,60 @@ _initialize_ui_style ()
error. */
gdb_assert (code == 0);
}
+
+/* See ui-style.h. */
+
+const std::vector<color_space> &
+colorsupport ()
+{
+ static const std::vector<color_space> value = []
+ {
+ std::vector<color_space> result = {color_space::MONOCHROME};
+
+ int colors = tgetnum ("Co");
+ if (colors >= 8)
+ result.push_back (color_space::ANSI_8COLOR);
+ if (colors >= 16)
+ result.push_back (color_space::AIXTERM_16COLOR);
+ if (colors >= 256)
+ result.push_back (color_space::XTERM_256COLOR);
+
+ const char *colorterm = getenv ("COLORTERM");
+ if (colorterm != nullptr && (!strcmp (colorterm, "truecolor")
+ || !strcmp (colorterm, "24bit")))
+ result.push_back (color_space::RGB_24BIT);
+
+ return result;
+ } ();
+ return value;
+}
+
+const char *
+color_space_name (color_space c)
+{
+ switch (c)
+ {
+ case color_space::MONOCHROME: return "monochrome";
+ case color_space::ANSI_8COLOR: return "ansi_8color";
+ case color_space::AIXTERM_16COLOR: return "aixterm_16color";
+ case color_space::XTERM_256COLOR: return "xterm_256color";
+ case color_space::RGB_24BIT: return "rgb_24bit";
+ }
+ gdb_assert_not_reached ("color_space_name called on invalid color");
+}
+
+bool
+color_space_safe_cast (color_space *result, long c)
+{
+ switch (static_cast<color_space>(c))
+ {
+ case color_space::MONOCHROME:
+ case color_space::ANSI_8COLOR:
+ case color_space::AIXTERM_16COLOR:
+ case color_space::XTERM_256COLOR:
+ case color_space::RGB_24BIT:
+ *result = static_cast<color_space>(c);
+ return true;
+ }
+ return false;
+}
diff --git a/gdb/ui-style.h b/gdb/ui-style.h
index 1b7b5fafb9d..0a1b87d5137 100644
--- a/gdb/ui-style.h
+++ b/gdb/ui-style.h
@@ -19,6 +19,38 @@
#ifndef UI_STYLE_H
#define UI_STYLE_H
+/* One of the color spaces that usually supported by terminals. */
+enum class color_space
+{
+ /* one default terminal color */
+ MONOCHROME,
+
+ /* foreground colors \e[30m ... \e[37m,
+ background colors \e[40m ... \e[47m */
+ ANSI_8COLOR,
+
+ /* foreground colors \e[30m ... \e[37m, \e[90m ... \e[97m
+ background colors \e[40m ... \e[47m, \e[100m ... \e107m */
+ AIXTERM_16COLOR,
+
+ /* foreground colors \e[38;5;0m ... \e[38;5;255m
+ background colors \e[48;5;0m ... \e[48;5;255m */
+ XTERM_256COLOR,
+
+ /* foreground colors \e[38;2;0;0;0m ... \e[38;2;255;255;255m
+ background colors \e[48;2;0;0;0m ... \e[48;2;255;255;255m */
+ RGB_24BIT
+};
+
+/* Color spaces supported by terminal. */
+extern const std::vector<color_space> & colorsupport ();
+
+/* Textual representation of C. */
+extern const char * color_space_name (color_space c);
+
+/* Cast C to RESULT and return true if it's value is valid; false otherwise. */
+extern bool color_space_safe_cast (color_space *result, long c);
+
/* Styles that can be applied to a ui_file. */
struct ui_file_style
{
@@ -43,20 +75,61 @@ struct ui_file_style
public:
color (basic_color c)
- : m_simple (true),
+ : m_color_space (c == NONE ? color_space::MONOCHROME
+ : color_space::ANSI_8COLOR),
m_value (c)
{
}
color (int c)
- : m_simple (true),
+ : m_value (c)
+ {
+ if (c < -1 || c > 255)
+ error (_("Palette color index %d is out of range."), c);
+ if (c == -1)
+ m_color_space = color_space::MONOCHROME;
+ else if (c <= 7)
+ m_color_space = color_space::ANSI_8COLOR;
+ else if (c <= 15)
+ m_color_space = color_space::AIXTERM_16COLOR;
+ else
+ m_color_space = color_space::XTERM_256COLOR;
+ }
+
+ color (color_space cs, int c)
+ : m_color_space (cs),
m_value (c)
{
- gdb_assert (c >= -1 && c <= 255);
+ if (c < -1 || c > 255)
+ error (_("Palette color index %d is out of range."), c);
+
+ std::pair<int, int> range;
+ switch (cs)
+ {
+ case color_space::MONOCHROME:
+ range = {-1, -1};
+ break;
+ case color_space::ANSI_8COLOR:
+ range = {0, 7};
+ break;
+ case color_space::AIXTERM_16COLOR:
+ range = {0, 15};
+ break;
+ case color_space::XTERM_256COLOR:
+ range = {0, 255};
+ break;
+ default:
+ error (_("Color space %d is incompatible with indexed colors."),
+ static_cast<int> (cs));
+ }
+
+ if (c < range.first || c > range.second)
+ error (_("Color %d is out of range [%d, %d] of color space %d."),
+ c, range.first, range.second, static_cast<int> (cs));
}
color (uint8_t r, uint8_t g, uint8_t b)
- : m_simple (false),
+ : m_color_space (color_space::RGB_24BIT),
m_red (r),
m_green (g),
m_blue (b)
@@ -65,19 +138,24 @@ struct ui_file_style
bool operator== (const color &other) const
{
- if (m_simple != other.m_simple)
+ if (m_color_space != other.m_color_space)
return false;
- if (m_simple)
+ if (is_simple ())
return m_value == other.m_value;
return (m_red == other.m_red && m_green == other.m_green
&& m_blue == other.m_blue);
}
+ bool operator!= (const color &other) const
+ {
+ return ! (*this == other);
+ }
+
bool operator< (const color &other) const
{
- if (m_simple != other.m_simple)
- return m_simple < other.m_simple;
- if (m_simple)
+ if (m_color_space != other.m_color_space)
+ return m_color_space < other.m_color_space;
+ if (is_simple ())
return m_value < other.m_value;
if (m_red < other.m_red)
return true;
@@ -91,23 +169,54 @@ struct ui_file_style
return false;
}
+ color_space colorspace () const
+ {
+ return m_color_space;
+ }
+
/* Return true if this is the "NONE" color, false otherwise. */
bool is_none () const
{
- return m_simple && m_value == NONE;
+ return m_color_space == color_space::MONOCHROME && m_value == NONE;
}
/* Return true if this is one of the basic colors, false
otherwise. */
bool is_basic () const
{
- return m_simple && m_value >= BLACK && m_value <= WHITE;
+ if (m_color_space == color_space::ANSI_8COLOR
+ || m_color_space == color_space::AIXTERM_16COLOR)
+ return BLACK <= m_value && m_value <= WHITE;
+ else
+ return false;
+ }
+
+ /* Return true if this is one of the colors, stored as int, false
+ otherwise. */
+ bool is_simple () const
+ {
+ return m_color_space != color_space::RGB_24BIT;
+ }
+
+ /* Return true if this is one of the indexed colors, false
+ otherwise. */
+ bool is_indexed () const
+ {
+ return m_color_space != color_space::RGB_24BIT
+ && m_color_space != color_space::MONOCHROME;
+ }
+
+ /* Return true if this is one of the direct colors (RGB, CMY, CMYK), false
+ otherwise. */
+ bool is_direct () const
+ {
+ return m_color_space == color_space::RGB_24BIT;
}
- /* Return the value of a basic color. */
+ /* Return the value of a simple color. */
int get_value () const
{
- gdb_assert (is_basic ());
+ gdb_assert (is_simple ());
return m_value;
}
@@ -118,14 +227,23 @@ struct ui_file_style
/* Append the ANSI terminal escape sequence for this color to STR.
IS_FG indicates whether this is a foreground or background
- color. Returns true if any characters were written; returns
- false otherwise (which can only happen for the "NONE"
- color). */
- bool append_ansi (bool is_fg, std::string *str) const;
+ color. */
+ void append_ansi (bool is_fg, std::string *str) const;
+
+ /* Return the ANSI escape sequence for this color.
+ IS_FG indicates whether this is a foreground or background color. */
+ std::string to_ansi (bool is_fg) const;
+
+ /* Returns text representation of this object.
+ It is "none", name of a basic color, number or a #RRGGBB hex triplet. */
+ std::string to_string () const;
+
+ /* Approximates THIS color by closest one from SPACES. */
+ color approximate (const std::vector<color_space> &spaces) const;
private:
- bool m_simple;
+ color_space m_color_space;
union
{
int m_value;
@@ -235,6 +353,9 @@ struct ui_file_style
return this;
}
+ /* nullptr-terminated list of names corresponding to enum basic_color. */
+ static const std::vector<const char *> basic_color_enums;
+
private:
color m_foreground = NONE;
diff --git a/gdb/unittests/style-selftests.c b/gdb/unittests/style-selftests.c
index 4dc3c842c47..72bc878582a 100644
--- a/gdb/unittests/style-selftests.c
+++ b/gdb/unittests/style-selftests.c
@@ -58,7 +58,7 @@ run_tests ()
SELF_CHECK (style.get_background ().is_none ());
SELF_CHECK (style.get_intensity () == ui_file_style::NORMAL);
SELF_CHECK (style.is_reverse ());
- SELF_CHECK (style.to_ansi () == "\033[7m");
+ SELF_CHECK (style.to_ansi () == "\033[39;49;22;7m");
style = ui_file_style ();
SELF_CHECK (style.parse ("\033[32;1m", &n_read));
@@ -68,7 +68,7 @@ run_tests ()
SELF_CHECK (style.get_background ().is_none ());
SELF_CHECK (style.get_intensity () == ui_file_style::BOLD);
SELF_CHECK (!style.is_reverse ());
- SELF_CHECK (style.to_ansi () == "\033[32;1m");
+ SELF_CHECK (style.to_ansi () == "\033[32;49;1;27m");
style = ui_file_style ();
SELF_CHECK (style.parse ("\033[38;5;112;48;5;249m", &n_read));
@@ -81,7 +81,7 @@ run_tests ()
CHECK_RGB (0xb2, 0xb2, 0xb2);
SELF_CHECK (style.get_intensity () == ui_file_style::NORMAL);
SELF_CHECK (!style.is_reverse ());
- SELF_CHECK (style.to_ansi () == "\033[38;5;112;48;5;249m");
+ SELF_CHECK (style.to_ansi () == "\033[38;5;112;48;5;249;22;27m");
style = ui_file_style ();
SELF_CHECK (style.parse ("\033[38;2;83;84;85;48;2;0;1;254;2;7m", &n_read));
--
2.34.1
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v7] Add an option with a color type.
2024-10-05 19:11 ` Andrei Pikas
@ 2024-10-05 19:40 ` Eli Zaretskii
0 siblings, 0 replies; 33+ messages in thread
From: Eli Zaretskii @ 2024-10-05 19:40 UTC (permalink / raw)
To: Andrei Pikas; +Cc: tom, gdb-patches
> Date: Sat, 5 Oct 2024 22:11:12 +0300
> Cc: tom@tromey.com, gdb-patches@sourceware.org
> From: Andrei Pikas <gdb@mail.api.win>
>
> > Regardless, were these changes tested in the MinGW port of GDB? It
> > emulates Posix terminal handling of colors via SGR escape sequences,
> > and I wonder whether these changes might somehow break styling support
> > in the MinGW port.
> No, I can't build gdb with MinGW.
> The same escape sequences \033[30m ... \033[37m will be used
> for the basic eight colors as before. This patch only adds the possibility
> for the user to setup another colors in addition to the basic. But they
> are not used by default, so the styling will not be broken.
And if the user wants to add a color, what do they need to do after
these changes?
> > +@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}.
> > What does this return for the MS-Windows terminal? aixterm_16color?
> > IOW, will any 16-color terminal return aixterm_16color? I think this
> > should be documented, and perhaps we should remove the "aix" part from
> > the name (since it is not necessarily specific to AIX).
> tgetnum("Co") returns 8 in msys and 256 in cmd.exe because
> TERM=xterm in msys and TERM is unset in cmd.exe.
> When I set TERM=xterm or set TERM=ansi it returns 8.
But the MS-Windows console supports 16 colors, not 8.
> monochrome color space is enabled always.
> ansi_8color is enabled when tgetnum("Co") returns >= 8.
> aixterm_16color is enabled when tgetnum("Co") returns >= 16.
> xterm_256color is enabled when tgetnum("Co") returns >= 256.
> rgb_24bit is enabled when COLORTERM=truecolor or 24bit.
> I will document that it depends on "Co" termcap, TERM and COLORTERM
> in the next patch version.
The "Co" capability on Windows is emulated, as Windows doesn't really
support this stuff. So relying on what you get from tgetnum in
various cases, like with MSYS, will likely produce results you cannot
rely upon.
> I don't think that "aix" part should be removed. Because there is no
> standard
> for the escape sequences \033[90m ... \033[97m. They where first used in the
> aixterm and it is the only way to refer them unambiguously. Any other
> terminal
> in theory may invent it's own 16 colors and escape sequences for them.
Then I guess we will need a new name for the Windows terminal?
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v8] Add an option with a color type.
2024-10-05 19:27 ` [PATCH v8] " Andrei Pikas
@ 2024-10-06 5:40 ` Eli Zaretskii
2024-12-13 21:01 ` Tom Tromey
2025-02-03 16:23 ` Tom de Vries
2 siblings, 0 replies; 33+ messages in thread
From: Eli Zaretskii @ 2024-10-06 5:40 UTC (permalink / raw)
To: Andrei Pikas; +Cc: tom, gdb-patches, gdb
> From: Andrei Pikas <gdb@mail.api.win>
> Cc: gdb-patches@sourceware.org,
> Andrei Pikas <gdb@mail.api.win>
> Date: Sat, 5 Oct 2024 22:27:44 +0300
>
> gdb/Makefile.in | 2 +
> gdb/NEWS | 31 ++
> gdb/cli/cli-cmds.c | 6 +
> gdb/cli/cli-decode.c | 174 +++++++++
> gdb/cli/cli-decode.h | 21 ++
> gdb/cli/cli-option.c | 44 +++
> gdb/cli/cli-option.h | 21 ++
> gdb/cli/cli-setshow.c | 21 ++
> gdb/cli/cli-style.c | 49 +--
> gdb/cli/cli-style.h | 4 +-
> gdb/command.h | 26 +-
> gdb/doc/gdb.texinfo | 46 ++-
> gdb/doc/guile.texi | 104 ++++++
> gdb/doc/python.texi | 98 +++++
> gdb/guile/guile-internal.h | 9 +
> gdb/guile/guile.c | 1 +
> gdb/guile/scm-color.c | 427 ++++++++++++++++++++++
> gdb/guile/scm-param.c | 33 +-
> gdb/python/py-color.c | 336 +++++++++++++++++
> gdb/python/py-color.h | 35 ++
> gdb/python/py-param.c | 40 +-
> gdb/python/python.c | 7 +
> gdb/testsuite/gdb.base/default.exp | 8 +-
> gdb/testsuite/gdb.base/style.exp | 197 ++++++++++
> gdb/testsuite/gdb.guile/scm-color.exp | 110 ++++++
> gdb/testsuite/gdb.guile/scm-parameter.exp | 47 +++
> gdb/testsuite/gdb.python/py-color.exp | 100 +++++
> gdb/testsuite/gdb.python/py-parameter.exp | 53 +++
> gdb/testsuite/lib/gdb-utils.exp | 24 +-
> gdb/testsuite/lib/gdb.exp | 3 +-
> gdb/top.c | 14 +
> gdb/ui-style.c | 353 ++++++++++++++----
> gdb/ui-style.h | 157 +++++++-
> gdb/unittests/style-selftests.c | 6 +-
> 34 files changed, 2436 insertions(+), 171 deletions(-)
> create mode 100644 gdb/guile/scm-color.c
> create mode 100644 gdb/python/py-color.c
> create mode 100644 gdb/python/py-color.h
> create mode 100644 gdb/testsuite/gdb.guile/scm-color.exp
> create mode 100644 gdb/testsuite/gdb.python/py-color.exp
Thanks, some comments for the documentation parts below.
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -49,11 +49,42 @@
> history has been reached. It also specifies that the forward execution can
> continue, and the recording will also continue.
>
> +* "set style" commands now supports numeric format for basic colors
> + from 0 to 255 and #RRGGBB format for TrueColor.
> +
> +* New built-in convenience variable $_colorsupport provides comma-separated
> + list of color space names supported by terminal. Each color space name is one
> + of monochrome, ansi_8color, aixterm_16color, xterm_256color or rgb_24bit.
> + It is handy for conditionally using styling colors based on terminal features.
> + For example:
> +
> + (gdb) if $_regex ($_colorsupport, ".*(^|,)rgb_24bit($|,).*")
> + >set style filename background #FACADE
> + >else
> + >if $_regex ($_colorsupport, ".*(^|,)xterm_256color($|,).*")
> + >set style filename background 224
> + >else
> + >set style filename background red
> + >end
> + >end
> +
> * Python API
>
> ** Added gdb.record.clear. Clears the trace data of the current recording.
> This forces re-decoding of the trace for successive commands.
>
> + ** New class gdb.Color for dealing with colors.
> +
> + ** New constant gdb.PARAM_COLOR represents color type of a
> + gdb.Parameter.value. Parameter's value is gdb.Color instance.
> +
> +* Guile API
> +
> + ** New type <gdb:color> for dealing with colors.
> +
> + ** New constant PARAM_COLOR represents color type of a value
> + of a <gdb:parameter> object. Parameter's value is <gdb::color> instance.
> +
> * Debugger Adapter Protocol changes
The NEWS part is okay.
> +@item $_colorsupport
> +@vindex $_colorsupport@r{, convenience variable}
> +@cindex color space
> +Comma-separated list of @dfn{color space} names supported by terminal. Names
Please always put all the index commands _before_ @item, not after it.
This is so following the index entry in an Info reader places you at
the line corresponding to @item and not on the line after that.
> @table @code
> @item set style filename background @var{color}
> -Set the background to @var{color}. Valid colors are @samp{none}
> -(meaning the terminal's default color), @samp{black}, @samp{red},
> -@samp{green}, @samp{yellow}, @samp{blue}, @samp{magenta}, @samp{cyan},
> -and@samp{white}.
> +Set the background to @var{color}. @var{color} can be @samp{none}
> +(meaning the terminal's default color), a name of one of the eight standard
> +colors of ISO/IEC 6429, index from 0 to 255 into terminal's color
> +palette or a hexadecimal RGB triplet in @samp{#RRGGBB} format for
> +24-bit TrueColor.
> +
> +Valid color names are @samp{black}, @samp{red}, @samp{green},
> +@samp{yellow}, @samp{blue}, @samp{magenta}, @samp{cyan}, and
> +@samp{white}.
To make sure the reader understands that these 8 names are what is
mentioned earlier as "the eight standard colors", I suggest to start
this paragraph with "Valid names of the eight standard colors are..."
> +Integers 0 to 7 are the synonyms for the standard colors. Integers 8-15 are
> +used for the so-called bright colors from the aixterm extended 16-color
> +palette.
As followup to my previous questions: can these bright colors also be
used on the Windows terminal?
Also, shouldn't we document in the manual the names of those bright
colors?
> Integers 16-255 are the indexes into xterm extended 256-color palette
> +(usually 6x6x6 cube plus gray ramp). In general, 256-color palette is terminal
> +dependent and sometimes can be changed with OSC 4 sequences, e.g.
> +"\033]4;1;rgb:00/FF/00\033\\". A hexadecimal 24-bit TrueColor is specified in
^^ ^^
What is that double backslash there? are those two literal
backslashes? Also, please leave two spaces between sentences.
> +@item PARAM_COLOR
> +The value is either a string or an unsigned integer. Integer from 0 to 255
> +means index into terminal's color palette. String can be a hex RGB triplet in
> +@samp{#RRGGBB} format or one of the following color names:
> +@samp{none} (meaning the terminal's default color), @samp{black}, @samp{red},
> +@samp{green}, @samp{yellow}, @samp{blue}, @samp{magenta}, @samp{cyan},
> +or @samp{white}.
> @end vtable
This text repeats what you say below in "Colors in Guile". It is
better to replace it with a cross-reference to that mode, saying here
just that the value can be either an integer or a string.
Btw, since this is describing a programmatic interface, should we also
say what happens if the value is an integer or a string that the
terminal does not support?
> +@deffn {Scheme Procedure} make-color @w{@r{[}value} @
> + @w{@r{[}#:color-space color-space@r{]}@r{]}}
> +
> +@var{value} is an integer index of a color in palette, tuple with color
> +components or a string. ^^^^^^^^^^^^^^^^
^^^^^^^^^^
This is the first and the only time you mention tuple of components in
the Guile section. If tuples are indeed allowed, they should be
mentioned in the other places and also documented in more detail.
> +format or one of the following color names:
> +@samp{none} (meaning the terminal's default color), @samp{black}, @samp{red},
> +@samp{green}, @samp{yellow}, @samp{blue}, @samp{magenta}, @samp{cyan},
> +or @samp{white}.
> +
> +@var{color-space} should be one of the @samp{COLORSPACE_} constants. This
> +argument tells @value{GDBN} which color space @var{value} belongs.
COLOR-SPACE is an optional argument, right? If so, this should
document what happens if it is omitted.
More generally, I'm a bit confused by the notion of "palette" or
"color space" (which are the same, right?). If I have a color whose
index is N < 8, doesn't that refer to the same color regardless of the
palette? And if I specify a color with #RRGGBB whose value exactly
corresponds to one of the 8 standard colors, don't I get the same
color regardless of the palette? If so, it sounds like the palette
only makes a difference when the color is specified by a value (index
or RGB string) that does NOT belong to the palette, right? And if so,
why do we at all need this notion of the palette in GDB?
> +@deffn {Scheme Procedure} color-none? color
> +Return @code{#t} if @var{color} is terminal's default.
Is it a good idea to use 'color-none?' as the name? why not
color-default? Also, AFAIU a terminal has two default colors: one for
the foreground, the other for the background. How does this procedure
know which of these two is the caller asking about?
> +@deffn {Scheme Procedure} color-indexed? color
> +Return @code{#t} if @var{color} is indexed, i.e. belongs to some palette.
Please use "i.e.@:" or "i.e.,". Otherwise, TeX could decide that the
period ends the sentence.
> +Otherwise return @code{#f}.
> +@end deffn
> +
> +@deffn {Scheme Procedure} color-direct? color
> +Return @code{#t} if @var{color} is direct in the sense of ISO/IEC 8613-6.
> +Otherwise return @code{#f}.
> +@end deffn
I'm a bit confused about these predicates: don't at least some,
perhaps even all, of the colors satisfy both indexed? and direct?
predicates? If so, when will these return #f? And why have these
predicates in the first place? when will they be useful?
> +@deffn {Scheme Procedure} color-string color
> +Return the textual representation of a @code{<gdb:color>} object.
> +@end deffn
This should probably explain what is the returned string. I'm
guessing it's the name for the 8 standard colors (or maybe for the 16
standard colors of a 16-color terminal), and #RRGGBB for the rest, but
the manual should tell that explicitly.
> +@deffn {Scheme Procedure} color-index color
> +Return index of the color of a @code{<gdb:color>} object in a palette.
> +@end deffn
What does this return for TrueColor colors?
> +@deffn {Scheme Procedure} color-components color
> +Return components of the direct @code{<gdb:color>} object.
> +@end deffn
Please document the form of the return value, i.e. how the components
are returned.
> +@deffn {Scheme Procedure} color-escape-sequence color is_foreground
> +Return string to change terminal's color to this.
^^^^^^
"the string"
> +When color is initialized, its color space must be specified.
What do you mean by "color is initialized"? This is the first time a
color's initialization is mentioned, and it must be explained.
> +@vtable @code
> +@item COLORSPACE_MONOCHROME
> +Palette with only terminal's default color.
Two colors, right? One for foreground, the other for background. Or
does this refer only to the foreground color?
> +@item COLORSPACE_AIXTERM_16COLOR
> +Palette with 16 colors. First eight are standard colors of ISO/IEC 6429
> +"black", "red", "green", etc. Next eight are their bright version.
^^^^^^^
"versions".
> +@defvar Color.is_none
> +This atribute is boolean. If its value is @code{True} then color is terminal's
> +default.
> +@end defvar
> +
> +@defvar Color.is_indexed
> +This atribute is boolean. If its value is @code{True} then color is indexed,
> +i.e. belongs to some palette.
> +@end defvar
> +
> +@defvar Color.is_direct
> +This atribute is boolean. If its value is @code{True} then this object
> +describes direct colour in the sense of ISO/IEC 8613-6.
> +@end defvar
Same questions/comments here as for the equivalent Guile APIs.
> +
> +@defvar Color.index
> +This attribute exist if @code{is_indexed} is @code{True}. Its integer value is
> +index of a color in a palette.
> +@end defvar
> +
> +@defvar Color.components
> +This attribute exist if @code{is_direct} is @code{True}. Its value is tuple
> +with integer components of a color.
> +@end defvar
The corresponding Guile APIs didn't include the conditions under which
the values are significant. Why not?
> +When color is initialized, its color space must be specified. The
Does "initialized" refer to Color.__init__? If so, I think this
should be said explicitly, perhaps where that function is described.
Finally, I see from the code that we sometimes approximate the
requested color (which is reasonable), but I see nothing about that in
the documentation. I think we should say something about that in the
manual.
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v8] Add an option with a color type.
2024-10-05 19:27 ` [PATCH v8] " Andrei Pikas
2024-10-06 5:40 ` Eli Zaretskii
@ 2024-12-13 21:01 ` Tom Tromey
2024-12-13 21:23 ` Andrei Pikas
2025-02-03 16:23 ` Tom de Vries
2 siblings, 1 reply; 33+ messages in thread
From: Tom Tromey @ 2024-12-13 21:01 UTC (permalink / raw)
To: Andrei Pikas; +Cc: tom, gdb-patches
>>>>> "Andrei" == Andrei Pikas <gdb@mail.api.win> writes:
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\\".
Thanks for the patch.
Do you have a copyright assignment? I looked through my email and
didn't see a note from the FSF, but I'm not always CC'd on those.
Anyway I think this is ready to land, we just have to sort that out
first.
Tom
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v8] Add an option with a color type.
2024-12-13 21:01 ` Tom Tromey
@ 2024-12-13 21:23 ` Andrei Pikas
2024-12-14 7:59 ` Eli Zaretskii
2025-01-12 20:34 ` Tom Tromey
0 siblings, 2 replies; 33+ messages in thread
From: Andrei Pikas @ 2024-12-13 21:23 UTC (permalink / raw)
To: Tom Tromey; +Cc: gdb-patches
[-- Attachment #1: Type: text/plain, Size: 392 bytes --]
>
> Do you have a copyright assignment?
>
Yes. I've sent my signed copy of copyright assignment to assign@gnu.org and
copyright-clerk@fsf.org on April 30, 2024 and received their signed copy on
May 20, 2024 with the date May 13, 2024 in the file on their side. I
specified your email in the field [Additional people we should notify about
the progress of the assignment.] of the request.
>
[-- Attachment #2: Type: text/html, Size: 984 bytes --]
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v8] Add an option with a color type.
2024-12-13 21:23 ` Andrei Pikas
@ 2024-12-14 7:59 ` Eli Zaretskii
2025-01-12 20:34 ` Tom Tromey
1 sibling, 0 replies; 33+ messages in thread
From: Eli Zaretskii @ 2024-12-14 7:59 UTC (permalink / raw)
To: Andrei Pikas; +Cc: tom, gdb-patches
> From: Andrei Pikas <gdb@mail.api.win>
> Date: Sat, 14 Dec 2024 00:23:05 +0300
> Cc: gdb-patches@sourceware.org
>
> Do you have a copyright assignment?
>
> Yes. I've sent my signed copy of copyright assignment to assign@gnu.org and copyright-clerk@fsf.org on
> April 30, 2024 and received their signed copy on May 20, 2024 with the date May 13, 2024 in the file on
> their side. I specified your email in the field [Additional people we should notify about the progress of the
> assignment.] of the request.
Andrei's assignment is on file.
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v8] Add an option with a color type.
2024-12-13 21:23 ` Andrei Pikas
2024-12-14 7:59 ` Eli Zaretskii
@ 2025-01-12 20:34 ` Tom Tromey
1 sibling, 0 replies; 33+ messages in thread
From: Tom Tromey @ 2025-01-12 20:34 UTC (permalink / raw)
To: Andrei Pikas; +Cc: Tom Tromey, gdb-patches
>>>>> Andrei Pikas <gdb@mail.api.win> writes:
>> Do you have a copyright assignment?
> Yes. I've sent my signed copy of copyright assignment to
> assign@gnu.org and copyright-clerk@fsf.org on April 30, 2024 and
> received their signed copy on May 20, 2024 with the date May 13, 2024
> in the file on their side. I specified your email in the field
> [Additional people we should notify about the progress of the
> assignment.] of the request.
Thanks. I've finally applied your patch to git master. A few minor
conflict fixes were needed, and rebuilding showed a complaint from
makeinfo -- @subsection was used where @subsubsection is needed. I
fixed this.
I've re-run the test suite and so I'm checking this in now.
Thank you for the patch and your patience.
Tom
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v8] Add an option with a color type.
2024-10-05 19:27 ` [PATCH v8] " Andrei Pikas
2024-10-06 5:40 ` Eli Zaretskii
2024-12-13 21:01 ` Tom Tromey
@ 2025-02-03 16:23 ` Tom de Vries
2025-02-04 0:24 ` Tom Tromey
2 siblings, 1 reply; 33+ messages in thread
From: Tom de Vries @ 2025-02-03 16:23 UTC (permalink / raw)
To: Andrei Pikas, tom; +Cc: gdb-patches
On 10/5/24 21:27, Andrei Pikas wrote:
> + int parsed_args = sscanf (*args, "#%2" SCNx8 "%2" SCNx8 "%2" SCNx8 "%n",
> + &r, &g, &b, &scanned_chars);
With an x86_64-w64-mingw32 targeted cross-build on x86_64-linux, I run into:
...
/data/vries/w/src/gdb/cli/cli-decode.c: In function
'ui_file_style::color parse_cli_var_color(const char**)':
/data/vries/w/src/gdb/cli/cli-decode.c:2917:41: error: expected ')'
before 'SCNx8'
2917 | int parsed_args = sscanf (*args, "#%2" SCNx8 "%2" SCNx8 "%2"
SCNx8 "\
%n",
| ~ ^~~~~~
| )
...
I tried adding an inttypes.h, but that didn't help. It seems gnulib has:
...
./gnulib/import/inttypes.in.h:#if !defined SCNx8
./gnulib/import/inttypes.in.h:# define SCNx8 "hhx"
...
which could help.
Thanks,
- Tom
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v8] Add an option with a color type.
2025-02-03 16:23 ` Tom de Vries
@ 2025-02-04 0:24 ` Tom Tromey
2025-02-04 14:24 ` Tom de Vries
0 siblings, 1 reply; 33+ messages in thread
From: Tom Tromey @ 2025-02-04 0:24 UTC (permalink / raw)
To: Tom de Vries; +Cc: Andrei Pikas, tom, gdb-patches
>>>>> "Tom" == Tom de Vries <tdevries@suse.de> writes:
Tom> I tried adding an inttypes.h, but that didn't help. It seems gnulib has:
Tom> ...
Tom> ./gnulib/import/inttypes.in.h:#if !defined SCNx8
Tom> ./gnulib/import/inttypes.in.h:# define SCNx8 "hhx"
Tom> ...
Tom> which could help.
Maybe we can just use int there instead.
Tom
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v8] Add an option with a color type.
2025-02-04 0:24 ` Tom Tromey
@ 2025-02-04 14:24 ` Tom de Vries
0 siblings, 0 replies; 33+ messages in thread
From: Tom de Vries @ 2025-02-04 14:24 UTC (permalink / raw)
To: Tom Tromey; +Cc: Andrei Pikas, gdb-patches
On 2/4/25 01:24, Tom Tromey wrote:
>>>>>> "Tom" == Tom de Vries <tdevries@suse.de> writes:
>
> Tom> I tried adding an inttypes.h, but that didn't help. It seems gnulib has:
> Tom> ...
> Tom> ./gnulib/import/inttypes.in.h:#if !defined SCNx8
> Tom> ./gnulib/import/inttypes.in.h:# define SCNx8 "hhx"
> Tom> ...
> Tom> which could help.
>
> Maybe we can just use int there instead.
>
I've submitted a patch (
https://sourceware.org/pipermail/gdb-patches/2025-February/215190.html )
implementing that approach.
Thanks,
- Tom
> Tom
^ permalink raw reply [flat|nested] 33+ messages in thread
end of thread, other threads:[~2025-02-04 14:24 UTC | newest]
Thread overview: 33+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-10-16 11:40 [PATCH v4] Add an option with a color type Andrei Pikas
2022-10-16 13:45 ` Eli Zaretskii via Gdb-patches
2022-10-16 16:03 ` [PATCH v5] " Andrei Pikas
2022-10-16 16:22 ` Eli Zaretskii via Gdb-patches
2022-10-16 16:28 ` [PATCH v6] " Andrei Pikas
2022-10-16 16:45 ` Eli Zaretskii via Gdb-patches
2024-04-19 19:33 ` Tom Tromey
2024-04-19 19:52 ` Andrei Pikas
2024-04-19 20:19 ` Tom Tromey
2024-04-20 18:24 ` Tom Tromey
2024-04-20 18:32 ` Andrei Pikas
2024-05-11 15:17 ` Andrei Pikas
2024-05-13 19:02 ` Tom Tromey
2024-05-21 9:00 ` Andrei Pikas
2024-09-13 20:16 ` Tom Tromey
2024-09-14 18:06 ` Andrei Pikas
2024-09-14 19:38 ` Tom Tromey
2024-09-14 19:43 ` Andrei Pikas
2024-09-14 19:04 ` [PATCH v7] " Andrei Pikas
2024-09-15 5:37 ` Eli Zaretskii
2024-10-05 19:11 ` Andrei Pikas
2024-10-05 19:40 ` Eli Zaretskii
2024-10-04 17:55 ` Tom Tromey
2024-10-05 17:55 ` Andrei Pikas
2024-10-05 19:27 ` [PATCH v8] " Andrei Pikas
2024-10-06 5:40 ` Eli Zaretskii
2024-12-13 21:01 ` Tom Tromey
2024-12-13 21:23 ` Andrei Pikas
2024-12-14 7:59 ` Eli Zaretskii
2025-01-12 20:34 ` Tom Tromey
2025-02-03 16:23 ` Tom de Vries
2025-02-04 0:24 ` Tom Tromey
2025-02-04 14:24 ` Tom de Vries
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox