public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH] Add an option with a color type.
@ 2022-08-15 21:15 Andrei Pikas
  2022-08-17 17:40 ` Andrew Burgess
  2022-08-17 17:57 ` [PATCH] " Eli Zaretskii
  0 siblings, 2 replies; 13+ messages in thread
From: Andrei Pikas @ 2022-08-15 21:15 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrei Pikas

Colors can be specified as name of one of the basic colors "none", "black",
"white", etc., as a number up to 255, or as RGB hexadecimal tripplet #RRGGBB.
---
 gdb/NEWS                         |   3 +
 gdb/cli/cli-cmds.c               |   8 ++
 gdb/cli/cli-decode.c             | 200 +++++++++++++++++++++++++++++++
 gdb/cli/cli-decode.h             |  30 +++++
 gdb/cli/cli-option.c             |  42 +++++++
 gdb/cli/cli-option.h             |  21 ++++
 gdb/cli/cli-setshow.c            |  53 ++++++++
 gdb/cli/cli-setshow.h            |   6 +
 gdb/cli/cli-style.c              |  49 ++------
 gdb/cli/cli-style.h              |   4 +-
 gdb/command.h                    |  26 +++-
 gdb/guile/scm-param.c            |  69 ++++++++++-
 gdb/python/py-param.c            |  63 +++++++++-
 gdb/python/python.c              |  18 +++
 gdb/testsuite/gdb.base/style.exp |  15 +++
 gdb/ui-style.h                   |  16 ++-
 16 files changed, 575 insertions(+), 48 deletions(-)

diff --git a/gdb/NEWS b/gdb/NEWS
index d2efe2a0a58..5234d4f8501 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -55,6 +55,9 @@
   Python Pygments is still used.  For supported targets, libopcodes
   styling is used by default.
 
+  "set style" commands now supports numeric format for basic colors
+  from -1 to 255 and #RRGGBB format for truecolor.
+
 * New commands
 
 maintenance set ignore-prologue-end-flag on|off
diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c
index 18fb6e6d869..5039c665b16 100644
--- a/gdb/cli/cli-cmds.c
+++ b/gdb/cli/cli-cmds.c
@@ -2275,6 +2275,13 @@ value_from_setting (const setting &var, struct gdbarch *gdbarch)
 	  return value_cstring ("", 1,
 				builtin_type (gdbarch)->builtin_char);
       }
+    case var_color:
+      {
+	char s[64];
+	print_color_str (s, sizeof s, var.get<ui_file_style::color> ());
+	size_t len = strlen (s);
+	return value_cstring (s, len, builtin_type (gdbarch)->builtin_char);
+      }
     default:
       gdb_assert_not_reached ("bad var_type");
     }
@@ -2324,6 +2331,7 @@ str_value_from_setting (const setting &var, struct gdbarch *gdbarch)
     case var_auto_boolean:
     case var_uinteger:
     case var_zuinteger:
+    case var_color:
       {
 	std::string cmd_val = get_setshow_command_value_string (var);
 
diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
index fde554c7e6c..1e36ae84123 100644
--- a/gdb/cli/cli-decode.c
+++ b/gdb/cli/cli-decode.c
@@ -670,6 +670,112 @@ add_setshow_enum_cmd (const char *name, command_class theclass,
   return cmds;
 }
 
+/* See cli-decode.h.  */
+/* Must correspond to ui_file_style::basic_color.  */
+const std::vector<const char *> basic_color_enums = {
+  "none",
+  "black",
+  "red",
+  "green",
+  "yellow",
+  "blue",
+  "magenta",
+  "cyan",
+  "white",
+  nullptr
+};
+
+/* See cli-decode.h.  */
+
+const char * basic_color_name (int color)
+{
+  int pos = color - ui_file_style::NONE;
+  if (0 <= pos && pos < basic_color_enums.size ())
+    if (const char *s = basic_color_enums[pos])
+      return s;
+  error (_("Basic color %d has no name."), pos);
+}
+
+/* See cli-decode.h.  */
+
+void
+complete_on_color (completion_tracker &tracker,
+		   const char *text, const char *word)
+{
+  complete_on_enum (tracker, 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 readline doesn't do
+	 a partial match.  */
+      tracker.add_completion (make_unique_xstrdup ("NUMBER"));
+      tracker.add_completion (make_unique_xstrdup ("#RRGGBB"));
+    }
+}
+
+/* Completer used in color commands.  */
+
+static void
+color_completer (struct cmd_list_element *ignore,
+		 completion_tracker &tracker,
+		 const char *text, const char *word)
+{
+  complete_on_color (tracker, text, word);
+}
+
+
+/* Add element named NAME to command list LIST (the list for set or
+   some sublist thereof).  CLASS is as in add_cmd. VAR is address
+   of the variable which will contain the color.  */
+
+set_show_commands
+add_setshow_color_cmd (const char *name,
+		       enum command_class theclass,
+		       ui_file_style::color *var,
+		       const char *set_doc,
+		       const char *show_doc,
+		       const char *help_doc,
+		       cmd_func_ftype *set_func,
+		       show_value_ftype *show_func,
+		       struct cmd_list_element **set_list,
+		       struct cmd_list_element **show_list)
+{
+  set_show_commands commands = add_setshow_cmd_full<ui_file_style::color>
+    (name, theclass, var_color, var,
+     set_doc, show_doc, help_doc,
+     nullptr, nullptr, set_func, show_func,
+     set_list, show_list);
+
+  set_cmd_completer (commands.set, color_completer);
+
+  return commands;
+}
+
+/* Same as above but using a getter and a setter function instead of a pointer
+   to a global storage buffer.  */
+
+set_show_commands
+add_setshow_color_cmd (const char *name, command_class theclass,
+		       const char *set_doc, const char *show_doc,
+		       const char *help_doc,
+		       setting_func_types<ui_file_style::color>::set set_func,
+		       setting_func_types<ui_file_style::color>::get get_func,
+		       show_value_ftype *show_func,
+		       cmd_list_element **set_list,
+		       cmd_list_element **show_list)
+{
+  auto cmds = add_setshow_cmd_full<ui_file_style::color>
+    (name, theclass, var_color, nullptr,
+     set_doc, show_doc, help_doc,
+     set_func, get_func, nullptr, show_func,
+     set_list, show_list);
+
+  set_cmd_completer (cmds.set, color_completer);
+
+  return cmds;
+}
+
 /* See cli-decode.h.  */
 const char * const auto_boolean_enums[] = { "on", "off", "auto", NULL };
 
@@ -2524,3 +2630,97 @@ cli_user_command_p (struct cmd_list_element *cmd)
 {
   return cmd->theclass == class_user && cmd->func == do_simple_func;
 }
+
+/* See cli-decode.h.  */
+
+ui_file_style::color
+parse_cli_var_color (const char **args)
+{
+  /* Do a "set" command.  ARG is NULL if no argument, or the
+     text of the argument. */
+
+  if (args == nullptr || *args == nullptr || **args == '\0')
+    {
+      std::string msg;
+
+      for (size_t i = 0; basic_color_enums[i]; ++i)
+	msg.append ("\"").append (basic_color_enums[i]).append ("\", ");
+
+      error (_("Requires an argument. Valid arguments are %s integer from -1 "
+	     "to 255 or an RGB hex triplet in a format #RRGGBB"),
+	     msg.c_str ());
+    }
+
+  const char *p = skip_to_space (*args);
+  size_t len = p - *args;
+
+  int nmatches = 0;
+  ui_file_style::basic_color match = ui_file_style::NONE;
+  for (size_t i = 0; basic_color_enums[i]; ++i)
+    if (strncmp (*args, basic_color_enums[i], len) == 0)
+      {
+	if (basic_color_enums[i][len] == '\0')
+	  {
+	    match = static_cast<ui_file_style::basic_color> (i - 1);
+	    nmatches = 1;
+	    break; /* Exact match.  */
+	  }
+	else
+	  {
+	    match = static_cast<ui_file_style::basic_color> (i - 1);
+	    nmatches++;
+	  }
+      }
+
+  if (nmatches == 1)
+    {
+      *args += len;
+      return ui_file_style::color (match);
+    }
+
+  if (nmatches > 1)
+    error (_("Ambiguous item \"%.*s\"."), (int) len, *args);
+
+  if (**args != '#')
+    {
+      char *end = nullptr;
+      long num = strtol (*args, &end, 0);
+      if (end - *args != len)
+	error (_("Expected integer at: %s"), *args);
+      if (num < -1 || num > 255)
+	error (_("integer %s out of range"), plongest (num));
+
+      *args = end;
+      return ui_file_style::color (static_cast<int> (num));
+    }
+
+  /* Try to parse #RRGGBB string.  */
+  if (len != 7)
+    error_no_arg (_("invalid RGB hex triplet format"));
+
+  uint8_t r, g, b;
+  int scanned_chars = 0;
+  int parsed_args = sscanf (*args, "#%2" SCNx8 "%2" SCNx8 "%2" SCNx8 "%n",
+			    &r, &g, &b, &scanned_chars);
+
+  if (parsed_args != 3 || scanned_chars != 7)
+    error_no_arg (_("invalid RGB hex triplet format"));
+
+  *args += len;
+  return ui_file_style::color (r, g, b);
+}
+
+/* See cli-decode.h.  */
+
+ui_file_style::color parse_var_color (const char *arg)
+{
+  const char *end_arg = arg;
+  ui_file_style::color color = parse_cli_var_color (&end_arg);
+
+  int len = end_arg - arg;
+  const char *after = skip_spaces (end_arg);
+  if (*after != '\0')
+    error (_("Junk after item \"%.*s\": %s"), len, arg, after);
+
+  return color;
+}
diff --git a/gdb/cli/cli-decode.h b/gdb/cli/cli-decode.h
index 18db8822af3..1f2ef80c7e9 100644
--- a/gdb/cli/cli-decode.h
+++ b/gdb/cli/cli-decode.h
@@ -306,6 +306,36 @@ extern const char * const boolean_enums[];
 /* The enums of auto-boolean commands.  */
 extern const char * const auto_boolean_enums[];
 
+/* The enums of color commands with names of the basic colors that
+   can be handled by ANSI terminals.
+   Corresponds to ui_file_style::basic_color and starts with name for
+   ui_file_style::NONE.  */
+extern const std::vector<const char *> basic_color_enums;
+
+/* Returns text representation of COLOR.  */
+extern const char * basic_color_name (int color);
+
+/* 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".  */
+
+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 -1 to 255, or RGB hex triplet #RRGGBB.
+   */
+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.  */
+ui_file_style::color parse_var_color (const char *arg);
+
 /* Verify whether a given cmd_list_element is a user-defined command.
    Return 1 if it is user-defined.  Return 0 otherwise.  */
 
diff --git a/gdb/cli/cli-option.c b/gdb/cli/cli-option.c
index b1794ad4b17..bd6556312db 100644
--- a/gdb/cli/cli-option.c
+++ b/gdb/cli/cli-option.c
@@ -46,6 +46,9 @@ union option_value
 
   /* For var_string options.  This is malloc-allocated.  */
   std::string *string;
+
+  /* For var_color options.  */
+  ui_file_style::color color = ui_file_style::NONE;
 };
 
 /* Holds an options definition and its value.  */
@@ -424,6 +427,33 @@ 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;
+	val.color = parse_cli_var_color (args);
+	return option_def_and_value {*match, match_ctx, val};
+      }
     case var_string:
       {
 	if (check_for_argument (args, "--"))
@@ -601,6 +631,10 @@ save_option_value_in_ctx (gdb::optional<option_def_and_value> &ov)
       *ov->option.var_address.enumeration (ov->option, ov->ctx)
 	= ov->value->enumeration;
       break;
+    case var_color:
+      *ov->option.var_address.color (ov->option, ov->ctx)
+	= ov->value->color;
+      break;
     case var_string:
       *ov->option.var_address.string (ov->option, ov->ctx)
 	= std::move (*ov->value->string);
@@ -677,6 +711,14 @@ get_val_type_str (const option_def &opt, std::string &buffer)
 	  }
 	return buffer.c_str ();
       }
+    case var_color:
+      {
+	buffer = "";
+	for (size_t i = 0; basic_color_enums[i]; ++i)
+	  buffer.append (basic_color_enums[i]).append ("|");
+	buffer += "NUMBER|#RRGGBB";
+	return buffer.c_str ();
+      }
     case var_string:
       return "STRING";
     default:
diff --git a/gdb/cli/cli-option.h b/gdb/cli/cli-option.h
index 26a8da3a5a4..d30c7024324 100644
--- a/gdb/cli/cli-option.h
+++ b/gdb/cli/cli-option.h
@@ -87,6 +87,7 @@ struct option_def
       int *(*integer) (const option_def &, void *ctx);
       const char **(*enumeration) (const option_def &, void *ctx);
       std::string *(*string) (const option_def &, void *ctx);
+      ui_file_style::color *(*color) (const option_def &, void *ctx);
     }
   var_address;
 
@@ -282,6 +283,26 @@ struct string_option_def : option_def
   }
 };
 
+/* A var_color command line option.  */
+
+template<typename Context>
+struct color_option_def : option_def
+{
+  color_option_def (const char *long_option_,
+		    ui_file_style::color *(*get_var_address_cb_) (Context *),
+		    show_value_ftype *show_cmd_cb_,
+		    const char *set_doc_,
+		    const char *show_doc_ = nullptr,
+		    const char *help_doc_ = nullptr)
+    : option_def (long_option_, var_color,
+		  (erased_get_var_address_ftype *) get_var_address_cb_,
+		  show_cmd_cb_,
+		  set_doc_, show_doc_, help_doc_)
+  {
+    var_address.color = detail::get_var_address<ui_file_style::color, Context>;
+  }
+};
+
 /* A group of options that all share the same context pointer to pass
    to the options' get-current-value callbacks.  */
 struct option_def_group
diff --git a/gdb/cli/cli-setshow.c b/gdb/cli/cli-setshow.c
index 139ebaf8323..5e4d0355ec5 100644
--- a/gdb/cli/cli-setshow.c
+++ b/gdb/cli/cli-setshow.c
@@ -297,6 +297,34 @@ parse_cli_var_enum (const char **args, const char *const *enums)
   return match;
 }
 
+/* See cli-setshow.h.  */
+
+bool
+print_color_str (char *s, size_t buf_size, const ui_file_style::color &color)
+{
+  if (!buf_size)
+    return false;
+
+  int n;
+  if (!color.is_simple ())
+    {
+      uint8_t rgb[3];
+      color.get_rgb (rgb);
+      n = snprintf (s, buf_size, "#%02X%02X%02X", rgb[0], rgb[1], rgb[2]);
+    }
+  else if (color.is_none () || color.is_basic ())
+    n = snprintf (s, buf_size, "%s", basic_color_name (color.get_value()));
+  else
+    n = snprintf (s, buf_size, "%d", color.get_value ());
+
+  if (n >= 0 && n < buf_size)
+    return true;
+
+  n = std::clamp<ptrdiff_t> (n, 0, buf_size - 1);
+  s[n] = 0;
+  return true;
+}
+
 /* Do a "set" command.  ARG is NULL if no argument, or the
    text of the argument, and FROM_TTY is nonzero if this command is
    being entered directly by the user (i.e. these are just like any
@@ -454,6 +482,12 @@ 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);
+	option_changed = c->var->set<ui_file_style::color> (color);
+      }
+      break;
     case var_zuinteger_unlimited:
       option_changed = c->var->set<int>
 	(parse_cli_var_zuinteger_unlimited (&arg, true));
@@ -535,6 +569,17 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c)
 	  gdb::observers::command_param_changed.notify
 	    (name, c->var->get<const char *> ());
 	  break;
+	case var_color:
+	  {
+	    char s[64];
+	    const ui_file_style::color &color
+	      = c->var->get<ui_file_style::color> ();
+	    print_color_str (s, sizeof s, color);
+
+	    gdb::observers::command_param_changed.notify
+	      (name, s);
+	  }
+	  break;
 	case var_boolean:
 	  {
 	    const char *opt = c->var->get<bool> () ? "on" : "off";
@@ -602,6 +647,14 @@ get_setshow_command_value_string (const setting &var)
 	  stb.puts (value);
       }
       break;
+    case var_color:
+      {
+	char s[64];
+	const ui_file_style::color &value = var.get<ui_file_style::color> ();
+	print_color_str (s, sizeof s, value);
+	stb.puts (s);
+      }
+      break;
     case var_boolean:
       stb.puts (var.get<bool> () ? "on" : "off");
       break;
diff --git a/gdb/cli/cli-setshow.h b/gdb/cli/cli-setshow.h
index 3e5d105eab7..3f50cf25263 100644
--- a/gdb/cli/cli-setshow.h
+++ b/gdb/cli/cli-setshow.h
@@ -52,6 +52,12 @@ extern int parse_cli_var_zuinteger_unlimited (const char **arg,
 const char *parse_cli_var_enum (const char **args,
 				const char *const *enums);
 
+/* Prints text representation of COLOR into S up to BUF_SIZE characters.
+   Returns true if value have been written without truncation,
+   false otherwise.  */
+bool
+print_color_str (char *s, size_t buf_size, const ui_file_style::color &color);
+
 extern void do_set_command (const char *arg, int from_tty,
 			    struct cmd_list_element *c);
 extern void do_show_command (const char *arg, int from_tty,
diff --git a/gdb/cli/cli-style.c b/gdb/cli/cli-style.c
index abf685561fa..3123a391763 100644
--- a/gdb/cli/cli-style.c
+++ b/gdb/cli/cli-style.c
@@ -43,20 +43,6 @@ bool source_styling = true;
 
 bool disassembler_styling = true;
 
-/* Name of colors; must correspond to ui_file_style::basic_color.  */
-static const char * const cli_colors[] = {
-  "none",
-  "black",
-  "red",
-  "green",
-  "yellow",
-  "blue",
-  "magenta",
-  "cyan",
-  "white",
-  nullptr
-};
-
 /* Names of intensities; must correspond to
    ui_file_style::intensity.  */
 static const char * const cli_intensities[] = {
@@ -132,8 +118,8 @@ cli_style_option::cli_style_option (const char *name,
 				    ui_file_style::intensity intensity)
   : changed (name),
     m_name (name),
-    m_foreground (cli_colors[fg - ui_file_style::NONE]),
-    m_background (cli_colors[0]),
+    m_foreground (fg),
+    m_background (ui_file_style::NONE),
     m_intensity (cli_intensities[intensity])
 {
 }
@@ -144,32 +130,17 @@ cli_style_option::cli_style_option (const char *name,
 				    ui_file_style::intensity i)
   : changed (name),
     m_name (name),
-    m_foreground (cli_colors[0]),
-    m_background (cli_colors[0]),
+    m_foreground (ui_file_style::NONE),
+    m_background (ui_file_style::NONE),
     m_intensity (cli_intensities[i])
 {
 }
 
-/* Return the color number corresponding to COLOR.  */
-
-static int
-color_number (const char *color)
-{
-  for (int i = 0; i < ARRAY_SIZE (cli_colors); ++i)
-    {
-      if (color == cli_colors[i])
-	return i - 1;
-    }
-  gdb_assert_not_reached ("color not found");
-}
-
 /* See cli-style.h.  */
 
 ui_file_style
 cli_style_option::style () const
 {
-  int fg = color_number (m_foreground);
-  int bg = color_number (m_background);
   ui_file_style::intensity intensity = ui_file_style::NORMAL;
 
   for (int i = 0; i < ARRAY_SIZE (cli_intensities); ++i)
@@ -181,7 +152,7 @@ cli_style_option::style () const
 	}
     }
 
-  return ui_file_style (fg, bg, intensity);
+  return ui_file_style (m_foreground, m_background, intensity);
 }
 
 /* See cli-style.h.  */
@@ -254,9 +225,8 @@ cli_style_option::add_setshow_commands (enum command_class theclass,
 
   set_show_commands commands;
 
-  commands = add_setshow_enum_cmd
-    ("foreground", theclass, cli_colors,
-     &m_foreground,
+  commands = add_setshow_color_cmd
+    ("foreground", theclass, &m_foreground,
      _("Set the foreground color for this property."),
      _("Show the foreground color for this property."),
      nullptr,
@@ -266,9 +236,8 @@ cli_style_option::add_setshow_commands (enum command_class theclass,
   commands.set->set_context (this);
   commands.show->set_context (this);
 
-  commands = add_setshow_enum_cmd
-    ("background", theclass, cli_colors,
-     &m_background,
+  commands = add_setshow_color_cmd
+    ("background", theclass, &m_background,
      _("Set the background color for this property."),
      _("Show the background color for this property."),
      nullptr,
diff --git a/gdb/cli/cli-style.h b/gdb/cli/cli-style.h
index 4090cf01333..4db86b00359 100644
--- a/gdb/cli/cli-style.h
+++ b/gdb/cli/cli-style.h
@@ -67,9 +67,9 @@ class cli_style_option
   const char *m_name;
 
   /* The foreground.  */
-  const char *m_foreground;
+  ui_file_style::color m_foreground;
   /* The background.  */
-  const char *m_background;
+  ui_file_style::color m_background;
   /* The intensity.  */
   const char *m_intensity;
 
diff --git a/gdb/command.h b/gdb/command.h
index d901da3c8cb..691c3999d78 100644
--- a/gdb/command.h
+++ b/gdb/command.h
@@ -119,7 +119,9 @@ enum var_types
     /* Enumerated type.  Can only have one of the specified values.
        *VAR is a char pointer to the name of the element that we
        find.  */
-    var_enum
+    var_enum,
+    /* Color type. *VAR is a ui_file_style::color structure. */
+    var_color
   };
 
 /* Return true if a setting of type VAR_TYPE is backed with type T.
@@ -179,6 +181,14 @@ inline bool var_type_uses<const char *> (var_types t)
   return t == var_enum;
 }
 
+/* Return true if a setting of type T is backed by an ui_file_style::color
+   variable.  */
+template<>
+inline bool var_type_uses<ui_file_style::color> (var_types t)
+{
+  return t == var_color;
+}
+
 template<bool is_scalar, typename T> struct setting_func_types_1;
 
 template<typename T>
@@ -665,6 +675,20 @@ extern set_show_commands add_setshow_enum_cmd
    setting_func_types<const char *>::get get_func, show_value_ftype *show_func,
    cmd_list_element **set_list, cmd_list_element **show_list);
 
+extern set_show_commands add_setshow_color_cmd
+  (const char *name, command_class theclass, ui_file_style::color *var,
+   const char *set_doc, const char *show_doc, const char *help_doc,
+   cmd_func_ftype *set_func, show_value_ftype *show_func,
+   cmd_list_element **set_list, cmd_list_element **show_list);
+
+extern set_show_commands add_setshow_color_cmd
+  (const char *name, command_class theclass,
+   const char *set_doc, const char *show_doc, const char *help_doc,
+   setting_func_types<ui_file_style::color>::set set_func,
+   setting_func_types<ui_file_style::color>::get get_func,
+   show_value_ftype *show_func, cmd_list_element **set_list,
+   cmd_list_element **show_list);
+
 extern set_show_commands add_setshow_auto_boolean_cmd
   (const char *name, command_class theclass, auto_boolean *var,
    const char *set_doc, const char *show_doc, const char *help_doc,
diff --git a/gdb/guile/scm-param.c b/gdb/guile/scm-param.c
index 54c8c27301a..c7ef3ddd03b 100644
--- a/gdb/guile/scm-param.c
+++ b/gdb/guile/scm-param.c
@@ -48,6 +48,9 @@ union pascm_variable
 
   /* Hold a string, for enums.  */
   const char *cstringval;
+
+  /* Hold a color.  */
+  ui_file_style::color color;
 };
 
 /* A GDB parameter.
@@ -193,7 +196,7 @@ pascm_make_param_smob (void)
     scm_gc_malloc (sizeof (param_smob), param_smob_name);
   SCM p_scm;
 
-  memset (p_smob, 0, sizeof (*p_smob));
+  memset (reinterpret_cast<void *> (p_smob), 0, sizeof (*p_smob));
   p_smob->cmd_class = no_class;
   p_smob->type = var_boolean; /* ARI: var_boolean */
   p_smob->set_func = SCM_BOOL_F;
@@ -466,6 +469,13 @@ add_setshow_generic (enum var_types param_type, enum command_class cmd_class,
 				       set_list, show_list);
       break;
 
+    case var_color:
+      commands = add_setshow_color_cmd (cmd_name, cmd_class, &self->value.color,
+					set_doc, show_doc, help_doc,
+					set_func, show_func,
+					set_list, show_list);
+      break;
+
     default:
       gdb_assert_not_reached ("bad param_type value");
     }
@@ -545,6 +555,7 @@ static const scheme_integer_constant parameter_types[] =
   { "PARAM_OPTIONAL_FILENAME", var_optional_filename },
   { "PARAM_FILENAME", var_filename },
   { "PARAM_ENUM", var_enum },
+  { "PARAM_COLOR", var_color },
 
   END_INTEGER_CONSTANTS
 };
@@ -611,6 +622,24 @@ 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:
+      {
+	const ui_file_style::color &color = var.get<ui_file_style::color> ();
+	if (color.is_none () || color.is_basic ())
+	  {
+	    const char *str = basic_color_name (color.get_value ());
+	    return gdbscm_scm_from_host_string (str, strlen (str));
+	  }
+	else if (color.is_simple ())
+	  return scm_from_int (color.get_value ());
+
+	uint8_t rgb[3];
+	color.get_rgb (rgb);
+	char s[64];
+	int n = snprintf (s, sizeof s, "#%02X%02X%02X", rgb[0], rgb[1], rgb[2]);
+	return gdbscm_scm_from_host_string (s, n);
+      }
+
     case var_boolean:
       {
 	if (var.get<bool> ())
@@ -716,6 +745,44 @@ pascm_set_param_value_x (param_smob *p_smob,
 	break;
       }
 
+    case var_color:
+      SCM_ASSERT_TYPE (scm_is_string (value) || scm_is_integer (value),
+		       value, arg_pos, func_name,
+		       _("string or integer"));
+
+      if (scm_is_integer (value))
+	{
+	  int i = scm_to_int (value);
+	  if (i < -1 || i > 255)
+	    gdbscm_out_of_range_error (func_name, arg_pos, value,
+				       _("must be in range from -1 to 255"));
+	  var.set<ui_file_style::color> (i);
+	}
+      else
+	{
+	  SCM exception;
+
+	  gdb::unique_xmalloc_ptr<char> string
+	    = gdbscm_scm_to_host_string (value, nullptr, &exception);
+	  if (string == nullptr)
+	    gdbscm_throw (exception);
+
+	  gdbscm_gdb_exception exc {};
+	  try
+	    {
+	      ui_file_style::color color = parse_var_color (string.get ());
+	      var.set<ui_file_style::color> (color);
+	    }
+	  catch (const gdb_exception &except)
+	    {
+	      exc = unpack (except);
+	    }
+
+	  GDBSCM_HANDLE_GDB_EXCEPTION (exc);
+	}
+
+      break;
+
     case var_boolean:
       SCM_ASSERT_TYPE (gdbscm_is_bool (value), value, arg_pos, func_name,
 		       _("boolean"));
diff --git a/gdb/python/py-param.c b/gdb/python/py-param.c
index 5d509ba4658..03db582a3ee 100644
--- a/gdb/python/py-param.c
+++ b/gdb/python/py-param.c
@@ -46,6 +46,7 @@ static struct {
   { "PARAM_ZUINTEGER", var_zuinteger },
   { "PARAM_ZUINTEGER_UNLIMITED", var_zuinteger_unlimited },
   { "PARAM_ENUM", var_enum },
+  { "PARAM_COLOR", var_color },
   { NULL, 0 }
 };
 
@@ -70,6 +71,9 @@ union parmpy_variable
 
   /* Hold a string, for enums.  */
   const char *cstringval;
+
+  /* Hold a color.  */
+  ui_file_style::color color = ui_file_style::NONE;
 };
 
 /* A GDB parameter.  */
@@ -108,6 +112,8 @@ make_setting (parmpy_object *s)
     return setting (s->type, s->value.stringval);
   else if (var_type_uses<const char *> (s->type))
     return setting (s->type, &s->value.cstringval);
+  else if (var_type_uses<ui_file_style::color> (s->type))
+    return setting (s->type, &s->value.color);
   else
     gdb_assert_not_reached ("unhandled var type");
 }
@@ -199,6 +205,49 @@ set_parameter_value (parmpy_object *self, PyObject *value)
 	break;
       }
 
+    case var_color:
+      {
+	if (gdbpy_is_string (value))
+	  {
+	    gdb::unique_xmalloc_ptr<char>
+	      str (python_string_to_host_string (value));
+	    if (str == NULL)
+	      return -1;
+	    try
+	      {
+		self->value.color = parse_var_color (str.get());
+	      }
+	    catch (const gdb_exception &except)
+	      {
+		gdbpy_convert_exception (except);
+		return -1;
+	      }
+	  }
+	else if (PyLong_Check (value))
+	  {
+	    long l;
+	    if (! gdb_py_int_as_long (value, &l))
+	      return -1;
+	    if (l < -1 || l > 255)
+	      {
+		PyErr_SetString (PyExc_RuntimeError,
+				 _("Range exceeded."));
+		return -1;
+	      }
+	    self->value.color = ui_file_style::color (l);
+	  }
+	else if (value == Py_None)
+	  self->value.color = ui_file_style::NONE;
+	else
+	  {
+	    PyErr_SetString (PyExc_RuntimeError,
+			     _("color arguments must be a string, an integer "
+			       "or None."));
+	    return -1;
+	  }
+      }
+      break;
+
     case var_boolean:
       if (! PyBool_Check (value))
 	{
@@ -637,6 +686,15 @@ add_setshow_generic (int parmclass, enum command_class cmdclass,
 				       get_show_value, set_list, show_list);
       break;
 
+    case var_color:
+      /* Initialize the value, just in case.  */
+      self->value.color = ui_file_style::NONE;
+      commands = add_setshow_color_cmd (cmd_name.get (), cmdclass,
+					&self->value.color, set_doc,
+					show_doc, help_doc, get_set_value,
+					get_show_value, set_list, show_list);
+      break;
+
     default:
       gdb_assert_not_reached ("Unhandled parameter class.");
     }
@@ -758,7 +816,8 @@ parmpy_init (PyObject *self, PyObject *args, PyObject *kwds)
       && parmclass != var_string && parmclass != var_string_noescape
       && parmclass != var_optional_filename && parmclass != var_filename
       && parmclass != var_zinteger && parmclass != var_zuinteger
-      && parmclass != var_zuinteger_unlimited && parmclass != var_enum)
+      && parmclass != var_zuinteger_unlimited && parmclass != var_enum
+      && parmclass != var_color)
     {
       PyErr_SetString (PyExc_RuntimeError,
 		       _("Invalid parameter class argument."));
@@ -779,7 +838,7 @@ parmpy_init (PyObject *self, PyObject *args, PyObject *kwds)
   else
     obj->enumeration = NULL;
   obj->type = (enum var_types) parmclass;
-  memset (&obj->value, 0, sizeof (obj->value));
+  memset (reinterpret_cast<void *> (&obj->value), 0, sizeof (obj->value));
 
   if (var_type_uses<std::string> (obj->type))
     obj->value.stringval = new std::string;
diff --git a/gdb/python/python.c b/gdb/python/python.c
index c7d4157b70c..b1caea5665d 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -484,6 +484,24 @@ 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> ();
+	if (color.is_none () || color.is_basic ())
+	  return host_string_to_python_string
+	    (basic_color_name (color.get_value ())).release ();
+	else if (color.is_simple ())
+	  return gdb_py_object_from_longest (color.get_value ()).release ();
+	else
+	  {
+	    uint8_t rgb[3];
+	    color.get_rgb (rgb);
+	    char s[64];
+	    snprintf (s, sizeof s, "#%02X%02X%02X", rgb[0], rgb[1], rgb[2]);
+	    return host_string_to_python_string (s).release ();
+	  }
+      }
+
     case var_boolean:
       {
 	if (var.get<bool> ())
diff --git a/gdb/testsuite/gdb.base/style.exp b/gdb/testsuite/gdb.base/style.exp
index 2242c5bf743..f6e71c89109 100644
--- a/gdb/testsuite/gdb.base/style.exp
+++ b/gdb/testsuite/gdb.base/style.exp
@@ -298,6 +298,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
+	    # tripplet. 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;255m.*\".*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;255m.*\".*version.*\".*style.*\033\\\[m foreground color is: #FED210" \
+		"Version's TrueColor foreground style"
+	}
     }
 }
 
diff --git a/gdb/ui-style.h b/gdb/ui-style.h
index fe1b2af611d..a1ba0ff413d 100644
--- a/gdb/ui-style.h
+++ b/gdb/ui-style.h
@@ -73,6 +73,11 @@ struct ui_file_style
 	      && 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)
@@ -104,10 +109,17 @@ struct ui_file_style
       return m_simple && m_value >= BLACK && m_value <= WHITE;
     }
 
-    /* Return the value of a basic color.  */
+    /* Return true if this is one of the simple colors, false
+       otherwise.  */
+    bool is_simple () const
+    {
+      return m_simple;
+    }
+
+    /* Return the value of a simple color.  */
     int get_value () const
     {
-      gdb_assert (is_basic ());
+      gdb_assert (is_simple ());
       return m_value;
     }
 
-- 
2.34.1


^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2022-09-13  0:44 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-08-15 21:15 [PATCH] Add an option with a color type Andrei Pikas
2022-08-17 17:40 ` Andrew Burgess
2022-08-17 18:46   ` Andrei Pikas
2022-08-20 12:19   ` [PATCH v2] " Andrei Pikas
2022-08-20 12:43     ` Eli Zaretskii
2022-08-21 20:09       ` Andrei Pikas
2022-08-22  2:26         ` Eli Zaretskii
2022-08-21 21:07       ` [PATCH v3 (documentation fixed)] " Andrei Pikas
2022-08-22 11:54         ` Eli Zaretskii
2022-08-22 16:26           ` Philippe Waroquiers
2022-08-23 11:29         ` Andrew Burgess
2022-09-13  0:44           ` [PATCH v4 (documentation + approximation + convenience variable + python and guile types)] " Andrei Pikas
2022-08-17 17:57 ` [PATCH] " Eli Zaretskii

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).