mirror of
https://github.com/fish-shell/fish-shell
synced 2024-12-26 12:53:13 +00:00
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:: if (idx < 16 || term256_support_is_native()) { // Use tparm to emit color escape writembs(tparm(todo, idx); If a terminal has max_colors = 8, here is what happenened, except inside fish: > env TERM=xterm tput setaf 7 | xxd 00000000: 1b5b 3337 6d .[37m > env TERM=xterm tput setaf 9 | xxd 00000000: 1b5b 3338 6d .[39m The first escape is good, that second escape is not valid. Bright colors should start at \e[90m: > env TERM=xterm-16color tput setaf 9 | xxd 00000000: 1b5b 3931 6d .[91m This is what caused "white" not to work in #3176 in Terminal.app, and obviously isn't good for real low-color terminals either. So we replace the term256_support_is_native(), which just checked if max_colors is 256 or not, with a function that takes an argument and checks terminfo for that to see if tparm can handle it. We only use this test, because otherwise, tparm should be expected to output garbage: /// 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; } ... if (term_supports_color_natively(idx) { And if terminfo can't do it, the "forced" escapes no longer use the fancy format when handling colors under 16, as this is not going to be compatible with low color terminals. The code before used: else { char buff[16] = ""; snprintf(buff, sizeof buff, "\x1b[%d;5;%dm", is_fg ? 38 : 48, idx); I added an intermediate format for colors 0-15: else { // We are attempting to bypass the term here. Generate the ANSI escape sequence ourself. 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); } Restores harmony to white, brwhite, brblack, black color names. We don't want "white" to refer to color color #16, but to the standard color #8. #16 is "brwhite". Move comments from output.h to output.cpp Nuke the config.fish set_color hack for linux VTs. Sync up our various incomplete color lists and fix all color values. Colors 0-8 are assumed to be brights - e.g. red was FF0000. Perplexing! Using this table: <http://www.calmar.ws/vim/256-xterm-24bit-rgb-color-chart.html> Fixes #3176
This commit is contained in:
parent
88688d02b2
commit
3669805627
5 changed files with 120 additions and 120 deletions
|
@ -303,26 +303,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
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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<rgb_color_t> &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<rgb_color_t> &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);
|
||||
|
|
55
src/output.h
55
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<rgb_color_t> &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);
|
||||
|
||||
|
|
Loading…
Reference in a new issue