Skip to content

Commit 2ebb1dd

Browse files
authored
Merge pull request #4822 from horkykuba/ncurses-truecolor
Tickets #4137 & #4821: true color support with ncurses and S-Lang color detection
2 parents e5f15aa + 5633ffe commit 2ebb1dd

File tree

16 files changed

+279
-154
lines changed

16 files changed

+279
-154
lines changed

doc/FAQ

Lines changed: 30 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -403,47 +403,51 @@ Frequently Asked Questions
403403
If you get colors, be happy.
404404

405405
If your terminal stays black and white, your terminal doesn't support
406-
color. You might want to upgrade to a terminal which compatible with
406+
color. You might want to upgrade to a terminal which is compatible with
407407
the ANSI color sequences.
408408

409409
If your terminal goes completely black, see the next question.
410410

411411
More detailed answer:
412412

413-
Check that your terminal supports color. color_xterm, rxvt and Linux
414-
console do support, most other terminals don't. You can test color
415-
support with following simple C program:
413+
Check that your terminal supports color. Most terminals do. You can
414+
test color support with the following command:
416415

417-
#include <stdio.h>
416+
printf "\033[32m Hello color world! \033[m\n"
418417

419-
int main (void){
420-
printf ("\033[32m Hello world! \033[m\n");
421-
return 0;
422-
}
418+
You can test 256 color support with the following command:
423419

424-
Compile and run it. If you see "Hello world!" text in green your
425-
terminal supports color, otherwise not (however, for color_xterm see
426-
also the next question).
420+
printf "\033[38;5;120m Hello 256-color world! \033[m\n"
427421

428-
Check whether you are using Ncurses or the S-Lang library (type
429-
"mc -V" to find out).
422+
You can test true color support with the following command:
430423

431-
With S-Lang library you can force color support by setting the
432-
environment variable COLORTERM to any value.
424+
printf "\033[38;2;0;200;0m Hello true color world! \033[m\n"
433425

434-
If you use ncurses library, check that your terminfo database
435-
supports color. If not, you should install one of the enhanced
436-
terminfo databases included in GNU Midnight Commander source
437-
distribution.
426+
If you see the text in green, your terminal supports the respective
427+
color mode.
438428

439-
You might want to set the TERM environment variable so that you are
440-
using the correct terminfo database or termcap entry.
429+
Check that you are using the proper TERM variable for your terminal.
430+
If not, set it accordingly. You can use the 'toe -a' command to list
431+
all available terminfo entries.
441432

442-
If you use color_xterm (or rxvt) the correct value might be
443-
xterm-color, xtermc or simply xterm.
433+
If your terminal supports 256 colors, the correct entry name may be
434+
appended with -256color. If your terminal supports true color, it
435+
should be appended with -direct, -direct16, or -direct256. All three
436+
variants provide both 256-color and true color support simultaneously.
437+
The latter two variants make no difference in mc, and they support
438+
basic colors including their bright versions along with true color
439+
support, in contrast to the -direct variant, which supports only basic
440+
8 colors together with true color when used in a skin.
444441

445-
If you use Linux console the correct value for TERM is linux or
446-
console.
442+
If there is no 256-color or true-color terminfo variant for your
443+
terminal, even though your terminal supports it, send an e-mail to
444+
ncurses/terminfo maintainers ([email protected]) and ask them to
445+
add one.
446+
447+
With the S-Lang library (you can check by 'mc -V'), you can force
448+
color support by setting the environment variable COLORTERM to any
449+
value, and specifically force true color support by setting it to
450+
'truecolor'.
447451

448452
4.5 My color_xterm goes completely (or partially) black!
449453

lib/skin/common.c

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ mc_skin_get_default_name (void)
7373
if (mc_global.tty.skin != NULL)
7474
return g_strdup (mc_global.tty.skin);
7575

76-
// from envirovement variable
76+
// from environment variable
7777
tmp_str = getenv ("MC_SKIN");
7878
if (tmp_str != NULL)
7979
return g_strdup (tmp_str);
@@ -164,9 +164,10 @@ mc_skin_init (const gchar *skin_override, GError **mcerror)
164164
if (is_good_init && mc_skin__default.have_256_colors && !tty_use_256colors (&error))
165165
{
166166
mc_propagate_error (mcerror, 0,
167-
_ ("Unable to use '%s' skin with 256 colors support\non non-256 colors "
168-
"terminal.\nDefault skin has been loaded"),
169-
mc_skin__default.name);
167+
_ ("Unable to use '%s' skin with 256 colors support:\n%s\nDefault "
168+
"skin has been loaded"),
169+
mc_skin__default.name, error->message);
170+
g_error_free (error);
170171
mc_skin_try_to_load_default ();
171172
mc_skin_colors_old_configure (&mc_skin__default);
172173
(void) mc_skin_ini_file_parse (&mc_skin__default);

lib/tty/color-internal.c

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ parse_256_or_true_color_name (const char *color_name)
155155
i = (h[0] << 20) | (h[0] << 16) | (h[1] << 12) | (h[1] << 8) | (h[2] << 4) | h[2];
156156
else
157157
i = (h[0] << 20) | (h[1] << 16) | (h[2] << 12) | (h[3] << 8) | (h[4] << 4) | h[5];
158-
return (1 << 24) | i;
158+
return FLAG_TRUECOLOR | i;
159159
}
160160
}
161161

@@ -178,7 +178,7 @@ tty_color_get_name_by_index (int idx)
178178
return color_table[i].name;
179179

180180
// Create and return the strings in "colorNNN" or "#rrggbb" format.
181-
if ((idx >= 16 && idx < 256) || (idx & (1 << 24)) != 0)
181+
if ((idx >= 16 && idx < 256) || (idx & FLAG_TRUECOLOR) != 0)
182182
{
183183
char name[9];
184184

@@ -241,3 +241,37 @@ tty_attr_get_bits (const char *attrs)
241241
}
242242

243243
/* --------------------------------------------------------------------------------------------- */
244+
245+
int
246+
convert_256color_to_truecolor (int color)
247+
{
248+
int r, g, b;
249+
250+
// Invalid color
251+
if (color > 255)
252+
return 0;
253+
254+
if (color >= 232) // Gray scale
255+
r = g = b = (color - 231) * 10 + 8;
256+
else if (color >= 16) // 6x6x6 color cube
257+
{
258+
color -= 16;
259+
260+
r = (color / (6 * 6) % 6);
261+
r = r > 0 ? r * 40 + 55 : 0;
262+
263+
g = (color / 6 % 6);
264+
g = g > 0 ? g * 40 + 55 : 0;
265+
266+
b = (color % 6);
267+
b = b > 0 ? b * 40 + 55 : 0;
268+
}
269+
else // We don't convert basic 16 colors as they are terminal-dependent and user-configurable
270+
return color;
271+
272+
color = FLAG_TRUECOLOR | (r << 16) | (g << 8) | b;
273+
274+
return color;
275+
}
276+
277+
/* --------------------------------------------------------------------------------------------- */

lib/tty/color-internal.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818

1919
/*** typedefs(not structures) and defined constants **********************************************/
2020

21+
#define FLAG_TRUECOLOR (1 << 24)
22+
#define COLORS_TRUECOLOR (1 << 24)
23+
2124
/*** enums ***************************************************************************************/
2225

2326
typedef enum
@@ -44,12 +47,14 @@ typedef struct
4447

4548
extern gboolean use_colors;
4649
extern gboolean mc_tty_color_disable;
50+
extern gboolean need_convert_256color;
4751

4852
/*** declarations of public functions ************************************************************/
4953

5054
const char *tty_color_get_name_by_index (int idx);
5155
int tty_color_get_index_by_name (const char *color_name);
5256
int tty_attr_get_bits (const char *attrs);
57+
int convert_256color_to_truecolor (int color);
5358

5459
void tty_color_init_lib (gboolean disable, gboolean force);
5560
void tty_color_deinit_lib (void);

lib/tty/color-ncurses.c

Lines changed: 72 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838

3939
#include "lib/global.h"
4040

41+
#include "tty.h"
4142
#include "tty-ncurses.h"
4243
#include "color.h" // variables
4344
#include "color-internal.h"
@@ -53,6 +54,7 @@
5354
/*** file scope variables ************************************************************************/
5455

5556
static GHashTable *mc_tty_color_color_pair_attrs = NULL;
57+
static int overlay_colors = 0;
5658

5759
/* --------------------------------------------------------------------------------------------- */
5860
/*** file scope functions ************************************************************************/
@@ -127,6 +129,10 @@ tty_color_init_lib (gboolean disable, gboolean force)
127129
use_colors = TRUE;
128130
start_color ();
129131
use_default_colors ();
132+
133+
// Extended color mode detection routines must first be called before loading any skin
134+
tty_use_256colors (NULL);
135+
tty_use_truecolors (NULL);
130136
}
131137

132138
mc_tty_color_color_pair_attrs = g_hash_table_new_full (
@@ -179,8 +185,9 @@ tty_color_try_alloc_lib_pair (tty_color_lib_pair_t *mc_color_pair)
179185
ibg = mc_color_pair->bg;
180186
attr = mc_color_pair->attr;
181187

182-
// In legacy color mode, change bright colors into bold
183-
if (!tty_use_256colors (NULL) && !tty_use_truecolors (NULL))
188+
// If we have 8 indexed colors only, change foreground bright colors into bold and
189+
// background bright colors to basic colors
190+
if (COLORS <= 8 || (tty_use_truecolors (NULL) && overlay_colors <= 8))
184191
{
185192
if (ifg >= 8 && ifg < 16)
186193
{
@@ -191,11 +198,31 @@ tty_color_try_alloc_lib_pair (tty_color_lib_pair_t *mc_color_pair)
191198
if (ibg >= 8 && ibg < 16)
192199
{
193200
ibg &= 0x07;
194-
// attr | = A_BOLD | A_REVERSE ;
195201
}
196202
}
197203

204+
// Shady trick: if we don't have the exact color, because it is overlaid by backwards
205+
// compatibility indexed values, just borrow one degree of red. The user won't notice :)
206+
if ((ifg & FLAG_TRUECOLOR) != 0)
207+
{
208+
ifg &= ~FLAG_TRUECOLOR;
209+
if (ifg != 0 && ifg <= overlay_colors)
210+
ifg += (1 << 16);
211+
}
212+
213+
if ((ibg & FLAG_TRUECOLOR) != 0)
214+
{
215+
ibg &= ~FLAG_TRUECOLOR;
216+
if (ibg != 0 && ibg <= overlay_colors)
217+
ibg += (1 << 16);
218+
}
219+
220+
#if NCURSES_VERSION_PATCH >= 20170401 && defined(NCURSES_EXT_COLORS) && defined(NCURSES_EXT_FUNCS) \
221+
&& defined(HAVE_NCURSES_WIDECHAR)
222+
init_extended_pair (mc_color_pair->pair_index, ifg, ibg);
223+
#else
198224
init_pair (mc_color_pair->pair_index, ifg, ibg);
225+
#endif
199226
mc_tty_color_save_attr (mc_color_pair->pair_index, attr);
200227
}
201228
}
@@ -231,17 +258,56 @@ tty_use_256colors (GError **error)
231258
{
232259
(void) error;
233260

234-
return (COLORS == 256);
261+
overlay_colors = tty_tigetnum ("CO", NULL);
262+
263+
if (COLORS == 256 || (COLORS > 256 && overlay_colors == 256))
264+
return TRUE;
265+
266+
if (tty_use_truecolors (NULL))
267+
{
268+
need_convert_256color = TRUE;
269+
return TRUE;
270+
}
271+
272+
g_set_error (error, MC_ERROR, -1,
273+
_ ("\nIf your terminal supports 256 colors, you need to set your TERM\n"
274+
"environment variable to match your terminal, perhaps using\n"
275+
"a *-256color or *-direct256 variant. Use the 'toe -a'\n"
276+
"command to list all available variants on your system.\n"));
277+
return FALSE;
235278
}
236279

237280
/* --------------------------------------------------------------------------------------------- */
238281

239282
gboolean
240283
tty_use_truecolors (GError **error)
241284
{
242-
// Not yet supported in ncurses
243-
g_set_error (error, MC_ERROR, -1, _ ("True color not supported with ncurses."));
285+
// Low level true color is supported since ncurses 6.0 patch 20170401 preceding release
286+
// of ncurses 6.1. It needs ABI 6 or higher.
287+
#if !(NCURSES_VERSION_PATCH >= 20170401 && defined(NCURSES_EXT_COLORS) \
288+
&& defined(NCURSES_EXT_FUNCS) && defined(HAVE_NCURSES_WIDECHAR))
289+
g_set_error (error, MC_ERROR, -1,
290+
_ ("For true color support, you need version 6.1 or later of the ncurses\n"
291+
"library with wide character and ABI 6 or higher support.\n"
292+
"Please upgrade your system.\n"));
244293
return FALSE;
294+
#else
295+
// We support only bool RGB cap configuration (8:8:8 bits), but the other variants are so rare
296+
// that we don't need to bother.
297+
if (!(tty_tigetflag ("RGB", NULL) && COLORS == COLORS_TRUECOLOR))
298+
{
299+
g_set_error (
300+
error, MC_ERROR, -1,
301+
_ ("\nIf your terminal supports true colors, you need to set your TERM\n"
302+
"environment variable to a *-direct256, *-direct16, or *-direct variant.\n"
303+
"Use the 'toe -a' command to list all available variants on your system.\n"));
304+
return FALSE;
305+
}
306+
307+
overlay_colors = tty_tigetnum ("CO", NULL);
308+
309+
return TRUE;
310+
#endif
245311
}
246312

247313
/* --------------------------------------------------------------------------------------------- */

lib/tty/color-slang.c

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#include "lib/global.h"
3939
#include "lib/util.h" // whitespace()
4040

41+
#include "tty.h"
4142
#include "tty-slang.h"
4243
#include "color.h" // variables
4344
#include "color-internal.h"
@@ -61,7 +62,10 @@ has_colors (gboolean disable, gboolean force)
6162
{
6263
mc_tty_color_disable = disable;
6364

64-
if (force || (getenv ("COLORTERM") != NULL))
65+
// S-Lang enables color if the setaf/setab/setf/setb terminfo capabilities are set or
66+
// the COLORTERM environment variable is set
67+
68+
if (force)
6569
SLtt_Use_Ansi_Colors = 1;
6670

6771
if (!mc_tty_color_disable)
@@ -132,6 +136,10 @@ tty_color_init_lib (gboolean disable, gboolean force)
132136
if (has_colors (disable, force) && !disable)
133137
{
134138
use_colors = TRUE;
139+
140+
// Extended color mode detection routines must first be called before loading any skin
141+
tty_use_256colors (NULL);
142+
tty_use_truecolors (NULL);
135143
}
136144
}
137145

@@ -214,15 +222,27 @@ tty_set_normal_attrs (void)
214222
gboolean
215223
tty_use_256colors (GError **error)
216224
{
217-
gboolean ret;
225+
int colors, overlay_colors;
218226

219-
ret = (SLtt_Use_Ansi_Colors && SLtt_tgetnum ((char *) "Co") == 256);
227+
colors = tty_tigetnum ("colors", "Co");
228+
overlay_colors = tty_tigetnum ("CO", NULL);
220229

221-
if (!ret)
222-
g_set_error (error, MC_ERROR, -1,
223-
_ ("Your terminal doesn't even seem to support 256 colors."));
230+
if (SLtt_Use_Ansi_Colors && (colors == 256 || (colors > 256 && overlay_colors == 256)))
231+
return TRUE;
232+
233+
if (tty_use_truecolors (NULL))
234+
{
235+
need_convert_256color = TRUE;
236+
return TRUE;
237+
}
238+
239+
g_set_error (error, MC_ERROR, -1,
240+
_ ("\nIf your terminal supports 256 colors, you need to set your TERM\n"
241+
"environment variable to match your terminal, perhaps using\n"
242+
"a *-256color or *-direct256 variant. Use the 'toe -a'\n"
243+
"command to list all available variants on your system.\n"));
224244

225-
return ret;
245+
return FALSE;
226246
}
227247

228248
/* --------------------------------------------------------------------------------------------- */
@@ -245,11 +265,15 @@ tty_use_truecolors (GError **error)
245265
/* Duplicate slang's check so that we can pop up an error message
246266
rather than silently use wrong colors. */
247267
colorterm = getenv ("COLORTERM");
248-
if (colorterm == NULL
249-
|| (strcmp (colorterm, "truecolor") != 0 && strcmp (colorterm, "24bit") != 0))
268+
if (!((tty_tigetflag ("RGB", NULL) && tty_tigetnum ("colors", "Co") == COLORS_TRUECOLOR)
269+
|| (colorterm != NULL
270+
&& (strcmp (colorterm, "truecolor") == 0 || strcmp (colorterm, "24bit") == 0))))
250271
{
251272
g_set_error (error, MC_ERROR, -1,
252-
_ ("Set COLORTERM=truecolor if your terminal really supports true colors."));
273+
_ ("\nIf your terminal supports true colors, you need to set your TERM\n"
274+
"environment variable to a *-direct256, *-direct16, or *-direct variant.\n"
275+
"Use the 'toe -a' command to list all available variants on your system.\n"
276+
"Alternatively, you can set COLORTERM=truecolor.\n"));
253277
return FALSE;
254278
}
255279

0 commit comments

Comments
 (0)