From patchwork Sat Oct 5 19:27:44 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrei Pikas X-Patchwork-Id: 98428 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 92980385F01B for ; Sat, 5 Oct 2024 19:29:13 +0000 (GMT) X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org Received: from mail-lj1-f171.google.com (mail-lj1-f171.google.com [209.85.208.171]) by sourceware.org (Postfix) with ESMTPS id 482883858D20 for ; Sat, 5 Oct 2024 19:28:29 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 482883858D20 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=mail.api.win Authentication-Results: sourceware.org; spf=none smtp.mailfrom=api.win ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 482883858D20 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=209.85.208.171 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1728156516; cv=none; b=aoP4vvRSGYcvceD5kVQxniiugwu9fvH8kZnH2gybp/owlXAOD+NM+jrTqE3bn7oLX+FzFQFThyeuWFkPjZtWnyxCyoZL0/S1oUEHJ85JCRJ/CTrjQAyVNrP3oA7HO4LzITTbIVzDTRNp6ndvFO38esKbfjwu4SPLvBvtt9m4i9w= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1728156516; c=relaxed/simple; bh=ZmSWq26cfDYV46sTjJTe8zcbpRCE3d65yw9OaA13Bo8=; h=From:To:Subject:Date:Message-Id:MIME-Version; b=SaMbwiEp8NwlyKZDNLXCdLLB6+W8+Cnn/Y06NwIRbA4OfYV1LFBbCd6TyL2kolRIBHsZ76ZNGN0+Twrmvj0DWfzhDwqHgyrh+LDY7h4TIAp0QkR2gxTUxiUEm71Meq6FuNYLp3gq4+1JIijrbixJ67o8DdEPK72jO6HTFWnshFo= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-lj1-f171.google.com with SMTP id 38308e7fff4ca-2facf40737eso35721041fa.0 for ; Sat, 05 Oct 2024 12:28:29 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1728156507; x=1728761307; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=6i3Yn1AHthugW17St6R3asFtIOkGFd3VyqPDgzxUTVw=; b=BcpYcCddieiQJSgGvtOC5btZbcu7zDRLDcxBw+QaPtBgD0/O9ewyprWwV2KmVJdaT9 PBvX2wB4PvmQy8atf+P+pZySWzUTiuILKKS0aHLLWrkMEec9QX7nNqxMnKjd0Gxx7akU rNUJ+FDfa5OseCegRpIc/ddUcROW4VhWdyEF+P6xxPp1uusCesWd4GRoQMM/Vf5afiLn /xxD3rUz/2G1WeAGlbTM4Zyq7KdF3iDx3VVim4S7lSoQGu5CYLZrl4M5a5jq2BKQdnou 7oUjhpcXOHkwK6de0x6VppzhAe5VZosBncytz5YynyGiIFoLZCozioO0wq1M37qYBVeu /LcQ== X-Gm-Message-State: AOJu0YzwK3euKBghz6zWzCG7QqAwWthKgzZylQKostklJm/Bignrbxvk ucxNAwkC9d71flQZdxUKKGtuwK04ROYobXLMNi4SFxkMXR9Qr0Fdm7qCa8djlB4= X-Google-Smtp-Source: AGHT+IFxcEfaIJf8JZUhHVRpz2GxbpWQ3z8+qG2dBKAp4Q3097s1Pyx7vdm07gXRoYXd+K8XFnnY9A== X-Received: by 2002:a05:6512:3b8e:b0:537:a855:7d6f with SMTP id 2adb3069b0e04-539ab891f52mr3740604e87.34.1728156506128; Sat, 05 Oct 2024 12:28:26 -0700 (PDT) Received: from API.. ([2a03:1ac0:b0d6:4d64:f410:d445:727f:4122]) by smtp.gmail.com with ESMTPSA id 2adb3069b0e04-539afec1120sm297395e87.14.2024.10.05.12.28.25 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 05 Oct 2024 12:28:25 -0700 (PDT) From: Andrei Pikas To: tom@tromey.com Cc: gdb-patches@sourceware.org, Andrei Pikas Subject: [PATCH v8] Add an option with a color type. Date: Sat, 5 Oct 2024 22:27:44 +0300 Message-Id: <20241005192744.976782-1-gdb@mail.api.win> X-Mailer: git-send-email 2.34.1 In-Reply-To: <87r08vhhx8.fsf@tromey.com> References: <87r08vhhx8.fsf@tromey.com> MIME-Version: 1.0 X-Spam-Status: No, score=-7.3 required=5.0 tests=BAYES_00, GIT_PATCH_0, KAM_DMARC_STATUS, KAM_LAZY_DOMAIN_SECURITY, KAM_SHORT, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gdb-patches-bounces~patchwork=sourceware.org@sourceware.org Colors can be specified as "none" for terminal's default color, as a name of one of the eight standard colors of ISO/IEC 6429 "black", "red", "green", etc., as an RGB hexadecimal tripplet #RRGGBB for 24-bit TrueColor, or as an integer from 0 to 255. Integers 0 to 7 are the synonyms for the standard colors. Integers 8-15 are used for the so-called bright colors from the aixterm extended 16-color palette. Integers 16-255 are the indexes into xterm extended 256-color palette (usually 6x6x6 cube plus gray ramp). In general, 256-color palette is terminal dependent and sometimes can be changed with OSC 4 sequences, e.g. "\033]4;1;rgb:00/FF/00\033\\". It is the responsibility of the user to verify that the terminal supports the specified colors. PATCH v5 changes: documentation fixed. PATCH v6 changes: documentation fixed. PATCH v7 changes: rebase onto master and fixes after review. PATCH v8 changes: fixes after review. --- gdb/Makefile.in | 2 + gdb/NEWS | 31 ++ gdb/cli/cli-cmds.c | 6 + gdb/cli/cli-decode.c | 174 +++++++++ gdb/cli/cli-decode.h | 21 ++ gdb/cli/cli-option.c | 44 +++ gdb/cli/cli-option.h | 21 ++ gdb/cli/cli-setshow.c | 21 ++ gdb/cli/cli-style.c | 49 +-- gdb/cli/cli-style.h | 4 +- gdb/command.h | 26 +- gdb/doc/gdb.texinfo | 46 ++- gdb/doc/guile.texi | 104 ++++++ gdb/doc/python.texi | 98 +++++ gdb/guile/guile-internal.h | 9 + gdb/guile/guile.c | 1 + gdb/guile/scm-color.c | 427 ++++++++++++++++++++++ gdb/guile/scm-param.c | 33 +- gdb/python/py-color.c | 336 +++++++++++++++++ gdb/python/py-color.h | 35 ++ gdb/python/py-param.c | 40 +- gdb/python/python.c | 7 + gdb/testsuite/gdb.base/default.exp | 8 +- gdb/testsuite/gdb.base/style.exp | 197 ++++++++++ gdb/testsuite/gdb.guile/scm-color.exp | 110 ++++++ gdb/testsuite/gdb.guile/scm-parameter.exp | 47 +++ gdb/testsuite/gdb.python/py-color.exp | 100 +++++ gdb/testsuite/gdb.python/py-parameter.exp | 53 +++ gdb/testsuite/lib/gdb-utils.exp | 24 +- gdb/testsuite/lib/gdb.exp | 3 +- gdb/top.c | 14 + gdb/ui-style.c | 353 ++++++++++++++---- gdb/ui-style.h | 157 +++++++- gdb/unittests/style-selftests.c | 6 +- 34 files changed, 2436 insertions(+), 171 deletions(-) create mode 100644 gdb/guile/scm-color.c create mode 100644 gdb/python/py-color.c create mode 100644 gdb/python/py-color.h create mode 100644 gdb/testsuite/gdb.guile/scm-color.exp create mode 100644 gdb/testsuite/gdb.python/py-color.exp diff --git a/gdb/Makefile.in b/gdb/Makefile.in index bcf1ee45a70..502b4b6f9e5 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -359,6 +359,7 @@ SUBDIR_GUILE_SRCS = \ guile/scm-block.c \ guile/scm-breakpoint.c \ guile/scm-cmd.c \ + guile/scm-color.c \ guile/scm-disasm.c \ guile/scm-exception.c \ guile/scm-frame.c \ @@ -395,6 +396,7 @@ SUBDIR_PYTHON_SRCS = \ python/py-bpevent.c \ python/py-breakpoint.c \ python/py-cmd.c \ + python/py-color.c \ python/py-connection.c \ python/py-continueevent.c \ python/py-dap.c \ diff --git a/gdb/NEWS b/gdb/NEWS index 3a0c8d1049f..f0b17000401 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -49,11 +49,42 @@ history has been reached. It also specifies that the forward execution can continue, and the recording will also continue. +* "set style" commands now supports numeric format for basic colors + from 0 to 255 and #RRGGBB format for TrueColor. + +* New built-in convenience variable $_colorsupport provides comma-separated + list of color space names supported by terminal. Each color space name is one + of monochrome, ansi_8color, aixterm_16color, xterm_256color or rgb_24bit. + It is handy for conditionally using styling colors based on terminal features. + For example: + + (gdb) if $_regex ($_colorsupport, ".*(^|,)rgb_24bit($|,).*") + >set style filename background #FACADE + >else + >if $_regex ($_colorsupport, ".*(^|,)xterm_256color($|,).*") + >set style filename background 224 + >else + >set style filename background red + >end + >end + * Python API ** Added gdb.record.clear. Clears the trace data of the current recording. This forces re-decoding of the trace for successive commands. + ** New class gdb.Color for dealing with colors. + + ** New constant gdb.PARAM_COLOR represents color type of a + gdb.Parameter.value. Parameter's value is gdb.Color instance. + +* Guile API + + ** New type for dealing with colors. + + ** New constant PARAM_COLOR represents color type of a value + of a object. Parameter's value is instance. + * Debugger Adapter Protocol changes ** The "scopes" request will now return a scope holding global diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c index ea2e156a00c..12d5ac5fe45 100644 --- a/gdb/cli/cli-cmds.c +++ b/gdb/cli/cli-cmds.c @@ -2390,6 +2390,11 @@ value_from_setting (const setting &var, struct gdbarch *gdbarch) return current_language->value_string (gdbarch, value, len); } + case var_color: + { + std::string s = var.get ().to_string (); + return current_language->value_string (gdbarch, s.c_str (), s.size ()); + } default: gdb_assert_not_reached ("bad var_type"); } @@ -2437,6 +2442,7 @@ str_value_from_setting (const setting &var, struct gdbarch *gdbarch) case var_pinteger: case var_boolean: case var_auto_boolean: + case var_color: { std::string cmd_val = get_setshow_command_value_string (var); diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c index 163012a6bec..527a96ed3e4 100644 --- a/gdb/cli/cli-decode.c +++ b/gdb/cli/cli-decode.c @@ -23,6 +23,7 @@ #include "cli/cli-cmds.h" #include "cli/cli-decode.h" #include "cli/cli-style.h" +#include "cli/cli-utils.h" #include /* 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 + (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::set set_func, + setting_func_types::get get_func, + show_value_ftype *show_func, + cmd_list_element **set_list, + cmd_list_element **show_list) +{ + auto cmds = add_setshow_cmd_full + (name, theclass, var_color, nullptr, + set_doc, show_doc, help_doc, + set_func, get_func, nullptr, show_func, + set_list, show_list); + + set_cmd_completer (cmds.set, color_completer); + + return cmds; +} + /* See cli-decode.h. */ const char * const auto_boolean_enums[] = { "on", "off", "auto", NULL }; @@ -2749,3 +2831,95 @@ cli_user_command_p (struct cmd_list_element *cmd) { return cmd->theclass == class_user && cmd->func == do_simple_func; } + +/* See cli-decode.h. */ + +ui_file_style::color +parse_cli_var_color (const char **args) +{ + /* Do a "set" command. ARG is nullptr if no argument, or the + text of the argument. */ + + if (args == nullptr || *args == nullptr || **args == '\0') + { + std::string msg; + + for (size_t i = 0; ui_file_style::basic_color_enums[i]; ++i) + { + msg.append ("\""); + msg.append (ui_file_style::basic_color_enums[i]); + msg.append ("\", "); + } + + error (_("Requires an argument. Valid arguments are %sinteger from -1 " + "to 255 or an RGB hex triplet in a format #RRGGBB"), + msg.c_str ()); + } + + const char *p = skip_to_space (*args); + size_t len = p - *args; + + int nmatches = 0; + ui_file_style::basic_color match = ui_file_style::NONE; + for (int i = 0; ui_file_style::basic_color_enums[i]; ++i) + if (strncmp (*args, ui_file_style::basic_color_enums[i], len) == 0) + { + match = static_cast (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 (num)); + } + + /* Try to parse #RRGGBB string. */ + if (len != 7) + error_no_arg (_("invalid RGB hex triplet format")); + + uint8_t r, g, b; + int scanned_chars = 0; + int parsed_args = sscanf (*args, "#%2" SCNx8 "%2" SCNx8 "%2" SCNx8 "%n", + &r, &g, &b, &scanned_chars); + + if (parsed_args != 3 || scanned_chars != 7) + error_no_arg (_("invalid RGB hex triplet format")); + + *args += len; + return ui_file_style::color (r, g, b); +} + +/* See cli-decode.h. */ + +ui_file_style::color +parse_var_color (const char *arg) +{ + const char *end_arg = arg; + ui_file_style::color color = parse_cli_var_color (&end_arg); + + int len = end_arg - arg; + const char *after = skip_spaces (end_arg); + if (*after != '\0') + error (_("Junk after item \"%.*s\": %s"), len, arg, after); + + return color; +} diff --git a/gdb/cli/cli-decode.h b/gdb/cli/cli-decode.h index 7365c3f0157..5918d3b9dbe 100644 --- a/gdb/cli/cli-decode.h +++ b/gdb/cli/cli-decode.h @@ -306,6 +306,27 @@ extern const char * const boolean_enums[]; /* The enums of auto-boolean commands. */ extern const char * const auto_boolean_enums[]; +/* Add the different possible completions of TEXT with color. + + WORD points in the same buffer as TEXT, and completions should be + returned relative to this position. For example, suppose TEXT is "foo" + and we want to complete to "foobar". If WORD is "oo", return + "oobar"; if WORD is "baz/foo", return "baz/foobar". */ + +extern void complete_on_color (completion_tracker &tracker, + const char *text, const char *word); + +/* Parse ARGS, an option to a var_color variable. + * + Either returns the parsed value on success or throws an error. ARGS may be + one of strings {none, black, red, green, yellow, blue, magenta, + cyan, white}, or color number from 0 to 255, or RGB hex triplet #RRGGBB. + */ +extern ui_file_style::color parse_cli_var_color (const char **args); + +/* Same as above but additionally check that there is no junk in the end. */ +extern ui_file_style::color parse_var_color (const char *arg); + /* Verify whether a given cmd_list_element is a user-defined command. Return 1 if it is user-defined. Return 0 otherwise. */ diff --git a/gdb/cli/cli-option.c b/gdb/cli/cli-option.c index 05539285c80..3a165ef6d2c 100644 --- a/gdb/cli/cli-option.c +++ b/gdb/cli/cli-option.c @@ -45,6 +45,9 @@ union option_value /* For var_string options. This is malloc-allocated. */ std::string *string; + + /* For var_color options. */ + ui_file_style::color color = ui_file_style::NONE; }; /* Holds an options definition and its value. */ @@ -433,6 +436,35 @@ parse_option (gdb::array_view options_group, val.enumeration = parse_cli_var_enum (args, match->enums); return option_def_and_value {*match, match_ctx, val}; } + case var_color: + { + if (completion != nullptr) + { + const char *after_arg = skip_to_space (*args); + if (*after_arg == '\0') + { + complete_on_color (completion->tracker, *args, *args); + + if (completion->tracker.have_completions ()) + return {}; + } + } + + if (check_for_argument (args, "--")) + { + /* Treat e.g., "backtrace -entry-values --" as if there + was no argument after "-entry-values". This makes + parse_cli_var_color throw an error with a suggestion of + what are the valid options. */ + args = nullptr; + } + + option_value val; + ui_file_style::color color = parse_cli_var_color (args); + ui_file_style::color approx_color = color.approximate (colorsupport ()); + val.color = approx_color; + return option_def_and_value {*match, match_ctx, val}; + } case var_string: { if (check_for_argument (args, "--")) @@ -611,6 +643,10 @@ save_option_value_in_ctx (std::optional &ov) *ov->option.var_address.enumeration (ov->option, ov->ctx) = ov->value->enumeration; break; + case var_color: + *ov->option.var_address.color (ov->option, ov->ctx) + = ov->value->color; + break; case var_string: *ov->option.var_address.string (ov->option, ov->ctx) = std::move (*ov->value->string); @@ -699,6 +735,14 @@ get_val_type_str (const option_def &opt, std::string &buffer) } return buffer.c_str (); } + case var_color: + { + buffer = ""; + for (size_t i = 0; ui_file_style::basic_color_enums[i]; ++i) + buffer.append (ui_file_style::basic_color_enums[i]).append ("|"); + buffer += "NUMBER|#RRGGBB"; + return buffer.c_str (); + } case var_string: return "STRING"; default: diff --git a/gdb/cli/cli-option.h b/gdb/cli/cli-option.h index bbe281d9721..2fc354ef1a2 100644 --- a/gdb/cli/cli-option.h +++ b/gdb/cli/cli-option.h @@ -91,6 +91,7 @@ struct option_def int *(*integer) (const option_def &, void *ctx); const char **(*enumeration) (const option_def &, void *ctx); std::string *(*string) (const option_def &, void *ctx); + ui_file_style::color *(*color) (const option_def &, void *ctx); } var_address; @@ -308,6 +309,26 @@ struct string_option_def : option_def } }; +/* A var_color command line option. */ + +template +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; + } +}; + /* A group of options that all share the same context pointer to pass to the options' get-current-value callbacks. */ struct option_def_group diff --git a/gdb/cli/cli-setshow.c b/gdb/cli/cli-setshow.c index bcc793b3148..a8f22c9bde6 100644 --- a/gdb/cli/cli-setshow.c +++ b/gdb/cli/cli-setshow.c @@ -443,6 +443,13 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c) option_changed = c->var->set (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 (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 ()); break; + case var_color: + { + const ui_file_style::color &color + = c->var->get (); + interps_notify_param_changed + (name, color.to_string ().c_str ()); + } + break; case var_boolean: { const char *opt = c->var->get () ? "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 (); + stb.puts (value.to_string ().c_str ()); + } + break; case var_boolean: stb.puts (var.get () ? "on" : "off"); break; diff --git a/gdb/cli/cli-style.c b/gdb/cli/cli-style.c index 36a8bd9c526..8a47daadcf9 100644 --- a/gdb/cli/cli-style.c +++ b/gdb/cli/cli-style.c @@ -42,20 +42,6 @@ bool source_styling = true; bool disassembler_styling = true; -/* Name of colors; must correspond to ui_file_style::basic_color. */ -static const char * const cli_colors[] = { - "none", - "black", - "red", - "green", - "yellow", - "blue", - "magenta", - "cyan", - "white", - nullptr -}; - /* Names of intensities; must correspond to ui_file_style::intensity. */ static const char * const cli_intensities[] = { @@ -135,8 +121,8 @@ cli_style_option::cli_style_option (const char *name, ui_file_style::intensity intensity) : changed (name), m_name (name), - m_foreground (cli_colors[fg - ui_file_style::NONE]), - m_background (cli_colors[0]), + m_foreground (fg), + m_background (ui_file_style::NONE), m_intensity (cli_intensities[intensity]) { } @@ -147,32 +133,17 @@ cli_style_option::cli_style_option (const char *name, ui_file_style::intensity i) : changed (name), m_name (name), - m_foreground (cli_colors[0]), - m_background (cli_colors[0]), + m_foreground (ui_file_style::NONE), + m_background (ui_file_style::NONE), m_intensity (cli_intensities[i]) { } -/* Return the color number corresponding to COLOR. */ - -static int -color_number (const char *color) -{ - for (int i = 0; i < ARRAY_SIZE (cli_colors); ++i) - { - if (color == cli_colors[i]) - return i - 1; - } - gdb_assert_not_reached ("color not found"); -} - /* See cli-style.h. */ ui_file_style cli_style_option::style () const { - int fg = color_number (m_foreground); - int bg = color_number (m_background); ui_file_style::intensity intensity = ui_file_style::NORMAL; for (int i = 0; i < ARRAY_SIZE (cli_intensities); ++i) @@ -184,7 +155,7 @@ cli_style_option::style () const } } - return ui_file_style (fg, bg, intensity); + return ui_file_style (m_foreground, m_background, intensity); } /* See cli-style.h. */ @@ -257,9 +228,8 @@ cli_style_option::add_setshow_commands (enum command_class theclass, set_show_commands commands; - commands = add_setshow_enum_cmd - ("foreground", theclass, cli_colors, - &m_foreground, + commands = add_setshow_color_cmd + ("foreground", theclass, &m_foreground, _("Set the foreground color for this property."), _("Show the foreground color for this property."), nullptr, @@ -269,9 +239,8 @@ cli_style_option::add_setshow_commands (enum command_class theclass, commands.set->set_context (this); commands.show->set_context (this); - commands = add_setshow_enum_cmd - ("background", theclass, cli_colors, - &m_background, + commands = add_setshow_color_cmd + ("background", theclass, &m_background, _("Set the background color for this property."), _("Show the background color for this property."), nullptr, diff --git a/gdb/cli/cli-style.h b/gdb/cli/cli-style.h index 5052b867cfa..5af299f1c6f 100644 --- a/gdb/cli/cli-style.h +++ b/gdb/cli/cli-style.h @@ -67,9 +67,9 @@ class cli_style_option const char *m_name; /* The foreground. */ - const char *m_foreground; + ui_file_style::color m_foreground; /* The background. */ - const char *m_background; + ui_file_style::color m_background; /* The intensity. */ const char *m_intensity; diff --git a/gdb/command.h b/gdb/command.h index 0ceaf3e2a6d..715fcd8096f 100644 --- a/gdb/command.h +++ b/gdb/command.h @@ -111,7 +111,9 @@ enum var_types /* Enumerated type. Can only have one of the specified values. *VAR is a char pointer to the name of the element that we find. */ - var_enum + var_enum, + /* Color type. *VAR is a ui_file_style::color structure. */ + var_color }; /* A structure describing an extra literal accepted and shown in place @@ -185,6 +187,14 @@ inline bool var_type_uses (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 (var_types t) +{ + return t == var_color; +} + template struct setting_func_types_1; template @@ -681,6 +691,20 @@ extern set_show_commands add_setshow_enum_cmd setting_func_types::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::set set_func, + setting_func_types::get get_func, + show_value_ftype *show_func, cmd_list_element **set_list, + cmd_list_element **show_list); + extern set_show_commands add_setshow_auto_boolean_cmd (const char *name, command_class theclass, auto_boolean *var, const char *set_doc, const char *show_doc, const char *help_doc, diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index cc1b69c6978..e49a0425e59 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -12973,6 +12973,20 @@ and @code{$_shell_exitsignal} according to the exit status of the last launched command. These variables are set and used similarly to the variables @code{$_exitcode} and @code{$_exitsignal}. +@item $_colorsupport +@vindex $_colorsupport@r{, convenience variable} +@cindex color space +Comma-separated list of @dfn{color space} names supported by terminal. Names +could be any of @samp{monochrome}, @samp{ansi_8color}, @samp{aixterm_16color}, +@samp{xterm_256color}, @samp{rgb_24bit}. E.g., for plain linux terminal the +value could be @samp{monochrome,ansi_8color} and for terminal with truecolor +support it could be +@samp{monochrome,ansi_8color,aixterm_16color,xterm_256color,rgb_24bit}. +@samp{rgb_24bit} availability is determined by the @env{COLORTERM} environment +variable which may be @samp{truecolor} or @samp{24bit}. Other color spaces are +determined by the "Co" termcap which in turn depends on the @env{TERM} +environment variable. + @end table @node Convenience Funs @@ -27815,16 +27829,32 @@ For example, the style of file names can be controlled using the @table @code @item set style filename background @var{color} -Set the background to @var{color}. Valid colors are @samp{none} -(meaning the terminal's default color), @samp{black}, @samp{red}, -@samp{green}, @samp{yellow}, @samp{blue}, @samp{magenta}, @samp{cyan}, -and@samp{white}. +Set the background to @var{color}. @var{color} can be @samp{none} +(meaning the terminal's default color), a name of one of the eight standard +colors of ISO/IEC 6429, index from 0 to 255 into terminal's color +palette or a hexadecimal RGB triplet in @samp{#RRGGBB} format for +24-bit TrueColor. + +Valid color names are @samp{black}, @samp{red}, @samp{green}, +@samp{yellow}, @samp{blue}, @samp{magenta}, @samp{cyan}, and +@samp{white}. + +Integers 0 to 7 are the synonyms for the standard colors. Integers 8-15 are +used for the so-called bright colors from the aixterm extended 16-color +palette. Integers 16-255 are the indexes into xterm extended 256-color palette +(usually 6x6x6 cube plus gray ramp). In general, 256-color palette is terminal +dependent and sometimes can be changed with OSC 4 sequences, e.g. +"\033]4;1;rgb:00/FF/00\033\\". A hexadecimal 24-bit TrueColor is specified in +the format @samp{#RRGGBB} where RR, GG and BB are the 2-digit hexadecimal +integers specifiing the intensity of the red, green and blue color components, +respectively. + +It is the responsibility of the user to verify that the terminal supports +the specified colors. @item set style filename foreground @var{color} -Set the foreground to @var{color}. Valid colors are @samp{none} -(meaning the terminal's default color), @samp{black}, @samp{red}, -@samp{green}, @samp{yellow}, @samp{blue}, @samp{magenta}, @samp{cyan}, -and@samp{white}. +Set the foreground to @var{color}. @var{color} can be given in the same ways +as for the background. @item set style filename intensity @var{value} Set the intensity to @var{value}. Valid intensities are @samp{normal} diff --git a/gdb/doc/guile.texi b/gdb/doc/guile.texi index a66e8bfe16b..0cb01663d6e 100644 --- a/gdb/doc/guile.texi +++ b/gdb/doc/guile.texi @@ -155,6 +155,7 @@ from the Guile interactive prompt. * I/O Ports in Guile:: GDB I/O ports * Memory Ports in Guile:: Accessing memory through ports and bytevectors * Iterators In Guile:: Basic iterator support +* Colors In Guile:: Colorize output with Guile @end menu @node Basic Guile @@ -408,6 +409,9 @@ as a symbol. @item @xref{Values From Inferior In Guile}. + +@item +@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 +You can assign instance of @code{} to the value of +a @code{} object created with @code{PARAM_COLOR}. + +@code{} 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{} 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{} object. +@end deffn + +@deffn {Scheme Procedure} color-colorspace color +Return the color space of a @code{} object. +@end deffn + +@deffn {Scheme Procedure} color-index color +Return index of the color of a @code{} object in a palette. +@end deffn + +@deffn {Scheme Procedure} color-components color +Return components of the direct @code{} object. +@end deffn + +@deffn {Scheme Procedure} color-escape-sequence color is_foreground +Return string to change terminal's color to this. + +If @var{is_foreground} is @code{#t}, then the returned sequence will change +foreground color. Otherwise, the returned sequence will change background +color. +@end deffn + +When color is initialized, its color space must be specified. The +available color spaces are represented by constants defined in the @code{gdb} +module: + +@vtable @code +@item COLORSPACE_MONOCHROME +Palette with only terminal's default color. + +@item COLORSPACE_ANSI_8COLOR +Palette with eight standard colors of ISO/IEC 6429 "black", "red", "green", etc. + +@item COLORSPACE_AIXTERM_16COLOR +Palette with 16 colors. First eight are standard colors of ISO/IEC 6429 +"black", "red", "green", etc. Next eight are their bright version. + +@item COLORSPACE_XTERM_256COLOR +Palette with 256 colors. First 16 are from COLORSPACE_AIXTERM_16COLOR. Next +216 colors are 6x6x6 RGB cube. And last 24 colors form grayscale ramp. + +@item COLORSPACE_RGB_24BIT +Direct 24-bit RGB colors. +@end vtable + @node Guile Auto-loading @subsection Guile Auto-loading @cindex guile auto-loading diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi index 22f0e6c6d0a..e27bc68bf20 100644 --- a/gdb/doc/python.texi +++ b/gdb/doc/python.texi @@ -225,6 +225,7 @@ optional arguments while skipping others. Example: * Finish Breakpoints in Python:: Setting Breakpoints on function return using Python. * Lazy Strings In Python:: Python representation of lazy strings. +* Colors In Python:: Python representation of colors. * Architectures In Python:: Python representation of architectures. * Registers In Python:: Python representation of registers. * Connections In Python:: Python representation of connections. @@ -5230,6 +5231,11 @@ except the special value -1 is returned for the setting of ``unlimited''. @item gdb.PARAM_ENUM The value is a string, which must be one of a collection string constants provided when the parameter is created. + +@findex PARAM_COLOR +@findex gdb.PARAM_COLOR +@item gdb.PARAM_COLOR +The value is @code{gdb.Color} instance. @end table @node Functions In Python @@ -6999,6 +7005,98 @@ resolve this to the lazy string's character type, use the type's writable. @end defvar +@node Colors In Python +@subsection Python representation of colors + +@cindex colors in python +@tindex gdb.Color +You can assign instance of @code{Color} to the @code{value} of +a @code{Parameter} instance created with @code{PARAM_COLOR}. + +@code{Color} may refer to an index from color palette or contain components +of a color from some colorspace. + +@defun Color.__init__ (@r{[}@var{value} @r{[}, @var{color-space}@r{]}@r{]}) + +@var{value} is @code{None} (meaning the terminal's default color), +an integer index of a color in palette, tuple with color components +or a string. String can be a hex RGB triplet in @samp{#RRGGBB} format +or one of the following color names: +@samp{none} (meaning the terminal's default color), @samp{black}, @samp{red}, +@samp{green}, @samp{yellow}, @samp{blue}, @samp{magenta}, @samp{cyan}, +or @samp{white}. + +@var{color-space} should be one of the @samp{COLORSPACE_} constants. This +argument tells @value{GDBN} which color space @var{value} belongs. +@end defun + +@defvar Color.is_none +This atribute is boolean. If its value is @code{True} then color is terminal's +default. +@end defvar + +@defvar Color.is_indexed +This atribute is boolean. If its value is @code{True} then color is indexed, +i.e. belongs to some palette. +@end defvar + +@defvar Color.is_direct +This atribute is boolean. If its value is @code{True} then this object +describes direct colour in the sense of ISO/IEC 8613-6. +@end defvar + +@defvar Color.index +This attribute exist if @code{is_indexed} is @code{True}. Its integer value is +index of a color in a palette. +@end defvar + +@defvar Color.components +This attribute exist if @code{is_direct} is @code{True}. Its value is tuple +with integer components of a color. +@end defvar + +@defun Color.escape_sequence (@var{self}, @var{is_foreground}) +Returns string to change terminal's color to this. + +If @var{is_foreground} is @code{True}, then the returned sequence will change +foreground color. Otherwise, the returned sequence will change background +color. +@end defun + +When color is initialized, its color space must be specified. The +available color spaces are represented by constants defined in the @code{gdb} +module: + +@table @code +@findex COLORSPACE_MONOCHROME +@findex gdb.COLORSPACE_MONOCHROME +@item gdb.COLORSPACE_MONOCHROME +Palette with only terminal's default color. + +@findex COLORSPACE_ANSI_8COLOR +@findex gdb.COLORSPACE_ANSI_8COLOR +@item gdb.COLORSPACE_ANSI_8COLOR +Palette with eight standard colors of ISO/IEC 6429 "black", "red", "green", etc. + +@findex COLORSPACE_AIXTERM_16COLOR +@findex gdb.COLORSPACE_AIXTERM_16COLOR +@item gdb.COLORSPACE_AIXTERM_16COLOR +Palette with 16 colors. First eight are standard colors of ISO/IEC 6429 +"black", "red", "green", etc. Next eight are their bright version. + +@findex COLORSPACE_XTERM_256COLOR +@findex gdb.COLORSPACE_XTERM_256COLOR +@item gdb.COLORSPACE_XTERM_256COLOR +Palette with 256 colors. First 16 are from COLORSPACE_AIXTERM_16COLOR. Next +216 colors are 6x6x6 RGB cube. And last 24 colors form grayscale ramp. + +@findex COLORSPACE_RGB_24BIT +@findex gdb.COLORSPACE_RGB_24BIT +@item gdb.COLORSPACE_RGB_24BIT +Direct 24-bit RGB colors. + +@end table + @node Architectures In Python @subsubsection Python representation of architectures @cindex Python architectures diff --git a/gdb/guile/guile-internal.h b/gdb/guile/guile-internal.h index dea78845088..b137713d133 100644 --- a/gdb/guile/guile-internal.h +++ b/gdb/guile/guile-internal.h @@ -439,6 +439,14 @@ extern int gdbscm_valid_command_class_p (int command_class); extern char *gdbscm_canonicalize_command_name (const char *name, int want_trailing_space); +/* scm-color.c */ + +extern SCM coscm_scm_from_color (const ui_file_style::color &color); + +extern int coscm_is_color (SCM scm); + +extern const ui_file_style::color & coscm_get_color (SCM color_scm); + /* scm-frame.c */ struct frame_smob; @@ -617,6 +625,7 @@ extern void gdbscm_initialize_arches (void); extern void gdbscm_initialize_auto_load (void); extern void gdbscm_initialize_blocks (void); extern void gdbscm_initialize_breakpoints (void); +extern void gdbscm_initialize_colors (void); extern void gdbscm_initialize_commands (void); extern void gdbscm_initialize_disasm (void); extern void gdbscm_initialize_exceptions (void); diff --git a/gdb/guile/guile.c b/gdb/guile/guile.c index 432093b6aea..5358b8330a3 100644 --- a/gdb/guile/guile.c +++ b/gdb/guile/guile.c @@ -594,6 +594,7 @@ initialize_gdb_module (void *data) gdbscm_initialize_auto_load (); gdbscm_initialize_blocks (); gdbscm_initialize_breakpoints (); + gdbscm_initialize_colors (); gdbscm_initialize_commands (); gdbscm_initialize_disasm (); gdbscm_initialize_frames (); diff --git a/gdb/guile/scm-color.c b/gdb/guile/scm-color.c new file mode 100644 index 00000000000..6ebe252f125 --- /dev/null +++ b/gdb/guile/scm-color.c @@ -0,0 +1,427 @@ +/* GDB parameters implemented in Guile. + + Copyright (C) 2008-2024 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#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 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, + _("")); + + color_smob *c_smob = (color_smob *) SCM_SMOB_DATA (color_scm); + return c_smob->color; + +} + +/* Returns non-zero if SCM is a 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 (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]]) -> + + 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 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 (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 (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 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 ) -> 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 ) -> value */ + +static SCM +gdbscm_color_colorspace (SCM self) +{ + const ui_file_style::color &color = coscm_get_color (self); + return scm_from_int (static_cast (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 ) -> 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 ) -> 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 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 ]]\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 object." }, + + { "color-none?", 1, 0, 0, as_a_scm_t_subr (gdbscm_color_none_p), + "\ +Return #t if the object has default color." }, + + { "color-indexed?", 1, 0, 0, as_a_scm_t_subr (gdbscm_color_indexed_p), + "\ +Return #t if the object is from indexed color space." }, + + { "color-direct?", 1, 0, 0, as_a_scm_t_subr (gdbscm_color_direct_p), + "\ +Return #t if the 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 object." }, + + { "color-colorspace", 1, 0, 0, as_a_scm_t_subr (gdbscm_color_colorspace), + "\ +Return the color space of a object." }, + + { "color-index", 1, 0, 0, as_a_scm_t_subr (gdbscm_color_index), + "\ +Return index of the color of a object in a palette." }, + + { "color-components", 1, 0, 0, as_a_scm_t_subr (gdbscm_color_components), + "\ +Return components of the direct object." }, + + { "color-escape-sequence", 2, 0, 0, + as_a_scm_t_subr (gdbscm_color_escape_sequence), + "\ +Return string to change terminal's color to this." }, + + END_FUNCTIONS +}; + +void +gdbscm_initialize_colors (void) +{ + color_smob_tag = gdbscm_make_smob_type (color_smob_name, sizeof (color_smob)); + scm_set_smob_free (color_smob_tag, coscm_free_color_smob); + scm_set_smob_print (color_smob_tag, coscm_print_color_smob); + + gdbscm_define_integer_constants (colorspaces, 1); + gdbscm_define_functions (color_functions, 1); + + colorspace_keyword = scm_from_latin1_keyword ("color-space"); +} diff --git a/gdb/guile/scm-param.c b/gdb/guile/scm-param.c index 3a1e1583f54..749c5ea1f7d 100644 --- a/gdb/guile/scm-param.c +++ b/gdb/guile/scm-param.c @@ -47,6 +47,9 @@ union pascm_variable /* Hold a string, for enums. */ const char *cstringval; + + /* Hold a color. */ + ui_file_style::color color; }; /* A GDB parameter. @@ -130,6 +133,7 @@ enum scm_param_types param_optional_filename, param_filename, param_enum, + param_color, }; /* Translation from Guile parameters to GDB variable types. Keep in the @@ -155,7 +159,8 @@ param_to_var[] = { var_string_noescape }, { var_optional_filename }, { var_filename }, - { var_enum } + { var_enum }, + { var_color } }; /* Wraps a setting around an existing param_smob. This abstraction @@ -179,6 +184,8 @@ make_setting (param_smob *s) return setting (type, s->value.stringval); else if (var_type_uses (type)) return setting (type, &s->value.cstringval); + else if (var_type_uses (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 ()); + } + case var_boolean: { if (var.get ()) @@ -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, + _("")); + var.set (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 (p_smob->type)) p_smob->value.stringval = new std::string; + else if (var_type_uses (p_smob->type)) + p_smob->value.color = ui_file_style::NONE; if (initial_value_arg_pos > 0) { diff --git a/gdb/python/py-color.c b/gdb/python/py-color.c new file mode 100644 index 00000000000..3a90b620d5f --- /dev/null +++ b/gdb/python/py-color.c @@ -0,0 +1,336 @@ +/* Python interface to ui_file_style::color objects. + + Copyright (C) 2008-2024 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + + +#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 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 (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 (item_value); + } + + obj->color = ui_file_style::color (rgb[0], rgb[1], rgb[2]); + } + else if (PyUnicode_Check (value_obj)) + { + gdb::unique_xmalloc_ptr + 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 (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 (pair.value)) < 0) + return -1; + + colorpy_object_type.tp_new = PyType_GenericNew; + return gdbpy_type_ready (&colorpy_object_type, gdb_module); +} + +/* Color methods. */ + +static PyMethodDef color_methods[] = +{ + { "escape_sequence", colorpy_escape_sequence, METH_O, + "escape_sequence (is_foreground) -> str.\n\ +Return the ANSI escape sequence for this color.\n\ +IS_FOREGROUND indicates whether this is a foreground or background color."}, + {nullptr} +}; + +PyTypeObject colorpy_object_type = +{ + PyVarObject_HEAD_INIT (nullptr, 0) + "gdb.Color", /*tp_name*/ + sizeof (colorpy_object), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + 0, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + colorpy_str, /*tp_str*/ + get_attr, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "GDB color object", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + color_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + colorpy_init, /* tp_init */ + 0, /* tp_alloc */ +}; + +GDBPY_INITIALIZE_FILE (gdbpy_initialize_color); diff --git a/gdb/python/py-color.h b/gdb/python/py-color.h new file mode 100644 index 00000000000..a778d5ba06e --- /dev/null +++ b/gdb/python/py-color.h @@ -0,0 +1,35 @@ +/* Python interface to ui_file_style::color objects. + + Copyright (C) 2009-2024 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef PYTHON_PY_COLOR_H +#define PYTHON_PY_COLOR_H + +#include "python-internal.h" +#include "ui-style.h" + +/* Create a new gdb.Color object from COLOR. */ +extern gdbpy_ref<> create_color_object (const ui_file_style::color &color); + +/* Check if OBJ is instance of a gdb.Color type. */ +extern bool gdbpy_is_color (PyObject *obj); + +/* Extracts value from OBJ object of gdb.Color type. */ +extern const ui_file_style::color &gdbpy_get_color (PyObject *obj); + +#endif /* PYTHON_PY_COLOR_H */ diff --git a/gdb/python/py-param.c b/gdb/python/py-param.c index 9741782d3f1..0e79d2199f6 100644 --- a/gdb/python/py-param.c +++ b/gdb/python/py-param.c @@ -26,6 +26,7 @@ #include "completer.h" #include "language.h" #include "arch-utils.h" +#include "py-color.h" /* Python parameter types as in PARM_CONSTANTS below. */ @@ -43,6 +44,7 @@ enum py_param_types param_zuinteger, param_zuinteger_unlimited, param_enum, + param_color, }; /* Translation from Python parameters to GDB variable types. Keep in the @@ -69,7 +71,8 @@ param_to_var[] = { var_integer }, { var_uinteger }, { var_pinteger, pinteger_unlimited_literals }, - { var_enum } + { var_enum }, + { var_color } }; /* Parameter constants and their values. */ @@ -90,6 +93,7 @@ static struct { { "PARAM_ZUINTEGER", param_zuinteger }, { "PARAM_ZUINTEGER_UNLIMITED", param_zuinteger_unlimited }, { "PARAM_ENUM", param_enum }, + { "PARAM_COLOR", param_color }, { NULL, 0 } }; @@ -114,6 +118,9 @@ union parmpy_variable /* Hold a string, for enums. */ const char *cstringval; + + /* Hold a color. */ + ui_file_style::color color; }; /* A GDB parameter. */ @@ -157,6 +164,8 @@ make_setting (parmpy_object *s) return setting (type, s->value.stringval); else if (var_type_uses (type)) return setting (type, &s->value.cstringval); + else if (var_type_uses (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 (obj->type)) obj->value.stringval = new std::string; @@ -900,6 +932,8 @@ parmpy_dealloc (PyObject *obj) if (var_type_uses (parm_obj->type)) delete parm_obj->value.stringval; + else if (var_type_uses (parm_obj->type)) + parm_obj->value.color.~color(); } /* Initialize the 'parameters' module. */ diff --git a/gdb/python/python.c b/gdb/python/python.c index cc06526ffcf..aed7b8c0ae1 100644 --- a/gdb/python/python.c +++ b/gdb/python/python.c @@ -35,6 +35,7 @@ #include "location.h" #include "run-on-main-thread.h" #include "observable.h" +#include "py-color.h" #if GDB_SELF_TEST #include "gdbsupport/selftest.h" @@ -506,6 +507,12 @@ gdbpy_parameter_value (const setting &var) return host_string_to_python_string (str).release (); } + case var_color: + { + const ui_file_style::color &color = var.get (); + return create_color_object (color).release (); + } + case var_boolean: { if (var.get ()) diff --git a/gdb/testsuite/gdb.base/default.exp b/gdb/testsuite/gdb.base/default.exp index 628f0705b7f..a5fbebbbea5 100644 --- a/gdb/testsuite/gdb.base/default.exp +++ b/gdb/testsuite/gdb.base/default.exp @@ -15,8 +15,11 @@ # Start with a fresh gdb -gdb_exit -gdb_start +with_ansi_styling_terminal { + setenv TERM dumb + gdb_exit + gdb_start +} set prev_timeout $timeout set timeout 60 @@ -684,6 +687,7 @@ set show_conv_list \ {$_probe_arg10 = } \ {$_probe_arg11 = } \ {$_cimag = } \ + {$_colorsupport = "monochrome"} \ {$_creal = } \ {$_isvoid = } \ {$_shell = } \ diff --git a/gdb/testsuite/gdb.base/style.exp b/gdb/testsuite/gdb.base/style.exp index d29b2381923..14f6a0a8597 100644 --- a/gdb/testsuite/gdb.base/style.exp +++ b/gdb/testsuite/gdb.base/style.exp @@ -70,6 +70,9 @@ proc run_style_tests { } { global currently_disabled_style decimal hex with_ansi_styling_terminal { + setenv TERM xterm-256color + setenv COLORTERM truecolor + # Restart GDB with the correct TERM variable setting, this # means that GDB will enable styling. clean_restart_and_disable "restart 1" ${binfile} @@ -310,6 +313,21 @@ proc run_style_tests { } { set url [limited_style "http:.*html" file] gdb_test "show version" "${vers}.*<$url>.*" \ "'show version' is styled" + + if { $currently_disabled_style != "version" } { + # Check that colors in styling can be set as integer and as RGB hex + # triplet. Check that the version string is styled in the output of + # 'show version' according to the set colors. + gdb_test_no_output "set style version intensity normal" + gdb_test_no_output "set style version background 255" + gdb_test_no_output "set style version foreground #FED210" + gdb_test "show style version background" \ + "The \033\\\[38;2;254;210;16;48;5;255;22;27m.*\".*version.*\".*style.*\033\\\[m background color is: 255" \ + "Version's 256-color background style" + gdb_test "show style version foreground" \ + "The \033\\\[38;2;254;210;16;48;5;255;22;27m.*\".*version.*\".*style.*\033\\\[m foreground color is: #FED210" \ + "Version's TrueColor foreground style" + } } } @@ -486,6 +504,179 @@ proc test_startup_version_string { } { gdb_test "" "${vers}.*" "version is styled at startup" } +# Color support is disabled when TERM=dumb and COLORTERM="". +# All colors are approximated to none when set. +proc test_colorsupport_monochrome { } { + with_test_prefix "colorsupport_monochrome" { + with_ansi_styling_terminal { + setenv TERM dumb + gdb_exit + gdb_start + gdb_test "print \$_colorsupport" \ + "\\\$1 = \"monochrome\"" \ + "color support is monochrome" + gdb_test_no_output "set style enabled off" + gdb_test_no_output "set style filename foreground none" + gdb_test "show style filename foreground" \ + "The \"filename\" style foreground color is: none" \ + "none without approximation" + gdb_test_no_output "set style filename foreground blue" + gdb_test "show style filename foreground" \ + "The \"filename\" style foreground color is: none" \ + "blue approximated to none" + } + } +} + +# Color support is limited by 8 colors when TERM=ansi and COLORTERM="". +# All colors are approximated to basic colors when set. +proc test_colorsupport_8color { } { + with_test_prefix "colorsupport_8color" { + with_ansi_styling_terminal { + gdb_exit + gdb_start + gdb_test "print \$_colorsupport" \ + "\033\\\[.*m\\\$1\033\\\[m = \"monochrome,ansi_8color\"" \ + "color support is 8 color" + gdb_test_no_output "set style enabled off" + gdb_test_no_output "set style filename foreground none" + gdb_test "show style filename foreground" \ + "The \"filename\" style foreground color is: none" \ + "none without approximation" + gdb_test_no_output "set style filename foreground yellow" + gdb_test "show style filename foreground" \ + "The \"filename\" style foreground color is: yellow" \ + "yellow without approximation" + gdb_test_no_output "set style filename foreground 9" + gdb_test "show style filename foreground" \ + "The \"filename\" style foreground color is: red" \ + "9 approximated to red" + gdb_test_no_output "set style filename foreground 118" + gdb_test "show style filename foreground" \ + "The \"filename\" style foreground color is: green" \ + "118 approximated to green" + gdb_test_no_output "set style filename foreground #000ABC" + gdb_test "show style filename foreground" \ + "The \"filename\" style foreground color is: blue" \ + "#000ABC approximated to blue" + } + } +} + +# Color support is limited by 256 colors when TERM=xterm-256color and +# COLORTERM="". All colors are approximated by 256 color palette when set. +proc test_colorsupport_256color { } { + with_test_prefix "colorsupport_256color" { + with_ansi_styling_terminal { + setenv TERM xterm-256color + gdb_exit + gdb_start + gdb_test "print \$_colorsupport" \ + "\033\\\[.*m\\\$1\033\\\[m = \"monochrome,ansi_8color,aixterm_16color,xterm_256color\"" \ + "color support is 256 color" + gdb_test_no_output "set style enabled off" + gdb_test_no_output "set style filename foreground none" + gdb_test "show style filename foreground" \ + "The \"filename\" style foreground color is: none" \ + "none without approximation" + gdb_test_no_output "set style filename foreground red" + gdb_test "show style filename foreground" \ + "The \"filename\" style foreground color is: red" \ + "red without approximation" + gdb_test_no_output "set style filename foreground 9" + gdb_test "show style filename foreground" \ + "The \"filename\" style foreground color is: 9" \ + "9 without approximation" + gdb_test_no_output "set style filename foreground 118" + gdb_test "show style filename foreground" \ + "The \"filename\" style foreground color is: 118" \ + "118 without approximation" + gdb_test_no_output "set style filename foreground #CD00CD" + gdb_test "show style filename foreground" \ + "The \"filename\" style foreground color is: 5" \ + "#CD00CD approximated to 5" + gdb_test_no_output "set style filename foreground #FFAF12" + gdb_test "show style filename foreground" \ + "The \"filename\" style foreground color is: 214" \ + "#FFAF12 approximated to 214" + } + } +} + +# Color support is limited by 16777216 colors when TERM=xterm-256color and +# COLORTERM="truecolor". No approximation needed. +proc test_colorsupport_truecolor { } { + with_test_prefix "colorsupport_truecolor" { + with_ansi_styling_terminal { + setenv TERM xterm-256color + setenv COLORTERM truecolor + gdb_exit + gdb_start + gdb_test "print \$_colorsupport" \ + "\033\\\[.*m\\\$1\033\\\[m = \"monochrome,ansi_8color,aixterm_16color,xterm_256color,rgb_24bit\"" \ + "color support is truecolor" + gdb_test_no_output "set style enabled off" + gdb_test_no_output "set style filename foreground none" + gdb_test "show style filename foreground" \ + "The \"filename\" style foreground color is: none" \ + "none without approximation" + gdb_test_no_output "set style filename foreground red" + gdb_test "show style filename foreground" \ + "The \"filename\" style foreground color is: red" \ + "red without approximation" + gdb_test_no_output "set style filename foreground 9" + gdb_test "show style filename foreground" \ + "The \"filename\" style foreground color is: 9" \ + "9 without approximation" + gdb_test_no_output "set style filename foreground 118" + gdb_test "show style filename foreground" \ + "The \"filename\" style foreground color is: 118" \ + "118 without approximation" + gdb_test_no_output "set style filename foreground #CD00CD" + gdb_test "show style filename foreground" \ + "The \"filename\" style foreground color is: #CD00CD" \ + "#CD00CD without approximation" + } + } +} + +# Color support is limited by 16777216 colors when TERM=dumb and +# COLORTERM=24bit. No approximation needed. Basic colors replaced with RGB. +proc test_colorsupport_truecolor_only { } { + with_test_prefix "colorsupport_truecolor_only" { + with_ansi_styling_terminal { + setenv TERM dumb + setenv COLORTERM truecolor + gdb_exit + gdb_start + gdb_test "print \$_colorsupport" \ + "\\\$1 = \"monochrome,rgb_24bit\"" \ + "color support is truecolor only" + gdb_test_no_output "set style enabled off" + gdb_test_no_output "set style filename foreground none" + gdb_test "show style filename foreground" \ + "The \"filename\" style foreground color is: none" \ + "none without approximation" + gdb_test_no_output "set style filename foreground red" + gdb_test "show style filename foreground" \ + "The \"filename\" style foreground color is: #DE382B" \ + "red replaced by #DE382B" + gdb_test_no_output "set style filename foreground 9" + gdb_test "show style filename foreground" \ + "The \"filename\" style foreground color is: #FF0000" \ + "9 replaced by #FF0000" + gdb_test_no_output "set style filename foreground 118" + gdb_test "show style filename foreground" \ + "The \"filename\" style foreground color is: #87FF00" \ + "118 replaced by #87FF00" + gdb_test_no_output "set style filename foreground #CD00CD" + gdb_test "show style filename foreground" \ + "The \"filename\" style foreground color is: #CD00CD" \ + "#CD00CD without approximation" + } + } +} + # Check to see if the Python styling of disassembler output is # expected or not, this styling requires Python support in GDB, and # the Python pygments module to be available. @@ -520,3 +711,9 @@ test_disassembler_error_handling # Finally, check the styling of the version string during startup. test_startup_version_string + +test_colorsupport_monochrome +test_colorsupport_8color +test_colorsupport_256color +test_colorsupport_truecolor +test_colorsupport_truecolor_only diff --git a/gdb/testsuite/gdb.guile/scm-color.exp b/gdb/testsuite/gdb.guile/scm-color.exp new file mode 100644 index 00000000000..9fd7f38b495 --- /dev/null +++ b/gdb/testsuite/gdb.guile/scm-color.exp @@ -0,0 +1,110 @@ +# Copyright (C) 2010-2024 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# This file is part of the GDB testsuite. +# It tests GDB parameter support in Guile. + +load_lib gdb-guile.exp + +require allow_guile_tests + +clean_restart + +gdb_install_guile_utils +gdb_install_guile_module + +gdb_test_no_output [concat "guile (define (print_color_attrs c) " \ + "(display (color-string c)) (display \" \") " \ + "(display (color-colorspace c)) (display \" \") " \ + "(display (color-none? c)) (display \" \") " \ + "(display (color-indexed? c)) (display \" \") " \ + "(display (color-direct? c)) (newline))"] \ + "print_color_attrs helper" + +gdb_test "guile (print_color_attrs (make-color))" \ + "none 0 #t #f #f" \ + "print attrs of a color without params" + +gdb_test_no_output "guile (define c (make-color \"green\"))" \ + "create color from basic name string" +gdb_test "guile (print_color_attrs c)" "green 1 #f #t #f" \ + "print attrs of a basic color name" +gdb_test "guile (print (color-index c))" "2" \ + "print index of a basic color name" + +gdb_test_no_output "guile (define c (make-color 2))" \ + "create color from basic index" +gdb_test "guile (print_color_attrs c)" "green 1 #f #t #f" \ + "print attrs of a basic color" +gdb_test "guile (print (color-index c))" "2" \ + "print index of a basic color" + +gdb_test_no_output "guile (define c (make-color 14))" \ + "create color from integer 14" +gdb_test "guile (print_color_attrs c)" "14 2 #f #t #f" \ + "print attrs of an color 14" +gdb_test "guile (print (color-index c))" "14" \ + "print index of color 14" + +gdb_test_no_output "guile (define c (make-color 2 #:color-space COLORSPACE_ANSI_8COLOR))" \ + "create color from basic index and ansi colorspace" +gdb_test "guile (print_color_attrs c)" "green 1 #f #t #f" \ + "print attrs of a basic color with ansi colorspace" +gdb_test "guile (print (color-index c))" "2" \ + "print index of a basic color with ansi colorspace" + +gdb_test_no_output "guile (define c (make-color 2 #:color-space COLORSPACE_XTERM_256COLOR))" \ + "create color from basic index and xterm256 colorspace" +gdb_test "guile (print_color_attrs c)" "2 3 #f #t #f" \ + "print attrs of a basic color with xterm256 colorspace" +gdb_test "guile (print (color-index c))" "2" \ + "print index of a basic color with xterm256 colorspace" + +gdb_test_no_output "guile (define c (make-color '(171 205 239) #:color-space COLORSPACE_RGB_24BIT))" \ + "create color from rgb components" +gdb_test "guile (print_color_attrs c)" "#ABCDEF 4 #f #f #t" \ + "print attrs of an RGB color" +gdb_test "guile (print (color-components c))" "\\(171 205 239\\)" \ + "print components of an RGB color" + +gdb_test_no_output "guile (define c (make-color \"none\"))" \ + "create color from string none" +gdb_test "guile (print_color_attrs c)" "none 0 #t #f #f" \ + "print attrs of a color none" + +gdb_test_no_output "guile (define c (make-color \"254\"))" \ + "create color from string 254" +gdb_test "guile (print_color_attrs c)" "254 3 #f #t #f" \ + "print attrs of an color 254" +gdb_test "guile (print (color-index c))" "254" \ + "print index of color 254" + +gdb_test_no_output "guile (define c_none (make-color \"none\"))" \ + "save default color" +gdb_test_no_output "guile (define c_red (make-color \"red\"))" \ + "save blue color" +gdb_test_no_output "guile (define c_green (make-color \"green\"))" \ + "save yellow color" +gdb_test [concat "guile " \ + "(display (color-escape-sequence c_red #t)) " \ + "(display (color-escape-sequence c_green #f)) " \ + "(display \"red on green\") " \ + "(display (color-escape-sequence c_none #f)) " \ + "(display \" red on default\") " \ + "(display (color-escape-sequence c_none #t)) " \ + "(newline)"] \ + "\033\\\[31m\033\\\[42mred on green\033\\\[49m red on default\033\\\[39m" \ + "escape sequences" + diff --git a/gdb/testsuite/gdb.guile/scm-parameter.exp b/gdb/testsuite/gdb.guile/scm-parameter.exp index d10e9d24065..eabd17980e7 100644 --- a/gdb/testsuite/gdb.guile/scm-parameter.exp +++ b/gdb/testsuite/gdb.guile/scm-parameter.exp @@ -389,3 +389,50 @@ with_test_prefix "previously-ambiguous" { } rename scm_param_test_maybe_no_output "" + +# Test a color parameter. + +with_ansi_styling_terminal { + # This enables 256 colors support and disables colors approximation. + setenv TERM xterm-256color + setenv COLORTERM truecolor + + # Start with a fresh gdb. + gdb_exit + gdb_start + gdb_reinitialize_dir $srcdir/$subdir + + gdb_install_guile_utils + gdb_install_guile_module + + # We use "." here instead of ":" so that this works on win32 too. + set escaped_directory [string_to_regexp "$srcdir/$subdir"] + + gdb_test_multiline "color gdb parameter" \ + "guile" "" \ + "(define test-color-param" "" \ + " (make-parameter \"print test-color-param\"" "" \ + " #:command-class COMMAND_DATA" "" \ + " #:parameter-type PARAM_COLOR" "" \ + " #:doc \"When set, test param does something useful. When disabled, does nothing.\"" "" \ + " #:show-doc \"Show the state of the test-color-param.\"" "" \ + " #:set-doc \"Set the state of the test-color-param.\"" "" \ + " #:show-func (lambda (self value)" "" \ + " (format #f \"The state of the test-color-param is ~a.\" value))" "" \ + " #:initial-value (make-color \"green\")))" "" \ + "(register-parameter! test-color-param)" "" \ + "end" + + with_test_prefix "test-color-param" { + with_test_prefix "initial-value" { + gdb_test "guile (print (parameter-value test-color-param))" "= #" "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))" "= #" "color parameter value (255)" + gdb_test "set print test-color-param 256" "integer 256 out of range.*" "set invalid color parameter" + } + } +} diff --git a/gdb/testsuite/gdb.python/py-color.exp b/gdb/testsuite/gdb.python/py-color.exp new file mode 100644 index 00000000000..eb62d7f84a2 --- /dev/null +++ b/gdb/testsuite/gdb.python/py-color.exp @@ -0,0 +1,100 @@ +# Copyright (C) 2010-2024 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# This file is part of the GDB testsuite. +# It tests gdb.parameter and gdb.Parameter. + +load_lib gdb-python.exp + +require allow_python_tests + +# Start with a fresh gdb. +clean_restart + +gdb_test_no_output "python print_color_attrs = lambda c: print (c, c.colorspace, c.is_none, c.is_indexed, c.is_direct)" \ + "print_color_attrs helper" + +gdb_test_no_output "python c = gdb.Color ()" \ + "create color without params" +gdb_test "python print_color_attrs (c)" "none 0 True False False" \ + "print attrs of a color without params" + +gdb_test_no_output "python c = gdb.Color ('green')" \ + "create color from basic name string" +gdb_test "python print_color_attrs (c)" "green 1 False True False" \ + "print attrs of a basic color name" +gdb_test "python print (c.index)" "2" \ + "print index of a basic color name" + +gdb_test_no_output "python c = gdb.Color (2)" \ + "create color from basic index" +gdb_test "python print_color_attrs (c)" "green 1 False True False" \ + "print attrs of a basic color" +gdb_test "python print (c.index)" "2" \ + "print index of a basic color" + +gdb_test_no_output "python c = gdb.Color (14)" \ + "create color from integer 14" +gdb_test "python print_color_attrs (c)" "14 2 False True False" \ + "print attrs of an color 14" +gdb_test "python print (c.index)" "14" \ + "print index of color 14" + +gdb_test_no_output "python c = gdb.Color (2, gdb.COLORSPACE_ANSI_8COLOR)" \ + "create color from basic index and ansi colorspace" +gdb_test "python print_color_attrs (c)" "green 1 False True False" \ + "print attrs of a basic color with ansi colorspace" +gdb_test "python print (c.index)" "2" \ + "print index of a basic color with ansi colorspace" + +gdb_test_no_output "python c = gdb.Color (2, gdb.COLORSPACE_XTERM_256COLOR)" \ + "create color from basic index and xterm256 colorspace" +gdb_test "python print_color_attrs (c)" "2 3 False True False" \ + "print attrs of a basic color with xterm256 colorspace" +gdb_test "python print (c.index)" "2" \ + "print index of a basic color with xterm256 colorspace" + +gdb_test_no_output "python c = gdb.Color ((171, 205, 239), gdb.COLORSPACE_RGB_24BIT)" \ + "create color from rgb components" +gdb_test "python print_color_attrs (c)" "#ABCDEF 4 False False True" \ + "print attrs of an RGB color" +gdb_test "python print (c.components)" "\\(171, 205, 239\\)" \ + "print components of an RGB color" + +gdb_test_no_output "python c = gdb.Color ('none')" \ + "create color from string none" +gdb_test "python print_color_attrs (c)" "none 0 True False False" \ + "print attrs of a color none" + +gdb_test_no_output "python c = gdb.Color ('254')" \ + "create color from string 254" +gdb_test "python print_color_attrs (c)" "254 3 False True False" \ + "print attrs of an color 254" +gdb_test "python print (c.index)" "254" \ + "print index of color 254" + +gdb_test_no_output "python c_none = gdb.Color ('none')" \ + "save default color" +gdb_test_no_output "python c_red = gdb.Color ('red')" \ + "save blue color" +gdb_test_no_output "python c_green = gdb.Color ('green')" \ + "save yellow color" +gdb_test [concat "python print (c_red.escape_sequence (True) + " \ + "c_green.escape_sequence (False) + 'red on green' + " \ + "c_none.escape_sequence (False) + ' red on default' + " \ + "c_none.escape_sequence (True))"] \ + "\033\\\[31m\033\\\[42mred on green\033\\\[49m red on default\033\\\[39m" \ + "escape sequences" + diff --git a/gdb/testsuite/gdb.python/py-parameter.exp b/gdb/testsuite/gdb.python/py-parameter.exp index de524f49ad6..74e4178bf35 100644 --- a/gdb/testsuite/gdb.python/py-parameter.exp +++ b/gdb/testsuite/gdb.python/py-parameter.exp @@ -185,6 +185,58 @@ proc_with_prefix test_enum_parameter { } { "Undefined item: \"three\".*" "set invalid enum parameter" } +# Test an color parameter. +proc_with_prefix test_color_parameter { } { + global env + with_ansi_styling_terminal { + # This enables 256 colors support and disables colors approximation. + setenv TERM xterm-256color + setenv COLORTERM truecolor + + clean_restart + + gdb_test_multiline "color gdb parameter" \ + "python" "" \ + "class TestColorParam (gdb.Parameter):" "" \ + " \"\"\"When set, test param does something useful. When disabled, does nothing.\"\"\"" "" \ + " show_doc = \"Show the state of the color\"" ""\ + " set_doc = \"Set the state of the color\"" "" \ + " def get_show_string (self, pvalue):" ""\ + " return \"The state of the color is \" + str(pvalue)" ""\ + " def get_set_string (self):" ""\ + " return \"The state of the color has been set to \" + str(self.value)" ""\ + " def __init__ (self, name):" "" \ + " super (TestColorParam, self).__init__ (name, gdb.COMMAND_DATA, gdb.PARAM_COLOR)" "" \ + " self.value = gdb.Color(\"green\")" "" \ + "test_color_param = TestColorParam ('print test-color-param')" ""\ + "end" + + gdb_test "python print (test_color_param.value)" "green" \ + "test color parameter value is green" + gdb_test "show print test-color-param" \ + "The state of the color is green.*" \ + "show parameter is initial value" + gdb_test "set print test-color-param 255" \ + "The state of the color has been set to 255" "set color to 255" + gdb_test "show print test-color-param" \ + "The state of the color is 255.*" "show parameter is new value" + gdb_test "python print (test_color_param.value)" "255" \ + "test color parameter value is 255" + gdb_test_no_output "python test_color_param.value = gdb.Color(254)" \ + "assign test_color_param.value to 254" + gdb_test "python print (test_color_param.value)" "254" \ + "test color parameter value is integer" + gdb_test_no_output "python test_color_param.value = gdb.Color('#FED210')" \ + "assign test_color_param.value to #FED210" + gdb_test "python print (test_color_param.value.components)" "\\(254, 210, 16\\)" \ + "test color parameter components from RGB hex tripple value" + gdb_test "set print test-color-param 256" \ + "integer 256 out of range.*" "set invalid color parameter" + gdb_test "python test_color_param.value = gdb.Color(256)" \ + ".*Error occurred in Python: Palette color index 256 is out of range.*" "set invalid color value" + } +} + # Test a file parameter. proc_with_prefix test_file_parameter { } { clean_restart @@ -623,6 +675,7 @@ test_directories test_data_directory test_boolean_parameter test_enum_parameter +test_color_parameter test_file_parameter test_undocumented_parameter test_really_undocumented_parameter diff --git a/gdb/testsuite/lib/gdb-utils.exp b/gdb/testsuite/lib/gdb-utils.exp index a1fdf7381a1..52e73a3d9fd 100644 --- a/gdb/testsuite/lib/gdb-utils.exp +++ b/gdb/testsuite/lib/gdb-utils.exp @@ -67,19 +67,23 @@ proc string_list_to_regexp { args } { # "function", "variable", "address", etc. proc style {str style} { + set fg 39 + set bg 49 + set intensity 22 + set reverse 27 switch -exact -- $style { - title { set style 1 } - file { set style 32 } - function { set style 33 } - highlight { set style 31 } - variable { set style 36 } - address { set style 34 } - metadata { set style 2 } - version { set style "35;1" } - line-number { set style 2 } + title { set intensity 1 } + file { set fg 32 } + function { set fg 33 } + highlight { set fg 31 } + variable { set fg 36 } + address { set fg 34 } + metadata { set intensity 2 } + version { set fg 35; set intensity 1 } + line-number { set intensity 2 } none { return $str } } - return "\033\\\[${style}m${str}\033\\\[m" + return "\033\\\[${fg};${bg};${intensity};${reverse}m${str}\033\\\[m" } # gdb_get_bp_addr num diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp index 738cd2fe9c6..f7dcec8a597 100644 --- a/gdb/testsuite/lib/gdb.exp +++ b/gdb/testsuite/lib/gdb.exp @@ -10126,10 +10126,11 @@ proc with_override { name override body } { # Run BODY after setting the TERM environment variable to 'ansi', and # unsetting the NO_COLOR environment variable. proc with_ansi_styling_terminal { body } { - save_vars { ::env(TERM) ::env(NO_COLOR) } { + save_vars { ::env(TERM) ::env(NO_COLOR) ::env(COLORTERM) } { # Set environment variables to allow styling. setenv TERM ansi unset -nocomplain ::env(NO_COLOR) + unset -nocomplain ::env(COLORTERM) set code [catch {uplevel 1 $body} result] } diff --git a/gdb/top.c b/gdb/top.c index eae54aae1ff..0352997dc93 100644 --- a/gdb/top.c +++ b/gdb/top.c @@ -2112,6 +2112,17 @@ show_startup_quiet (struct ui_file *file, int from_tty, value); } +static void +init_colorsupport_var () +{ + const std::vector &cs = colorsupport (); + std::string s; + for (color_space c : cs) + s.append (s.empty () ? "" : ",").append (color_space_name (c)); + struct internalvar *colorsupport_var = create_internalvar ("_colorsupport"); + set_internalvar_string (colorsupport_var, s.c_str ()); +} + static void init_main (void) { @@ -2316,6 +2327,9 @@ gdb_init () during startup. */ set_language (language_c); expected_language = current_language; /* Don't warn about the change. */ + + /* Create $_colorsupport convenience variable. */ + init_colorsupport_var (); } void _initialize_top (); diff --git a/gdb/ui-style.c b/gdb/ui-style.c index 952102e30c3..f0a8e81d637 100644 --- a/gdb/ui-style.c +++ b/gdb/ui-style.c @@ -17,6 +17,7 @@ along with this program. If not, see . */ #include "ui-style.h" +#include "gdb_curses.h" #include "gdbsupport/gdb_regex.h" /* A regular expression that is used for matching ANSI terminal escape @@ -46,119 +47,250 @@ static const char ansi_regex_text[] = static regex_t ansi_regex; -/* This maps bright colors to RGB triples. The index is the bright - color index, starting with bright black. The values come from - xterm. */ - -static const uint8_t bright_colors[][3] = { - { 127, 127, 127 }, /* Black. */ - { 255, 0, 0 }, /* Red. */ - { 0, 255, 0 }, /* Green. */ - { 255, 255, 0 }, /* Yellow. */ - { 92, 92, 255 }, /* Blue. */ - { 255, 0, 255 }, /* Magenta. */ - { 0, 255, 255 }, /* Cyan. */ - { 255, 255, 255 } /* White. */ +/* This maps 8-color palette to RGB triples. The values come from + plain linux terminal. */ + +static const uint8_t palette_8colors[][3] = { + { 1, 1, 1 }, /* Black. */ + { 222, 56, 43 }, /* Red. */ + { 57, 181, 74 }, /* Green. */ + { 255, 199, 6 }, /* Yellow. */ + { 0, 111, 184 }, /* Blue. */ + { 118, 38, 113 }, /* Magenta. */ + { 44, 181, 233 }, /* Cyan. */ + { 204, 204, 204 }, /* White. */ +}; + +/* This maps 16-color palette to RGB triples. The values come from xterm. */ + +static const uint8_t palette_16colors[][3] = { + { 0, 0, 0 }, /* Black. */ + { 205, 0, 0 }, /* Red. */ + { 0, 205, 0 }, /* Green. */ + { 205, 205, 0 }, /* Yellow. */ + { 0, 0, 238 }, /* Blue. */ + { 205, 0, 205 }, /* Magenta. */ + { 0, 205, 205 }, /* Cyan. */ + { 229, 229, 229 }, /* White. */ + { 127, 127, 127 }, /* Bright Black. */ + { 255, 0, 0 }, /* Bright Red. */ + { 0, 255, 0 }, /* Bright Green. */ + { 255, 255, 0 }, /* Bright Yellow. */ + { 92, 92, 255 }, /* Bright Blue. */ + { 255, 0, 255 }, /* Bright Magenta. */ + { 0, 255, 255 }, /* Bright Cyan. */ + { 255, 255, 255 } /* Bright White. */ }; /* See ui-style.h. */ +/* Must correspond to ui_file_style::basic_color. */ +const std::vector ui_file_style::basic_color_enums = { + "none", + "black", + "red", + "green", + "yellow", + "blue", + "magenta", + "cyan", + "white", + nullptr +}; -bool +/* Returns text representation of a basic COLOR. */ + +static const char * +basic_color_name (int color) +{ + int pos = color - ui_file_style::NONE; + if (0 <= pos && pos < ui_file_style::basic_color_enums.size ()) + if (const char *s = ui_file_style::basic_color_enums[pos]) + return s; + error (_("Basic color %d has no name."), color); +} + +/* See ui-style.h. */ + +void ui_file_style::color::append_ansi (bool is_fg, std::string *str) const { - if (m_simple) - { - if (m_value >= BLACK && m_value <= WHITE) - str->append (std::to_string (m_value + (is_fg ? 30 : 40))); - else if (m_value > WHITE && m_value <= WHITE + 8) - str->append (std::to_string (m_value - WHITE + (is_fg ? 90 : 100))); - else if (m_value != -1) - { - str->append (is_fg ? "38;5;" : "48;5;"); - str->append (std::to_string (m_value)); - } - else - return false; - } - else + if (m_color_space == color_space::MONOCHROME) + str->append (is_fg ? "39" : "49"); + else if (is_basic ()) + str->append (std::to_string (m_value + (is_fg ? 30 : 40))); + else if (m_color_space == color_space::AIXTERM_16COLOR) + str->append (std::to_string (m_value - WHITE - 1 + (is_fg ? 90 : 100))); + else if (m_color_space == color_space::XTERM_256COLOR) + str->append (is_fg ? "38;5;" : "48;5;").append (std::to_string (m_value)); + else if (m_color_space == color_space::RGB_24BIT) { + // See ISO/IEC 8613-6 (or ITU T.416) 13.1.8 Select Graphic Rendition (SGR) str->append (is_fg ? "38;2;" : "48;2;"); str->append (std::to_string (m_red) + ";" + std::to_string (m_green) + ";" + std::to_string (m_blue)); } - return true; + else + gdb_assert_not_reached ("no valid ansi representation of the color"); } /* See ui-style.h. */ +std::string +ui_file_style::color::to_ansi (bool is_fg) const +{ + std::string s = "\033["; + append_ansi (is_fg, &s); + s.push_back ('m'); + return s; +} -void -ui_file_style::color::get_rgb (uint8_t *rgb) const +/* See ui-style.h. */ + +std::string +ui_file_style::color::to_string () const { - if (m_simple) + if (m_color_space == color_space::RGB_24BIT) { - /* Can't call this for a basic color or NONE -- those will end - up in the assert below. */ - if (m_value >= 8 && m_value <= 15) - memcpy (rgb, bright_colors[m_value - 8], 3 * sizeof (uint8_t)); - else if (m_value >= 16 && m_value <= 231) - { - int value = m_value; - value -= 16; - /* This obscure formula seems to be what terminals actually - do. */ - int component = value / 36; - rgb[0] = component == 0 ? 0 : (55 + component * 40); - value %= 36; - component = value / 6; - rgb[1] = component == 0 ? 0 : (55 + component * 40); - value %= 6; - rgb[2] = value == 0 ? 0 : (55 + value * 40); - } - else if (m_value >= 232) - { - uint8_t v = (m_value - 232) * 10 + 8; - rgb[0] = v; - rgb[1] = v; - rgb[2] = v; - } - else - gdb_assert_not_reached ("get_rgb called on invalid color"); + char s[64]; + snprintf (s, sizeof s, "#%02X%02X%02X", m_red, m_green, m_blue); + return s; } + else if (is_none () || is_basic ()) + return basic_color_name (m_value); else + return std::to_string (get_value ()); +} + +/* See ui-style.h. */ + +void +ui_file_style::color::get_rgb (uint8_t *rgb) const +{ + if (m_color_space == color_space::RGB_24BIT) { rgb[0] = m_red; rgb[1] = m_green; rgb[2] = m_blue; } + else if (m_color_space == color_space::ANSI_8COLOR + && 0 <= m_value && m_value <= 7) + memcpy (rgb, palette_8colors[m_value], 3 * sizeof (uint8_t)); + else if (m_color_space == color_space::AIXTERM_16COLOR + && 0 <= m_value && m_value <= 15) + memcpy (rgb, palette_16colors[m_value], 3 * sizeof (uint8_t)); + else if (m_color_space != color_space::XTERM_256COLOR) + gdb_assert_not_reached ("get_rgb called on invalid color"); + else if (0 <= m_value && m_value <= 15) + memcpy (rgb, palette_16colors[m_value], 3 * sizeof (uint8_t)); + else if (m_value >= 16 && m_value <= 231) + { + int value = m_value; + value -= 16; + /* This obscure formula seems to be what terminals actually + do. */ + int component = value / 36; + rgb[0] = component == 0 ? 0 : (55 + component * 40); + value %= 36; + component = value / 6; + rgb[1] = component == 0 ? 0 : (55 + component * 40); + value %= 6; + rgb[2] = value == 0 ? 0 : (55 + value * 40); + } + else if (232 <= m_value && m_value <= 255) + { + uint8_t v = (m_value - 232) * 10 + 8; + rgb[0] = v; + rgb[1] = v; + rgb[2] = v; + } + else + gdb_assert_not_reached ("get_rgb called on invalid color"); } /* See ui-style.h. */ -std::string -ui_file_style::to_ansi () const +ui_file_style::color +ui_file_style::color::approximate (const std::vector &spaces) const { - std::string result ("\033["); - bool need_semi = m_foreground.append_ansi (true, &result); - if (!m_background.is_none ()) + if (spaces.empty () || is_none ()) + return NONE; + + color_space target_space = color_space::MONOCHROME; + for (color_space sp : spaces) + if (sp == m_color_space) + return *this; + else if (sp > target_space) + target_space = sp; + + if (target_space == color_space::RGB_24BIT) { - if (need_semi) - result.push_back (';'); - m_background.append_ansi (false, &result); - need_semi = true; + uint8_t rgb[3]; + get_rgb (rgb); + return color (rgb[0], rgb[1], rgb[2]); + } + + int target_size = 0; + switch (target_space) + { + case color_space::ANSI_8COLOR: + target_size = 8; + break; + case color_space::AIXTERM_16COLOR: + target_size = 16; + break; + case color_space::XTERM_256COLOR: + target_size = 256; + break; } - if (m_intensity != NORMAL) + + if (is_simple() && m_value < target_size) + return color (target_space, m_value); + + color result = NONE; + int best_distance = std::numeric_limits::max (); + uint8_t rgb[3]; + get_rgb (rgb); + + for (int i = 0; i < target_size; ++i) { - if (need_semi) - result.push_back (';'); - result.append (std::to_string (m_intensity)); - need_semi = true; + uint8_t c_rgb[3]; + color c (target_space, i); + c.get_rgb (c_rgb); + int d_red = std::abs (rgb[0] - c_rgb[0]); + int d_green = std::abs (rgb[1] - c_rgb[1]); + int d_blue = std::abs (rgb[2] - c_rgb[2]); + int dist = d_red * d_red + d_green * d_green + d_blue * d_blue; + if (dist < best_distance) + { + best_distance = dist; + result = c; + } } - if (m_reverse) + + return result; +} + +/* See ui-style.h. */ + +std::string +ui_file_style::to_ansi () const +{ + std::string result ("\033["); + if (!is_default ()) { - if (need_semi) - result.push_back (';'); - result.push_back ('7'); + m_foreground.append_ansi (true, &result); + result.push_back (';'); + m_background.append_ansi (false, &result); + result.push_back (';'); + if (m_intensity == NORMAL) + result.append ("22"); + else + result.append (std::to_string (m_intensity)); + result.push_back (';'); + if (m_reverse) + result.push_back ('7'); + else + result.append ("27"); } result.push_back ('m'); return result; @@ -315,9 +447,11 @@ ui_file_style::parse (const char *buf, size_t *n_read) case 35: case 36: case 37: + m_foreground = color (value - 30); + break; /* Note: not 38. */ case 39: - m_foreground = color (value - 30); + m_foreground = NONE; break; case 40: @@ -328,9 +462,11 @@ ui_file_style::parse (const char *buf, size_t *n_read) case 45: case 46: case 47: + m_background = color (value - 40); + break; /* Note: not 48. */ case 49: - m_background = color (value - 40); + m_background = NONE; break; case 90: @@ -412,3 +548,60 @@ _initialize_ui_style () error. */ gdb_assert (code == 0); } + +/* See ui-style.h. */ + +const std::vector & +colorsupport () +{ + static const std::vector value = [] + { + std::vector 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(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(c); + return true; + } + return false; +} diff --git a/gdb/ui-style.h b/gdb/ui-style.h index 1b7b5fafb9d..0a1b87d5137 100644 --- a/gdb/ui-style.h +++ b/gdb/ui-style.h @@ -19,6 +19,38 @@ #ifndef UI_STYLE_H #define UI_STYLE_H +/* One of the color spaces that usually supported by terminals. */ +enum class color_space +{ + /* one default terminal color */ + MONOCHROME, + + /* foreground colors \e[30m ... \e[37m, + background colors \e[40m ... \e[47m */ + ANSI_8COLOR, + + /* foreground colors \e[30m ... \e[37m, \e[90m ... \e[97m + background colors \e[40m ... \e[47m, \e[100m ... \e107m */ + AIXTERM_16COLOR, + + /* foreground colors \e[38;5;0m ... \e[38;5;255m + background colors \e[48;5;0m ... \e[48;5;255m */ + XTERM_256COLOR, + + /* foreground colors \e[38;2;0;0;0m ... \e[38;2;255;255;255m + background colors \e[48;2;0;0;0m ... \e[48;2;255;255;255m */ + RGB_24BIT +}; + +/* Color spaces supported by terminal. */ +extern const std::vector & 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 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 (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 (cs)); } color (uint8_t r, uint8_t g, uint8_t b) - : m_simple (false), + : m_color_space (color_space::RGB_24BIT), m_red (r), m_green (g), m_blue (b) @@ -65,19 +138,24 @@ struct ui_file_style bool operator== (const color &other) const { - if (m_simple != other.m_simple) + if (m_color_space != other.m_color_space) return false; - if (m_simple) + if (is_simple ()) return m_value == other.m_value; return (m_red == other.m_red && m_green == other.m_green && m_blue == other.m_blue); } + bool operator!= (const color &other) const + { + return ! (*this == other); + } + bool operator< (const color &other) const { - if (m_simple != other.m_simple) - return m_simple < other.m_simple; - if (m_simple) + if (m_color_space != other.m_color_space) + return m_color_space < other.m_color_space; + if (is_simple ()) return m_value < other.m_value; if (m_red < other.m_red) return true; @@ -91,23 +169,54 @@ struct ui_file_style return false; } + color_space colorspace () const + { + return m_color_space; + } + /* Return true if this is the "NONE" color, false otherwise. */ bool is_none () const { - return m_simple && m_value == NONE; + return m_color_space == color_space::MONOCHROME && m_value == NONE; } /* Return true if this is one of the basic colors, false otherwise. */ bool is_basic () const { - return m_simple && m_value >= BLACK && m_value <= WHITE; + if (m_color_space == color_space::ANSI_8COLOR + || m_color_space == color_space::AIXTERM_16COLOR) + return BLACK <= m_value && m_value <= WHITE; + else + return false; + } + + /* Return true if this is one of the colors, stored as int, false + otherwise. */ + bool is_simple () const + { + return m_color_space != color_space::RGB_24BIT; + } + + /* Return true if this is one of the indexed colors, false + otherwise. */ + bool is_indexed () const + { + return m_color_space != color_space::RGB_24BIT + && m_color_space != color_space::MONOCHROME; + } + + /* Return true if this is one of the direct colors (RGB, CMY, CMYK), false + otherwise. */ + bool is_direct () const + { + return m_color_space == color_space::RGB_24BIT; } - /* Return the value of a basic color. */ + /* Return the value of a simple color. */ int get_value () const { - gdb_assert (is_basic ()); + gdb_assert (is_simple ()); return m_value; } @@ -118,14 +227,23 @@ struct ui_file_style /* Append the ANSI terminal escape sequence for this color to STR. IS_FG indicates whether this is a foreground or background - color. Returns true if any characters were written; returns - false otherwise (which can only happen for the "NONE" - color). */ - bool append_ansi (bool is_fg, std::string *str) const; + color. */ + void append_ansi (bool is_fg, std::string *str) const; + + /* Return the ANSI escape sequence for this color. + IS_FG indicates whether this is a foreground or background color. */ + std::string to_ansi (bool is_fg) const; + + /* Returns text representation of this object. + It is "none", name of a basic color, number or a #RRGGBB hex triplet. */ + std::string to_string () const; + + /* Approximates THIS color by closest one from SPACES. */ + color approximate (const std::vector &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 basic_color_enums; + private: color m_foreground = NONE; diff --git a/gdb/unittests/style-selftests.c b/gdb/unittests/style-selftests.c index 4dc3c842c47..72bc878582a 100644 --- a/gdb/unittests/style-selftests.c +++ b/gdb/unittests/style-selftests.c @@ -58,7 +58,7 @@ run_tests () SELF_CHECK (style.get_background ().is_none ()); SELF_CHECK (style.get_intensity () == ui_file_style::NORMAL); SELF_CHECK (style.is_reverse ()); - SELF_CHECK (style.to_ansi () == "\033[7m"); + SELF_CHECK (style.to_ansi () == "\033[39;49;22;7m"); style = ui_file_style (); SELF_CHECK (style.parse ("\033[32;1m", &n_read)); @@ -68,7 +68,7 @@ run_tests () SELF_CHECK (style.get_background ().is_none ()); SELF_CHECK (style.get_intensity () == ui_file_style::BOLD); SELF_CHECK (!style.is_reverse ()); - SELF_CHECK (style.to_ansi () == "\033[32;1m"); + SELF_CHECK (style.to_ansi () == "\033[32;49;1;27m"); style = ui_file_style (); SELF_CHECK (style.parse ("\033[38;5;112;48;5;249m", &n_read)); @@ -81,7 +81,7 @@ run_tests () CHECK_RGB (0xb2, 0xb2, 0xb2); SELF_CHECK (style.get_intensity () == ui_file_style::NORMAL); SELF_CHECK (!style.is_reverse ()); - SELF_CHECK (style.to_ansi () == "\033[38;5;112;48;5;249m"); + SELF_CHECK (style.to_ansi () == "\033[38;5;112;48;5;249;22;27m"); style = ui_file_style (); SELF_CHECK (style.parse ("\033[38;2;83;84;85;48;2;0;1;254;2;7m", &n_read));