cmus / cmus

Small, fast and powerful console music player for Unix-like operating systems.
https://cmus.github.io/
GNU General Public License v2.0
5.49k stars 467 forks source link

24-bit direct color support (true color) #799

Open XVilka opened 6 years ago

XVilka commented 6 years ago

Would be nice to have the ability to use 16 million colors in themes, since most of the terminals supports it. See the summary taken from https://gist.github.com/XVilka/8346728

Colours in terminal

It's a common confusion about terminal colours... Actually we have this:

printf "\x1b[${bg};2;${red};${green};${blue}m\n"

The 256 colour palete is configured at start, and it's a 666 cube of colours, each of them defined as a 24bit (888 rgb) colour.

This means that current support can only display 256 different colours in the terminal, while truecolour means that you can display 16 milion different colours at the same time.

Truecolour escape codes doesn't use a colour palette. They just specify the colour itself.

Here's a test case:

printf "\x1b[38;2;255;100;0mTRUECOLOR\x1b[0m\n"

Keep in mind that it is possible to use both ';' and ':' as parameters delimiter.

According to Wikipedia[1], this is only supported by xterm and konsole.

[1] https://en.wikipedia.org/wiki/ANSI_color

Since ncurses-6.0-20180121 terminfo started to support 24-bit truecolor capability under the name of "RGB" - you need to use the "setaf" and "setab" commands to set foreground and background correspondingly.

Detection

There's no reliable way until the new release of terminfo/ncurses. After that the "RGB" flag should be available for detection. S-Lang author added a check for $COLORTERM containing either "truecolor" or "24bit" (case sensitive). In turn, VTE, Konsole and iTerm2 set this variable to "truecolor" (it's been there in VTE for a while, it's relatively new and maybe still git-only in Konsole and iTerm2).

This is obviously not a reliable method, and is not forwarded via sudo, ssh etc. However, whenever it errs, it errs on the safe side: does not advertise support whereas it's actually supported. App developers can freely choose to check for this same variable, or introduce their own method (e.g. an option in their config file), whichever matches better the overall design of the given app. Checking $COLORTERM is recommended though, since that would lead to a more unique desktop experience where the user has to set one variable only and it takes effect across all the apps, rather than something separately for each app.

Here are terminals discussions:

Now supporting truecolour

But there are bunch of libvte-based terminals for GTK2 so they are listed in the another section.

Also, while this one is not exactly a terminal, but a terminal replayer, it still worth mentioning:

Improper support for true colors

Parsing ANSI colour sequences, but approximating them to 256 palette

Note about colour differences: a) RGB axes are not orthogonal, so you cannot use sqrt(R^2+G^2+B^2) formula, b) for colour differences there is more correct (but much more complex) CIEDE2000 formula (which may easily blow up performance if used blindly) [2].

[2] https://github.com/neovim/neovim/issues/793#issuecomment-48106948

Terminal multiplexers

NOT supporting truecolour

Here are another console programs discussions:

Supporting True Colour:

Not supporting True Colour:

iamsubhranil commented 6 years ago

Have you seen the way cava does it?

It does the same using ncurses. It takes a #abcdef color in config, separates it into r, g and b, i.e. in this case ab, cd and ef. Then it normalizes the color to map to the ncurses color, and initializes the color using init_color.

Since cmus heavily depends on the ncurses anyway, I think it will be a very sane way for supporting 16million colors.

mayanksuman commented 5 years ago

ncurses supports true colors since 6.1 version.

thriveth commented 4 years ago

This would be a very very nice addition!

pgaskin commented 3 years ago

I am planning to work on this.

pgaskin commented 3 years ago

I'm probably going to implement the config by allowing |#rrggbb to be added to the end of the existing config colors. This will maintain backwards-compatibility and reduce the number of additional config options while allowing themes to add true color support independently and possibly for specific colors only.

pgaskin commented 3 years ago

I will implement the colors themselves by using init_color on an unused color index within ui_curses.c/update_colors.

pgaskin commented 3 years ago
Some notes from me attempting to figure out how the current cmus and ncurses color stuff works: ```diff diff --git a/ui_curses.c b/ui_curses.c index 12621c5..9726c3e 100644 --- a/ui_curses.c +++ b/ui_curses.c @@ -151,6 +151,7 @@ static const int default_esc_delay = 25; static char *title_buf = NULL; +/* indexes for different interface elements to be mapped to curses colors/pairs */ enum { CURSED_WIN, CURSED_WIN_CUR, @@ -177,6 +178,7 @@ enum { NR_CURSED }; +/* maps the elements to the source index in the colors array for the bg */ static unsigned char cursed_to_bg_idx[NR_CURSED] = { COLOR_WIN_BG, COLOR_WIN_BG, @@ -201,6 +203,7 @@ static unsigned char cursed_to_bg_idx[NR_CURSED] = { COLOR_TRACKWIN_ALBUM_BG, }; +/* maps the elements to the source index in the colors array for the fg */ static unsigned char cursed_to_fg_idx[NR_CURSED] = { COLOR_WIN_FG, COLOR_WIN_CUR, @@ -225,6 +228,7 @@ static unsigned char cursed_to_fg_idx[NR_CURSED] = { COLOR_TRACKWIN_ALBUM_FG, }; +/* maps the elements to the text attrs in the attrs array */ static unsigned char cursed_to_attr_idx[NR_CURSED] = { COLOR_WIN_ATTR, COLOR_WIN_CUR_ATTR, @@ -249,7 +253,7 @@ static unsigned char cursed_to_attr_idx[NR_CURSED] = { COLOR_TRACKWIN_ALBUM_ATTR, }; -/* index is CURSED_*, value is fucking color pair */ +/* maps the elements to the ncurses color pair index */ static int pairs[NR_CURSED]; enum { @@ -1764,6 +1768,34 @@ void enter_search_backward_mode(void) update_commandline(); } +static const char * const color_names[NR_COLORS] = { + "color_cmdline_bg", + "color_cmdline_fg", + "color_error", + "color_info", + "color_separator", + "color_statusline_bg", + "color_statusline_fg", + "color_titleline_bg", + "color_titleline_fg", + "color_win_bg", + "color_win_cur", + "color_win_cur_sel_bg", + "color_win_cur_sel_fg", + "color_win_dir", + "color_win_fg", + "color_win_inactive_cur_sel_bg", + "color_win_inactive_cur_sel_fg", + "color_win_inactive_sel_bg", + "color_win_inactive_sel_fg", + "color_win_sel_bg", + "color_win_sel_fg", + "color_win_title_bg", + "color_win_title_fg", + "color_trackwin_album_bg", + "color_trackwin_album_fg", +}; + void update_colors(void) { int i; @@ -1777,6 +1809,19 @@ void update_colors(void) int attr = attrs[cursed_to_attr_idx[i]]; int pair = i + 1; + fprintf(stderr, "%s ", color_names[i]); + #define tmp(xx) { \ + short r, g, b; \ + color_content(xx, &r, &g, &b); \ + int xr = r*.255, xg = g*.255, xb = b*.255; \ + fprintf(stderr, #xx " [%d #%02x%02x%02x] [attr %d] ", xx, xr, xg, xb, attr); \ + if (getenv("DEBUG_USE_NCURSES_PALETTE_INSTEAD_OF_XTERM")) init_color(xx, r, g, b); \ + } + tmp(fg) + tmp(bg) + #undef tmp + fprintf(stderr, "\r\n"); + if (fg >= 8 && fg <= 15) { /* fg colors 8..15 are special (0..7 + bold) */ init_pair(pair, fg & 7, bg); @@ -1786,6 +1831,8 @@ void update_colors(void) pairs[i] = COLOR_PAIR(pair) | attr; } } + + sleep(100); } ``` https://jonasjacek.github.io/colors/ https://stackoverflow.com/a/28401477 https://stackoverflow.com/a/49139882 https://invisible-island.net/ncurses/man/curs_color.3x.html https://github.com/mirror/ncurses/blob/349761f30e7fc0b4bf2718f7fc3da34e09ea6735/ncurses/base/lib_color.c#L779 https://reversed.top/2019-02-05/more-than-256-curses-color-pairs/ Some notes: - It appears that the themes depend on the xterm 256-color palette being used rather than the default ansi colors. - There isn't a way to get the original color since color_content only gets it from ncurses' internal state, which is initialized to an ugly set of ansi colors (i.e. we can't restore colors changed by `init_color` to the original xterm palette without including the whole thing). - The number of available color indexes is limited by the current terminfo. - So we probably don't want to depend on defining >256 colors if we can avoid it. - But we don't want to overwrite the existing ones either. - xfce4-terminal uses xterm-256, which is 256 colors. - It might be easiest to convert all themes to RGB colors with a manual or automated fallback xterm color number. - This could be done automatically. - This would also break old themes unless we embed the xterm palette. - Which might be the best idea since everything else I can think of will either be buggy or be just as complex to implement. - It would also have the advantage of ensuring colors render consistently on all terminals which support defining 256 colors. - We can still fall back to using the xterm colors blindly if we make themes also choose an xterm color. - Or we could be a bit smarter and approximate it.

Edit: I'm not going to implement it this way. I started, but there are too many caveats to it which would be overly complex to fix and would have a high likelihood of causing regressions on terminals without can_change_color. If anything, I'll do direct color, but I'm not planning to work on that for now.

dinotheextinct commented 1 year ago

Is there anyway to translate hex colors into the current cmus value? I have no clue what the correct terminology is. But e.g. I would like to create my own theme but, I have no idea how to translate hexcolors into "255" values.

paniash commented 12 months ago

Any updates on this? or just like @dinotheextinct's question, is there a way to implement it?