Add an option with a color type.

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

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

PATCH v5 changes: documentation fixed.
PATCH v6 changes: documentation fixed.
PATCH v7 changes: rebase onto master and fixes after review.
PATCH v8 changes: fixes after review.
This commit is contained in:
Andrei Pikas
2024-10-05 22:27:44 +03:00
committed by Tom Tromey
parent 338e0b05d8
commit 6447969d0a
34 changed files with 2437 additions and 172 deletions

View File

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

View File

@@ -3,6 +3,25 @@
*** Changes since GDB 16
* "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
* New commands
maintenance check psymtabs
@@ -11,6 +30,20 @@ maintenance check psymtabs
maintenance check symtabs
Renamed from maintenance check-symtabs
* Python API
** 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.
*** Changes in GDB 16
* Support for Nios II targets has been removed as this architecture

View File

@@ -2398,6 +2398,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");
}
@@ -2445,6 +2450,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);

View File

@@ -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 };
@@ -2756,3 +2838,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;
}

View File

@@ -309,6 +309,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. */

View File

@@ -45,6 +45,9 @@ union option_value
/* For var_string and var_filename options. This is allocated with new. */
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, "--"))
@@ -683,6 +715,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:
case var_filename:
*ov->option.var_address.string (ov->option, ov->ctx)
@@ -789,6 +825,12 @@ append_val_type_str (std::string &help, const option_def &opt,
}
}
break;
case var_color:
help += ' ';
for (size_t i = 0; ui_file_style::basic_color_enums[i]; ++i)
help.append (ui_file_style::basic_color_enums[i]).append ("|");
help += "NUMBER|#RRGGBB";
break;
case var_string:
help += "STRING";
break;

View File

@@ -91,6 +91,7 @@ public:
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;
@@ -328,6 +329,26 @@ struct filename_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

View File

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

View File

@@ -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[] = {
@@ -139,8 +125,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])
{
}
@@ -151,32 +137,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)
@@ -188,7 +159,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. */
@@ -261,9 +232,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,
@@ -273,9 +243,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,

View File

@@ -67,9 +67,9 @@ private:
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;

View File

@@ -110,7 +110,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
@@ -184,6 +186,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>
@@ -680,6 +690,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,

View File

@@ -13009,6 +13009,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
@@ -27871,16 +27885,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}

View File

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

View File

@@ -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.
@@ -5242,6 +5243,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
@@ -7020,6 +7026,98 @@ resolve this to the lazy string's character type, use the type's
writable.
@end defvar
@node Colors In Python
@subsubsection 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

View File

@@ -452,6 +452,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;
@@ -630,6 +638,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);

View File

@@ -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 ();

427
gdb/guile/scm-color.c Normal file
View File

@@ -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");
}

View File

@@ -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)
{

336
gdb/python/py-color.c Normal file
View File

@@ -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);

35
gdb/python/py-color.h Normal file
View File

@@ -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 */

View File

@@ -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. */

View File

@@ -36,6 +36,7 @@
#include "run-on-main-thread.h"
#include "observable.h"
#include "build-id.h"
#include "py-color.h"
#if GDB_SELF_TEST
#include "gdbsupport/selftest.h"
@@ -511,6 +512,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> ())

View File

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

View File

@@ -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}
@@ -311,6 +314,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"
}
}
}
@@ -487,6 +505,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.
@@ -521,3 +712,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

View File

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

View File

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

View File

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

View File

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

View File

@@ -67,20 +67,24 @@ 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 }
command { 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 }
command { 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

View File

@@ -10265,10 +10265,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]
}

View File

@@ -2129,6 +2129,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)
{
@@ -2333,6 +2344,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 ();

View File

@@ -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,48 +47,118 @@ 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. */
/* This maps 8-color palette to RGB triples. The values come from
plain linux terminal. */
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. */
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;
}
/* See ui-style.h. */
std::string
ui_file_style::color::to_string () const
{
if (m_color_space == color_space::RGB_24BIT)
{
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. */
@@ -95,42 +166,108 @@ ui_file_style::color::append_ansi (bool is_fg, std::string *str) const
void
ui_file_style::color::get_rgb (uint8_t *rgb) const
{
if (m_simple)
{
/* 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");
}
else
if (m_color_space == color_space::RGB_24BIT)
{
rgb[0] = m_red;
rgb[1] = m_green;
rgb[2] = m_blue;
}
else if (m_color_space == color_space::ANSI_8COLOR
&& 0 <= m_value && m_value <= 7)
memcpy (rgb, palette_8colors[m_value], 3 * sizeof (uint8_t));
else if (m_color_space == color_space::AIXTERM_16COLOR
&& 0 <= m_value && m_value <= 15)
memcpy (rgb, palette_16colors[m_value], 3 * sizeof (uint8_t));
else if (m_color_space != color_space::XTERM_256COLOR)
gdb_assert_not_reached ("get_rgb called on invalid color");
else if (0 <= m_value && m_value <= 15)
memcpy (rgb, palette_16colors[m_value], 3 * sizeof (uint8_t));
else if (m_value >= 16 && m_value <= 231)
{
int value = m_value;
value -= 16;
/* This obscure formula seems to be what terminals actually
do. */
int component = value / 36;
rgb[0] = component == 0 ? 0 : (55 + component * 40);
value %= 36;
component = value / 6;
rgb[1] = component == 0 ? 0 : (55 + component * 40);
value %= 6;
rgb[2] = value == 0 ? 0 : (55 + value * 40);
}
else if (232 <= m_value && m_value <= 255)
{
uint8_t v = (m_value - 232) * 10 + 8;
rgb[0] = v;
rgb[1] = v;
rgb[2] = v;
}
else
gdb_assert_not_reached ("get_rgb called on invalid color");
}
/* See ui-style.h. */
ui_file_style::color
ui_file_style::color::approximate (const std::vector<color_space> &spaces) const
{
if (spaces.empty () || is_none ())
return NONE;
color_space target_space = color_space::MONOCHROME;
for (color_space sp : spaces)
if (sp == m_color_space)
return *this;
else if (sp > target_space)
target_space = sp;
if (target_space == color_space::RGB_24BIT)
{
uint8_t rgb[3];
get_rgb (rgb);
return color (rgb[0], rgb[1], rgb[2]);
}
int target_size = 0;
switch (target_space)
{
case color_space::ANSI_8COLOR:
target_size = 8;
break;
case color_space::AIXTERM_16COLOR:
target_size = 16;
break;
case color_space::XTERM_256COLOR:
target_size = 256;
break;
}
if (is_simple() && m_value < target_size)
return color (target_space, m_value);
color result = NONE;
int best_distance = std::numeric_limits<int>::max ();
uint8_t rgb[3];
get_rgb (rgb);
for (int i = 0; i < target_size; ++i)
{
uint8_t c_rgb[3];
color c (target_space, i);
c.get_rgb (c_rgb);
int d_red = std::abs (rgb[0] - c_rgb[0]);
int d_green = std::abs (rgb[1] - c_rgb[1]);
int d_blue = std::abs (rgb[2] - c_rgb[2]);
int dist = d_red * d_red + d_green * d_green + d_blue * d_blue;
if (dist < best_distance)
{
best_distance = dist;
result = c;
}
}
return result;
}
/* See ui-style.h. */
@@ -139,26 +276,21 @@ std::string
ui_file_style::to_ansi () const
{
std::string result ("\033[");
bool need_semi = m_foreground.append_ansi (true, &result);
if (!m_background.is_none ())
if (!is_default ())
{
if (need_semi)
result.push_back (';');
m_foreground.append_ansi (true, &result);
result.push_back (';');
m_background.append_ansi (false, &result);
need_semi = true;
}
if (m_intensity != NORMAL)
{
if (need_semi)
result.push_back (';');
result.append (std::to_string (m_intensity));
need_semi = true;
}
if (m_reverse)
{
if (need_semi)
result.push_back (';');
result.push_back ('7');
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;
}

View File

@@ -19,6 +19,38 @@
#ifndef GDB_UI_STYLE_H
#define GDB_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 the value of a basic color. */
/* 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 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;

View File

@@ -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));