summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRichard Russon <rich@flatcap.org>2023-10-18 14:17:00 +0100
committerRichard Russon <rich@flatcap.org>2023-10-18 14:17:00 +0100
commitc00f9e0ec6540e1f4c1008e19eefc009814a99c8 (patch)
treeda71128e5590f3ea5cd88520ffcff5940422d754
parent8a0abf4555742eca4bfe6c1d350348c77a3fbb1e (diff)
parente28295faa44349ace4baad4a026fc3401d1b0516 (diff)
merge: color light refactoring
* color: move color_dump() * color: tidy color_dump() * color: tidy color_debug_log_color_attrs() * color: define a type for colours * color: drop COLOR_UNSET * color: split out ANSI parsing code * color: split out Colour parsing code * color: move colour helper functions * color: refactor the attribute handling * color: add +truecolor to version string
-rw-r--r--Makefile.autosetup3
-rw-r--r--color/ansi.c221
-rw-r--r--color/ansi.h5
-rw-r--r--color/attr.c187
-rw-r--r--color/attr.h15
-rw-r--r--color/color.c16
-rw-r--r--color/color.h8
-rw-r--r--color/command.c757
-rw-r--r--color/command2.h9
-rw-r--r--color/curses.c14
-rw-r--r--color/curses2.h18
-rw-r--r--color/debug.c248
-rw-r--r--color/debug.h14
-rw-r--r--color/lib.h4
-rw-r--r--color/merged.c7
-rw-r--r--color/merged.h4
-rw-r--r--color/parse_ansi.c259
-rw-r--r--color/parse_ansi.h35
-rw-r--r--color/parse_color.c386
-rw-r--r--color/parse_color.h34
-rw-r--r--color/quoted.c3
-rw-r--r--color/quoted.h4
-rw-r--r--color/regex.c10
-rw-r--r--color/regex4.h26
-rw-r--r--color/simple.c2
-rw-r--r--color/simple2.h3
-rw-r--r--debug/lib.h1
-rw-r--r--pager/dlg_pager.c2
-rw-r--r--pager/lib.h5
-rw-r--r--pager/pager.c2
-rw-r--r--version.c8
31 files changed, 1209 insertions, 1101 deletions
diff --git a/Makefile.autosetup b/Makefile.autosetup
index ec158febb..908727e2d 100644
--- a/Makefile.autosetup
+++ b/Makefile.autosetup
@@ -181,7 +181,8 @@ $(PWD)/browser:
# libcolor
LIBCOLOR= libcolor.a
LIBCOLOROBJS= color/ansi.o color/attr.o color/color.o color/command.o \
- color/curses.o color/merged.o color/notify.o color/quoted.o \
+ color/curses.o color/merged.o color/notify.o \
+ color/parse_ansi.o color/parse_color.o color/quoted.o \
color/regex.o color/simple.o
@if USE_DEBUG_COLOR
LIBCOLOROBJS+= color/debug.o
diff --git a/color/ansi.c b/color/ansi.c
index 7dbb259b4..eb0b075a3 100644
--- a/color/ansi.c
+++ b/color/ansi.c
@@ -27,10 +27,7 @@
*/
#include "config.h"
-#include <stddef.h>
-#include <ctype.h>
#include <stdbool.h>
-#include <stdlib.h>
#include "mutt/lib.h"
#include "gui/lib.h"
#include "ansi.h"
@@ -38,226 +35,10 @@
#include "color.h"
#include "curses2.h"
#include "debug.h"
+#include "parse_ansi.h"
#include "simple2.h"
/**
- * ansi_is_end_char - Is this the end of a sequence?
- * @param c Character to test
- * @retval true Is it a valid end char
- */
-static inline bool ansi_is_end_char(char c)
-{
- return ((c == 'm') || (c == ';'));
-}
-
-/**
- * ansi_skip_sequence - Skip an unrecognised sequence
- * @param str String to examine
- * @retval num Number of characters to skip over
- */
-static int ansi_skip_sequence(const char *str)
-{
- if (!str || (str[0] == '\0'))
- return 0;
-
- int count = 1;
- while ((str[0] != '\0') && !ansi_is_end_char(str[0]))
- {
- str++;
- count++;
- }
-
- return count;
-}
-
-/**
- * ansi_color_seq_length - Is this an ANSI escape sequence?
- * @param str String to test
- * @retval 0 No, not an ANSI sequence
- * @retval >0 Length of the ANSI sequence
- *
- * Match ANSI escape sequences of type 'm', e.g.
- * - `<esc>[1;32m`
- */
-int ansi_color_seq_length(const char *str)
-{
- if (!str || !*str)
- return 0;
-
- if ((str[0] != '\033') || (str[1] != '[')) // Escape
- return 0;
-
- int i = 2;
- while ((str[i] != '\0') && (isdigit(str[i]) || (str[i] == ';')))
- {
- i++;
- }
-
- if (str[i] == 'm')
- return i + 1;
-
- return 0;
-}
-
-/**
- * ansi_color_parse_single - Parse a single ANSI escape sequence
- * @param buf String to parse
- * @param ansi AnsiColor for the result
- * @param dry_run Don't parse anything, just skip over
- * @retval num Length of the escape sequence
- *
- * Parse an ANSI escape sequence into @a ansi.
- * Calling this function repeatedly, will accumulate sequences in @a ansi.
- */
-static int ansi_color_parse_single(const char *buf, struct AnsiColor *ansi, bool dry_run)
-{
- int seq_len = ansi_color_seq_length(buf);
- if (seq_len == 0)
- return 0;
-
- if (dry_run || !ansi)
- return seq_len;
-
- int pos = 2; // Skip '<esc>['
-
- while (pos < seq_len)
- {
- if ((buf[pos] == '0') && isdigit(buf[pos + 1]))
- {
- pos++; // Skip the leading zero
- }
- else if ((buf[pos] == '0') && ansi_is_end_char(buf[pos + 1]))
- {
- ansi->fg = COLOR_DEFAULT;
- ansi->bg = COLOR_DEFAULT;
- ansi->attrs = 0;
- ansi->attr_color = NULL;
- pos += 2;
- }
- else if ((buf[pos] == '1') && ansi_is_end_char(buf[pos + 1]))
- {
- ansi->attrs |= A_BOLD;
- pos += 2;
- }
- else if ((buf[pos] == '3') && ansi_is_end_char(buf[pos + 1]))
- {
- ansi->attrs |= A_ITALIC;
- pos += 2;
- }
- else if (buf[pos] == '3')
- {
- // 30-37 basic fg
- if ((buf[pos + 1] >= '0') && (buf[pos + 1] < '8') && ansi_is_end_char(buf[pos + 2]))
- {
- ansi->fg = buf[pos + 1] - '0';
- pos += 3;
- }
- else if (buf[pos + 1] == '8')
- {
- if (mutt_str_startswith(buf + pos, "38;5;") && isdigit(buf[pos + 5]))
- {
- // 38;5;n palette fg
- char *end = NULL;
- int value = strtoul(buf + pos + 5, &end, 10);
- if ((value >= 0) && (value < 256) && end && ansi_is_end_char(end[0]))
- {
- ansi->fg = value;
- pos += end - &buf[pos];
- }
- else
- {
- pos += ansi_skip_sequence(buf + pos);
- }
- }
- else if (mutt_str_startswith(buf + pos, "38;2;") && isdigit(buf[pos + 5]))
- {
- // 38;2;R;G;B true colour fg
- pos += ansi_skip_sequence(buf + pos + 5);
- pos += ansi_skip_sequence(buf + pos);
- pos += ansi_skip_sequence(buf + pos);
- }
- }
- else if ((buf[pos + 1] == '9') && ansi_is_end_char(buf[pos + 2]))
- {
- // default fg
- ansi->fg = COLOR_DEFAULT;
- pos += 2;
- }
- else
- {
- pos += ansi_skip_sequence(buf + pos);
- }
- }
- else if ((buf[pos] == '4') && ansi_is_end_char(buf[pos + 1]))
- {
- ansi->attrs |= A_UNDERLINE;
- pos += 2;
- }
- else if (buf[pos] == '4')
- {
- // 40-47 basic bg
- if ((buf[pos + 1] >= '0') && (buf[pos + 1] < '8'))
- {
- ansi->bg = buf[pos + 1] - '0';
- pos += 3;
- }
- else if (buf[pos + 1] == '8')
- {
- if (mutt_str_startswith(buf + pos, "48;5;") && isdigit(buf[pos + 5]))
- {
- // 48;5;n palette bg
- char *end = NULL;
- int value = strtoul(buf + pos + 5, &end, 10);
- if ((value >= 0) && (value < 256) && end && ansi_is_end_char(end[0]))
- {
- ansi->bg = value;
- pos += end - &buf[pos];
- }
- else
- {
- pos += ansi_skip_sequence(buf + pos);
- }
- }
- else if (mutt_str_startswith(buf + pos, "48;2;") && isdigit(buf[pos + 5]))
- {
- // 48;2;R;G;B true colour bg
- pos += ansi_skip_sequence(buf + pos + 5);
- pos += ansi_skip_sequence(buf + pos);
- pos += ansi_skip_sequence(buf + pos);
- }
- else
- {
- pos += ansi_skip_sequence(buf + pos);
- }
- }
- else if ((buf[pos + 1] == '9') && ansi_is_end_char(buf[pos + 2]))
- {
- // default bg
- ansi->bg = COLOR_DEFAULT;
- pos += 2;
- }
- }
- else if ((buf[pos] == '5') && ansi_is_end_char(buf[pos + 1]))
- {
- ansi->attrs |= A_BLINK;
- pos += 2;
- }
- else if ((buf[pos] == '7') && ansi_is_end_char(buf[pos + 1]))
- {
- ansi->attrs |= A_REVERSE;
- pos += 2;
- }
- else
- {
- while ((pos < seq_len) && (buf[pos] != ';'))
- pos++;
- }
- }
-
- return pos;
-}
-
-/**
* ansi_color_list_add - Add an Ansi colour to the list
* @param acl List of unique colours
* @param ansi Colour to add
diff --git a/color/ansi.h b/color/ansi.h
index ccd189f28..b78b4427d 100644
--- a/color/ansi.h
+++ b/color/ansi.h
@@ -24,6 +24,7 @@
#define MUTT_COLOR_ANSI_H
#include <stdbool.h>
+#include "curses2.h"
struct AttrColorList;
@@ -34,8 +35,8 @@ struct AnsiColor
{
const struct AttrColor *attr_color; ///< Curses colour of text
int attrs; ///< Attributes, e.g. A_BOLD
- int fg; ///< Foreground colour
- int bg; ///< Background colour
+ color_t fg; ///< Foreground colour
+ color_t bg; ///< Background colour
};
int ansi_color_parse (const char *str, struct AnsiColor *ansi, struct AttrColorList *acl, bool dry_run);
diff --git a/color/attr.c b/color/attr.c
index b4db66545..cc1b06c65 100644
--- a/color/attr.c
+++ b/color/attr.c
@@ -29,8 +29,9 @@
#include "config.h"
#include <stddef.h>
-#include <stdint.h>
+#include <assert.h>
#include "mutt/lib.h"
+#include "gui/lib.h"
#include "attr.h"
#include "curses2.h"
#include "debug.h"
@@ -116,8 +117,8 @@ void attr_color_list_clear(struct AttrColorList *acl)
* @param attrs Attributes, e.g. A_UNDERLINE
* @retval ptr Matching AttrColor
*/
-struct AttrColor *attr_color_list_find(struct AttrColorList *acl, uint32_t fg,
- uint32_t bg, int attrs)
+struct AttrColor *attr_color_list_find(struct AttrColorList *acl, color_t fg,
+ color_t bg, int attrs)
{
if (!acl)
return NULL;
@@ -181,3 +182,183 @@ bool attr_color_match(struct AttrColor *ac1, struct AttrColor *ac2)
return ((ac1->curses_color == ac2->curses_color) && (ac1->attrs == ac2->attrs));
}
+
+/**
+ * modify_color_by_prefix - Modify a colour/attributes based on a prefix, e.g. "bright"
+ * @param[in] prefix prefix to apply
+ * @param[in] is_fg true if a foreground colour should be modified
+ * @param[in,out] col colour to modify
+ * @param[in,out] attrs attributes to modify
+ */
+void modify_color_by_prefix(enum ColorPrefix prefix, bool is_fg, color_t *col, int *attrs)
+{
+ if (prefix == COLOR_PREFIX_NONE)
+ return; // nothing to do here
+
+ if (prefix == COLOR_PREFIX_ALERT)
+ {
+ *attrs |= A_BOLD;
+ *attrs |= A_BLINK;
+ }
+ else if (is_fg)
+ {
+ if ((COLORS >= 16) && (prefix == COLOR_PREFIX_LIGHT))
+ {
+ if (*col <= 7)
+ {
+ /* Advance the color 0-7 by 8 to get the light version */
+ *col += 8;
+ }
+ }
+ else
+ {
+ *attrs |= A_BOLD;
+ }
+ }
+ else
+ {
+ if (COLORS >= 16)
+ {
+ if (*col <= 7)
+ {
+ /* Advance the color 0-7 by 8 to get the light version */
+ *col += 8;
+ }
+ }
+ }
+}
+
+#ifdef NEOMUTT_DIRECT_COLORS
+/**
+ * color_xterm256_to_24bit - Convert a xterm color to its RGB value
+ * @param[in] color xterm color number to be converted
+ * @retval num The color's RGB value as number with value 0xRRGGBB
+ *
+ * There are 256 xterm colors numbered 0 to 255.
+ *
+ * Caller contract: color must be between 0 and 255.
+ *
+ * ## Xterm Color Codes
+ *
+ * ### Basic and Bright Colors
+ *
+ * - 0-7 correspond to the 8 terminal colours
+ * - 8-15 are the bright variants of 0-7
+ *
+ * | | | | | | | | | | | | | | | | |
+ * | :-- | :-------- | :-- | :------- | :-- | :------- | :-- | :------- | :-- | :------- | :-- | :------- | :- | :------- | :- | :-------- |
+ * | 0 | `#000000` | 1 | `#800000` | 2 | `#008000` | 3 | `#808000` | 4 | `#000080` | 5 | `#800080` | 6 | `#008080` | 7 | `#c0c0c0` |
+ * | 8 | `#808080` | 9 | `#ff0000` | 10 | `#00ff00` | 11 | `#ffff00` | 12 | `#0000ff` | 13 | `#ff00ff` | 14 | `#00ffff` | 15 | `#ffffff` |
+ *
+ * ### Color palette
+ *
+ * | | | | | | | | | | | | |
+ * | :-- | :------- | :-- | :------- | :-- | :------- | :-- | :------- | :-- | :------- | :-- | :------- |
+ * | 16 | `#000000` | 17 | `#00005f` | 18 | `#000087` | 19 | `#0000af` | 20 | `#0000d7` | 21 | `#0000ff` |
+ * | 22 | `#005f00` | 23 | `#005f5f` | 24 | `#005f87` | 25 | `#005faf` | 26 | `#005fd7` | 27 | `#005fff` |
+ * | 28 | `#008700` | 29 | `#00875f` | 30 | `#008787` | 31 | `#0087af` | 32 | `#0087d7` | 33 | `#0087ff` |
+ * | 34 | `#00af00` | 35 | `#00af5f` | 36 | `#00af87` | 37 | `#00afaf` | 38 | `#00afd7` | 39 | `#00afff` |
+ * | 40 | `#00d700` | 41 | `#00d75f` | 42 | `#00d787` | 43 | `#00d7af` | 44 | `#00d7d7` | 45 | `#00d7ff` |
+ * | 46 | `#00ff00` | 47 | `#00ff5f` | 48 | `#00ff87` | 49 | `#00ffaf` | 50 | `#00ffd7` | 51 | `#00ffff` |
+ * | 52 | `#5f0000` | 53 | `#5f005f` | 54 | `#5f0087` | 55 | `#5f00af` | 56 | `#5f00d7` | 57 | `#5f00ff` |
+ * | 58 | `#5f5f00` | 59 | `#5f5f5f` | 60 | `#5f5f87` | 61 | `#5f5faf` | 62 | `#5f5fd7` | 63 | `#5f5fff` |
+ * | 64 | `#5f8700` | 65 | `#5f875f` | 66 | `#5f8787` | 67 | `#5f87af` | 68 | `#5f87d7` | 69 | `#5f87ff` |
+ * | 70 | `#5faf00` | 71 | `#5faf5f` | 72 | `#5faf87` | 73 | `#5fafaf` | 74 | `#5fafd7` | 75 | `#5fafff` |
+ * | 76 | `#5fd700` | 77 | `#5fd75f` | 78 | `#5fd787` | 79 | `#5fd7af` | 80 | `#5fd7d7` | 81 | `#5fd7ff` |
+ * | 82 | `#5fff00` | 83 | `#5fff5f` | 84 | `#5fff87` | 85 | `#5fffaf` | 86 | `#5fffd7` | 87 | `#5fffff` |
+ * | 88 | `#870000` | 89 | `#87005f` | 90 | `#870087` | 91 | `#8700af` | 92 | `#8700d7` | 93 | `#8700ff` |
+ * | 94 | `#875f00` | 95 | `#875f5f` | 96 | `#875f87` | 97 | `#875faf` | 98 | `#875fd7` | 99 | `#875fff` |
+ * | 100 | `#878700` | 101 | `#87875f` | 102 | `#878787` | 103 | `#8787af` | 104 | `#8787d7` | 105 | `#8787ff` |
+ * | 106 | `#87af00` | 107 | `#87af5f` | 108 | `#87af87` | 109 | `#87afaf` | 110 | `#87afd7` | 111 | `#87afff` |
+ * | 112 | `#87d700` | 113 | `#87d75f` | 114 | `#87d787` | 115 | `#87d7af` | 116 | `#87d7d7` | 117 | `#87d7ff` |
+ * | 118 | `#87ff00` | 119 | `#87ff5f` | 120 | `#87ff87` | 121 | `#87ffaf` | 122 | `#87ffd7` | 123 | `#87ffff` |
+ * | 124 | `#af0000` | 125 | `#af005f` | 126 | `#af0087` | 127 | `#af00af` | 128 | `#af00d7` | 129 | `#af00ff` |
+ * | 130 | `#af5f00` | 131 | `#af5f5f` | 132 | `#af5f87` | 133 | `#af5faf` | 134 | `#af5fd7` | 135 | `#af5fff` |
+ * | 136 | `#af8700` | 137 | `#af875f` | 138 | `#af8787` | 139 | `#af87af` | 140 | `#af87d7` | 141 | `#af87ff` |
+ * | 142 | `#afaf00` | 143 | `#afaf5f` | 144 | `#afaf87` | 145 | `#afafaf` | 146 | `#afafd7` | 147 | `#afafff` |
+ * | 148 | `#afd700` | 149 | `#afd75f` | 150 | `#afd787` | 151 | `#afd7af` | 152 | `#afd7d7` | 153 | `#afd7ff` |
+ * | 154 | `#afff00` | 155 | `#afff5f` | 156 | `#afff87` | 157 | `#afffaf` | 158 | `#afffd7` | 159 | `#afffff` |
+ * | 160 | `#d70000` | 161 | `#d7005f` | 162 | `#d70087` | 163 | `#d700af` | 164 | `#d700d7` | 165 | `#d700ff` |
+ * | 166 | `#d75f00` | 167 | `#d75f5f` | 168 | `#d75f87` | 169 | `#d75faf` | 170 | `#d75fd7` | 171 | `#d75fff` |
+ * | 172 | `#d78700` | 173 | `#d7875f` | 174 | `#d78787` | 175 | `#d787af` | 176 | `#d787d7` | 177 | `#d787ff` |
+ * | 178 | `#d7af00` | 179 | `#d7af5f` | 180 | `#d7af87` | 181 | `#d7afaf` | 182 | `#d7afd7` | 183 | `#d7afff` |
+ * | 184 | `#d7d700` | 185 | `#d7d75f` | 186 | `#d7d787` | 187 | `#d7d7af` | 188 | `#d7d7d7` | 189 | `#d7d7ff` |
+ * | 190 | `#d7ff00` | 191 | `#d7ff5f` | 192 | `#d7ff87` | 193 | `#d7ffaf` | 194 | `#d7ffd7` | 195 | `#d7ffff` |
+ * | 196 | `#ff0000` | 197 | `#ff005f` | 198 | `#ff0087` | 199 | `#ff00af` | 200 | `#ff00d7` | 201 | `#ff00ff` |
+ * | 202 | `#ff5f00` | 203 | `#ff5f5f` | 204 | `#ff5f87` | 205 | `#ff5faf` | 206 | `#ff5fd7` | 207 | `#ff5fff` |
+ * | 208 | `#ff8700` | 209 | `#ff875f` | 210 | `#ff8787` | 211 | `#ff87af` | 212 | `#ff87d7` | 213 | `#ff87ff` |
+ * | 214 | `#ffaf00` | 215 | `#ffaf5f` | 216 | `#ffaf87` | 217 | `#ffafaf` | 218 | `#ffafd7` | 219 | `#ffafff` |
+ * | 220 | `#ffd700` | 221 | `#ffd75f` | 222 | `#ffd787` | 223 | `#ffd7af` | 224 | `#ffd7d7` | 225 | `#ffd7ff` |
+ * | 226 | `#ffff00` | 227 | `#ffff5f` | 228 | `#ffff87` | 229 | `#ffffaf` | 230 | `#ffffd7` | 231 | `#ffffff` |
+ *
+ * ### Grey Scale Ramp
+ *
+ * | | | | | | | | | | | | | | | | |
+ * | :-- | :-------- | :-- | :------- | :-- | :------- | :-- | :------- | :-- | :------- | :-- | :------- | :-- | :------- | :-- | :-------- |
+ * | 232 | `#080808` | 233 | `#121212` | 234 | `#1c1c1c` | 235 | `#262626` | 236 | `#303030` | 237 | `#3a3a3a` | 238 | `#444444` | 239 | `#4e4e4e` |
+ * | 240 | `#585858` | 241 | `#606060` | 242 | `#666666` | 243 | `#767676` | 244 | `#808080` | 245 | `#8a8a8a` | 246 | `#949494` | 247 | `#9e9e9e` |
+ * | 248 | `#a8a8a8` | 249 | `#b2b2b2` | 250 | `#bcbcbc` | 251 | `#c6c6c6` | 252 | `#d0d0d0` | 253 | `#dadada` | 254 | `#e4e4e4` | 255 | `#eeeeee` |
+ */
+color_t color_xterm256_to_24bit(const color_t color)
+{
+ static const color_t basic[] = {
+ 0x000000, 0x800000, 0x008000, 0x808000, 0x000080, 0x800080,
+ 0x008080, 0xc0c0c0, 0x808080, 0xff0000, 0x00ff00, 0xffff00,
+ 0x0000ff, 0xff00ff, 0x00ffff, 0xffffff,
+ };
+
+ assert(color < 256);
+
+ if (color < 0)
+ return color;
+
+ if (color < 16)
+ {
+ color_debug(LL_DEBUG5, "Converted color 0-15: %d\n", color);
+ /* The first 16 colours are the "usual" terminal colours */
+ return basic[color];
+ }
+
+ if (color < 232)
+ {
+ /* The Color palette is divided in 6x6x6 colours, i.e. each R, G, B channel
+ * has six values:
+ *
+ * value: 1 2 3 4 5 6
+ * color: 0x00 0x5f 0x87 0xaf 0xd7 0xff
+ *
+ * The steps between the values is 0x28 = 40, the EXCEPT for the first one
+ * where it is 0x5f = 95.
+ *
+ * If we express the xterm color number minus 16 to base 6, i.e.
+ *
+ * color - 16 = (vr * 36) + (vg * 6) + (vb * 1)
+ *
+ * with vr, vg, vb integers between 0 and 5, then vr, vg, vb is the channel
+ * value for red, green, and blue, respectively.
+ */
+
+ color_t normalised_color = color - 16;
+ color_t vr = (normalised_color % 216) / 36; /* 216 = 6*6*6 */
+ color_t vg = (normalised_color % 36) / 6;
+ color_t vb = (normalised_color % 6) / 1;
+
+ /* First step is wider than the other ones, so add the difference if needed */
+ color_t r = vr * 0x28 + ((vr > 0) ? (0x5f - 0x40) : 0);
+ color_t g = vg * 0x28 + ((vg > 0) ? (0x5f - 0x40) : 0);
+ color_t b = vb * 0x28 + ((vb > 0) ? (0x5f - 0x40) : 0);
+
+ color_t rgb = (r << 16) + (g << 8) + (b << 0);
+ color_debug(LL_DEBUG5, "Converted xterm color %d to RGB #%x:\n", color, rgb);
+ return rgb;
+ }
+
+ /* Grey scale starts at 0x08 and adds 0xa = 10 in very step ending in 0xee.
+ * There are a total of 6*4 = 24 grey colors in total. */
+ color_t steps = color - 232;
+ color_t grey = (steps * 0x0a) + 0x08;
+ color_t rgb = (grey << 16) + (grey << 8) + (grey << 0);
+ color_debug(LL_DEBUG5, "Converted xterm color %d to RGB #%x:\n", color, rgb);
+ return rgb;
+}
+#endif
diff --git a/color/attr.h b/color/attr.h
index 0a3f5aec2..9d6e2970a 100644
--- a/color/attr.h
+++ b/color/attr.h
@@ -25,8 +25,19 @@
#include "config.h"
#include <stdbool.h>
-#include <stdint.h>
#include "mutt/lib.h"
+#include "curses2.h"
+
+/**
+ * ColorPrefix - Constants for colour prefixes of named colours
+ */
+enum ColorPrefix
+{
+ COLOR_PREFIX_NONE, ///< no prefix
+ COLOR_PREFIX_ALERT, ///< "alert" colour prefix
+ COLOR_PREFIX_BRIGHT, ///< "bright" colour prefix
+ COLOR_PREFIX_LIGHT, ///< "light" colour prefix
+};
/**
* struct AttrColor - A curses colour and its attributes
@@ -48,6 +59,6 @@ bool attr_color_match (struct AttrColor *ac1, struct AttrColor *ac2
struct AttrColor *attr_color_new (void);
void attr_color_list_clear(struct AttrColorList *acl);
-struct AttrColor *attr_color_list_find (struct AttrColorList *acl, uint32_t fg, uint32_t bg, int attrs);
+struct AttrColor *attr_color_list_find (struct AttrColorList *acl, color_t fg, color_t bg, int attrs);
#endif /* MUTT_COLOR_ATTR_H */
diff --git a/color/color.c b/color/color.c
index 947bbb689..7db81983c 100644
--- a/color/color.c
+++ b/color/color.c
@@ -42,22 +42,6 @@
#include "regex4.h"
#include "simple2.h"
-/// Mapping between a colour name and an ncurses colour
-const struct Mapping ColorNames[] = {
- // clang-format off
- { "black", COLOR_BLACK },
- { "blue", COLOR_BLUE },
- { "cyan", COLOR_CYAN },
- { "green", COLOR_GREEN },
- { "magenta", COLOR_MAGENTA },
- { "red", COLOR_RED },
- { "white", COLOR_WHITE },
- { "yellow", COLOR_YELLOW },
- { "default", COLOR_DEFAULT },
- { 0, 0 },
- // clang-format on
-};
-
/**
* colors_cleanup - Reset all the simple, quoted and regex colours
*/
diff --git a/color/color.h b/color/color.h
index c2405cd4d..010c982fc 100644
--- a/color/color.h
+++ b/color/color.h
@@ -25,6 +25,7 @@
#define MUTT_COLOR_COLOR_H
#include "config.h"
+#include "mutt/lib.h"
#include <stdbool.h>
/**
@@ -94,15 +95,10 @@ enum ColorId
MT_COLOR_MAX,
};
-#include <stdint.h>
-#include "mutt/lib.h"
-
-extern const struct Mapping ColorNames[];
extern const struct Mapping ColorFields[];
extern const struct Mapping ComposeColorFields[];
-#define COLOR_DEFAULT (-2)
-#define COLOR_UNSET UINT32_MAX
+#define COLOR_DEFAULT -1
void mutt_colors_init(void);
void mutt_colors_cleanup(void);
diff --git a/color/command.c b/color/command.c
index e7e36c9d4..85765183d 100644
--- a/color/command.c
+++ b/color/command.c
@@ -28,32 +28,23 @@
#include "config.h"
#include <stddef.h>
-#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
-#include <stdlib.h>
#include "mutt/lib.h"
-#include "config/lib.h"
#include "core/lib.h"
#include "gui/lib.h"
+#include "mutt.h"
#include "parse/lib.h"
#include "color.h"
#include "command2.h"
+#include "curses2.h"
#include "debug.h"
#include "globals.h"
#include "notify2.h"
+#include "parse_color.h"
#include "quoted.h"
#include "regex4.h"
#include "simple2.h"
-#ifdef USE_DEBUG_COLOR
-#include <stdio.h>
-#include "mutt.h"
-#include "pager/lib.h"
-#include "attr.h"
-#include "curses2.h"
-#include "merged.h"
-#include "pager/private_data.h" // IWYU pragma: keep
-#endif
/**
* ColorFields - Mapping of colour names to their IDs
@@ -127,549 +118,6 @@ const struct Mapping ComposeColorFields[] = {
};
/**
- * ColorPrefix - Constants for colour prefixes of named colours
- */
-enum ColorPrefix
-{
- COLOR_PREFIX_NONE, ///< no prefix
- COLOR_PREFIX_ALERT, ///< "alert" colour prefix
- COLOR_PREFIX_BRIGHT, ///< "bright" colour prefix
- COLOR_PREFIX_LIGHT, ///< "light" colour prefix
-};
-
-#ifdef NEOMUTT_DIRECT_COLORS
-/**
- * color_xterm256_to_24bit - Convert a xterm color to its RGB value
- * @param[in] color xterm color number to be converted
- * @retval num The color's RGB value as number with value 0xRRGGBB
- *
- * There are 256 xterm colors numbered 0 to 255.
- *
- * Caller contract: color must be between 0 and 255.
- *
- * ## Xterm Color Codes
- *
- * ### Basic and Bright Colors
- *
- * - 0-7 correspond to the 8 terminal colours
- * - 8-15 are the bright variants of 0-7
- *
- * | | | | | | | | | | | | | | | | |
- * | :-- | :-------- | :-- | :------- | :-- | :------- | :-- | :------- | :-- | :------- | :-- | :------- | :- | :------- | :- | :-------- |
- * | 0 | `#000000` | 1 | `#800000` | 2 | `#008000` | 3 | `#808000` | 4 | `#000080` | 5 | `#800080` | 6 | `#008080` | 7 | `#c0c0c0` |
- * | 8 | `#808080` | 9 | `#ff0000` | 10 | `#00ff00` | 11 | `#ffff00` | 12 | `#0000ff` | 13 | `#ff00ff` | 14 | `#00ffff` | 15 | `#ffffff` |
- *
- * ### Color palette
- *
- * | | | | | | | | | | | | |
- * | :-- | :------- | :-- | :------- | :-- | :------- | :-- | :------- | :-- | :------- | :-- | :------- |
- * | 16 | `#000000` | 17 | `#00005f` | 18 | `#000087` | 19 | `#0000af` | 20 | `#0000d7` | 21 | `#0000ff` |
- * | 22 | `#005f00` | 23 | `#005f5f` | 24 | `#005f87` | 25 | `#005faf` | 26 | `#005fd7` | 27 | `#005fff` |
- * | 28 | `#008700` | 29 | `#00875f` | 30 | `#008787` | 31 | `#0087af` | 32 | `#0087d7` | 33 | `#0087ff` |
- * | 34 | `#00af00` | 35 | `#00af5f` | 36 | `#00af87` | 37 | `#00afaf` | 38 | `#00afd7` | 39 | `#00afff` |
- * | 40 | `#00d700` | 41 | `#00d75f` | 42 | `#00d787` | 43 | `#00d7af` | 44 | `#00d7d7` | 45 | `#00d7ff` |
- * | 46 | `#00ff00` | 47 | `#00ff5f` | 48 | `#00ff87` | 49 | `#00ffaf` | 50 | `#00ffd7` | 51 | `#00ffff` |
- * | 52 | `#5f0000` | 53 | `#5f005f` | 54 | `#5f0087` | 55 | `#5f00af` | 56 | `#5f00d7` | 57 | `#5f00ff` |
- * | 58 | `#5f5f00` | 59 | `#5f5f5f` | 60 | `#5f5f87` | 61 | `#5f5faf` | 62 | `#5f5fd7` | 63 | `#5f5fff` |
- * | 64 | `#5f8700` | 65 | `#5f875f` | 66 | `#5f8787` | 67 | `#5f87af` | 68 | `#5f87d7` | 69 | `#5f87ff` |
- * | 70 | `#5faf00` | 71 | `#5faf5f` | 72 | `#5faf87` | 73 | `#5fafaf` | 74 | `#5fafd7` | 75 | `#5fafff` |
- * | 76 | `#5fd700` | 77 | `#5fd75f` | 78 | `#5fd787` | 79 | `#5fd7af` | 80 | `#5fd7d7` | 81 | `#5fd7ff` |
- * | 82 | `#5fff00` | 83 | `#5fff5f` | 84 | `#5fff87` | 85 | `#5fffaf` | 86 | `#5fffd7` | 87 | `#5fffff` |
- * | 88 | `#870000` | 89 | `#87005f` | 90 | `#870087` | 91 | `#8700af` | 92 | `#8700d7` | 93 | `#8700ff` |
- * | 94 | `#875f00` | 95 | `#875f5f` | 96 | `#875f87` | 97 | `#875faf` | 98 | `#875fd7` | 99 | `#875fff` |
- * | 100 | `#878700` | 101 | `#87875f` | 102 | `#878787` | 103 | `#8787af` | 104 | `#8787d7` | 105 | `#8787ff` |
- * | 106 | `#87af00` | 107 | `#87af5f` | 108 | `#87af87` | 109 | `#87afaf` | 110 | `#87afd7` | 111 | `#87afff` |
- * | 112 | `#87d700` | 113 | `#87d75f` | 114 | `#87d787` | 115 | `#87d7af` | 116 | `#87d7d7` | 117 | `#87d7ff` |
- * | 118 | `#87ff00` | 119 | `#87ff5f` | 120 | `#87ff87` | 121 | `#87ffaf` | 122 | `#87ffd7` | 123 | `#87ffff` |
- * | 124 | `#af0000` | 125 | `#af005f` | 126 | `#af0087` | 127 | `#af00af` | 128 | `#af00d7` | 129 | `#af00ff` |
- * | 130 | `#af5f00` | 131 | `#af5f5f` | 132 | `#af5f87` | 133 | `#af5faf` | 134 | `#af5fd7` | 135 | `#af5fff` |
- * | 136 | `#af8700` | 137 | `#af875f` | 138 | `#af8787` | 139 | `#af87af` | 140 | `#af87d7` | 141 | `#af87ff` |
- * | 142 | `#afaf00` | 143 | `#afaf5f` | 144 | `#afaf87` | 145 | `#afafaf` | 146 | `#afafd7` | 147 | `#afafff` |
- * | 148 | `#afd700` | 149 | `#afd75f` | 150 | `#afd787` | 151 | `#afd7af` | 152 | `#afd7d7` | 153 | `#afd7ff` |
- * | 154 | `#afff00` | 155 | `#afff5f` | 156 | `#afff87` | 157 | `#afffaf` | 158 | `#afffd7` | 159 | `#afffff` |
- * | 160 | `#d70000` | 161 | `#d7005f` | 162 | `#d70087` | 163 | `#d700af` | 164 | `#d700d7` | 165 | `#d700ff` |
- * | 166 | `#d75f00` | 167 | `#d75f5f` | 168 | `#d75f87` | 169 | `#d75faf` | 170 | `#d75fd7` | 171 | `#d75fff` |
- * | 172 | `#d78700` | 173 | `#d7875f` | 174 | `#d78787` | 175 | `#d787af` | 176 | `#d787d7` | 177 | `#d787ff` |
- * | 178 | `#d7af00` | 179 | `#d7af5f` | 180 | `#d7af87` | 181 | `#d7afaf` | 182 | `#d7afd7` | 183 | `#d7afff` |
- * | 184 | `#d7d700` | 185 | `#d7d75f` | 186 | `#d7d787` | 187 | `#d7d7af` | 188 | `#d7d7d7` | 189 | `#d7d7ff` |
- * | 190 | `#d7ff00` | 191 | `#d7ff5f` | 192 | `#d7ff87` | 193 | `#d7ffaf` | 194 | `#d7ffd7` | 195 | `#d7ffff` |
- * | 196 | `#ff0000` | 197 | `#ff005f` | 198 | `#ff0087` | 199 | `#ff00af` | 200 | `#ff00d7` | 201 | `#ff00ff` |
- * | 202 | `#ff5f00` | 203 | `#ff5f5f` | 204 | `#ff5f87` | 205 | `#ff5faf` | 206 | `#ff5fd7` | 207 | `#ff5fff` |
- * | 208 | `#ff8700` | 209 | `#ff875f` | 210 | `#ff8787` | 211 | `#ff87af` | 212 | `#ff87d7` | 213 | `#ff87ff` |
- * | 214 | `#ffaf00` | 215 | `#ffaf5f` | 216 | `#ffaf87` | 217 | `#ffafaf` | 218 | `#ffafd7` | 219 | `#ffafff` |
- * | 220 | `#ffd700` | 221 | `#ffd75f` | 222 | `#ffd787` | 223 | `#ffd7af` | 224 | `#ffd7d7` | 225 | `#ffd7ff` |
- * | 226 | `#ffff00` | 227 | `#ffff5f` | 228 | `#ffff87` | 229 | `#ffffaf` | 230 | `#ffffd7` | 231 | `#ffffff` |
- *
- * ### Grey Scale Ramp
- *
- * | | | | | | | | | | | | | | | | |
- * | :-- | :-------- | :-- | :------- | :-- | :------- | :-- | :------- | :-- | :------- | :-- | :------- | :-- | :------- | :-- | :-------- |
- * | 232 | `#080808` | 233 | `#121212` | 234 | `#1c1c1c` | 235 | `#262626` | 236 | `#303030` | 237 | `#3a3a3a` | 238 | `#444444` | 239 | `#4e4e4e` |
- * | 240 | `#585858` | 241 | `#606060` | 242 | `#666666` | 243 | `#767676` | 244 | `#808080` | 245 | `#8a8a8a` | 246 | `#949494` | 247 | `#9e9e9e` |
- * | 248 | `#a8a8a8` | 249 | `#b2b2b2` | 250 | `#bcbcbc` | 251 | `#c6c6c6` | 252 | `#d0d0d0` | 253 | `#dadada` | 254 | `#e4e4e4` | 255 | `#eeeeee` |
- */
-static uint32_t color_xterm256_to_24bit(const uint32_t color)
-{
- static const uint32_t basic[] = {
- 0x000000, 0x800000, 0x008000, 0x808000, 0x000080, 0x800080,
- 0x008080, 0xc0c0c0, 0x808080, 0xff0000, 0x00ff00, 0xffff00,
- 0x0000ff, 0xff00ff, 0x00ffff, 0xffffff,
- };
-
- assert(color < 256);
-
- if (color < 16)
- {
- color_debug(LL_DEBUG5, "Converted color 0-15: %d\n", color);
- /* The first 16 colours are the "usual" terminal colours */
- return basic[color];
- }
-
- if (color < 232)
- {
- /* The Color palette is divided in 6x6x6 colours, i.e. each R, G, B channel
- * has six values:
- *
- * value: 1 2 3 4 5 6
- * color: 0x00 0x5f 0x87 0xaf 0xd7 0xff
- *
- * The steps between the values is 0x28 = 40, the EXCEPT for the first one
- * where it is 0x5f = 95.
- *
- * If we express the xterm color number minus 16 to base 6, i.e.
- *
- * color - 16 = (vr * 36) + (vg * 6) + (vb * 1)
- *
- * with vr, vg, vb integers between 0 and 5, then vr, vg, vb is the channel
- * value for red, green, and blue, respectively.
- */
-
- uint32_t normalised_color = color - 16;
- uint32_t vr = (normalised_color % 216) / 36; /* 216 = 6*6*6 */
- uint32_t vg = (normalised_color % 36) / 6;
- uint32_t vb = (normalised_color % 6) / 1;
-
- /* First step is wider than the other ones, so add the difference if needed */
- uint32_t r = vr * 0x28 + ((vr > 0) ? (0x5f - 0x40) : 0);
- uint32_t g = vg * 0x28 + ((vg > 0) ? (0x5f - 0x40) : 0);
- uint32_t b = vb * 0x28 + ((vb > 0) ? (0x5f - 0x40) : 0);
-
- uint32_t rgb = (r << 16) + (g << 8) + (b << 0);
- color_debug(LL_DEBUG5, "Converted xterm color %d to RGB #%x:\n", color, rgb);
- return rgb;
- }
-
- /* Grey scale starts at 0x08 and adds 0xa = 10 in very step ending in 0xee.
- * There are a total of 6*4 = 24 grey colors in total. */
- uint32_t steps = color - 232;
- uint32_t grey = (steps * 0x0a) + 0x08;
- uint32_t rgb = (grey << 16) + (grey << 8) + (grey << 0);
- color_debug(LL_DEBUG5, "Converted xterm color %d to RGB #%x:\n", color, rgb);
- return rgb;
-}
-#endif
-
-/**
- * modify_color_by_prefix - Modify a colour/attributes based on a prefix, e.g. "bright"
- * @param[in] prefix prefix to apply
- * @param[in] is_fg true if a foreground colour should be modified
- * @param[in,out] col colour to modify
- * @param[in,out] attrs attributes to modify
- */
-static void modify_color_by_prefix(enum ColorPrefix prefix, bool is_fg,
- uint32_t *col, int *attrs)
-{
- if (prefix == COLOR_PREFIX_NONE)
- return; // nothing to do here
-
- if (prefix == COLOR_PREFIX_ALERT)
- {
- *attrs |= A_BOLD;
- *attrs |= A_BLINK;
- }
- else if (is_fg)
- {
- if ((COLORS >= 16) && (prefix == COLOR_PREFIX_LIGHT))
- {
- if (*col <= 7)
- {
- /* Advance the color 0-7 by 8 to get the light version */
- *col += 8;
- }
- }
- else
- {
- *attrs |= A_BOLD;
- }
- }
- else
- {
- if (COLORS >= 16)
- {
- if (*col <= 7)
- {
- /* Advance the color 0-7 by 8 to get the light version */
- *col += 8;
- }
- }
- }
-}
-
-/**
- * parse_color_prefix - Parse a colour prefix, e.g. "bright"
- * @param[in] s String to parse
- * @param[out] prefix parsed prefix, see #ColorPrefix
- * @retval num Length of the matched prefix
- * @retval 0 No prefix matched
- *
- * If prefixes should be parsed, but their value is irrelevant, NULL can be
- * passed as 'prefix'.
- */
-static int parse_color_prefix(const char *s, enum ColorPrefix *prefix)
-{
- int clen = 0;
-
- if ((clen = mutt_istr_startswith(s, "bright")))
- {
- color_debug(LL_DEBUG5, "bright\n");
- if (prefix)
- *prefix = COLOR_PREFIX_BRIGHT;
- }
- else if ((clen = mutt_istr_startswith(s, "alert")))
- {
- color_debug(LL_DEBUG5, "alert\n");
- if (prefix)
- *prefix = COLOR_PREFIX_ALERT;
- }
- else if ((clen = mutt_istr_startswith(s, "light")))
- {
- color_debug(LL_DEBUG5, "light\n");
- if (prefix)
- *prefix = COLOR_PREFIX_LIGHT;
- }
-
- return clen;
-}
-
-/**
- * parse_color_namedcolor - Parse a named colour, e.g. "brightred"
- * @param[in] s String to parse
- * @param[out] col Number for 'colorNNN' colours
- * @param[out] attrs Attributes, e.g. A_UNDERLINE
- * @param[in] is_fg true if this is a foreground colour
- * @param[out] err Buffer for error messages
- * @retval #MUTT_CMD_SUCCESS Colour parsed successfully
- * @retval #MUTT_CMD_WARNING Unknown colour, try other parsers
- */
-static enum CommandResult parse_color_namedcolor(const char *s, uint32_t *col, int *attrs,
- bool is_fg, struct Buffer *err)
-{
- enum ColorPrefix prefix = COLOR_PREFIX_NONE;
- s += parse_color_prefix(s, &prefix);
-
- if ((*col = mutt_map_get_value(s, ColorNames)) == -1)
- return MUTT_CMD_WARNING;
-
- const char *name = mutt_map_get_name(*col, ColorNames);
- if (name)
- color_debug(LL_DEBUG5, "color: %s\n", name);
-
- modify_color_by_prefix(prefix, is_fg, col, attrs);
-
-#ifdef NEOMUTT_DIRECT_COLORS
- /* If we are running in direct color mode, we must convert the color
- * number 0-15 to an RGB value.
- * The first 16 colours of the xterm palette correspond to the terminal
- * colours. Note that this replace the colour with a predefined RGB value
- * and not the RGB value the terminal configured to use.
- *
- * Note that some colors are "special" e.g. "default" and do not fall in
- * the range from 0 to 15. These must not be converted.
- */
- const bool c_color_directcolor = cs_subset_bool(NeoMutt->sub, "color_directcolor");
- if (c_color_directcolor && (*col < 16))
- {
- *col = color_xterm256_to_24bit(*col);
- }
-#endif
- return MUTT_CMD_SUCCESS;
-}
-
-/**
- * parse_color_colornnn - Parse a colorNNN, e.g. "color123".
- * @param[in] s String to parse
- * @param[out] col Number for 'colorNNN' colours
- * @param[out] attrs Attributes, e.g. A_UNDERLINE
- * @param[in] is_fg true if this is a foreground colour
- * @param[out] err Buffer for error messages
- * @retval #MUTT_CMD_SUCCESS Colour parsed successfully
- * @retval #MUTT_CMD_WARNING Unknown colour, try other parsers
- * @retval #MUTT_CMD_ERROR Error, colour could not be parsed
- *
- * On #MUTT_CMD_ERROR, an error message will be written to err.
- */
-static enum CommandResult parse_color_colornnn(const char *s, uint32_t *col, int *attrs,
- bool is_fg, struct Buffer *err)
-{
- /* prefixes bright, alert, light are only allowed for named colours and
- * colorNNN for backwards compatibility. */
- enum ColorPrefix prefix = COLOR_PREFIX_NONE;
- s += parse_color_prefix(s, &prefix);
-
- int clen = 0;
- /* allow aliases for xterm color resources */
- if ((clen = mutt_istr_startswith(s, "color")) == 0)
- return MUTT_CMD_WARNING;
-
- s += clen;
- char *eptr = NULL;
- *col = strtoul(s, &eptr, 10);
- /* There are only 256 xterm colors. Do not confuse with COLORS which is
- * the number of colours the terminal supports (usually one of 16, 256,
- * 16777216 (=24bit)). */
- if ((*s == '\0') || (*eptr != '\0') || (*col >= 256) || ((*col >= COLORS) && !OptNoCurses))
- {
- buf_printf(err, _("%s: color not supported by term"), s);
- return MUTT_CMD_ERROR;
- }
-
- modify_color_by_prefix(prefix, is_fg, col, attrs);
-
-#ifdef NEOMUTT_DIRECT_COLORS
- const bool c_color_directcolor = cs_subset_bool(NeoMutt->sub, "color_directcolor");
- if (c_color_directcolor)
- {
- /* If we are running in direct color mode, we must convert the xterm
- * color numbers 0-255 to an RGB value. */
- *col = color_xterm256_to_24bit(*col);
- /* FIXME: The color values 0 to 7 (both inclusive) are still occupied by
- * the default terminal colours. As a workaround we round them up to
- * #000008 which is the blackest black we can produce. */
- if (*col < 8)
- *col = 8;
- }
-#endif
- color_debug(LL_DEBUG5, "colorNNN %d\n", *col);
- return MUTT_CMD_SUCCESS;
-}
-
-/**
- * parse_color_rrggbb - Parse an RGB colour, e.g. "#12FE45"
- * @param[in] s String to parse
- * @param[out] col Number for 'colorNNN' colours
- * @param[out] attrs Attributes, e.g. A_UNDERLINE
- * @param[in] is_fg true if this is a foreground colour
- * @param[out] err Buffer for error messages
- * @retval #MUTT_CMD_SUCCESS Colour parsed successfully
- * @retval #MUTT_CMD_WARNING Unknown colour, try other parsers
- * @retval #MUTT_CMD_ERROR Error, colour could not be parsed
- *
- * On #MUTT_CMD_ERROR, an error message will be written to err.
- */
-static enum CommandResult parse_color_rrggbb(const char *s, uint32_t *col, int *attrs,
- bool is_fg, struct Buffer *err)
-{
- /* parse #RRGGBB colours */
- if (s[0] != '#')
- return MUTT_CMD_WARNING;
-
-#ifndef NEOMUTT_DIRECT_COLORS
- buf_printf(err, _("Direct colors support not compiled in: %s"), s);
- return MUTT_CMD_ERROR;
-#endif
- const bool c_color_directcolor = cs_subset_bool(NeoMutt->sub, "color_directcolor");
- if (!c_color_directcolor)
- {
- buf_printf(err, _("Direct colors support disabled: %s"), s);
- return MUTT_CMD_ERROR;
- }
- s++;
- char *eptr = NULL;
- *col = strtoul(s, &eptr, 16);
- if ((*s == '\0') || (*eptr != '\0') || ((*col >= COLORS) && !OptNoCurses))
- {
- buf_printf(err, _("%s: color not supported by term"), s);
- return MUTT_CMD_ERROR;
- }
- /* FIXME: The color values 0 to 7 (both inclusive) are still occupied by
- * the default terminal colours. As a workaround we round them up to
- * #000008 which is the blackest black we can produce. */
- if (*col < 8)
- *col = 8;
-
- color_debug(LL_DEBUG5, "#RRGGBB: %d\n", *col);
- return MUTT_CMD_SUCCESS;
-}
-
-/**
- * parse_color_name - Parse a colour name
- * @param[in] s String to parse
- * @param[out] col Number for 'colorNNN' colours
- * @param[out] attrs Attributes, e.g. A_UNDERLINE
- * @param[in] is_fg true if this is a foreground colour
- * @param[out] err Buffer for error messages
- * @retval #CommandResult Result e.g. #MUTT_CMD_SUCCESS
- *
- * Parse a colour name, such as "red", "brightgreen", "color123", "#12FE45"
- */
-static enum CommandResult parse_color_name(const char *s, uint32_t *col, int *attrs,
- bool is_fg, struct Buffer *err)
-{
- mutt_debug(LL_DEBUG5, "Parsing color name: %s\n", s);
-
- /* Try the different colour syntaxes. A return value of MUTT_CMD_WARNING
- * means, we should try the next syntax. */
- enum CommandResult cr;
-
- /* #RRGGBB */
- cr = parse_color_rrggbb(s, col, attrs, is_fg, err);
- if (cr != MUTT_CMD_WARNING)
- return cr;
- /* color123 */
- cr = parse_color_colornnn(s, col, attrs, is_fg, err);
- if (cr != MUTT_CMD_WARNING)
- return cr;
- /* named color, e.g. "brightred" */
- cr = parse_color_namedcolor(s, col, attrs, is_fg, err);
- if (cr != MUTT_CMD_WARNING)
- return cr;
-
- buf_printf(err, _("%s: no such color"), s);
- return MUTT_CMD_WARNING;
-}
-
-/**
- * parse_attr_spec - Parse an attribute description - Implements ::parser_callback_t - @ingroup parser_callback_api
- */
-static enum CommandResult parse_attr_spec(struct Buffer *buf, struct Buffer *s,
- uint32_t *fg, uint32_t *bg,
- int *attrs, struct Buffer *err)
-{
- if (fg)
- *fg = COLOR_UNSET;
- if (bg)
- *bg = COLOR_UNSET;
-
- if (!MoreArgs(s))
- {
- buf_printf(err, _("%s: too few arguments"), "mono");
- return MUTT_CMD_WARNING;
- }
-
- parse_extract_token(buf, s, TOKEN_NO_FLAGS);
-
- if (mutt_istr_equal("bold", buf->data))
- {
- *attrs |= A_BOLD;
- }
- else if (mutt_istr_equal("italic", buf->data))
- {
- *attrs |= A_ITALIC;
- }
- else if (mutt_istr_equal("none", buf->data))
- {
- *attrs = A_NORMAL; // Use '=' to clear other bits
- }
- else if (mutt_istr_equal("normal", buf->data))
- {
- *attrs = A_NORMAL; // Use '=' to clear other bits
- }
- else if (mutt_istr_equal("reverse", buf->data))
- {
- *attrs |= A_REVERSE;
- }
- else if (mutt_istr_equal("standout", buf->data))
- {
- *attrs |= A_STANDOUT;
- }
- else if (mutt_istr_equal("underline", buf->data))
- {
- *attrs |= A_UNDERLINE;
- }
- else
- {
- buf_printf(err, _("%s: no such attribute"), buf->data);
- return MUTT_CMD_WARNING;
- }
-
- return MUTT_CMD_SUCCESS;
-}
-
-/**
- * parse_color_pair - Parse a pair of colours - Implements ::parser_callback_t - @ingroup parser_callback_api
- *
- * Parse a pair of colours, e.g. "red default"
- */
-static enum CommandResult parse_color_pair(struct Buffer *buf, struct Buffer *s,
- uint32_t *fg, uint32_t *bg,
- int *attrs, struct Buffer *err)
-{
- while (true)
- {
- if (!MoreArgsF(s, TOKEN_COMMENT))
- {
- buf_printf(err, _("%s: too few arguments"), "color");
- return MUTT_CMD_WARNING;
- }
-
- parse_extract_token(buf, s, TOKEN_COMMENT);
-
- if (mutt_istr_equal("bold", buf->data))
- {
- *attrs |= A_BOLD;
- color_debug(LL_DEBUG5, "bold\n");
- }
- else if (mutt_istr_equal("italic", buf->data))
- {
- *attrs |= A_ITALIC;
- color_debug(LL_DEBUG5, "italic\n");
- }
- else if (mutt_istr_equal("none", buf->data))
- {
- *attrs = A_NORMAL; // Use '=' to clear other bits
- color_debug(LL_DEBUG5, "none\n");
- }
- else if (mutt_istr_equal("normal", buf->data))
- {
- *attrs = A_NORMAL; // Use '=' to clear other bits
- color_debug(LL_DEBUG5, "normal\n");
- }
- else if (mutt_istr_equal("reverse", buf->data))
- {
- *attrs |= A_REVERSE;
- color_debug(LL_DEBUG5, "reverse\n");
- }
- else if (mutt_istr_equal("standout", buf->data))
- {
- *attrs |= A_STANDOUT;
- color_debug(LL_DEBUG5, "standout\n");
- }
- else if (mutt_istr_equal("underline", buf->data))
- {
- *attrs |= A_UNDERLINE;
- color_debug(LL_DEBUG5, "underline\n");
- }
- else
- {
- enum CommandResult rc = parse_color_name(buf->data, fg, attrs, true, err);
- if (rc != MUTT_CMD_SUCCESS)
- return rc;
- break;
- }
- }
-
- if (!MoreArgsF(s, TOKEN_COMMENT))
- {
- buf_printf(err, _("%s: too few arguments"), "color");
- return MUTT_CMD_WARNING;
- }
-
- parse_extract_token(buf, s, TOKEN_COMMENT);
-
- return parse_color_name(buf->data, bg, attrs, false, err);
-}
-
-/**
* get_colorid_name - Get the name of a color id
* @param cid Colour, e.g. #MT_COLOR_HEADER
* @param buf Buffer for result
@@ -862,191 +310,6 @@ static enum CommandResult parse_uncolor(struct Buffer *buf, struct Buffer *s,
return MUTT_CMD_SUCCESS;
}
-#ifdef USE_DEBUG_COLOR
-/**
- * color_dump - Parse 'color' command to display colours - Implements Command::parse() - @ingroup command_parse
- */
-static enum CommandResult color_dump(struct Buffer *buf, struct Buffer *s,
- intptr_t data, struct Buffer *err)
-{
- if (MoreArgs(s))
- return MUTT_CMD_ERROR;
-
- FILE *fp_out = NULL;
- struct Buffer *tempfile = buf_pool_get();
- struct Buffer filebuf = buf_make(4096);
- char color_fg[32] = { 0 };
- char color_bg[32] = { 0 };
-
- buf_mktemp(tempfile);
- fp_out = mutt_file_fopen(buf_string(tempfile), "w");
- if (!fp_out)
- {
- // L10N: '%s' is the file name of the temporary file
- buf_printf(err, _("Could not create temporary file %s"), buf_string(tempfile));
- buf_dealloc(&filebuf);
- buf_pool_release(&tempfile);
- return MUTT_CMD_ERROR;
- }
-
- buf_addstr(&filebuf, "# All Colours\n\n");
- buf_addstr(&filebuf, "# Simple Colours\n");
- for (enum ColorId cid = MT_COLOR_NONE + 1; cid < MT_COLOR_MAX; cid++)
- {
- struct AttrColor *ac = simple_color_get(cid);
- if (!ac)
- continue;
-
- struct CursesColor *cc = ac->curses_color;
- if (!cc)
- continue;
-
- const char *name = mutt_map_get_name(cid, ColorFields);
- if (!name)
- continue;
-
- const char *swatch = color_debug_log_color_attrs(cc->fg, cc->bg, ac->attrs);
- buf_add_printf(&filebuf, "color %-18s %-30s %-8s %-8s # %s\n", name,
- color_debug_log_attrs_list(ac->attrs),
- color_debug_log_name(color_fg, sizeof(color_fg), cc->fg),
- color_debug_log_name(color_bg, sizeof(color_bg), cc->bg), swatch);
- }
-
- if (NumQuotedColors > 0)
- {
- buf_addstr(&filebuf, "\n# Quoted Colours\n");
- for (int i = 0; i < NumQuotedColors; i++)
- {
- struct AttrColor *ac = quoted_colors_get(i);
- if (!ac)
- continue;
-
- struct CursesColor *cc = ac->curses_color;
- if (!cc)
- continue;
-
- const char *swatch = color_debug_log_color_attrs(cc->fg, cc->bg, ac->attrs);
- buf_add_printf(&filebuf, "color quoted%d %-30s %-8s %-8s # %s\n", i,
- color_debug_log_attrs_list(ac->attrs),
- color_debug_log_name(color_fg, sizeof(color_fg), cc->fg),
- color_debug_log_name(color_bg, sizeof(color_bg), cc->bg), swatch);
- }
- }
-
- int rl_count = 0;
- for (enum ColorId id = MT_COLOR_NONE; id != MT_COLOR_MAX; ++id)
- {
- if (!mutt_color_has_pattern(id))
- {
- continue;
- }
-
- struct RegexColorList *rcl = regex_colors_get_list(id);
- if (!STAILQ_EMPTY(rcl))
- rl_count++;
- }
-
- if (rl_count > 0)
- {
- for (enum ColorId id = MT_COLOR_NONE; id != MT_COLOR_MAX; ++id)
- {
- if (!mutt_color_has_pattern(id))
- {
- continue;
- }
-
- struct RegexColorList *rcl = regex_colors_get_list(id);
- if (STAILQ_EMPTY(rcl))
- continue;
-
- const char *name = mutt_map_get_name(id, ColorFields);
- if (!name)
- continue;
-
- buf_add_printf(&filebuf, "\n# Regex Colour %s\n", name);
-
- struct RegexColor *rc = NULL;
- STAILQ_FOREACH(rc, rcl, entries)
- {
- struct AttrColor *ac = &rc->attr_color;
- struct CursesColor *cc = ac->curses_color;
- if (!cc)
- continue;
-
- const char *swatch = color_debug_log_color_attrs(cc->fg, cc->bg, ac->attrs);
- buf_add_printf(&filebuf, "color %-14s %-30s %-8s %-8s %-30s # %s\n",
- name, color_debug_log_attrs_list(ac->attrs),
- color_debug_log_name(color_fg, sizeof(color_fg), cc->fg),
- color_debug_log_name(color_bg, sizeof(color_bg), cc->bg),
- rc->pattern, swatch);
- }
- }
- }
-
-#ifdef USE_DEBUG_COLOR
- if (!TAILQ_EMPTY(&MergedColors))
- {
- buf_addstr(&filebuf, "\n# Merged Colours\n");
- struct AttrColor *ac = NULL;
- TAILQ_FOREACH(ac, &MergedColors, entries)
- {
- struct CursesColor *cc = ac->curses_color;
- if (!cc)
- continue;
-
- const char *swatch = color_debug_log_color_attrs(cc->fg, cc->bg, ac->attrs);
- buf_add_printf(&filebuf, "# %-30s %-8s %-8s # %s\n",
- color_debug_log_attrs_list(ac->attrs),
- color_debug_log_name(color_fg, sizeof(color_fg), cc->fg),
- color_debug_log_name(color_bg, sizeof(color_bg), cc->bg), swatch);
- }
- }
-
- struct MuttWindow *win = window_get_focus();
- if (win && (win->type == WT_CUSTOM) && win->parent && (win->parent->type == WT_PAGER))
- {
- struct PagerPrivateData *priv = win->parent->wdata;
- if (priv && !TAILQ_EMPTY(&priv->ansi_list))
- {
- buf_addstr(&filebuf, "\n# Ansi Colours\n");
- struct AttrColor *ac = NULL;
- TAILQ_FOREACH(ac, &priv->ansi_list, entries)
- {
- struct CursesColor *cc = ac->curses_color;
- if (!cc)
- continue;
-
- const char *swatch = color_debug_log_color_attrs(cc->fg, cc->bg, ac->attrs);
- buf_add_printf(&filebuf, "# %-30s %-8s %-8s # %s\n",
- color_debug_log_attrs_list(ac->attrs),
- color_debug_log_name(color_fg, sizeof(color_fg), cc->fg),
- color_debug_log_name(color_bg, sizeof(color_bg), cc->bg), swatch);
- }
- }
- }
-#endif
-
- fputs(filebuf.data, fp_out);
-
- mutt_file_fclose(&fp_out);
- buf_dealloc(&filebuf);
-
- struct PagerData pdata = { 0 };
- struct PagerView pview = { &pdata };
-
- pdata.fname = buf_string(tempfile);
-
- pview.banner = "color";
- pview.flags = MUTT_SHOWCOLOR;
- pview.mode = PAGER_MODE_OTHER;
-
- mutt_do_pager(&pview, NULL);
- buf_pool_release(&tempfile);
-
- return MUTT_CMD_SUCCESS;
-}
-#endif
-
/**
* parse_color - Parse a 'color' command
* @param buf Temporary Buffer space
@@ -1066,16 +329,18 @@ static enum CommandResult parse_color(struct Buffer *buf, struct Buffer *s,
bool dry_run, bool color)
{
int attrs = 0, q_level = 0;
- uint32_t fg = 0, bg = 0, match = 0;
+ color_t fg = 0, bg = 0;
+ unsigned int match = 0;
enum ColorId cid = MT_COLOR_NONE;
enum CommandResult rc;
if (!MoreArgs(s))
{
-#ifdef USE_DEBUG_COLOR
if (StartupComplete)
- return color_dump(buf, s, 0, err);
-#endif
+ {
+ color_dump();
+ return MUTT_CMD_SUCCESS;
+ }
buf_printf(err, _("%s: too few arguments"), "color");
return MUTT_CMD_WARNING;
@@ -1094,7 +359,7 @@ static enum CommandResult parse_color(struct Buffer *buf, struct Buffer *s,
/* extract a regular expression if needed */
- if (mutt_color_has_pattern(cid) && cid != MT_COLOR_STATUS)
+ if (mutt_color_has_pattern(cid) && (cid != MT_COLOR_STATUS))
{
color_debug(LL_DEBUG5, "regex needed\n");
if (MoreArgs(s))
@@ -1241,5 +506,5 @@ enum CommandResult mutt_parse_color(struct Buffer *buf, struct Buffer *s,
enum CommandResult mutt_parse_mono(struct Buffer *buf, struct Buffer *s,
intptr_t data, struct Buffer *err)
{
- return parse_color(buf, s, err, parse_attr_spec, true, false);
+ return parse_color(buf, s, err, parse_attr_spec, false, false);
}
diff --git a/color/command2.h b/color/command2.h
index 87c754e82..f3ff74e4e 100644
--- a/color/command2.h
+++ b/color/command2.h
@@ -26,6 +26,7 @@
#include "config.h"
#include <stdint.h>
#include "core/lib.h"
+#include "curses2.h"
struct Buffer;
@@ -36,15 +37,15 @@ struct Buffer;
*
* @param[in] buf Temporary Buffer space
* @param[in] s Buffer containing string to be parsed
- * @param[out] fg Foreground colour (set to -1)
- * @param[out] bg Background colour (set to -1)
+ * @param[out] fg Foreground colour
+ * @param[out] bg Background colour
* @param[out] attrs Attributes, e.g. A_UNDERLINE
* @param[out] err Buffer for error messages
* @retval 0 Success
* @retval -1 Error
*/
-typedef int (*parser_callback_t)(struct Buffer *buf, struct Buffer *s, uint32_t *fg,
- uint32_t *bg, int *attrs, struct Buffer *err);
+typedef int (*parser_callback_t)(struct Buffer *buf, struct Buffer *s, color_t *fg,
+ color_t *bg, int *attrs, struct Buffer *err);
enum CommandResult mutt_parse_color (struct Buffer *buf, struct Buffer *s, intptr_t data, struct Buffer *err);
enum CommandResult mutt_parse_mono (struct Buffer *buf, struct Buffer *s, intptr_t data, struct Buffer *err);
diff --git a/color/curses.c b/color/curses.c
index 96c18a102..3af271946 100644
--- a/color/curses.c
+++ b/color/curses.c
@@ -54,7 +54,7 @@ void curses_colors_init(void)
* @param bg Background colour
* @retval ptr Curses colour
*/
-struct CursesColor *curses_colors_find(int fg, int bg)
+struct CursesColor *curses_colors_find(color_t fg, color_t bg)
{
struct CursesColor *cc = NULL;
TAILQ_FOREACH(cc, &CursesColors, entries)
@@ -74,7 +74,7 @@ struct CursesColor *curses_colors_find(int fg, int bg)
* @param bg Background colour
* @retval num Index of Curses colour
*/
-static int curses_color_init(int fg, int bg)
+static int curses_color_init(color_t fg, color_t bg)
{
color_debug(LL_DEBUG5, "find lowest index\n");
int index = 16;
@@ -98,11 +98,6 @@ static int curses_color_init(int fg, int bg)
return 0;
}
- if (fg == COLOR_DEFAULT)
- fg = COLOR_UNSET;
- if (bg == COLOR_DEFAULT)
- bg = COLOR_UNSET;
-
#ifdef NEOMUTT_DIRECT_COLORS
int rc = init_extended_pair(index, fg, bg);
color_debug(LL_DEBUG5, "init_extended_pair(%d,%d,%d) -> %d\n", index, fg, bg, rc);
@@ -148,11 +143,10 @@ void curses_color_free(struct CursesColor **ptr)
* If the colour already exists, this function will return a pointer to the
* object (and increase its ref-count).
*/
-struct CursesColor *curses_color_new(int fg, int bg)
+struct CursesColor *curses_color_new(color_t fg, color_t bg)
{
color_debug(LL_DEBUG5, "fg %d, bg %d\n", fg, bg);
- if (((fg == COLOR_UNSET) && (bg == COLOR_UNSET)) ||
- ((fg == COLOR_DEFAULT) && (bg == COLOR_DEFAULT)))
+ if ((fg == COLOR_DEFAULT) && (bg == COLOR_DEFAULT))
{
color_debug(LL_DEBUG5, "both unset\n");
return NULL;
diff --git a/color/curses2.h b/color/curses2.h
index b1b2ec045..8b033689c 100644
--- a/color/curses2.h
+++ b/color/curses2.h
@@ -27,6 +27,9 @@
#include <stdint.h>
#include "mutt/lib.h"
+/// Type for 24-bit colour value
+typedef int32_t color_t;
+
/**
* struct CursesColor - Colour in the ncurses palette
*
@@ -36,25 +39,18 @@
*/
struct CursesColor
{
- /* TrueColor uses 24bit. Use fixed-width integer type to make sure it fits.
- * Use the upper 8 bits to store flags. */
- uint32_t fg; ///< Foreground colour
- uint32_t bg; ///< Background colour
+ color_t fg; ///< Foreground colour
+ color_t bg; ///< Background colour
short index; ///< Index number
short ref_count; ///< Number of users
TAILQ_ENTRY(CursesColor) entries; ///< Linked list
};
TAILQ_HEAD(CursesColorList, CursesColor);
-#ifdef USE_DEBUG_COLOR
-extern struct CursesColorList CursesColors;
-extern int NumCursesColors;
-#endif
-
void curses_color_free(struct CursesColor **ptr);
-struct CursesColor *curses_color_new (int fg, int bg);
+struct CursesColor *curses_color_new (color_t fg, color_t bg);
void curses_colors_init(void);
-struct CursesColor *curses_colors_find (int fg, int bg);
+struct CursesColor *curses_colors_find(color_t fg, color_t bg);
#endif /* MUTT_COLOR_CURSES2_H */
diff --git a/color/debug.c b/color/debug.c
index fa20766cd..a592a76cf 100644
--- a/color/debug.c
+++ b/color/debug.c
@@ -31,16 +31,38 @@
#include <stdbool.h>
#include <stdio.h>
#include "mutt/lib.h"
+#include "core/lib.h"
#include "gui/lib.h"
#include "debug.h"
+#include "pager/lib.h"
#include "attr.h"
#include "color.h"
#include "curses2.h"
-#include "merged.h"
+#include "pager/private_data.h" // IWYU pragma: keep
#include "quoted.h"
#include "regex4.h"
#include "simple2.h"
+extern struct AttrColorList MergedColors;
+extern struct CursesColorList CursesColors;
+extern int NumCursesColors;
+
+extern struct RegexColorList AttachList;
+extern struct RegexColorList BodyList;
+extern struct RegexColorList HeaderList;
+extern struct RegexColorList IndexAuthorList;
+extern struct RegexColorList IndexCollapsedList;
+extern struct RegexColorList IndexDateList;
+extern struct RegexColorList IndexFlagsList;
+extern struct RegexColorList IndexLabelList;
+extern struct RegexColorList IndexList;
+extern struct RegexColorList IndexNumberList;
+extern struct RegexColorList IndexSizeList;
+extern struct RegexColorList IndexSubjectList;
+extern struct RegexColorList IndexTagList;
+extern struct RegexColorList IndexTagsList;
+extern struct RegexColorList StatusList;
+
/**
* color_debug - Write to the log file
* @param level Logging level, e.g. #LL_DEBUG1
@@ -64,39 +86,36 @@ int color_debug(enum LogLevel level, const char *format, ...)
/**
* color_debug_log_color_attrs - Get a colourful string to represent a colour in the log
- * @param fg Foreground colour
- * @param bg Background colour
- * @param attrs Attributes, e.g. A_UNDERLINE
- * @retval ptr Generated string
+ * @param fg Foreground colour
+ * @param bg Background colour
+ * @param attrs Attributes, e.g. A_UNDERLINE
+ * @param swatch Buffer for swatch
*
* @note Do not free the returned string
*/
-const char *color_debug_log_color_attrs(int fg, int bg, int attrs)
+void color_debug_log_color_attrs(color_t fg, color_t bg, int attrs, struct Buffer *swatch)
{
- static char text[64];
- int pos = 0;
+ buf_reset(swatch);
if (attrs & A_BLINK)
- pos += snprintf(text + pos, sizeof(text) - pos, "\033[5m");
+ buf_add_printf(swatch, "\033[5m");
if (attrs & A_BOLD)
- pos += snprintf(text + pos, sizeof(text) - pos, "\033[1m");
+ buf_add_printf(swatch, "\033[1m");
if (attrs & A_NORMAL)
- pos += snprintf(text + pos, sizeof(text) - pos, "\033[0m");
+ buf_add_printf(swatch, "\033[0m");
if (attrs & A_REVERSE)
- pos += snprintf(text + pos, sizeof(text) - pos, "\033[7m");
+ buf_add_printf(swatch, "\033[7m");
if (attrs & A_STANDOUT)
- pos += snprintf(text + pos, sizeof(text) - pos, "\033[1m");
+ buf_add_printf(swatch, "\033[1m");
if (attrs & A_UNDERLINE)
- pos += snprintf(text + pos, sizeof(text) - pos, "\033[4m");
+ buf_add_printf(swatch, "\033[4m");
if (fg >= 0)
- pos += snprintf(text + pos, sizeof(text) - pos, "\033[38;5;%dm", fg);
+ buf_add_printf(swatch, "\033[38;5;%dm", fg);
if (bg >= 0)
- pos += snprintf(text + pos, sizeof(text) - pos, "\033[48;5;%dm", bg);
-
- snprintf(text + pos, sizeof(text) - pos, "XXXXXX\033[0m");
+ buf_add_printf(swatch, "\033[48;5;%dm", bg);
- return text;
+ buf_addstr(swatch, "XXXXXX\033[0m");
}
/**
@@ -107,7 +126,7 @@ const char *color_debug_log_color_attrs(int fg, int bg, int attrs)
*
* @note Do not free the returned string
*/
-const char *color_debug_log_color(int fg, int bg)
+const char *color_debug_log_color(color_t fg, color_t bg)
{
static char text[64];
snprintf(text, sizeof(text), "\033[38;5;%dm\033[48;5;%dmXXXXXX\033[0m", fg, bg);
@@ -210,8 +229,8 @@ void attr_color_dump(struct AttrColor *ac, const char *prefix)
int index = ac->curses_color ? ac->curses_color->index : -1;
- int fg = COLOR_DEFAULT;
- int bg = COLOR_DEFAULT;
+ color_t fg = COLOR_DEFAULT;
+ color_t bg = COLOR_DEFAULT;
struct CursesColor *cc = ac->curses_color;
if (cc)
{
@@ -299,8 +318,8 @@ void quoted_color_dump(struct AttrColor *ac, int q_level, const char *prefix)
int index = ac->curses_color ? ac->curses_color->index : -1;
- int fg = COLOR_DEFAULT;
- int bg = COLOR_DEFAULT;
+ color_t fg = COLOR_DEFAULT;
+ color_t bg = COLOR_DEFAULT;
struct CursesColor *cc = ac->curses_color;
if (cc)
{
@@ -339,8 +358,8 @@ void regex_color_dump(struct RegexColor *rcol, const char *prefix)
struct AttrColor *ac = &rcol->attr_color;
int index = ac->curses_color ? ac->curses_color->index : -1;
- int fg = COLOR_DEFAULT;
- int bg = COLOR_DEFAULT;
+ color_t fg = COLOR_DEFAULT;
+ color_t bg = COLOR_DEFAULT;
struct CursesColor *cc = ac->curses_color;
if (cc)
{
@@ -425,8 +444,8 @@ void simple_color_dump(enum ColorId cid, const char *prefix)
}
}
- int fg = COLOR_DEFAULT;
- int bg = COLOR_DEFAULT;
+ color_t fg = COLOR_DEFAULT;
+ color_t bg = COLOR_DEFAULT;
struct CursesColor *cc = ac->curses_color;
if (cc)
{
@@ -464,3 +483,176 @@ void merged_colors_dump(void)
{
attr_color_list_dump(&MergedColors, "MergedColors");
}
+
+/**
+ * color_dump - Display all the colours in the Pager
+ */
+void color_dump(void)
+{
+ struct Buffer *tmp_file = buf_pool_get();
+
+ buf_mktemp(tmp_file);
+ FILE *fp = mutt_file_fopen(buf_string(tmp_file), "w");
+ if (!fp)
+ {
+ // L10N: '%s' is the file name of the temporary file
+ mutt_error(_("Could not create temporary file %s"), buf_string(tmp_file));
+ buf_pool_release(&tmp_file);
+ return;
+ }
+
+ struct Buffer *swatch = buf_pool_get();
+ char color_fg[32] = { 0 };
+ char color_bg[32] = { 0 };
+
+ fputs("# All Colours\n\n", fp);
+ fputs("# Simple Colours\n", fp);
+ for (enum ColorId cid = MT_COLOR_NONE + 1; cid < MT_COLOR_MAX; cid++)
+ {
+ struct AttrColor *ac = simple_color_get(cid);
+ if (!ac)
+ continue;
+
+ struct CursesColor *cc = ac->curses_color;
+ if (!cc)
+ continue;
+
+ const char *name = mutt_map_get_name(cid, ColorFields);
+ if (!name)
+ continue;
+
+ color_debug_log_color_attrs(cc->fg, cc->bg, ac->attrs, swatch);
+ fprintf(fp, "color %-18s %-30s %-8s %-8s # %s\n", name,
+ color_debug_log_attrs_list(ac->attrs),
+ color_debug_log_name(color_fg, sizeof(color_fg), cc->fg),
+ color_debug_log_name(color_bg, sizeof(color_bg), cc->bg), buf_string(swatch));
+ }
+
+ if (NumQuotedColors > 0)
+ {
+ fputs("\n# Quoted Colours\n", fp);
+ for (int i = 0; i < NumQuotedColors; i++)
+ {
+ struct AttrColor *ac = quoted_colors_get(i);
+ if (!ac)
+ continue;
+
+ struct CursesColor *cc = ac->curses_color;
+ if (!cc)
+ continue;
+
+ color_debug_log_color_attrs(cc->fg, cc->bg, ac->attrs, swatch);
+ fprintf(fp, "color quoted%d %-30s %-8s %-8s # %s\n", i,
+ color_debug_log_attrs_list(ac->attrs),
+ color_debug_log_name(color_fg, sizeof(color_fg), cc->fg),
+ color_debug_log_name(color_bg, sizeof(color_bg), cc->bg),
+ buf_string(swatch));
+ }
+ }
+
+ int rl_count = 0;
+ for (enum ColorId id = MT_COLOR_NONE; id != MT_COLOR_MAX; id++)
+ {
+ if (!mutt_color_has_pattern(id))
+ {
+ continue;
+ }
+
+ struct RegexColorList *rcl = regex_colors_get_list(id);
+ if (!STAILQ_EMPTY(rcl))
+ rl_count++;
+ }
+
+ if (rl_count > 0)
+ {
+ for (enum ColorId id = MT_COLOR_NONE; id != MT_COLOR_MAX; id++)
+ {
+ if (!mutt_color_has_pattern(id))
+ {
+ continue;
+ }
+
+ struct RegexColorList *rcl = regex_colors_get_list(id);
+ if (STAILQ_EMPTY(rcl))
+ continue;
+
+ const char *name = mutt_map_get_name(id, ColorFields);
+ if (!name)
+ continue;
+
+ fprintf(fp, "\n# Regex Colour %s\n", name);
+
+ struct RegexColor *rc = NULL;
+ STAILQ_FOREACH(rc, rcl, entries)
+ {
+ struct AttrColor *ac = &rc->attr_color;
+ struct CursesColor *cc = ac->curses_color;
+ if (!cc)
+ continue;
+
+ color_debug_log_color_attrs(cc->fg, cc->bg, ac->attrs, swatch);
+ fprintf(fp, "color %-14s %-30s %-8s %-8s %-30s # %s\n", name,
+ color_debug_log_attrs_list(ac->attrs),
+ color_debug_log_name(color_fg, sizeof(color_fg), cc->fg),
+ color_debug_log_name(color_bg, sizeof(color_bg), cc->bg),
+ rc->pattern, buf_string(swatch));
+ }
+ }
+ }
+
+ if (!TAILQ_EMPTY(&MergedColors))
+ {
+ fputs("\n# Merged Colours\n", fp);
+ struct AttrColor *ac = NULL;
+ TAILQ_FOREACH(ac, &MergedColors, entries)
+ {
+ struct CursesColor *cc = ac->curses_color;
+ if (!cc)
+ continue;
+
+ color_debug_log_color_attrs(cc->fg, cc->bg, ac->attrs, swatch);
+ fprintf(fp, "# %-30s %-8s %-8s # %s\n", color_debug_log_attrs_list(ac->attrs),
+ color_debug_log_name(color_fg, sizeof(color_fg), cc->fg),
+ color_debug_log_name(color_bg, sizeof(color_bg), cc->bg),
+ buf_string(swatch));
+ }
+ }
+
+ struct MuttWindow *win = window_get_focus();
+ if (win && (win->type == WT_CUSTOM) && win->parent && (win->parent->type == WT_PAGER))
+ {
+ struct PagerPrivateData *priv = win->parent->wdata;
+ if (priv && !TAILQ_EMPTY(&priv->ansi_list))
+ {
+ fputs("\n# Ansi Colours\n", fp);
+ struct AttrColor *ac = NULL;
+ TAILQ_FOREACH(ac, &priv->ansi_list, entries)
+ {
+ struct CursesColor *cc = ac->curses_color;
+ if (!cc)
+ continue;
+
+ color_debug_log_color_attrs(cc->fg, cc->bg, ac->attrs, swatch);
+ fprintf(fp, "# %-30s %-8s %-8s # %s\n", color_debug_log_attrs_list(ac->attrs),
+ color_debug_log_name(color_fg, sizeof(color_fg), cc->fg),
+ color_debug_log_name(color_bg, sizeof(color_bg), cc->bg),
+ buf_string(swatch));
+ }
+ }
+ }
+
+ mutt_file_fclose(&fp);
+
+ struct PagerData pdata = { 0 };
+ struct PagerView pview = { &pdata };
+
+ pdata.fname = buf_string(tmp_file);
+
+ pview.banner = "color";
+ pview.flags = MUTT_SHOWCOLOR;
+ pview.mode = PAGER_MODE_OTHER;
+
+ mutt_do_pager(&pview, NULL);
+ buf_pool_release(&tmp_file);
+ buf_pool_release(&swatch);
+}
diff --git a/color/debug.h b/color/debug.h
index 9df5deda4..96c4f6d54 100644
--- a/color/debug.h
+++ b/color/debug.h
@@ -27,21 +27,19 @@
#include <stdbool.h>
#include "mutt/lib.h"
#include "color.h"
+#include "curses2.h"
struct AttrColor;
struct AttrColorList;
-struct CursesColor;
struct RegexColor;
struct RegexColorList;
-const char *color_debug_log_color_attrs(int fg, int bg, int attrs);
-const char *color_debug_log_name(char *buf, int buflen, int color);
-const char *color_debug_log_attrs_list(int attrs);
-
#ifdef USE_DEBUG_COLOR
+void color_dump(void);
+
const char *color_debug_log_attrs(int attrs);
-const char *color_debug_log_color(int fg, int bg);
+const char *color_debug_log_color(color_t fg, color_t bg);
void attr_color_dump (struct AttrColor *ac, const char *prefix);
void attr_color_list_dump (struct AttrColorList *acl, const char *title);
@@ -66,8 +64,10 @@ int color_debug(enum LogLevel level, const char *format, ...)
#else
+static inline void color_dump(void) {}
+
static inline const char *color_debug_log_attrs(int attrs) { return ""; }
-static inline const char *color_debug_log_color(int fg, int bg) { return ""; }
+static inline const char *color_debug_log_color(color_t fg, color_t bg) { return ""; }
static inline void attr_color_dump (struct AttrColor *ac, const char *prefix) {}
static inline void attr_color_list_dump (struct AttrColorList *acl, const char *title) {}
diff --git a/color/lib.h b/color/lib.h
index 29d1a09c3..3e44d6de9 100644
--- a/color/lib.h
+++ b/color/lib.h
@@ -35,6 +35,8 @@
* | color/debug.c | @subpage color_debug |
* | color/merged.c | @subpage color_merge |
* | color/notify.c | @subpage color_notify |
+ * | color/parse_ansi.c | @subpage color_parse_ansi |
+ * | color/parse_color.c | @subpage color_parse_color |
* | color/quoted.c | @subpage color_quote |
* | color/regex.c | @subpage color_regex |
* | color/simple.c | @subpage color_simple |
@@ -52,6 +54,8 @@
#include "debug.h"
#include "merged.h"
#include "notify2.h"
+#include "parse_ansi.h"
+#include "parse_color.h"
#include "quoted.h"
#include "regex4.h"
#include "simple2.h"
diff --git a/color/merged.c b/color/merged.c
index da4f41708..3cb8573f2 100644
--- a/color/merged.c
+++ b/color/merged.c
@@ -31,7 +31,6 @@
#include "config.h"
#include <stddef.h>
#include <stdbool.h>
-#include <stdint.h>
#include "mutt/lib.h"
#include "attr.h"
#include "color.h"
@@ -71,7 +70,7 @@ void merged_colors_cleanup(void)
* @param attrs Attributes, e.g. A_UNDERLINE
* @retval ptr Matching Merged colour
*/
-static struct AttrColor *merged_colors_find(int fg, int bg, int attrs)
+static struct AttrColor *merged_colors_find(color_t fg, color_t bg, int attrs)
{
struct AttrColor *ac = NULL;
TAILQ_FOREACH(ac, &MergedColors, entries)
@@ -117,8 +116,8 @@ const struct AttrColor *merged_color_overlay(const struct AttrColor *base,
struct CursesColor *cc_base = base->curses_color;
struct CursesColor *cc_over = over->curses_color;
- uint32_t fg = COLOR_DEFAULT;
- uint32_t bg = COLOR_DEFAULT;
+ color_t fg = COLOR_DEFAULT;
+ color_t bg = COLOR_DEFAULT;
if (cc_over)
{
diff --git a/color/merged.h b/color/merged.h
index 7d1b52bef..a1cf0ead6 100644
--- a/color/merged.h
+++ b/color/merged.h
@@ -27,10 +27,6 @@
struct AttrColor;
-#ifdef USE_DEBUG_COLOR
-extern struct AttrColorList MergedColors;
-#endif
-
const struct AttrColor * merged_color_overlay(const struct AttrColor *base, const struct AttrColor *over);
void merged_colors_cleanup(void);
diff --git a/color/parse_ansi.c b/color/parse_ansi.c
new file mode 100644
index 000000000..d61570875
--- /dev/null
+++ b/color/parse_ansi.c
@@ -0,0 +1,259 @@
+/**
+ * @file
+ * Parse ANSI Sequences
+ *
+ * @authors
+ * Copyright (C) 2023 Richard Russon <rich@flatcap.org>
+ *
+ * @copyright
+ * 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 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * @page color_parse_ansi Parse ANSI Sequences
+ *
+ * Parse ANSI Sequences
+ */
+
+#include "config.h"
+#include <stddef.h>
+#include <ctype.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include "mutt/lib.h"
+#include "gui/lib.h"
+#include "parse_ansi.h"
+#include "ansi.h"
+#include "color.h"
+
+/**
+ * ansi_is_end_char - Is this the end of a sequence?
+ * @param c Character to test
+ * @retval true Is it a valid end char
+ */
+static inline bool ansi_is_end_char(char c)
+{
+ return ((c == 'm') || (c == ';'));
+}
+
+/**
+ * ansi_skip_sequence - Skip an unrecognised sequence
+ * @param str String to examine
+ * @retval num Number of characters to skip over
+ */
+int ansi_skip_sequence(const char *str)
+{
+ if (!str || (str[0] == '\0'))
+ return 0;
+
+ int count = 1;
+ while ((str[0] != '\0') && !ansi_is_end_char(str[0]))
+ {
+ str++;
+ count++;
+ }
+
+ return count;
+}
+
+/**
+ * ansi_color_seq_length - Is this an ANSI escape sequence?
+ * @param str String to test
+ * @retval 0 No, not an ANSI sequence
+ * @retval >0 Length of the ANSI sequence
+ *
+ * Match ANSI escape sequences of type 'm', e.g.
+ * - `<esc>[1;32m`
+ */
+int ansi_color_seq_length(const char *str)
+{
+ if (!str || !*str)
+ return 0;
+
+ if ((str[0] != '\033') || (str[1] != '[')) // Escape
+ return 0;
+
+ int i = 2;
+ while ((str[i] != '\0') && (isdigit(str[i]) || (str[i] == ';')))
+ {
+ i++;
+ }
+
+ if (str[i] == 'm')
+ return i + 1;
+
+ return 0;
+}
+
+/**
+ * ansi_color_parse_single - Parse a single ANSI escape sequence
+ * @param buf String to parse
+ * @param ansi AnsiColor for the result
+ * @param dry_run Don't parse anything, just skip over
+ * @retval num Length of the escape sequence
+ *
+ * Parse an ANSI escape sequence into @a ansi.
+ * Calling this function repeatedly, will accumulate sequences in @a ansi.
+ */
+int ansi_color_parse_single(const char *buf, struct AnsiColor *ansi, bool dry_run)
+{
+ int seq_len = ansi_color_seq_length(buf);
+ if (seq_len == 0)
+ return 0;
+
+ if (dry_run || !ansi)
+ return seq_len;
+
+ int pos = 2; // Skip '<esc>['
+
+ while (pos < seq_len)
+ {
+ if ((buf[pos] == '0') && isdigit(buf[pos + 1]))
+ {
+ pos++; // Skip the leading zero
+ }
+ else if ((buf[pos] == '0') && ansi_is_end_char(buf[pos + 1]))
+ {
+ ansi->fg = COLOR_DEFAULT;
+ ansi->bg = COLOR_DEFAULT;
+ ansi->attrs = 0;
+ ansi->attr_color = NULL;
+ pos += 2;
+ }
+ else if ((buf[pos] == '1') && ansi_is_end_char(buf[pos + 1]))
+ {
+ ansi->attrs |= A_BOLD;
+ pos += 2;
+ }
+ else if ((buf[pos] == '3') && ansi_is_end_char(buf[pos + 1]))
+ {
+ ansi->attrs |= A_ITALIC;
+ pos += 2;
+ }
+ else if (buf[pos] == '3')
+ {
+ // 30-37 basic foreground
+ if ((buf[pos + 1] >= '0') && (buf[pos + 1] < '8') && ansi_is_end_char(buf[pos + 2]))
+ {
+ ansi->fg = buf[pos + 1] - '0';
+ pos += 3;
+ }
+ else if (buf[pos + 1] == '8')
+ {
+ if (mutt_str_startswith(buf + pos, "38;5;") && isdigit(buf[pos + 5]))
+ {
+ // 38;5;n palette foreground
+ char *end = NULL;
+ int value = strtoul(buf + pos + 5, &end, 10);
+ if ((value >= 0) && (value < 256) && end && ansi_is_end_char(end[0]))
+ {
+ ansi->fg = value;
+ pos += end - &buf[pos];
+ }
+ else
+ {
+ pos += ansi_skip_sequence(buf + pos);
+ }
+ }
+ else if (mutt_str_startswith(buf + pos, "38;2;") && isdigit(buf[pos + 5]))
+ {
+ // 38;2;R;G;B true colour foreground
+ pos += ansi_skip_sequence(buf + pos + 5);
+ pos += ansi_skip_sequence(buf + pos);
+ pos += ansi_skip_sequence(buf + pos);
+ }
+ else
+ {
+ return pos;
+ }
+ }
+ else if ((buf[pos + 1] == '9') && ansi_is_end_char(buf[pos + 2]))
+ {
+ // default fg
+ ansi->fg = COLOR_DEFAULT;
+ pos += 2;
+ }
+ else
+ {
+ pos += ansi_skip_sequence(buf + pos);
+ }
+ }
+ else if ((buf[pos] == '4') && ansi_is_end_char(buf[pos + 1]))
+ {
+ ansi->attrs |= A_UNDERLINE;
+ pos += 2;
+ }
+ else if (buf[pos] == '4')
+ {
+ // 40-47 basic background
+ if ((buf[pos + 1] >= '0') && (buf[pos + 1] < '8'))
+ {
+ ansi->bg = buf[pos + 1] - '0';
+ pos += 3;
+ }
+ else if (buf[pos + 1] == '8')
+ {
+ if (mutt_str_startswith(buf + pos, "48;5;") && isdigit(buf[pos + 5]))
+ {
+ // 48;5;n palette background
+ char *end = NULL;
+ int value = strtoul(buf + pos + 5, &end, 10);
+ if ((value >= 0) && (value < 256) && end && ansi_is_end_char(end[0]))
+ {
+ ansi->bg = value;
+ pos += end - &buf[pos];
+ }
+ else
+ {
+ pos += ansi_skip_sequence(buf + pos);
+ }
+ }
+ else if (mutt_str_startswith(buf + pos, "48;2;") && isdigit(buf[pos + 5]))
+ {
+ // 48;2;R;G;B true colour background
+ pos += ansi_skip_sequence(buf + pos + 5);
+ pos += ansi_skip_sequence(buf + pos);
+ pos += ansi_skip_sequence(buf + pos);
+ }
+ else
+ {
+ pos += ansi_skip_sequence(buf + pos);
+ }
+ }
+ else if ((buf[pos + 1] == '9') && ansi_is_end_char(buf[pos + 2]))
+ {
+ // default background
+ ansi->bg = COLOR_DEFAULT;
+ pos += 2;
+ }
+ }
+ else if ((buf[pos] == '5') && ansi_is_end_char(buf[pos + 1]))
+ {
+ ansi->attrs |= A_BLINK;
+ pos += 2;
+ }
+ else if ((buf[pos] == '7') && ansi_is_end_char(buf[pos + 1]))
+ {
+ ansi->attrs |= A_REVERSE;
+ pos += 2;
+ }
+ else
+ {
+ while ((pos < seq_len) && (buf[pos] != ';'))
+ pos++;
+ }
+ }
+
+ return pos;
+}
diff --git a/color/parse_ansi.h b/color/parse_ansi.h
new file mode 100644
index 000000000..599335a7f
--- /dev/null
+++ b/color/parse_ansi.h
@@ -0,0 +1,35 @@
+/**
+ * @file
+ * Parse ANSI Sequences
+ *
+ * @authors
+ * Copyright (C) 2023 Richard Russon <rich@flatcap.org>
+ *
+ * @copyright
+ * 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 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef MUTT_COLOR_PARSE_ANSI_H
+#define MUTT_COLOR_PARSE_ANSI_H
+
+#include <stdbool.h>
+
+struct AnsiColor;
+
+int ansi_color_parse_single(const char *buf, struct AnsiColor *ansi, bool dry_run);
+int ansi_color_seq_length (const char *str);
+
+#endif /* MUTT_COLOR_PARSE_ANSI_H */
+
+
diff --git a/color/parse_color.c b/color/parse_color.c
new file mode 100644
index 000000000..2cfa393f9
--- /dev/null
+++ b/color/parse_color.c
@@ -0,0 +1,386 @@
+/**
+ * @file
+ * Parse colours
+ *
+ * @authors
+ * Copyright (C) 2023 Richard Russon <rich@flatcap.org>
+ *
+ * @copyright
+ * 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 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * @page color_parse_color Parse colours
+ */
+
+#include "config.h"
+#include <stddef.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include "mutt/lib.h"
+#include "config/lib.h"
+#include "core/lib.h"
+#include "gui/lib.h"
+#include "parse_color.h"
+#include "parse/lib.h"
+#include "attr.h"
+#include "color.h"
+#include "curses2.h"
+#include "debug.h"
+#include "globals.h"
+
+color_t color_xterm256_to_24bit(const color_t color);
+void modify_color_by_prefix(enum ColorPrefix prefix, bool is_fg, color_t *col, int *attrs);
+
+/// Mapping between a colour name and an ncurses colour
+const struct Mapping ColorNames[] = {
+ // clang-format off
+ { "black", COLOR_BLACK },
+ { "blue", COLOR_BLUE },
+ { "cyan", COLOR_CYAN },
+ { "green", COLOR_GREEN },
+ { "magenta", COLOR_MAGENTA },
+ { "red", COLOR_RED },
+ { "white", COLOR_WHITE },
+ { "yellow", COLOR_YELLOW },
+ { "default", COLOR_DEFAULT },
+ { 0, 0 },
+ // clang-format on
+};
+
+/**
+ * AttributeNames - Mapping of attribute names to their IDs
+ */
+static struct Mapping AttributeNames[] = {
+ // clang-format off
+ { "bold", A_BOLD },
+ { "italic", A_ITALIC },
+ { "none", A_NORMAL },
+ { "normal", A_NORMAL },
+ { "reverse", A_REVERSE },
+ { "standout", A_STANDOUT },
+ { "underline", A_UNDERLINE },
+ { NULL, 0 },
+ // clang-format on
+};
+
+/**
+ * parse_color_prefix - Parse a colour prefix, e.g. "bright"
+ * @param[in] s String to parse
+ * @param[out] prefix parsed prefix, see #ColorPrefix
+ * @retval num Length of the matched prefix
+ * @retval 0 No prefix matched
+ *
+ * If prefixes should be parsed, but their value is irrelevant, NULL can be
+ * passed as 'prefix'.
+ */
+int parse_color_prefix(const char *s, enum ColorPrefix *prefix)
+{
+ int clen = 0;
+
+ if ((clen = mutt_istr_startswith(s, "bright")))
+ {
+ color_debug(LL_DEBUG5, "bright\n");
+ if (prefix)
+ *prefix = COLOR_PREFIX_BRIGHT;
+ }
+ else if ((clen = mutt_istr_startswith(s, "alert")))
+ {
+ color_debug(LL_DEBUG5, "alert\n");
+ if (prefix)
+ *prefix = COLOR_PREFIX_ALERT;
+ }
+ else if ((clen = mutt_istr_startswith(s, "light")))
+ {
+ color_debug(LL_DEBUG5, "light\n");
+ if (prefix)
+ *prefix = COLOR_PREFIX_LIGHT;
+ }
+
+ return clen;
+}
+
+/**
+ * parse_color_namedcolor - Parse a named colour, e.g. "brightred"
+ * @param[in] s String to parse
+ * @param[out] col Number for 'colorNNN' colours
+ * @param[out] attrs Attributes, e.g. A_UNDERLINE
+ * @param[in] is_fg true if this is a foreground colour
+ * @param[out] err Buffer for error messages
+ * @retval #MUTT_CMD_SUCCESS Colour parsed successfully
+ * @retval #MUTT_CMD_WARNING Unknown colour, try other parsers
+ */
+enum CommandResult parse_color_namedcolor(const char *s, color_t *col, int *attrs,
+ bool is_fg, struct Buffer *err)
+{
+ enum ColorPrefix prefix = COLOR_PREFIX_NONE;
+ s += parse_color_prefix(s, &prefix);
+
+ // COLOR_DEFAULT (-1) interferes with mutt_map_get_value()
+ if (mutt_str_equal(s, "default"))
+ {
+ *col = COLOR_DEFAULT;
+ }
+ else if ((*col = mutt_map_get_value(s, ColorNames)) == -1)
+ {
+ return MUTT_CMD_WARNING;
+ }
+
+ const char *name = mutt_map_get_name(*col, ColorNames);
+ if (name)
+ color_debug(LL_DEBUG5, "color: %s\n", name);
+
+ modify_color_by_prefix(prefix, is_fg, col, attrs);
+
+#ifdef NEOMUTT_DIRECT_COLORS
+ /* If we are running in direct color mode, we must convert the color
+ * number 0-15 to an RGB value.
+ * The first 16 colours of the xterm palette correspond to the terminal
+ * colours. Note that this replace the colour with a predefined RGB value
+ * and not the RGB value the terminal configured to use.
+ *
+ * Note that some colors are "special" e.g. "default" and do not fall in
+ * the range from 0 to 15. These must not be converted.
+ */
+ const bool c_color_directcolor = cs_subset_bool(NeoMutt->sub, "color_directcolor");
+ if (c_color_directcolor && (*col < 16))
+ {
+ *col = color_xterm256_to_24bit(*col);
+ }
+#endif
+ return MUTT_CMD_SUCCESS;
+}
+
+/**
+ * parse_color_colornnn - Parse a colorNNN, e.g. "color123".
+ * @param[in] s String to parse
+ * @param[out] col Number for 'colorNNN' colours
+ * @param[out] attrs Attributes, e.g. A_UNDERLINE
+ * @param[in] is_fg true if this is a foreground colour
+ * @param[out] err Buffer for error messages
+ * @retval #MUTT_CMD_SUCCESS Colour parsed successfully
+ * @retval #MUTT_CMD_WARNING Unknown colour, try other parsers
+ * @retval #MUTT_CMD_ERROR Error, colour could not be parsed
+ *
+ * On #MUTT_CMD_ERROR, an error message will be written to err.
+ */
+enum CommandResult parse_color_colornnn(const char *s, color_t *col, int *attrs,
+ bool is_fg, struct Buffer *err)
+{
+ /* prefixes bright, alert, light are only allowed for named colours and
+ * colorNNN for backwards compatibility. */
+ enum ColorPrefix prefix = COLOR_PREFIX_NONE;
+ s += parse_color_prefix(s, &prefix);
+
+ int clen = 0;
+ /* allow aliases for xterm color resources */
+ if ((clen = mutt_istr_startswith(s, "color")) == 0)
+ return MUTT_CMD_WARNING;
+
+ s += clen;
+ char *eptr = NULL;
+ *col = strtoul(s, &eptr, 10);
+ /* There are only 256 xterm colors. Do not confuse with COLORS which is
+ * the number of colours the terminal supports (usually one of 16, 256,
+ * 16777216 (=24bit)). */
+ if ((*s == '\0') || (*eptr != '\0') || (*col >= 256) || ((*col >= COLORS) && !OptNoCurses))
+ {
+ buf_printf(err, _("%s: color not supported by term"), s);
+ return MUTT_CMD_ERROR;
+ }
+
+ modify_color_by_prefix(prefix, is_fg, col, attrs);
+
+#ifdef NEOMUTT_DIRECT_COLORS
+ const bool c_color_directcolor = cs_subset_bool(NeoMutt->sub, "color_directcolor");
+ if (c_color_directcolor)
+ {
+ /* If we are running in direct color mode, we must convert the xterm
+ * color numbers 0-255 to an RGB value. */
+ *col = color_xterm256_to_24bit(*col);
+ /* FIXME: The color values 0 to 7 (both inclusive) are still occupied by
+ * the default terminal colours. As a workaround we round them up to
+ * #000008 which is the blackest black we can produce. */
+ if (*col < 8)
+ *col = 8;
+ }
+#endif
+ color_debug(LL_DEBUG5, "colorNNN %d\n", *col);
+ return MUTT_CMD_SUCCESS;
+}
+
+/**
+ * parse_color_rrggbb - Parse an RGB colour, e.g. "#12FE45"
+ * @param[in] s String to parse
+ * @param[out] col Number for 'colorNNN' colours
+ * @param[out] attrs Attributes, e.g. A_UNDERLINE
+ * @param[in] is_fg true if this is a foreground colour
+ * @param[out] err Buffer for error messages
+ * @retval #MUTT_CMD_SUCCESS Colour parsed successfully
+ * @retval #MUTT_CMD_WARNING Unknown colour, try other parsers
+ * @retval #MUTT_CMD_ERROR Error, colour could not be parsed
+ *
+ * On #MUTT_CMD_ERROR, an error message will be written to err.
+ */
+enum CommandResult parse_color_rrggbb(const char *s, color_t *col, int *attrs,
+ bool is_fg, struct Buffer *err)
+{
+ /* parse #RRGGBB colours */
+ if (s[0] != '#')
+ return MUTT_CMD_WARNING;
+
+#ifndef NEOMUTT_DIRECT_COLORS
+ buf_printf(err, _("Direct colors support not compiled in: %s"), s);
+ return MUTT_CMD_ERROR;
+#endif
+ const bool c_color_directcolor = cs_subset_bool(NeoMutt->sub, "color_directcolor");
+ if (!c_color_directcolor)
+ {
+ buf_printf(err, _("Direct colors support disabled: %s"), s);
+ return MUTT_CMD_ERROR;
+ }
+ s++;
+ char *eptr = NULL;
+ *col = strtoul(s, &eptr, 16);
+ if ((*s == '\0') || (*eptr != '\0') || ((*col >= COLORS) && !OptNoCurses))
+ {
+ buf_printf(err, _("%s: color not supported by term"), s);
+ return MUTT_CMD_ERROR;
+ }
+ /* FIXME: The color values 0 to 7 (both inclusive) are still occupied by
+ * the default terminal colours. As a workaround we round them up to
+ * #000008 which is the blackest black we can produce. */
+ if (*col < 8)
+ *col = 8;
+
+ color_debug(LL_DEBUG5, "#RRGGBB: %d\n", *col);
+ return MUTT_CMD_SUCCESS;
+}
+
+/**
+ * parse_color_name - Parse a colour name
+ * @param[in] s String to parse
+ * @param[out] col Number for 'colorNNN' colours
+ * @param[out] attrs Attributes, e.g. A_UNDERLINE
+ * @param[in] is_fg true if this is a foreground colour
+ * @param[out] err Buffer for error messages
+ * @retval #CommandResult Result e.g. #MUTT_CMD_SUCCESS
+ *
+ * Parse a colour name, such as "red", "brightgreen", "color123", "#12FE45"
+ */
+enum CommandResult parse_color_name(const char *s, color_t *col, int *attrs,
+ bool is_fg, struct Buffer *err)
+{
+ mutt_debug(LL_DEBUG5, "Parsing color name: %s\n", s);
+
+ /* Try the different colour syntaxes. A return value of MUTT_CMD_WARNING
+ * means, we should try the next syntax. */
+ enum CommandResult cr;
+
+ /* #RRGGBB */
+ cr = parse_color_rrggbb(s, col, attrs, is_fg, err);
+ if (cr != MUTT_CMD_WARNING)
+ return cr;
+
+ /* color123 */
+ cr = parse_color_colornnn(s, col, attrs, is_fg, err);
+ if (cr != MUTT_CMD_WARNING)
+ return cr;
+
+ /* named color, e.g. "brightred" */
+ cr = parse_color_namedcolor(s, col, attrs, is_fg, err);
+ if (cr != MUTT_CMD_WARNING)
+ return cr;
+
+ buf_printf(err, _("%s: no such color"), s);
+ return MUTT_CMD_WARNING;
+}
+
+/**
+ * parse_color_pair - Parse a pair of colours - Implements ::parser_callback_t - @ingroup parser_callback_api
+ *
+ * Parse a pair of colours, e.g. "red default"
+ */
+enum CommandResult parse_color_pair(struct Buffer *buf, struct Buffer *s, color_t *fg,
+ color_t *bg, int *attrs, struct Buffer *err)
+{
+ while (true)
+ {
+ if (!MoreArgsF(s, TOKEN_COMMENT))
+ {
+ buf_printf(err, _("%s: too few arguments"), "color");
+ return MUTT_CMD_WARNING;
+ }
+
+ parse_extract_token(buf, s, TOKEN_COMMENT);
+
+ int attr = mutt_map_get_value(buf->data, AttributeNames);
+ if (attr == -1)
+ {
+ enum CommandResult rc = parse_color_name(buf->data, fg, attrs, true, err);
+ if (rc != MUTT_CMD_SUCCESS)
+ return rc;
+ break;
+ }
+
+ if (attr == A_NORMAL)
+ *attrs = attr; // Clear all attributes
+ else
+ *attrs |= attr; // Merge with other attributes
+ }
+
+ if (!MoreArgsF(s, TOKEN_COMMENT))
+ {
+ buf_printf(err, _("%s: too few arguments"), "color");
+ return MUTT_CMD_WARNING;
+ }
+
+ parse_extract_token(buf, s, TOKEN_COMMENT);
+
+ return parse_color_name(buf->data, bg, attrs, false, err);
+}
+
+/**
+ * parse_attr_spec - Parse an attribute description - Implements ::parser_callback_t - @ingroup parser_callback_api
+ */
+enum CommandResult parse_attr_spec(struct Buffer *buf, struct Buffer *s, color_t *fg,
+ color_t *bg, int *attrs, struct Buffer *err)
+{
+ if (fg)
+ *fg = COLOR_DEFAULT;
+ if (bg)
+ *bg = COLOR_DEFAULT;
+
+ if (!MoreArgs(s))
+ {
+ buf_printf(err, _("%s: too few arguments"), "mono");
+ return MUTT_CMD_WARNING;
+ }
+
+ parse_extract_token(buf, s, TOKEN_NO_FLAGS);
+
+ int attr = mutt_map_get_value(buf->data, AttributeNames);
+ if (attr == -1)
+ {
+ buf_printf(err, _("%s: no such attribute"), buf->data);
+ return MUTT_CMD_WARNING;
+ }
+
+ if (attr == A_NORMAL)
+ *attrs = attr; // Clear all attributes
+ else
+ *attrs |= attr; // Merge with other attributes
+
+ return MUTT_CMD_SUCCESS;
+}
diff --git a/color/parse_color.h b/color/parse_color.h
new file mode 100644
index 000000000..92adfa308
--- /dev/null
+++ b/color/parse_color.h
@@ -0,0 +1,34 @@
+/**
+ * @file
+ * Parse colour commands
+ *
+ * @authors
+ * Copyright (C) 2023 Richard Russon <rich@flatcap.org>
+ *
+ * @copyright
+ * 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 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef MUTT_COLOR_PARSE_COLOR_H
+#define MUTT_COLOR_PARSE_COLOR_H
+
+#include "core/lib.h"
+#include "curses2.h"
+
+struct Buffer;
+
+enum CommandResult parse_attr_spec (struct Buffer *buf, struct Buffer *s, color_t *fg, color_t *bg, int *attrs, struct Buffer *err);
+enum CommandResult parse_color_pair(struct Buffer *buf, struct Buffer *s, color_t *fg, color_t *bg, int *attrs, struct Buffer *err);
+
+#endif /* MUTT_COLOR_PARSE_COLOR_H */
diff --git a/color/quoted.c b/color/quoted.c
index aeebd5441..135e28f8d 100644
--- a/color/quoted.c
+++ b/color/quoted.c
@@ -29,7 +29,6 @@
#include "config.h"
#include <stddef.h>
#include <stdbool.h>
-#include <stdint.h>
#include "mutt/lib.h"
#include "core/lib.h"
#include "quoted.h"
@@ -103,7 +102,7 @@ int quoted_colors_num_used(void)
* @param err Buffer for error messages
* @retval true Colour was parsed
*/
-bool quoted_colors_parse_color(enum ColorId cid, uint32_t fg, uint32_t bg,
+bool quoted_colors_parse_color(enum ColorId cid, color_t fg, color_t bg,
int attrs, int q_level, int *rc, struct Buffer *err)
{
if (cid != MT_COLOR_QUOTED)
diff --git a/color/quoted.h b/color/quoted.h
index 611071d12..ccca2c4cb 100644
--- a/color/quoted.h
+++ b/color/quoted.h
@@ -26,10 +26,10 @@
#include "config.h"
#include <stddef.h>
#include <stdbool.h>
-#include <stdint.h>
#include "core/lib.h"
#include "attr.h"
#include "color.h"
+#include "curses2.h"
struct Buffer;
@@ -78,7 +78,7 @@ void quoted_colors_cleanup(void);
struct AttrColor * quoted_colors_get(int q);
int quoted_colors_num_used(void);
-bool quoted_colors_parse_color (enum ColorId cid, uint32_t fg, uint32_t bg, int attrs, int q_level, int *rc, struct Buffer *err);
+bool quoted_colors_parse_color (enum ColorId cid, color_t fg, color_t bg, int attrs, int q_level, int *rc, struct Buffer *err);
enum CommandResult quoted_colors_parse_uncolor(enum ColorId cid, int q_level, struct Buffer *err);
struct QuoteStyle *qstyle_classify (struct QuoteStyle **quote_list, const char *qptr, size_t length, bool *force_redraw, int *q_level);
diff --git a/color/regex.c b/color/regex.c
index c8a1f5978..28775e6ae 100644
--- a/color/regex.c
+++ b/color/regex.c
@@ -237,7 +237,7 @@ struct RegexColorList *regex_colors_get_list(enum ColorId cid)
* called from mutt_parse_color()
*/
static enum CommandResult add_pattern(struct RegexColorList *rcl, const char *s,
- bool sensitive, uint32_t fg, uint32_t bg, int attrs,
+ bool sensitive, color_t fg, color_t bg, int attrs,
struct Buffer *err, bool is_index, int match)
{
struct RegexColor *rcol = NULL;
@@ -337,8 +337,8 @@ static enum CommandResult add_pattern(struct RegexColorList *rcl, const char *s,
*
* Parse a Regex 'color' command, e.g. "color index green default pattern"
*/
-bool regex_colors_parse_color_list(enum ColorId cid, const char *pat, uint32_t fg,
- uint32_t bg, int attrs, int *rc, struct Buffer *err)
+bool regex_colors_parse_color_list(enum ColorId cid, const char *pat, color_t fg,
+ color_t bg, int attrs, int *rc, struct Buffer *err)
{
if (cid == MT_COLOR_STATUS)
@@ -407,8 +407,8 @@ bool regex_colors_parse_color_list(enum ColorId cid, const char *pat, uint32_t f
* @param err Buffer for error messages
* @retval #CommandResult Result e.g. #MUTT_CMD_SUCCESS
*/
-int regex_colors_parse_status_list(enum ColorId cid, const char *pat, uint32_t fg,
- uint32_t bg, int attrs, int match, struct Buffer *err)
+int regex_colors_parse_status_list(enum ColorId cid, const char *pat, color_t fg,
+ color_t bg, int attrs, int match, struct Buffer *err)
{
if (cid != MT_COLOR_STATUS)
return MUTT_CMD_ERROR;
diff --git a/color/regex4.h b/color/regex4.h
index 31af406c8..dea7fc50d 100644
--- a/color/regex4.h
+++ b/color/regex4.h
@@ -25,10 +25,10 @@
#include "config.h"
#include <stdbool.h>
-#include <stdint.h>
#include "mutt/lib.h"
#include "attr.h"
#include "color.h"
+#include "curses2.h"
/**
* struct RegexColor - A regular expression and a color to highlight a line
@@ -43,28 +43,10 @@ struct RegexColor
bool stop_matching : 1; ///< Used by the pager for body patterns, to prevent the color from being retried once it fails
- STAILQ_ENTRY(RegexColor) entries; ///< Linked list
+ STAILQ_ENTRY(RegexColor) entries; ///< Linked list
};
STAILQ_HEAD(RegexColorList, RegexColor);
-#ifdef USE_DEBUG_COLOR
-extern struct RegexColorList AttachList;
-extern struct RegexColorList BodyList;
-extern struct RegexColorList HeaderList;
-extern struct RegexColorList IndexAuthorList;
-extern struct RegexColorList IndexCollapsedList;
-extern struct RegexColorList IndexDateList;
-extern struct RegexColorList IndexFlagsList;
-extern struct RegexColorList IndexLabelList;
-extern struct RegexColorList IndexList;
-extern struct RegexColorList IndexNumberList;
-extern struct RegexColorList IndexSizeList;
-extern struct RegexColorList IndexSubjectList;
-extern struct RegexColorList IndexTagList;
-extern struct RegexColorList IndexTagsList;
-extern struct RegexColorList StatusList;
-#endif
-
void regex_color_clear(struct RegexColor *rcol);
void regex_color_free(struct RegexColorList *list, struct RegexColor **ptr);
struct RegexColor * regex_color_new (void);
@@ -75,8 +57,8 @@ void regex_colors_init(void);
void regex_color_list_clear(struct RegexColorList *rcl);
-bool regex_colors_parse_color_list (enum ColorId cid, const char *pat, uint32_t fg, uint32_t bg, int attrs, int *rc, struct Buffer *err);
-int regex_colors_parse_status_list(enum ColorId cid, const char *pat, uint32_t fg, uint32_t bg, int attrs, int match, struct Buffer *err);
+bool regex_colors_parse_color_list (enum ColorId cid, const char *pat, color_t fg, color_t bg, int attrs, int *rc, struct Buffer *err);
+int regex_colors_parse_status_list(enum ColorId cid, const char *pat, color_t fg, color_t bg, int attrs, int match, struct Buffer *err);
bool regex_colors_parse_uncolor (enum ColorId cid, const char *pat, bool uncolor);
#endif /* MUTT_COLOR_REGEX4_H */
diff --git a/color/simple.c b/color/simple.c
index bb1aec3c9..cdf57db7a 100644
--- a/color/simple.c
+++ b/color/simple.c
@@ -122,7 +122,7 @@ bool simple_color_is_header(enum ColorId cid)
* @param attrs Attributes, e.g. A_UNDERLINE
* @retval ptr Colour
*/
-struct AttrColor *simple_color_set(enum ColorId cid, int fg, int bg, int attrs)
+struct AttrColor *simple_color_set(enum ColorId cid, color_t fg, color_t bg, int attrs)
{
struct AttrColor *ac = simple_color_get(cid);
if (!ac)
diff --git a/color/simple2.h b/color/simple2.h
index b250eca45..62fcab464 100644
--- a/color/simple2.h
+++ b/color/simple2.h
@@ -27,6 +27,7 @@
#include <stdbool.h>
#include "attr.h"
#include "color.h"
+#include "curses2.h"
extern struct AttrColor SimpleColors[];
@@ -34,7 +35,7 @@ struct AttrColor *simple_color_get (enum ColorId cid);
bool simple_color_is_header(enum ColorId cid);
bool simple_color_is_set (enum ColorId cid);
void simple_color_reset (enum ColorId cid);
-struct AttrColor *simple_color_set (enum ColorId cid, int fg, int bg, int attrs);
+struct AttrColor *simple_color_set (enum ColorId cid, color_t fg, color_t bg, int attrs);
void simple_colors_cleanup(void);
void simple_colors_init(void);
diff --git a/debug/lib.h b/debug/lib.h
index 37b76d724..4990111e3 100644
--- a/debug/lib.h
+++ b/debug/lib.h
@@ -43,6 +43,7 @@
#include <stddef.h>
#include <stdbool.h>
+#include <time.h>
#include "mutt/lib.h"
#include "email/lib.h"
#include "core/lib.h"
diff --git a/pager/dlg_pager.c b/pager/dlg_pager.c
index 703df9bf8..171b4dbcd 100644
--- a/pager/dlg_pager.c
+++ b/pager/dlg_pager.c
@@ -492,9 +492,7 @@ int dlg_pager(struct PagerView *pview)
continue;
}
-#ifdef USE_DEBUG_COLOR
dump_pager(priv);
-#endif
//-------------------------------------------------------------------------
// Finally, read user's key press
diff --git a/pager/lib.h b/pager/lib.h
index 362d3d15d..5181963a5 100644
--- a/pager/lib.h
+++ b/pager/lib.h
@@ -210,6 +210,11 @@ struct TextSyntax;
struct Line;
void dump_text_syntax(struct TextSyntax *ts, int num);
void dump_line(int i, struct Line *line);
+
+#ifdef USE_DEBUG_COLOR
void dump_pager(struct PagerPrivateData *priv);
+#else
+static inline void dump_pager(struct PagerPrivateData *priv) {}
+#endif
#endif /* MUTT_PAGER_LIB_H */
diff --git a/pager/pager.c b/pager/pager.c
index 7dfa3d5b6..e8e77ebc7 100644
--- a/pager/pager.c
+++ b/pager/pager.c
@@ -133,9 +133,7 @@ static int pager_repaint(struct MuttWindow *win)
if (!priv || !priv->pview || !priv->pview->pdata)
return 0;
-#ifdef USE_DEBUG_COLOR
dump_pager(priv);
-#endif
// We need to populate more lines, but not change position
const bool repopulate = (priv->cur_line > priv->lines_used);
diff --git a/version.c b/version.c
index b96a1d774..2e1a13c4c 100644
--- a/version.c
+++ b/version.c
@@ -295,6 +295,11 @@ static const struct CompileOptions CompOpts[] = {
#else
{ "sun_attachment", 0 },
#endif
+#ifdef NEOMUTT_DIRECT_COLORS
+ { "truecolor", 1 },
+#else
+ { "truecolor", 0 },
+#endif
{ NULL, 0 },
};
@@ -318,6 +323,9 @@ static const struct CompileOptions DebugOpts[] = {
#ifdef USE_DEBUG_KEYMAP
{ "keymap", 2 },
#endif
+#ifdef USE_DEBUG_LOGGING
+ { "logging", 2 },
+#endif
#ifdef USE_DEBUG_NAMES
{ "names", 2 },
#endif