2016-05-01 22:29:21 -07:00
|
|
|
// Generic output functions.
|
2005-09-20 23:26:39 +10:00
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
#include <stdio.h>
|
2022-08-20 23:14:48 -07:00
|
|
|
#include <unistd.h>
|
2019-10-13 15:50:48 -07:00
|
|
|
|
2018-02-04 02:59:37 -06:00
|
|
|
#if HAVE_CURSES_H
|
2022-08-20 23:14:48 -07:00
|
|
|
#include <curses.h> // IWYU pragma: keep
|
2018-02-04 02:59:37 -06:00
|
|
|
#elif HAVE_NCURSES_H
|
2005-09-20 23:26:39 +10:00
|
|
|
#include <ncurses.h>
|
2014-12-07 16:41:15 +08:00
|
|
|
#elif HAVE_NCURSES_CURSES_H
|
|
|
|
#include <ncurses/curses.h>
|
2005-09-20 23:26:39 +10:00
|
|
|
#endif
|
2006-01-19 22:22:07 +10:00
|
|
|
#if HAVE_TERM_H
|
2005-09-20 23:26:39 +10:00
|
|
|
#include <term.h>
|
2006-01-19 22:22:07 +10:00
|
|
|
#elif HAVE_NCURSES_TERM_H
|
|
|
|
#include <ncurses/term.h>
|
|
|
|
#endif
|
2017-02-13 20:37:27 -08:00
|
|
|
|
2019-10-13 15:50:48 -07:00
|
|
|
#include <cwchar>
|
2022-08-20 23:14:48 -07:00
|
|
|
#include <mutex>
|
2015-07-25 23:14:25 +08:00
|
|
|
#include <string>
|
2016-04-20 23:00:54 -07:00
|
|
|
#include <vector>
|
2006-02-28 23:17:16 +10:00
|
|
|
|
2016-05-01 22:29:21 -07:00
|
|
|
#include "color.h"
|
2005-09-20 23:26:39 +10:00
|
|
|
#include "common.h"
|
2016-06-01 20:03:50 -07:00
|
|
|
#include "env.h"
|
2016-05-01 22:29:21 -07:00
|
|
|
#include "fallback.h" // IWYU pragma: keep
|
2019-05-27 15:56:53 -07:00
|
|
|
#include "flog.h"
|
2022-08-20 23:14:48 -07:00
|
|
|
#include "maybe.h"
|
2005-09-20 23:26:39 +10:00
|
|
|
#include "output.h"
|
2023-04-25 21:38:53 -05:00
|
|
|
#include "threads.rs.h"
|
2020-01-15 13:16:43 -08:00
|
|
|
#include "wcstringutil.h"
|
2016-05-01 22:29:21 -07:00
|
|
|
#include "wutil.h" // IWYU pragma: keep
|
2005-09-20 23:26:39 +10: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
2016-07-21 10:55:28 -07:00
|
|
|
/// Given a list of rgb_color_t, pick the "best" one, as determined by the color support. Returns
|
|
|
|
/// rgb_color_t::none() if empty.
|
2023-05-28 16:13:54 -07:00
|
|
|
/// TODO: This is duplicated with Rust.
|
2016-05-01 22:29:21 -07:00
|
|
|
rgb_color_t best_color(const std::vector<rgb_color_t> &candidates, color_support_t support) {
|
|
|
|
if (candidates.empty()) {
|
2014-11-09 16:42:35 -08:00
|
|
|
return rgb_color_t::none();
|
|
|
|
}
|
2016-05-01 22:29:21 -07:00
|
|
|
|
2014-11-09 16:42:35 -08:00
|
|
|
rgb_color_t first_rgb = rgb_color_t::none(), first_named = rgb_color_t::none();
|
2019-11-19 13:46:47 -08:00
|
|
|
for (const auto &color : candidates) {
|
2016-05-01 22:29:21 -07:00
|
|
|
if (first_rgb.is_none() && color.is_rgb()) {
|
2014-11-09 16:42:35 -08:00
|
|
|
first_rgb = color;
|
|
|
|
}
|
2016-05-01 22:29:21 -07:00
|
|
|
if (first_named.is_none() && color.is_named()) {
|
2014-11-09 16:42:35 -08:00
|
|
|
first_named = color;
|
|
|
|
}
|
|
|
|
}
|
2016-05-01 22:29:21 -07:00
|
|
|
// If we have both RGB and named colors, then prefer rgb if term256 is supported.
|
2014-11-09 16:42:35 -08:00
|
|
|
rgb_color_t result = rgb_color_t::none();
|
2016-10-20 21:14:40 -07:00
|
|
|
bool has_term256 = static_cast<bool>(support & color_support_term256);
|
2016-05-01 22:29:21 -07:00
|
|
|
if ((!first_rgb.is_none() && has_term256) || first_named.is_none()) {
|
2014-11-09 16:42:35 -08:00
|
|
|
result = first_rgb;
|
2016-05-01 22:29:21 -07:00
|
|
|
} else {
|
2014-11-09 16:42:35 -08:00
|
|
|
result = first_named;
|
|
|
|
}
|
2016-05-01 22:29:21 -07:00
|
|
|
if (result.is_none()) {
|
2014-11-09 16:42:35 -08:00
|
|
|
result = candidates.at(0);
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
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
2016-07-21 10:55:28 -07:00
|
|
|
/// Return the internal color code representing the specified color.
|
2017-08-05 21:40:27 -07:00
|
|
|
/// TODO: This code should be refactored to enable sharing with builtin_set_color.
|
2021-06-10 10:39:40 +02:00
|
|
|
/// In particular, the argument parsing still isn't fully capable.
|
2023-05-28 16:13:54 -07:00
|
|
|
/// TODO: This is duplicated with Rust.
|
2017-08-05 21:40:27 -07:00
|
|
|
rgb_color_t parse_color(const env_var_t &var, bool is_background) {
|
2020-09-24 17:21:49 +02:00
|
|
|
bool is_bold = false;
|
|
|
|
bool is_underline = false;
|
|
|
|
bool is_italics = false;
|
|
|
|
bool is_dim = false;
|
|
|
|
bool is_reverse = false;
|
2012-11-18 11:23:22 +01:00
|
|
|
|
2012-02-13 09:52:17 -08:00
|
|
|
std::vector<rgb_color_t> candidates;
|
2012-11-18 11:23:22 +01:00
|
|
|
|
2020-07-05 11:18:53 -07:00
|
|
|
const wchar_t *prefix = L"--background=";
|
2020-07-04 20:38:06 -05:00
|
|
|
// wcslen is not available as constexpr
|
2020-07-05 11:18:53 -07:00
|
|
|
size_t prefix_len = wcslen(prefix);
|
2020-07-04 20:38:06 -05:00
|
|
|
|
2021-06-10 10:39:40 +02:00
|
|
|
bool next_is_background = false;
|
2019-09-21 19:36:56 -07:00
|
|
|
wcstring color_name;
|
2023-10-08 23:22:27 +02:00
|
|
|
auto vals = var.as_list();
|
|
|
|
for (const wcstring &next : vals) {
|
2019-09-21 19:36:56 -07:00
|
|
|
color_name.clear();
|
2016-05-01 22:29:21 -07:00
|
|
|
if (is_background) {
|
2021-06-10 10:39:40 +02:00
|
|
|
if (color_name.empty() && next_is_background) {
|
|
|
|
color_name = next;
|
|
|
|
next_is_background = false;
|
|
|
|
} else if (string_prefixes_string(prefix, next)) {
|
|
|
|
// Look for something like "--background=red".
|
2020-07-04 20:38:06 -05:00
|
|
|
color_name = wcstring(next, prefix_len);
|
2021-06-10 10:39:40 +02:00
|
|
|
} else if (next == L"--background" || next == L"-b") {
|
|
|
|
// Without argument attached the next token is the color
|
|
|
|
// - if it's another option it's an error.
|
|
|
|
next_is_background = true;
|
|
|
|
} else if (next == L"--reverse" || next == L"-r") {
|
|
|
|
// Reverse should be meaningful in either context
|
2019-10-22 16:28:56 -07:00
|
|
|
is_reverse = true;
|
2021-06-10 10:39:40 +02:00
|
|
|
} else if (string_prefixes_string(L"-b", next)) {
|
|
|
|
// Look for something like "-bred".
|
|
|
|
// Yes, that length is hardcoded.
|
|
|
|
color_name = wcstring(next, 2);
|
2019-10-22 16:28:56 -07:00
|
|
|
}
|
2016-05-01 22:29:21 -07:00
|
|
|
} else {
|
2012-02-11 17:07:56 -08:00
|
|
|
if (next == L"--bold" || next == L"-o")
|
|
|
|
is_bold = true;
|
|
|
|
else if (next == L"--underline" || next == L"-u")
|
|
|
|
is_underline = true;
|
2016-12-31 06:33:25 +11:00
|
|
|
else if (next == L"--italics" || next == L"-i")
|
|
|
|
is_italics = true;
|
|
|
|
else if (next == L"--dim" || next == L"-d")
|
|
|
|
is_dim = true;
|
|
|
|
else if (next == L"--reverse" || next == L"-r")
|
|
|
|
is_reverse = true;
|
2012-02-11 17:07:56 -08:00
|
|
|
else
|
|
|
|
color_name = next;
|
|
|
|
}
|
2012-11-18 11:23:22 +01:00
|
|
|
|
2016-05-01 22:29:21 -07:00
|
|
|
if (!color_name.empty()) {
|
2012-02-13 09:52:17 -08:00
|
|
|
rgb_color_t color = rgb_color_t(color_name);
|
2016-05-01 22:29:21 -07:00
|
|
|
if (!color.is_none()) {
|
2012-02-13 09:52:17 -08:00
|
|
|
candidates.push_back(color);
|
2012-02-12 18:05:59 -08:00
|
|
|
}
|
2012-02-11 17:07:56 -08:00
|
|
|
}
|
|
|
|
}
|
2014-11-09 16:42:35 -08:00
|
|
|
rgb_color_t result = best_color(candidates, output_get_color_support());
|
2016-05-01 22:29:21 -07:00
|
|
|
|
|
|
|
if (result.is_none()) result = rgb_color_t::normal();
|
2012-11-18 11:23:22 +01:00
|
|
|
|
2012-02-13 09:52:17 -08:00
|
|
|
result.set_bold(is_bold);
|
|
|
|
result.set_underline(is_underline);
|
2016-12-31 06:33:25 +11:00
|
|
|
result.set_italics(is_italics);
|
|
|
|
result.set_dim(is_dim);
|
|
|
|
result.set_reverse(is_reverse);
|
2012-02-11 17:07:56 -08:00
|
|
|
return result;
|
2005-09-20 23:26:39 +10:00
|
|
|
}
|
2007-09-10 00:04:36 +10:00
|
|
|
|
2023-05-28 16:13:54 -07:00
|
|
|
void writembs_nofail(outputter_t &outp, const char *str) {
|
|
|
|
assert(str != nullptr && "Null string");
|
|
|
|
outp.writembs(str);
|
2014-05-09 14:37:23 -07:00
|
|
|
}
|
2023-05-28 16:13:54 -07:00
|
|
|
|
|
|
|
void writembs(outputter_t &outp, const char *str) { writembs_nofail(outp, str); }
|