aboutsummaryrefslogtreecommitdiff
path: root/gdb/ui-style.c
diff options
context:
space:
mode:
authorAndrei Pikas <gdb@mail.api.win>2024-10-05 22:27:44 +0300
committerTom Tromey <tom@tromey.com>2025-01-12 13:30:43 -0700
commit6447969d0ac774b6dec0f95a0d3d27c27d158690 (patch)
treee9812cfdd956f4c8e89f596b276ddc1c4ad8da45 /gdb/ui-style.c
parent338e0b05d8f2dd404eb0015bee31461dfe5ba307 (diff)
downloadbinutils-6447969d0ac774b6dec0f95a0d3d27c27d158690.zip
binutils-6447969d0ac774b6dec0f95a0d3d27c27d158690.tar.gz
binutils-6447969d0ac774b6dec0f95a0d3d27c27d158690.tar.bz2
Add an option with a color type.
Colors can be specified as "none" for terminal's default color, as a name of one of the eight standard colors of ISO/IEC 6429 "black", "red", "green", etc., as an RGB hexadecimal tripplet #RRGGBB for 24-bit TrueColor, or as an integer from 0 to 255. Integers 0 to 7 are the synonyms for the standard colors. Integers 8-15 are used for the so-called bright colors from the aixterm extended 16-color palette. Integers 16-255 are the indexes into xterm extended 256-color palette (usually 6x6x6 cube plus gray ramp). In general, 256-color palette is terminal dependent and sometimes can be changed with OSC 4 sequences, e.g. "\033]4;1;rgb:00/FF/00\033\\". It is the responsibility of the user to verify that the terminal supports the specified colors. PATCH v5 changes: documentation fixed. PATCH v6 changes: documentation fixed. PATCH v7 changes: rebase onto master and fixes after review. PATCH v8 changes: fixes after review.
Diffstat (limited to 'gdb/ui-style.c')
-rw-r--r--gdb/ui-style.c353
1 files changed, 273 insertions, 80 deletions
diff --git a/gdb/ui-style.c b/gdb/ui-style.c
index 952102e..f0a8e81 100644
--- a/gdb/ui-style.c
+++ b/gdb/ui-style.c
@@ -17,6 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include "ui-style.h"
+#include "gdb_curses.h"
#include "gdbsupport/gdb_regex.h"
/* A regular expression that is used for matching ANSI terminal escape
@@ -46,119 +47,250 @@ static const char ansi_regex_text[] =
static regex_t ansi_regex;
-/* This maps bright colors to RGB triples. The index is the bright
- color index, starting with bright black. The values come from
- xterm. */
-
-static const uint8_t bright_colors[][3] = {
- { 127, 127, 127 }, /* Black. */
- { 255, 0, 0 }, /* Red. */
- { 0, 255, 0 }, /* Green. */
- { 255, 255, 0 }, /* Yellow. */
- { 92, 92, 255 }, /* Blue. */
- { 255, 0, 255 }, /* Magenta. */
- { 0, 255, 255 }, /* Cyan. */
- { 255, 255, 255 } /* White. */
+/* This maps 8-color palette to RGB triples. The values come from
+ plain linux terminal. */
+
+static const uint8_t palette_8colors[][3] = {
+ { 1, 1, 1 }, /* Black. */
+ { 222, 56, 43 }, /* Red. */
+ { 57, 181, 74 }, /* Green. */
+ { 255, 199, 6 }, /* Yellow. */
+ { 0, 111, 184 }, /* Blue. */
+ { 118, 38, 113 }, /* Magenta. */
+ { 44, 181, 233 }, /* Cyan. */
+ { 204, 204, 204 }, /* White. */
+};
+
+/* This maps 16-color palette to RGB triples. The values come from xterm. */
+
+static const uint8_t palette_16colors[][3] = {
+ { 0, 0, 0 }, /* Black. */
+ { 205, 0, 0 }, /* Red. */
+ { 0, 205, 0 }, /* Green. */
+ { 205, 205, 0 }, /* Yellow. */
+ { 0, 0, 238 }, /* Blue. */
+ { 205, 0, 205 }, /* Magenta. */
+ { 0, 205, 205 }, /* Cyan. */
+ { 229, 229, 229 }, /* White. */
+ { 127, 127, 127 }, /* Bright Black. */
+ { 255, 0, 0 }, /* Bright Red. */
+ { 0, 255, 0 }, /* Bright Green. */
+ { 255, 255, 0 }, /* Bright Yellow. */
+ { 92, 92, 255 }, /* Bright Blue. */
+ { 255, 0, 255 }, /* Bright Magenta. */
+ { 0, 255, 255 }, /* Bright Cyan. */
+ { 255, 255, 255 } /* Bright White. */
};
/* See ui-style.h. */
+/* Must correspond to ui_file_style::basic_color. */
+const std::vector<const char *> ui_file_style::basic_color_enums = {
+ "none",
+ "black",
+ "red",
+ "green",
+ "yellow",
+ "blue",
+ "magenta",
+ "cyan",
+ "white",
+ nullptr
+};
-bool
+/* Returns text representation of a basic COLOR. */
+
+static const char *
+basic_color_name (int color)
+{
+ int pos = color - ui_file_style::NONE;
+ if (0 <= pos && pos < ui_file_style::basic_color_enums.size ())
+ if (const char *s = ui_file_style::basic_color_enums[pos])
+ return s;
+ error (_("Basic color %d has no name."), color);
+}
+
+/* See ui-style.h. */
+
+void
ui_file_style::color::append_ansi (bool is_fg, std::string *str) const
{
- if (m_simple)
- {
- if (m_value >= BLACK && m_value <= WHITE)
- str->append (std::to_string (m_value + (is_fg ? 30 : 40)));
- else if (m_value > WHITE && m_value <= WHITE + 8)
- str->append (std::to_string (m_value - WHITE + (is_fg ? 90 : 100)));
- else if (m_value != -1)
- {
- str->append (is_fg ? "38;5;" : "48;5;");
- str->append (std::to_string (m_value));
- }
- else
- return false;
- }
- else
+ if (m_color_space == color_space::MONOCHROME)
+ str->append (is_fg ? "39" : "49");
+ else if (is_basic ())
+ str->append (std::to_string (m_value + (is_fg ? 30 : 40)));
+ else if (m_color_space == color_space::AIXTERM_16COLOR)
+ str->append (std::to_string (m_value - WHITE - 1 + (is_fg ? 90 : 100)));
+ else if (m_color_space == color_space::XTERM_256COLOR)
+ str->append (is_fg ? "38;5;" : "48;5;").append (std::to_string (m_value));
+ else if (m_color_space == color_space::RGB_24BIT)
{
+ // See ISO/IEC 8613-6 (or ITU T.416) 13.1.8 Select Graphic Rendition (SGR)
str->append (is_fg ? "38;2;" : "48;2;");
str->append (std::to_string (m_red)
+ ";" + std::to_string (m_green)
+ ";" + std::to_string (m_blue));
}
- return true;
+ else
+ gdb_assert_not_reached ("no valid ansi representation of the color");
}
/* See ui-style.h. */
+std::string
+ui_file_style::color::to_ansi (bool is_fg) const
+{
+ std::string s = "\033[";
+ append_ansi (is_fg, &s);
+ s.push_back ('m');
+ return s;
+}
-void
-ui_file_style::color::get_rgb (uint8_t *rgb) const
+/* See ui-style.h. */
+
+std::string
+ui_file_style::color::to_string () const
{
- if (m_simple)
+ if (m_color_space == color_space::RGB_24BIT)
{
- /* Can't call this for a basic color or NONE -- those will end
- up in the assert below. */
- if (m_value >= 8 && m_value <= 15)
- memcpy (rgb, bright_colors[m_value - 8], 3 * sizeof (uint8_t));
- else if (m_value >= 16 && m_value <= 231)
- {
- int value = m_value;
- value -= 16;
- /* This obscure formula seems to be what terminals actually
- do. */
- int component = value / 36;
- rgb[0] = component == 0 ? 0 : (55 + component * 40);
- value %= 36;
- component = value / 6;
- rgb[1] = component == 0 ? 0 : (55 + component * 40);
- value %= 6;
- rgb[2] = value == 0 ? 0 : (55 + value * 40);
- }
- else if (m_value >= 232)
- {
- uint8_t v = (m_value - 232) * 10 + 8;
- rgb[0] = v;
- rgb[1] = v;
- rgb[2] = v;
- }
- else
- gdb_assert_not_reached ("get_rgb called on invalid color");
+ char s[64];
+ snprintf (s, sizeof s, "#%02X%02X%02X", m_red, m_green, m_blue);
+ return s;
}
+ else if (is_none () || is_basic ())
+ return basic_color_name (m_value);
else
+ return std::to_string (get_value ());
+}
+
+/* See ui-style.h. */
+
+void
+ui_file_style::color::get_rgb (uint8_t *rgb) const
+{
+ if (m_color_space == color_space::RGB_24BIT)
{
rgb[0] = m_red;
rgb[1] = m_green;
rgb[2] = m_blue;
}
+ else if (m_color_space == color_space::ANSI_8COLOR
+ && 0 <= m_value && m_value <= 7)
+ memcpy (rgb, palette_8colors[m_value], 3 * sizeof (uint8_t));
+ else if (m_color_space == color_space::AIXTERM_16COLOR
+ && 0 <= m_value && m_value <= 15)
+ memcpy (rgb, palette_16colors[m_value], 3 * sizeof (uint8_t));
+ else if (m_color_space != color_space::XTERM_256COLOR)
+ gdb_assert_not_reached ("get_rgb called on invalid color");
+ else if (0 <= m_value && m_value <= 15)
+ memcpy (rgb, palette_16colors[m_value], 3 * sizeof (uint8_t));
+ else if (m_value >= 16 && m_value <= 231)
+ {
+ int value = m_value;
+ value -= 16;
+ /* This obscure formula seems to be what terminals actually
+ do. */
+ int component = value / 36;
+ rgb[0] = component == 0 ? 0 : (55 + component * 40);
+ value %= 36;
+ component = value / 6;
+ rgb[1] = component == 0 ? 0 : (55 + component * 40);
+ value %= 6;
+ rgb[2] = value == 0 ? 0 : (55 + value * 40);
+ }
+ else if (232 <= m_value && m_value <= 255)
+ {
+ uint8_t v = (m_value - 232) * 10 + 8;
+ rgb[0] = v;
+ rgb[1] = v;
+ rgb[2] = v;
+ }
+ else
+ gdb_assert_not_reached ("get_rgb called on invalid color");
}
/* See ui-style.h. */
-std::string
-ui_file_style::to_ansi () const
+ui_file_style::color
+ui_file_style::color::approximate (const std::vector<color_space> &spaces) const
{
- std::string result ("\033[");
- bool need_semi = m_foreground.append_ansi (true, &result);
- if (!m_background.is_none ())
+ if (spaces.empty () || is_none ())
+ return NONE;
+
+ color_space target_space = color_space::MONOCHROME;
+ for (color_space sp : spaces)
+ if (sp == m_color_space)
+ return *this;
+ else if (sp > target_space)
+ target_space = sp;
+
+ if (target_space == color_space::RGB_24BIT)
{
- if (need_semi)
- result.push_back (';');
- m_background.append_ansi (false, &result);
- need_semi = true;
+ uint8_t rgb[3];
+ get_rgb (rgb);
+ return color (rgb[0], rgb[1], rgb[2]);
+ }
+
+ int target_size = 0;
+ switch (target_space)
+ {
+ case color_space::ANSI_8COLOR:
+ target_size = 8;
+ break;
+ case color_space::AIXTERM_16COLOR:
+ target_size = 16;
+ break;
+ case color_space::XTERM_256COLOR:
+ target_size = 256;
+ break;
}
- if (m_intensity != NORMAL)
+
+ if (is_simple() && m_value < target_size)
+ return color (target_space, m_value);
+
+ color result = NONE;
+ int best_distance = std::numeric_limits<int>::max ();
+ uint8_t rgb[3];
+ get_rgb (rgb);
+
+ for (int i = 0; i < target_size; ++i)
{
- if (need_semi)
- result.push_back (';');
- result.append (std::to_string (m_intensity));
- need_semi = true;
+ uint8_t c_rgb[3];
+ color c (target_space, i);
+ c.get_rgb (c_rgb);
+ int d_red = std::abs (rgb[0] - c_rgb[0]);
+ int d_green = std::abs (rgb[1] - c_rgb[1]);
+ int d_blue = std::abs (rgb[2] - c_rgb[2]);
+ int dist = d_red * d_red + d_green * d_green + d_blue * d_blue;
+ if (dist < best_distance)
+ {
+ best_distance = dist;
+ result = c;
+ }
}
- if (m_reverse)
+
+ return result;
+}
+
+/* See ui-style.h. */
+
+std::string
+ui_file_style::to_ansi () const
+{
+ std::string result ("\033[");
+ if (!is_default ())
{
- if (need_semi)
- result.push_back (';');
- result.push_back ('7');
+ m_foreground.append_ansi (true, &result);
+ result.push_back (';');
+ m_background.append_ansi (false, &result);
+ result.push_back (';');
+ if (m_intensity == NORMAL)
+ result.append ("22");
+ else
+ result.append (std::to_string (m_intensity));
+ result.push_back (';');
+ if (m_reverse)
+ result.push_back ('7');
+ else
+ result.append ("27");
}
result.push_back ('m');
return result;
@@ -315,9 +447,11 @@ ui_file_style::parse (const char *buf, size_t *n_read)
case 35:
case 36:
case 37:
+ m_foreground = color (value - 30);
+ break;
/* Note: not 38. */
case 39:
- m_foreground = color (value - 30);
+ m_foreground = NONE;
break;
case 40:
@@ -328,9 +462,11 @@ ui_file_style::parse (const char *buf, size_t *n_read)
case 45:
case 46:
case 47:
+ m_background = color (value - 40);
+ break;
/* Note: not 48. */
case 49:
- m_background = color (value - 40);
+ m_background = NONE;
break;
case 90:
@@ -412,3 +548,60 @@ _initialize_ui_style ()
error. */
gdb_assert (code == 0);
}
+
+/* See ui-style.h. */
+
+const std::vector<color_space> &
+colorsupport ()
+{
+ static const std::vector<color_space> value = []
+ {
+ std::vector<color_space> result = {color_space::MONOCHROME};
+
+ int colors = tgetnum ("Co");
+ if (colors >= 8)
+ result.push_back (color_space::ANSI_8COLOR);
+ if (colors >= 16)
+ result.push_back (color_space::AIXTERM_16COLOR);
+ if (colors >= 256)
+ result.push_back (color_space::XTERM_256COLOR);
+
+ const char *colorterm = getenv ("COLORTERM");
+ if (colorterm != nullptr && (!strcmp (colorterm, "truecolor")
+ || !strcmp (colorterm, "24bit")))
+ result.push_back (color_space::RGB_24BIT);
+
+ return result;
+ } ();
+ return value;
+}
+
+const char *
+color_space_name (color_space c)
+{
+ switch (c)
+ {
+ case color_space::MONOCHROME: return "monochrome";
+ case color_space::ANSI_8COLOR: return "ansi_8color";
+ case color_space::AIXTERM_16COLOR: return "aixterm_16color";
+ case color_space::XTERM_256COLOR: return "xterm_256color";
+ case color_space::RGB_24BIT: return "rgb_24bit";
+ }
+ gdb_assert_not_reached ("color_space_name called on invalid color");
+}
+
+bool
+color_space_safe_cast (color_space *result, long c)
+{
+ switch (static_cast<color_space>(c))
+ {
+ case color_space::MONOCHROME:
+ case color_space::ANSI_8COLOR:
+ case color_space::AIXTERM_16COLOR:
+ case color_space::XTERM_256COLOR:
+ case color_space::RGB_24BIT:
+ *result = static_cast<color_space>(c);
+ return true;
+ }
+ return false;
+}