From bb336ba84c94d6e25a7b84227a43c75426bfadc3 Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Thu, 21 Jul 2016 10:55:28 -0700 Subject: [PATCH] Improve compatibility with 0-16 color terminals. Fish assumed that it could use tparm to emit escapes to set colors as long as the color was under 16 or max_colors from terminfo was 256. Now only uses tparm if max_colors indicates support. Otherwise, use improved ability to generate color escapes ourselves. This is what caused "white" not to work in TERM=xterm on Terminal.app, and obviously isn't good for real low-color terminals either. Fixes #3176 Restores harmony to color white. Adds brwhite, brblack. Nukes the config.fish set_color hack for linux VTs. Fixes #2951 --- .../functions/__fish_config_interactive.fish | 20 ----- share/tools/web_config/webconfig.py | 34 +++++---- src/color.cpp | 74 +++++++++++-------- src/output.cpp | 57 +++++++++++--- src/output.h | 55 +++----------- 5 files changed, 120 insertions(+), 120 deletions(-) diff --git a/share/functions/__fish_config_interactive.fish b/share/functions/__fish_config_interactive.fish index 43b3695188cf..08c1d6b67822 100644 --- a/share/functions/__fish_config_interactive.fish +++ b/share/functions/__fish_config_interactive.fish @@ -299,26 +299,6 @@ function __fish_config_interactive -d "Initializations that should be performed set -g fish_pager_color_description yellow set -g fish_pager_color_progress cyan - # Don't allow setting color other than what linux offers (see #2001) - functions -e set_color - function set_color --shadow-builtin - set -l term_colors black red green yellow blue magenta cyan white normal - for a in $argv - if not contains -- $a $term_colors - switch $a - # Also allow options - case "-*" - continue - case "*" - echo "Color not valid in TERM = linux: $a" - return 1 - end - end - end - builtin set_color $argv - return $status - end - # Set fish_prompt to a VT-friendly version # without color or unicode function fish_prompt diff --git a/share/tools/web_config/webconfig.py b/share/tools/web_config/webconfig.py index 782751705b93..c020bea666d4 100755 --- a/share/tools/web_config/webconfig.py +++ b/share/tools/web_config/webconfig.py @@ -65,23 +65,27 @@ def escape_fish_cmd(text): named_colors = { 'black' : '000000', - 'red' : 'FF0000', - 'green' : '00FF00', + 'red' : '800000', + 'green' : '008000', 'brown' : '725000', - 'yellow' : 'FFFF00', - 'blue' : '0000FF', - 'magenta' : 'FF00FF', - 'purple' : 'FF00FF', - 'cyan' : '00FFFF', - 'grey' : 'E5E5E5', + 'yellow' : '808000', + 'blue' : '000080', + 'magenta' : '800080', + 'purple' : '800080', + 'cyan' : '008080', + 'grey' : 'e5e5e5', 'brgrey' : '555555', - 'brbrown' : 'FFFF55', - 'bryellow' : 'FFFF55', - 'brblue' : '5555FF', - 'brmagenta' : 'FF55FF', - 'brpurple' : 'FF55FF', - 'brcyan' : '55FFFF', - 'white' : 'FFFFFF' + 'white' : 'c0c0c0', + 'brblack' : '808080', + 'brred' : 'ff0000', + 'brgreen' : '00ff00', + 'brbrown' : 'ffff00', + 'bryellow' : 'ffff00', + 'brblue' : '0000ff', + 'brmagenta' : 'ff00ff', + 'brpurple' : 'ff00ff', + 'brcyan' : '00ffff', + 'brwhite' : 'ffffff' } def parse_one_color(comp): diff --git a/src/color.cpp b/src/color.cpp index 41f41acfe1a7..7b65904816f3 100644 --- a/src/color.cpp +++ b/src/color.cpp @@ -149,29 +149,32 @@ struct named_color_t { const wchar_t *name; unsigned char idx; unsigned char rgb[3]; + bool hidden; }; static const named_color_t named_colors[] = { - {L"black", 0, {0, 0, 0}}, - {L"red", 1, {0xFF, 0, 0}}, - {L"green", 2, {0, 0xFF, 0}}, - {L"brown", 3, {0x72, 0x50, 0}}, - {L"yellow", 3, {0xFF, 0xFF, 0}}, - {L"blue", 4, {0, 0, 0xFF}}, - {L"magenta", 5, {0xFF, 0, 0xFF}}, - {L"purple", 5, {0xFF, 0, 0xFF}}, - {L"cyan", 6, {0, 0xFF, 0xFF}}, - {L"grey", 7, {0xE5, 0xE5, 0xE5}}, - {L"brgrey", 8, {0x55, 0x55, 0x55}}, - {L"brred", 9, {0xFF, 0x55, 0x55}}, - {L"brgreen", 10, {0x55, 0xFF, 0x55}}, - {L"brbrown", 11, {0xFF, 0xFF, 0x55}}, - {L"bryellow", 11, {0xFF, 0xFF, 0x55}}, - {L"brblue", 12, {0x55, 0x55, 0xFF}}, - {L"brmagenta", 13, {0xFF, 0x55, 0xFF}}, - {L"brpurple", 13, {0xFF, 0x55, 0xFF}}, - {L"brcyan", 14, {0x55, 0xFF, 0xFF}}, - {L"white", 15, {0xFF, 0xFF, 0xFF}}, + {L"black", 0, {0, 0, 0}, false}, + {L"red", 1, {0x80, 0, 0}, false}, + {L"green", 2, {0, 0x80, 0}, false}, + {L"brown", 3, {0x72, 0x50, 0}, true}, + {L"yellow", 3, {0x80, 0x80, 0}, false}, + {L"blue", 4, {0, 0, 0x80}, false}, + {L"magenta", 5, {0x80, 0, 0x80}, false}, + {L"purple", 5, {0x80, 0, 0x80}, true}, + {L"cyan", 6, {0, 0x80, 0x80}, false}, + {L"white", 7, {0xC0, 0xC0, 0xC0}, false}, + {L"grey", 7, {0xe5, 0xe5, 0xe5}, true}, + {L"brblack", 8, {0x80, 0x80, 0x80}, false}, + {L"brgrey", 8, {0055, 0x55, 0x55}, true}, + {L"brred", 9, {0xFF, 0x00, 0x00}, false}, + {L"brgreen", 10, {0x00, 0xFF, 0x00}, false}, + {L"brbrown", 11, {0xFF, 0xFF, 0x00}, true}, + {L"bryellow", 11, {0xFF, 0xFF, 0x00}, false}, + {L"brblue", 12, {0x00, 0, 0xFF}, false}, + {L"brmagenta", 13, {0xFF, 0, 0xFF}, false}, + {L"brpurple", 13, {0xFF, 0, 0xFF}, true}, + {L"brcyan", 14, {0x00, 0xFF, 0xFF}, false}, + {L"brwhite", 15, {0xFF, 0xFF, 0xFF}, false}, }; wcstring_list_t rgb_color_t::named_color_names(void) { @@ -179,7 +182,9 @@ wcstring_list_t rgb_color_t::named_color_names(void) { wcstring_list_t result; result.reserve(1 + count); for (size_t i = 0; i < count; i++) { - result.push_back(named_colors[i].name); + if (named_colors[i].hidden == false) { + result.push_back(named_colors[i].name); + } } // "normal" isn't really a color and does not have a color palette index or // RGB value. Therefore, it does not appear in the named_colors table. @@ -227,16 +232,24 @@ rgb_color_t rgb_color_t::white() { return rgb_color_t(type_named, 7); } rgb_color_t rgb_color_t::black() { return rgb_color_t(type_named, 0); } -static unsigned char term8_color_for_rgb(const unsigned char rgb[3]) { +static unsigned char term16_color_for_rgb(const unsigned char rgb[3]) { const uint32_t kColors[] = { 0x000000, // Black - 0xFF0000, // Red - 0x00FF00, // Green - 0xFFFF00, // Yellow - 0x0000FF, // Blue - 0xFF00FF, // Magenta - 0x00FFFF, // Cyan - 0xFFFFFF, // White + 0x800000, // Red + 0x008000, // Green + 0x808000, // Yellow + 0x000080, // Blue + 0x800080, // Magenta + 0x008080, // Cyan + 0xc0c0c0, // White + 0x808080, // Bright Black + 0xFF0000, // Bright Red + 0x00FF00, // Bright Green + 0xFFFF00, // Bright Yellow + 0x0000FF, // Bright Blue + 0xFF00FF, // Bright Magenta + 0x00FFFF, // Bright Cyan + 0xFFFFFF // Bright White }; return convert_color(rgb, kColors, sizeof kColors / sizeof *kColors); } @@ -284,9 +297,10 @@ color24_t rgb_color_t::to_color24() const { } unsigned char rgb_color_t::to_name_index() const { + // XXX this should look for the nearest color assert(type == type_named || type == type_rgb); if (type == type_named) return data.name_idx; - if (type == type_rgb) return term8_color_for_rgb(data.color.rgb); + if (type == type_rgb) return term16_color_for_rgb(data.color.rgb); return (unsigned char)-1; // this is an error } diff --git a/src/output.cpp b/src/output.cpp index 85f1f1b9c486..937e78114f5b 100644 --- a/src/output.cpp +++ b/src/output.cpp @@ -37,15 +37,19 @@ static int (*out)(char c) = writeb_internal; /// Whether term256 and term24bit are supported. static color_support_t color_support = 0; +/// Set the function used for writing in move_cursor, writespace and set_color and all other output +/// functions in this library. By default, the write call is used to give completely unbuffered +/// output to stdout. void output_set_writer(int (*writer)(char)) { CHECK(writer, ); out = writer; } +/// Return the current output writer. int (*output_get_writer())(char) { return out; } -// Returns true if we think the term256 support is "native" as opposed to forced. -static bool term256_support_is_native(void) { return max_colors >= 256; } +/// Returns true if we think tparm can handle outputting a color index +static bool term_supports_color_natively(unsigned int c) { return max_colors >= c; } color_support_t output_get_color_support(void) { return color_support; } @@ -59,15 +63,18 @@ unsigned char index_for_color(rgb_color_t c) { } static bool write_color_escape(char *todo, unsigned char idx, bool is_fg) { - bool result = false; - if (idx < 16 || term256_support_is_native()) { + if (term_supports_color_natively(idx)) { // Use tparm to emit color escape. writembs(tparm(todo, idx)); - result = true; + return true; } else { // We are attempting to bypass the term here. Generate the ANSI escape sequence ourself. - char buff[128] = ""; - snprintf(buff, sizeof buff, "\x1b[%d;5;%dm", is_fg ? 38 : 48, idx); + char buff[16] = ""; + if (idx < 16) { + snprintf(buff, sizeof buff, "\x1b[%dm", ((idx > 7) ? 82 : 30) + idx + !is_fg * 10); + } else { + snprintf(buff, sizeof buff, "\x1b[%d;5;%dm", is_fg ? 38 : 48, idx); + } int (*writer)(char) = output_get_writer(); if (writer) { @@ -76,9 +83,8 @@ static bool write_color_escape(char *todo, unsigned char idx, bool is_fg) { } } - result = true; + return true; } - return result; } static bool write_foreground_color(unsigned char idx) { @@ -99,6 +105,7 @@ static bool write_background_color(unsigned char idx) { return false; } +// Exported for builtin_set_color's usage only. void write_color(rgb_color_t color, bool is_fg) { bool supports_term24bit = !!(output_get_color_support() & color_support_term24bit); if (!supports_term24bit || !color.is_rgb()) { @@ -122,6 +129,27 @@ void write_color(rgb_color_t color, bool is_fg) { } } +/// Sets the fg and bg color. May be called as often as you like, since if the new color is the same +/// as the previous, nothing will be written. Negative values for set_color will also be ignored. +/// Since the terminfo string this function emits can potentially cause the screen to flicker, the +/// function takes care to write as little as possible. +/// +/// Possible values for color are any form the FISH_COLOR_* enum and FISH_COLOR_RESET. +/// FISH_COLOR_RESET will perform an exit_attribute_mode, even if set_color thinks it is already in +/// FISH_COLOR_NORMAL mode. +/// +/// In order to set the color to normal, three terminfo strings may have to be written. +/// +/// - First a string to set the color, such as set_a_foreground. This is needed because otherwise +/// the previous strings colors might be removed as well. +/// +/// - After that we write the exit_attribute_mode string to reset all color attributes. +/// +/// - Lastly we may need to write set_a_background or set_a_foreground to set the other half of the +/// color pair to what it should be. +/// +/// \param c Foreground color. +/// \param c2 Background color. void set_color(rgb_color_t c, rgb_color_t c2) { #if 0 wcstring tmp = c.description(); @@ -260,11 +288,15 @@ static int writeb_internal(char c) { // cppcheck return 0; } +/// This is for writing process notification messages. Has to write to stdout, so clr_eol and such +/// functions will work correctly. Not an issue since this function is only used in interactive mode +/// anyway. int writeb(tputs_arg_t b) { out(b); return 0; } +/// Write a wide character using the output method specified using output_set_writer(). int writech(wint_t ch) { char buff[MB_LEN_MAX + 1]; size_t len; @@ -294,6 +326,7 @@ int writech(wint_t ch) { return 0; } +/// Write a wide character string to FD 1. void writestr(const wchar_t *str) { CHECK(str, ); @@ -329,6 +362,8 @@ void writestr(const wchar_t *str) { if (buffer != static_buffer) delete[] buffer; } +/// Given a list of rgb_color_t, pick the "best" one, as determined by the color support. Returns +/// rgb_color_t::none() if empty. rgb_color_t best_color(const std::vector &candidates, color_support_t support) { if (candidates.empty()) { return rgb_color_t::none(); @@ -358,7 +393,8 @@ rgb_color_t best_color(const std::vector &candidates, color_support return result; } -// This code should be refactored to enable sharing with builtin_set_color. +/// Return the internal color code representing the specified color. +/// XXX This code should be refactored to enable sharing with builtin_set_color. rgb_color_t parse_color(const wcstring &val, bool is_background) { int is_bold = 0; int is_underline = 0; @@ -408,6 +444,7 @@ rgb_color_t parse_color(const wcstring &val, bool is_background) { return result; } +/// Write specified multibyte string. void writembs_check(char *mbs, const char *mbs_name, const char *file, long line) { if (mbs != NULL) { tputs(mbs, 1, &writeb); diff --git a/src/output.h b/src/output.h index a7f87fab4b29..71f94de8076d 100644 --- a/src/output.h +++ b/src/output.h @@ -14,65 +14,33 @@ /// Constants for various colors as used by the set_color function. enum { - FISH_COLOR_BLACK, - FISH_COLOR_RED, - FISH_COLOR_GREEN, - FISH_COLOR_YELLOW, - FISH_COLOR_BLUE, - FISH_COLOR_MAGENTA, - FISH_COLOR_CYAN, - FISH_COLOR_WHITE, - FISH_COLOR_NORMAL, // the default fg color of the terminal - FISH_COLOR_RESET + FISH_COLOR_BLACK, // 0 + FISH_COLOR_RED, // 1 + FISH_COLOR_GREEN, // 2 + FISH_COLOR_YELLOW, // 3 + FISH_COLOR_BLUE, // 4 + FISH_COLOR_MAGENTA, // 5 + FISH_COLOR_CYAN, // 6 + FISH_COLOR_WHITE, // 7 + FISH_COLOR_NORMAL, // 8 terminal default + FISH_COLOR_RESET // 9 }; -/// Sets the fg and bg color. May be called as often as you like, since if the new color is the same -/// as the previous, nothing will be written. Negative values for set_color will also be ignored. -/// Since the terminfo string this function emits can potentially cause the screen to flicker, the -/// function takes care to write as little as possible. -/// -/// Possible values for color are any form the FISH_COLOR_* enum and FISH_COLOR_RESET. -/// FISH_COLOR_RESET will perform an exit_attribute_mode, even if set_color thinks it is already in -/// FISH_COLOR_NORMAL mode. -/// -/// In order to set the color to normal, three terminfo strings may have to be written. -/// -/// - First a string to set the color, such as set_a_foreground. This is needed because otherwise -/// the previous strings colors might be removed as well. -/// -/// - After that we write the exit_attribute_mode string to reset all color attributes. -/// -/// - Lastly we may need to write set_a_background or set_a_foreground to set the other half of the -/// color pair to what it should be. -/// -/// \param c Foreground color. -/// \param c2 Background color. void set_color(rgb_color_t c, rgb_color_t c2); -/// Write specified multibyte string. void writembs_check(char *mbs, const char *mbs_name, const char *file, long line); #define writembs(mbs) writembs_check((mbs), #mbs, __FILE__, __LINE__) -/// Write a wide character using the output method specified using output_set_writer(). int writech(wint_t ch); -/// Write a wide character string to FD 1. void writestr(const wchar_t *str); -/// Return the internal color code representing the specified color. rgb_color_t parse_color(const wcstring &val, bool is_background); -/// This is for writing process notification messages. Has to write to stdout, so clr_eol and such -/// functions will work correctly. Not an issue since this function is only used in interactive mode -/// anyway. int writeb(tputs_arg_t b); -/// Set the function used for writing in move_cursor, writespace and set_color and all other output -/// functions in this library. By default, the write call is used to give completely unbuffered -/// output to stdout. void output_set_writer(int (*writer)(char)); -/// Return the current output writer. int (*output_get_writer())(char); /// Sets what colors are supported. @@ -81,11 +49,8 @@ typedef unsigned int color_support_t; color_support_t output_get_color_support(); void output_set_color_support(color_support_t support); -/// Given a list of rgb_color_t, pick the "best" one, as determined by the color support. Returns -/// rgb_color_t::none() if empty. rgb_color_t best_color(const std::vector &colors, color_support_t support); -// Exported for builtin_set_color's usage only. void write_color(rgb_color_t color, bool is_fg); unsigned char index_for_color(rgb_color_t c);