Skip to content

Commit

Permalink
Improve compatibility with 16-color terminals.
Browse files Browse the repository at this point in the history
Fish assumed that it could use could use tparm to set colors
as long as the color was under 16 or max_colors from terminfo
is 256 (term256_support_is_native):

        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, essentially::

        > env TERM=xterm tput setaf 7 | xxd
        00000000: 1b5b 3337 6d                             .[37m
        > env TERM=xterm tput setaf 8 | xxd
        00000000: 1b5b 3338 6d                             .[38m

That second escape is not valid. Colors 8 through 16 ought to be \e[90m.

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:

        if (term_supports_color_natively(idx) {

And if that's not true, the "forced" colors no longer use this format,
as this is not going to be compatible with low color terminals:

        else {
                char buff[16] = "";
                snprintf(buff, sizeof buff, "\x1b[%d;5;%dm", is_fg ? 38 : 48, idx);

Add an intermediate formatter that can handle colors 0-16:
        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 number 16, but the
traditional color fish-shell#8.

Fixes fish-shell#3176

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 with bright hex values
being given for colors 0-8. Perplexing!

Using this table:

http://www.calmar.ws/vim/256-xterm-24bit-rgb-color-chart.html
  • Loading branch information
floam committed Jul 24, 2016
1 parent 88688d0 commit dd0a9b2
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 120 deletions.
20 changes: 0 additions & 20 deletions share/functions/__fish_config_interactive.fish
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
34 changes: 19 additions & 15 deletions share/tools/web_config/webconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
74 changes: 44 additions & 30 deletions src/color.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,37 +149,42 @@ 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) {
size_t count = sizeof named_colors / sizeof *named_colors;
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.
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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
}

Expand Down
57 changes: 47 additions & 10 deletions src/output.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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; }

Expand All @@ -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) {
Expand All @@ -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) {
Expand All @@ -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()) {
Expand All @@ -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();
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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, );

Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
Loading

0 comments on commit dd0a9b2

Please sign in to comment.