From 082cbe519bd14453fc4669758a81ff8d9ed6baca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Helleu?= Date: Wed, 1 Nov 2023 13:21:06 +0100 Subject: irc: add support of RGB colors in messages (issue #2025) This is made using standard color code '\x04' followed by text color (RGB as hexadecimal) and optional background (RGB as hexadecimal). --- ChangeLog.adoc | 1 + ReleaseNotes.adoc | 12 + doc/de/weechat_user.de.adoc | 14 +- doc/en/weechat_user.en.adoc | 10 +- doc/fr/weechat_user.fr.adoc | 10 +- doc/it/weechat_user.it.adoc | 14 +- doc/ja/weechat_user.ja.adoc | 14 +- doc/pl/weechat_user.pl.adoc | 14 +- doc/sr/weechat_user.sr.adoc | 15 +- src/gui/curses/gui-curses-key.c | 1 + src/plugins/irc/irc-color.c | 349 ++++++++++++++++++++++-------- src/plugins/irc/irc-color.h | 5 +- tests/unit/plugins/irc/test-irc-color.cpp | 114 +++++++--- 13 files changed, 448 insertions(+), 125 deletions(-) diff --git a/ChangeLog.adoc b/ChangeLog.adoc index 9c9aae167..d127b7c90 100644 --- a/ChangeLog.adoc +++ b/ChangeLog.adoc @@ -18,6 +18,7 @@ New features:: * core: display only version with command `/version`, add options `-o` and `-ol` in command `/upgrade` * core: add number of processes in command `/sys waitpid` * core, alias, trigger: allow wildcard in commands `/bar`, `/item`, `/proxy`, `/alias` and `/trigger` (issue #1956) + * irc: add support of RGB colors in messages (issue #2025) * irc: add tags "nick_xxx" and "host_xxx" in all messages, including self and server messages * irc: add option irc.look.ignore_tag_messages (issue #989) * trigger: rename local variable "trigger_filter" to "filter" on monitor buffer (issue #2037) diff --git a/ReleaseNotes.adoc b/ReleaseNotes.adoc index 3acd0fdde..69f27a313 100644 --- a/ReleaseNotes.adoc +++ b/ReleaseNotes.adoc @@ -14,6 +14,18 @@ For a complete list of changes, please look at ChangeLog. [[v4.2.0]] == Version 4.2.0 (under dev) +[[4.2.0_irc_rgb_colors]] +=== RGB colors in IRC messages + +Support for RGB colors in IRC messages has been added and a new key +[Ctrl+c], kbd:[d] is available to insert this color code in command line. + +You can add this key with this command: + +---- +/key missing +---- + [[v4.2.0_custom_bar_items]] === Custom bar items diff --git a/doc/de/weechat_user.de.adoc b/doc/de/weechat_user.de.adoc index 10e11fd6e..a30396ac1 100644 --- a/doc/de/weechat_user.de.adoc +++ b/doc/de/weechat_user.de.adoc @@ -1016,6 +1016,13 @@ Zeichen, genutzt werden: | kbd:[Ctrl+c], kbd:[c], kbd:[xx], kbd:[,], kbd:[yy] | Textfarbe `xx` und Hintergrundfarbe `yy` (siehe Farbtabelle). +// TRANSLATION MISSING +| kbd:[Ctrl+c], kbd:[d], + kbd:[xxxxxx] | Text color `xxxxxx` (RGB as hexadecimal, for example `FF0000` for red). +// TRANSLATION MISSING +| kbd:[Ctrl+c], kbd:[d], + kbd:[xxxxxx], kbd:[,], + kbd:[yyyyyy] | Text color `xxxxxx` and background `yyyyyy` (RGB as hexadecimal). | kbd:[Ctrl+c], kbd:[i] | Text wird kursiv dargestellt. | kbd:[Ctrl+c], kbd:[o] | deaktiviert Farben und Attribute. | kbd:[Ctrl+c], kbd:[v] | Farben umkehren (kehrt Textfarbe und Hintergrundfarbe um). @@ -1023,8 +1030,9 @@ Zeichen, genutzt werden: |=== [NOTE] -Der selbe Befehl (ohne den Wert für kbd:[Ctrl+c], kbd:[c]) sollte genutzt werden -um die Farbkodierungen bzw. Attribute zu deaktivieren. +// TRANSLATION MISSING +The same code (without number for kbd:[Ctrl+c], kbd:[c] and kbd:[Ctrl+c], kbd:[d]) +can be used to stop the attribute. Farbtabelle für kbd:[Ctrl+c], kbd:[c]: @@ -1587,6 +1595,8 @@ Sie können mit dem Befehl <> geändert und neue hinzu | Taste | Beschreibung | Befehl | kbd:[Ctrl+c], kbd:[b] | fügt Steuerzeichen für fett geschrieben Text ein. | `+/input insert \x02+` | kbd:[Ctrl+c], kbd:[c] | fügt Steuerzeichen für Textfarbe ein. | `+/input insert \x03+` +// TRANSLATION MISSING +| kbd:[Ctrl+c], kbd:[d] | Insert code for colored text (RGB color, as hexadecimal). | `+/input insert \x04+` | kbd:[Ctrl+c], kbd:[i] | fügt Steuerzeichen für kursiven Text ein. | `+/input insert \x1D+` | kbd:[Ctrl+c], kbd:[o] | fügt Steuerzeichen für Standardfarbe ein. | `+/input insert \x0F+` | kbd:[Ctrl+c], kbd:[v] | fügt Steuerzeichen für Hintergrundfarbe ein. | `+/input insert \x16+` diff --git a/doc/en/weechat_user.en.adoc b/doc/en/weechat_user.en.adoc index 61e0d69fe..41dc69b73 100644 --- a/doc/en/weechat_user.en.adoc +++ b/doc/en/weechat_user.en.adoc @@ -1007,6 +1007,11 @@ follow (press kbd:[Ctrl+c] then following letter, with optional value): | kbd:[Ctrl+c], kbd:[c], kbd:[xx], kbd:[,], kbd:[yy] | Text color `xx` and background `yy` (see list of colors below). +| kbd:[Ctrl+c], kbd:[d], + kbd:[xxxxxx] | Text color `xxxxxx` (RGB as hexadecimal, for example `FF0000` for red). +| kbd:[Ctrl+c], kbd:[d], + kbd:[xxxxxx], kbd:[,], + kbd:[yyyyyy] | Text color `xxxxxx` and background `yyyyyy` (RGB as hexadecimal). | kbd:[Ctrl+c], kbd:[i] | Italic text. | kbd:[Ctrl+c], kbd:[o] | Disable color and attributes. | kbd:[Ctrl+c], kbd:[v] | Reverse video (revert text color with background). @@ -1014,8 +1019,8 @@ follow (press kbd:[Ctrl+c] then following letter, with optional value): |=== [NOTE] -The same code (without number for kbd:[Ctrl+c], kbd:[c]) can be used to stop the -attribute. +The same code (without number for kbd:[Ctrl+c], kbd:[c] and kbd:[Ctrl+c], kbd:[d]) +can be used to stop the attribute. Color codes for kbd:[Ctrl+c], kbd:[c] are: @@ -1574,6 +1579,7 @@ They can be changed and new ones can be added with the <= IRC_COLOR_TERM2IRC_NUM_COLORS)) + { + free (info_color); + return -1; + } + + free (info_color); + + return irc_color_term2irc[number]; +} + +/* + * Converts a terminal color to IRC color. + * + * Returns a IRC color number (between 0 and 15), -1 if error. + */ + +int +irc_color_convert_term2irc (int color) +{ + char str_color[64], *error, *info_color; + long number; + + snprintf (str_color, sizeof (str_color), "%d", color); + + info_color = weechat_info_get ("color_term2rgb", str_color); + if (!info_color || !info_color[0]) + { + if (info_color) + free (info_color); + return -1; + } + + error = NULL; + number = strtol (info_color, &error, 10); + if (!error || error[0] || (number < 0) || (number > 0xFFFFFF)) + { + free (info_color); + return -1; + } + + free (info_color); + + return irc_color_convert_rgb2irc (number); +} + /* * Replaces IRC colors by WeeChat colors. * @@ -95,11 +210,12 @@ regex_t *irc_color_regex_ansi = NULL; char * irc_color_decode (const char *string, int keep_colors) { - char **out; - char str_fg[3], str_bg[3], str_color[128], str_key[128], str_to_add[128]; + char **out, *error; + char str_fg[16], str_bg[16], str_color[128], str_key[128], str_to_add[128]; const char *remapped_color; unsigned char *ptr_string; - int length, fg, bg, bold, reverse, italic, underline, rc; + int length, fg, bg, bold, reverse, italic, underline, color_number; + long fg_rgb, bg_rgb; if (!string) return NULL; @@ -211,19 +327,21 @@ irc_color_decode (const char *string, int keep_colors) bg = -1; if (str_fg[0]) { - rc = sscanf (str_fg, "%d", &fg); - if ((rc != EOF) && (rc >= 1)) - { + error = NULL; + fg = (int)strtol (str_fg, &error, 10); + if (error && !error[0]) fg %= IRC_NUM_COLORS; - } + else + fg = -1; } if (str_bg[0]) { - rc = sscanf (str_bg, "%d", &bg); - if ((rc != EOF) && (rc >= 1)) - { + error = NULL; + bg = (int)strtol (str_bg, &error, 10); + if (error && !error[0]) bg %= IRC_NUM_COLORS; - } + else + bg = -1; } /* search "fg,bg" in hashtable of remapped colors */ snprintf (str_key, sizeof (str_key), "%d,%d", fg, bg); @@ -253,6 +371,94 @@ irc_color_decode (const char *string, int keep_colors) } } break; + case IRC_COLOR_HEX_COLOR_CHAR: + ptr_string++; + str_fg[0] = '\0'; + str_bg[0] = '\0'; + if (isxdigit (ptr_string[0])) + { + /* foreground */ + length = 1; + while (isxdigit (ptr_string[length])) + { + length++; + if (length == 6) + break; + } + memcpy (str_fg, ptr_string, length); + str_fg[length] = '\0'; + ptr_string += length; + } + if ((ptr_string[0] == ',') && (isxdigit (ptr_string[1]))) + { + /* background */ + ptr_string++; + length = 1; + while (isxdigit (ptr_string[length])) + { + length++; + if (length == 6) + break; + } + memcpy (str_bg, ptr_string, length); + str_bg[length] = '\0'; + ptr_string += length; + } + if (keep_colors) + { + if (str_fg[0] || str_bg[0]) + { + fg_rgb = -1; + bg_rgb = -1; + if (str_fg[0]) + { + error = NULL; + fg_rgb = strtol (str_fg, &error, 16); + if (!error || error[0]) + fg_rgb = -1; + } + if (str_bg[0]) + { + error = NULL; + bg_rgb = strtol (str_bg, &error, 16); + if (!error || error[0]) + bg_rgb = -1; + } + str_fg[0] = '\0'; + str_bg[0] = '\0'; + if (fg_rgb >= 0) + { + color_number = irc_color_convert_rgb2term (fg_rgb); + if (color_number >= 0) + { + snprintf (str_fg, sizeof (str_fg), + "%d", color_number); + } + } + if (bg_rgb >= 0) + { + color_number = irc_color_convert_rgb2term (bg_rgb); + if (color_number >= 0) + { + snprintf (str_bg, sizeof (str_bg), + "%d", color_number); + } + } + snprintf (str_color, sizeof (str_color), + "|%s%s%s", + str_fg, + (str_bg[0]) ? "," : "", + str_bg); + snprintf (str_to_add, sizeof (str_to_add), "%s", + weechat_color (str_color)); + } + else + { + snprintf (str_to_add, sizeof (str_to_add), "%s", + weechat_color ("resetcolor")); + } + } + break; default: /* * we are not on an IRC color code, just copy the UTF-8 char @@ -364,6 +570,53 @@ irc_color_encode (const char *string, int keep_colors) } } break; + case 0x04: /* ^D */ + if (keep_colors) + weechat_string_dyn_concat (out, IRC_COLOR_HEX_COLOR_STR, -1); + ptr_string++; + if (isxdigit (ptr_string[0])) + { + /* foreground */ + length = 1; + while (isxdigit (ptr_string[length])) + { + length++; + if (length == 6) + break; + } + if (keep_colors) + { + weechat_string_dyn_concat (out, + (const char *)ptr_string, + length); + } + ptr_string += length; + } + if (ptr_string[0] == ',') + { + /* background */ + if (keep_colors) + weechat_string_dyn_concat (out, ",", -1); + ptr_string++; + if (isxdigit (ptr_string[0])) + { + length = 1; + while (isxdigit (ptr_string[length])) + { + length++; + if (length == 6) + break; + } + if (keep_colors) + { + weechat_string_dyn_concat (out, + (const char *)ptr_string, + length); + } + ptr_string += length; + } + } + break; case 0x0F: /* ^O */ if (keep_colors) weechat_string_dyn_concat (out, IRC_COLOR_RESET_STR, -1); @@ -398,80 +651,6 @@ irc_color_encode (const char *string, int keep_colors) return weechat_string_dyn_free (out, 0); } -/* - * Converts a RGB color to IRC color. - * - * Returns a IRC color number (between 0 and 15), -1 if error. - */ - -int -irc_color_convert_rgb2irc (int rgb) -{ - char str_color[64], *error, *info_color; - long number; - - snprintf (str_color, sizeof (str_color), - "%d,%d", - rgb, - IRC_COLOR_TERM2IRC_NUM_COLORS); - - info_color = weechat_info_get ("color_rgb2term", str_color); - if (!info_color || !info_color[0]) - { - if (info_color) - free (info_color); - return -1; - } - - error = NULL; - number = strtol (info_color, &error, 10); - if (!error || error[0] - || (number < 0) || (number >= IRC_COLOR_TERM2IRC_NUM_COLORS)) - { - free (info_color); - return -1; - } - - free (info_color); - - return irc_color_term2irc[number]; -} - -/* - * Converts a terminal color to IRC color. - * - * Returns a IRC color number (between 0 and 15), -1 if error. - */ - -int -irc_color_convert_term2irc (int color) -{ - char str_color[64], *error, *info_color; - long number; - - snprintf (str_color, sizeof (str_color), "%d", color); - - info_color = weechat_info_get ("color_term2rgb", str_color); - if (!info_color || !info_color[0]) - { - if (info_color) - free (info_color); - return -1; - } - - error = NULL; - number = strtol (info_color, &error, 10); - if (!error || error[0] || (number < 0) || (number > 0xFFFFFF)) - { - free (info_color); - return -1; - } - - free (info_color); - - return irc_color_convert_rgb2irc (number); -} - /* * Replaces ANSI colors by IRC colors (or removes them). * diff --git a/src/plugins/irc/irc-color.h b/src/plugins/irc/irc-color.h index 6e576c050..9f937c85f 100644 --- a/src/plugins/irc/irc-color.h +++ b/src/plugins/irc/irc-color.h @@ -41,9 +41,12 @@ #define IRC_COLOR_BOLD_CHAR '\x02' /* bold text */ #define IRC_COLOR_BOLD_STR "\x02" /* [02]...[02] */ -#define IRC_COLOR_COLOR_CHAR '\x03' /* text color: fg / fg,bg / ,bg */ +#define IRC_COLOR_COLOR_CHAR '\x03' /* text color: fg/fg,bg/,bg */ #define IRC_COLOR_COLOR_STR "\x03" /* [03]15,05...[03] */ +#define IRC_COLOR_HEX_COLOR_CHAR '\x04' /* text color (hex): fg/fg,bg/,bg */ +#define IRC_COLOR_HEX_COLOR_STR "\x04" /* [04]FFFF00,8B008B...[04] */ + #define IRC_COLOR_RESET_CHAR '\x0F' /* reset color/attributes */ #define IRC_COLOR_RESET_STR "\x0F" /* [0F]... */ diff --git a/tests/unit/plugins/irc/test-irc-color.cpp b/tests/unit/plugins/irc/test-irc-color.cpp index 3ed977364..e6065fdcc 100644 --- a/tests/unit/plugins/irc/test-irc-color.cpp +++ b/tests/unit/plugins/irc/test-irc-color.cpp @@ -31,7 +31,8 @@ extern "C" #include "src/plugins/irc/irc-color.h" #include "src/plugins/irc/irc-config.h" -extern int irc_color_convert_rgb2irc (int rgb); +extern int irc_color_convert_rgb2term (long rgb); +extern int irc_color_convert_rgb2irc (long rgb); extern int irc_color_convert_term2irc (int color); } @@ -70,6 +71,13 @@ extern int irc_color_convert_term2irc (int color); #define STRING_IRC_COLOR_REMAPPED \ "test_" \ IRC_COLOR_COLOR_STR "03,02" "remapped" +#define STRING_IRC_COLOR_FG_ORANGE \ + "test_" IRC_COLOR_HEX_COLOR_STR "FF7F00" "orange" \ + IRC_COLOR_HEX_COLOR_STR "_end" +#define STRING_IRC_COLOR_FG_YELLOW_BG_DARKMAGENTA \ + "test_" IRC_COLOR_HEX_COLOR_STR "FFFF00,8B008B" \ + "yellow/darkmagenta" \ + IRC_COLOR_HEX_COLOR_STR "_end" /* tests on irc_color_encode(): command line -> IRC color */ #define STRING_USER_BOLD \ @@ -92,6 +100,10 @@ extern int irc_color_convert_term2irc (int color); #define STRING_USER_ATTRS_AND_COLORS \ "test_" "\x02" "\x1F" "\x03" "08,02" "bold_underline_yellow/blue" \ "\x02" "\x1F" "_normal_yellow/blue" +#define STRING_USER_FG_ORANGE \ + "test_" "\x04" "FF7F00" "orange" "\x04" "_end" +#define STRING_USER_FG_YELLOW_BG_DARKMAGENTA \ + "test_" "\x04" "FFFF00,8B008B" "yellow/darkmagenta" "\x04" "_end" /* tests on irc_color_decode_ansi(): ANSI color -> IRC color */ #define STRING_ANSI_RESET "test_\x1B[mreset" @@ -144,6 +156,49 @@ TEST_GROUP(IrcColor) { }; +/* + * Tests functions: + * irc_color_convert_rgb2term + */ + +TEST(IrcColor, ConvertRgb2Term) +{ + LONGS_EQUAL(-1, irc_color_convert_rgb2term (-1)); + LONGS_EQUAL(0, irc_color_convert_rgb2term (0)); + LONGS_EQUAL(9, irc_color_convert_rgb2term (0xFF0000)); /* red */ + LONGS_EQUAL(10, irc_color_convert_rgb2term (0x00FF00)); /* green */ + LONGS_EQUAL(12, irc_color_convert_rgb2term (0x0000FF)); /* blue */ + LONGS_EQUAL(11, irc_color_convert_rgb2term (0xFFFF00)); /* yellow */ + LONGS_EQUAL(208, irc_color_convert_rgb2term (0xFF7F00)); /* orange */ + LONGS_EQUAL(90, irc_color_convert_rgb2term (0x8B008B)); /* dark magenta */ +} + +/* + * Tests functions: + * irc_color_convert_rgb2irc + */ + +TEST(IrcColor, ConvertRgb2Irc) +{ + LONGS_EQUAL(1, irc_color_convert_rgb2irc (0x000000)); + LONGS_EQUAL(1, irc_color_convert_rgb2irc (0x010203)); + LONGS_EQUAL(4, irc_color_convert_rgb2irc (0xFF0033)); + LONGS_EQUAL(15, irc_color_convert_rgb2irc (0xAABBCC)); +} + +/* + * Tests functions: + * irc_color_convert_term2irc + */ + +TEST(IrcColor, ConvertTerm2Irc) +{ + LONGS_EQUAL(1, irc_color_convert_term2irc (0)); + LONGS_EQUAL(15, irc_color_convert_term2irc (123)); + LONGS_EQUAL(13, irc_color_convert_term2irc (200)); + LONGS_EQUAL(0, irc_color_convert_term2irc (255)); +} + /* * Tests functions: * irc_color_decode @@ -269,6 +324,24 @@ TEST(IrcColor, Decode) gui_color_get_custom ("|green")); WEE_CHECK_DECODE(string, STRING_IRC_COLOR_REMAPPED, 1); config_file_option_unset (irc_config_color_mirc_remap); + + /* color: hex 0xFF7F00 (orange / 208) */ + WEE_CHECK_DECODE("test_orange_end", + STRING_IRC_COLOR_FG_ORANGE, 0); + snprintf (string, sizeof (string), + "test_%sorange%s_end", + gui_color_get_custom ("|208"), + gui_color_get_custom ("resetcolor")); + WEE_CHECK_DECODE(string, STRING_IRC_COLOR_FG_ORANGE, 1); + + /* color: hex 0xFFFF00 (yellow / 11) on 0x8B008B (dark magenta / 90) */ + WEE_CHECK_DECODE("test_yellow/darkmagenta_end", + STRING_IRC_COLOR_FG_YELLOW_BG_DARKMAGENTA, 0); + snprintf (string, sizeof (string), + "test_%syellow/darkmagenta%s_end", + gui_color_get_custom ("|11,90"), + gui_color_get_custom ("resetcolor")); + WEE_CHECK_DECODE(string, STRING_IRC_COLOR_FG_YELLOW_BG_DARKMAGENTA, 1); } /* @@ -375,32 +448,23 @@ TEST(IrcColor, Encode) IRC_COLOR_BOLD_STR, IRC_COLOR_UNDERLINE_STR); WEE_CHECK_ENCODE(string, STRING_USER_ATTRS_AND_COLORS, 1); -} - -/* - * Tests functions: - * irc_color_convert_rgb2irc - */ - -TEST(IrcColor, ConvertRgb2Irc) -{ - LONGS_EQUAL(1, irc_color_convert_rgb2irc (0x000000)); - LONGS_EQUAL(1, irc_color_convert_rgb2irc (0x010203)); - LONGS_EQUAL(4, irc_color_convert_rgb2irc (0xFF0033)); - LONGS_EQUAL(15, irc_color_convert_rgb2irc (0xAABBCC)); -} -/* - * Tests functions: - * irc_color_convert_term2irc - */ + /* color: hex 0xFF7F00 (orange / 208) */ + WEE_CHECK_ENCODE("test_orange_end", STRING_USER_FG_ORANGE, 0); + snprintf (string, sizeof (string), + "test_%sFF7F00orange%s_end", + IRC_COLOR_HEX_COLOR_STR, + IRC_COLOR_HEX_COLOR_STR); + WEE_CHECK_ENCODE(string, STRING_USER_FG_ORANGE, 1); -TEST(IrcColor, ConvertTerm2Irc) -{ - LONGS_EQUAL(1, irc_color_convert_term2irc (0)); - LONGS_EQUAL(15, irc_color_convert_term2irc (123)); - LONGS_EQUAL(13, irc_color_convert_term2irc (200)); - LONGS_EQUAL(0, irc_color_convert_term2irc (255)); + /* color: hex 0xFFFF00 (yellow / 11) on 0x8B008B (dark magenta / 90) */ + WEE_CHECK_ENCODE("test_yellow/darkmagenta_end", + STRING_USER_FG_YELLOW_BG_DARKMAGENTA, 0); + snprintf (string, sizeof (string), + "test_%sFFFF00,8B008Byellow/darkmagenta%s_end", + IRC_COLOR_HEX_COLOR_STR, + IRC_COLOR_HEX_COLOR_STR); + WEE_CHECK_ENCODE(string, STRING_USER_FG_YELLOW_BG_DARKMAGENTA, 1); } /* -- cgit v1.2.3