@@ -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 \
@@ -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
@@ -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);
@@ -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;
+}
@@ -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. */
@@ -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:
@@ -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
@@ -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;
@@ -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,
@@ -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;
@@ -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,
@@ -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}
@@ -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
@@ -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
@@ -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);
@@ -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 ();
new file mode 100644
@@ -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");
+}
@@ -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)
{
new file mode 100644
@@ -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);
new file mode 100644
@@ -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 */
@@ -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. */
@@ -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> ())
@@ -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>} \
@@ -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
new file mode 100644
@@ -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"
+
@@ -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"
+ }
+ }
+}
new file mode 100644
@@ -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"
+
@@ -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
@@ -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
@@ -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]
}
@@ -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 ();
@@ -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;
+}
@@ -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;
@@ -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));