Manual pages: mcmcdiffmceditmcview

root/lib/tty/color-ncurses.c

/* [previous][next][first][last][top][bottom][index][help]  */

DEFINITIONS

This source file includes following definitions.
  1. get_ncurses_color_pair
  2. tty_color_init_lib
  3. tty_color_deinit_lib
  4. tty_color_try_alloc_lib_pair
  5. tty_setcolor
  6. tty_set_normal_attrs
  7. tty_use_256colors
  8. tty_use_truecolors
  9. tty_colorize_area

   1 /*
   2    Color setup for NCurses screen library
   3 
   4    Copyright (C) 1994-2026
   5    Free Software Foundation, Inc.
   6 
   7    Written by:
   8    Andrew Borodin <aborodin@vmail.ru>, 2009
   9    Slava Zanko <slavazanko@gmail.com>, 2010
  10    Egmont Koblinger <egmont@gmail.com>, 2010
  11 
  12    This file is part of the Midnight Commander.
  13 
  14    The Midnight Commander is free software: you can redistribute it
  15    and/or modify it under the terms of the GNU General Public License as
  16    published by the Free Software Foundation, either version 3 of the License,
  17    or (at your option) any later version.
  18 
  19    The Midnight Commander is distributed in the hope that it will be useful,
  20    but WITHOUT ANY WARRANTY; without even the implied warranty of
  21    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  22    GNU General Public License for more details.
  23 
  24    You should have received a copy of the GNU General Public License
  25    along with this program.  If not, see <https://www.gnu.org/licenses/>.
  26  */
  27 
  28 /** \file color-ncurses.c
  29  *  \brief Source: NCUrses-specific color setup
  30  */
  31 
  32 #include <config.h>
  33 
  34 #include <stdio.h>
  35 #include <stdlib.h>
  36 #include <string.h>
  37 #include <sys/types.h>  // size_t
  38 
  39 #include "lib/global.h"
  40 
  41 #include "tty.h"
  42 #include "tty-ncurses.h"
  43 #include "color.h"  // variables
  44 #include "color-internal.h"
  45 
  46 /*** global variables ****************************************************************************/
  47 
  48 /*** file scope macro definitions ****************************************************************/
  49 
  50 /*** file scope type declarations ****************************************************************/
  51 
  52 typedef struct
  53 {
  54     int pair;   // ncurses color pair index
  55     int attrs;  // attributes
  56 } mc_tty_ncurses_color_pair_and_attrs_t;
  57 
  58 /*** forward declarations (file scope functions) *************************************************/
  59 
  60 /*** file scope variables ************************************************************************/
  61 
  62 /*
  63  * Our bookkeeping of the ncurses color pair indices, indexed by the "{fg}.{bg}" string.
  64  */
  65 static GHashTable *mc_tty_ncurses_color_pairs = NULL;
  66 
  67 /*
  68  * Indexed by mc's color index, points to the mc_tty_ncurses_color_pair_and_attrs_t object
  69  * representing the ncurses color pair index and the attributes.
  70  *
  71  * mc's color index represents unique (fg, bg, attrs) tuples. Allocating an ncurses color pair
  72  * for each of them might be too wasteful and might cause us to run out of available color pairs
  73  * too soon (especially in 8-color terminals), if many combinations only differ in attrs.
  74  * So we allocate a new ncurses color pair only if the (fg, bg) tuple is newly seen.
  75  * See #5020 for details.
  76  */
  77 static GArray *mc_tty_ncurses_color_pair_and_attrs = NULL;
  78 
  79 static int mc_tty_ncurses_next_color_pair = 0;
  80 
  81 static int overlay_colors = 0;
  82 
  83 /* --------------------------------------------------------------------------------------------- */
  84 /*** file scope functions ************************************************************************/
  85 /* --------------------------------------------------------------------------------------------- */
  86 
  87 static int
  88 get_ncurses_color_pair (int ifg, int ibg)
     /* [previous][next][first][last][top][bottom][index][help]  */
  89 {
  90     char *color_pair_str;
  91     int *ncurses_color_pair;
  92     int init_pair_ret;
  93 
  94     color_pair_str = g_strdup_printf ("%d.%d", ifg, ibg);
  95 
  96     ncurses_color_pair =
  97         (int *) g_hash_table_lookup (mc_tty_ncurses_color_pairs, (gpointer) color_pair_str);
  98 
  99     if (ncurses_color_pair == NULL)
 100     {
 101         ncurses_color_pair = g_try_new0 (int, 1);
 102         *ncurses_color_pair = mc_tty_ncurses_next_color_pair;
 103 #if NCURSES_VERSION_PATCH >= 20170401 && defined(NCURSES_EXT_COLORS) && defined(NCURSES_EXT_FUNCS) \
 104     && defined(HAVE_NCURSES_WIDECHAR)
 105         init_pair_ret = init_extended_pair (*ncurses_color_pair, ifg, ibg);
 106 #else
 107         init_pair_ret = init_pair (*ncurses_color_pair, ifg, ibg);
 108 #endif
 109 
 110         if (init_pair_ret == ERR)
 111         {
 112             g_free (ncurses_color_pair);
 113             g_free (color_pair_str);
 114             return 0;
 115         }
 116 
 117         g_hash_table_insert (mc_tty_ncurses_color_pairs, color_pair_str, ncurses_color_pair);
 118         mc_tty_ncurses_next_color_pair++;
 119     }
 120     else
 121         g_free (color_pair_str);
 122 
 123     return *ncurses_color_pair;
 124 }
 125 
 126 /* --------------------------------------------------------------------------------------------- */
 127 /*** public functions ****************************************************************************/
 128 /* --------------------------------------------------------------------------------------------- */
 129 
 130 void
 131 tty_color_init_lib (gboolean disable, gboolean force)
     /* [previous][next][first][last][top][bottom][index][help]  */
 132 {
 133     int default_color_pair_id;
 134 
 135     (void) force;
 136 
 137     if (has_colors () && !disable)
 138     {
 139         use_colors = TRUE;
 140         start_color ();
 141         use_default_colors ();
 142 
 143         // Extended color mode detection routines must first be called before loading any skin
 144         tty_use_256colors (NULL);
 145         tty_use_truecolors (NULL);
 146     }
 147 
 148     // our tracking of ncurses's color pairs
 149     mc_tty_ncurses_color_pairs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
 150 
 151     // ncurses color pair 0 always refers to the default colors; add it to our hash
 152     mc_tty_ncurses_next_color_pair = 0;
 153     default_color_pair_id = get_ncurses_color_pair (-1, -1);
 154     g_assert (default_color_pair_id == 0);
 155     (void) default_color_pair_id;  // unused if g_assert is eliminated
 156 
 157     // mapping from our index to ncurses's index and attributes
 158     mc_tty_ncurses_color_pair_and_attrs =
 159         g_array_new (FALSE, FALSE, sizeof (mc_tty_ncurses_color_pair_and_attrs_t));
 160 }
 161 
 162 /* --------------------------------------------------------------------------------------------- */
 163 
 164 void
 165 tty_color_deinit_lib (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 166 {
 167     g_hash_table_destroy (mc_tty_ncurses_color_pairs);
 168     mc_tty_ncurses_color_pairs = NULL;
 169 
 170     g_array_free (mc_tty_ncurses_color_pair_and_attrs, TRUE);
 171     mc_tty_ncurses_color_pair_and_attrs = NULL;
 172 
 173     mc_tty_ncurses_next_color_pair = 0;
 174 }
 175 
 176 /* --------------------------------------------------------------------------------------------- */
 177 
 178 void
 179 tty_color_try_alloc_lib_pair (tty_color_lib_pair_t *mc_color_pair)
     /* [previous][next][first][last][top][bottom][index][help]  */
 180 {
 181     int ifg, ibg, attr;
 182 
 183     ifg = mc_color_pair->fg;
 184     ibg = mc_color_pair->bg;
 185     attr = mc_color_pair->attr;
 186 
 187     // If we have 8 indexed colors only, change foreground bright colors into bold and
 188     // background bright colors to basic colors
 189     if (COLORS <= 8 || (tty_use_truecolors (NULL) && overlay_colors <= 8))
 190     {
 191         if (ifg >= 8 && ifg < 16)
 192         {
 193             ifg &= 0x07;
 194             attr |= A_BOLD;
 195         }
 196 
 197         if (ibg >= 8 && ibg < 16)
 198         {
 199             ibg &= 0x07;
 200         }
 201     }
 202 
 203     // Shady trick: if we don't have the exact color, because it is overlaid by backwards
 204     // compatibility indexed values, just borrow one degree of red. The user won't notice :)
 205     if (ifg >= 0 && (ifg & FLAG_TRUECOLOR) != 0)
 206     {
 207         ifg &= ~FLAG_TRUECOLOR;
 208         if (ifg <= overlay_colors)
 209             ifg += (1 << 16);
 210     }
 211 
 212     if (ibg >= 0 && (ibg & FLAG_TRUECOLOR) != 0)
 213     {
 214         ibg &= ~FLAG_TRUECOLOR;
 215         if (ibg <= overlay_colors)
 216             ibg += (1 << 16);
 217     }
 218 
 219     const int ncurses_color_pair = get_ncurses_color_pair (ifg, ibg);
 220     const mc_tty_ncurses_color_pair_and_attrs_t pair_and_attrs = { .pair = ncurses_color_pair,
 221                                                                    .attrs = attr };
 222 
 223     g_array_insert_val (mc_tty_ncurses_color_pair_and_attrs, mc_color_pair->pair_index,
 224                         pair_and_attrs);
 225 }
 226 
 227 /* --------------------------------------------------------------------------------------------- */
 228 
 229 void
 230 tty_setcolor (int color)
     /* [previous][next][first][last][top][bottom][index][help]  */
 231 {
 232     mc_tty_ncurses_color_pair_and_attrs_t *pair_and_attrs;
 233 
 234     color = tty_maybe_map_color (color);
 235     pair_and_attrs = &g_array_index (mc_tty_ncurses_color_pair_and_attrs,
 236                                      mc_tty_ncurses_color_pair_and_attrs_t, color);
 237     attr_set (pair_and_attrs->attrs, pair_and_attrs->pair, NULL);
 238 }
 239 
 240 /* --------------------------------------------------------------------------------------------- */
 241 
 242 void
 243 tty_set_normal_attrs (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 244 {
 245     standend ();
 246 }
 247 
 248 /* --------------------------------------------------------------------------------------------- */
 249 
 250 gboolean
 251 tty_use_256colors (GError **error)
     /* [previous][next][first][last][top][bottom][index][help]  */
 252 {
 253     (void) error;
 254 
 255     overlay_colors = tty_tigetnum ("CO", NULL);
 256 
 257     if (COLORS == 256 || (COLORS > 256 && overlay_colors == 256))
 258         return TRUE;
 259 
 260     if (tty_use_truecolors (NULL))
 261     {
 262         need_convert_256color = TRUE;
 263         return TRUE;
 264     }
 265 
 266     g_set_error (error, MC_ERROR, -1,
 267                  _ ("\nIf your terminal supports 256 colors, you need to set your TERM\n"
 268                     "environment variable to match your terminal, perhaps using\n"
 269                     "a *-256color or *-direct256 variant. Use the 'toe -a'\n"
 270                     "command to list all available variants on your system.\n"));
 271     return FALSE;
 272 }
 273 
 274 /* --------------------------------------------------------------------------------------------- */
 275 
 276 gboolean
 277 tty_use_truecolors (GError **error)
     /* [previous][next][first][last][top][bottom][index][help]  */
 278 {
 279     // Low level true color is supported since ncurses 6.0 patch 20170401 preceding release
 280     // of ncurses 6.1. It needs ABI 6 or higher.
 281 #if !(NCURSES_VERSION_PATCH >= 20170401 && defined(NCURSES_EXT_COLORS)                             \
 282       && defined(NCURSES_EXT_FUNCS) && defined(HAVE_NCURSES_WIDECHAR))
 283     g_set_error (error, MC_ERROR, -1,
 284                  _ ("For true color support, you need version 6.1 or later of the ncurses\n"
 285                     "library with wide character and ABI 6 or higher support.\n"
 286                     "Please upgrade your system.\n"));
 287     return FALSE;
 288 #else
 289     // We support only bool RGB cap configuration (8:8:8 bits), but the other variants are so rare
 290     // that we don't need to bother.
 291     if (!(tty_tigetflag ("RGB", NULL) && COLORS == COLORS_TRUECOLOR))
 292     {
 293         g_set_error (
 294             error, MC_ERROR, -1,
 295             _ ("\nIf your terminal supports true colors, you need to set your TERM\n"
 296                "environment variable to a *-direct256, *-direct16, or *-direct variant.\n"
 297                "Use the 'toe -a' command to list all available variants on your system.\n"));
 298         return FALSE;
 299     }
 300 
 301     overlay_colors = tty_tigetnum ("CO", NULL);
 302 
 303     return TRUE;
 304 #endif
 305 }
 306 
 307 /* --------------------------------------------------------------------------------------------- */
 308 
 309 void
 310 tty_colorize_area (int y, int x, int rows, int cols, int color)
     /* [previous][next][first][last][top][bottom][index][help]  */
 311 {
 312 #ifdef ENABLE_SHADOWS
 313     cchar_t *ctext;
 314     wchar_t wch[CCHARW_MAX + 1];
 315     attr_t attrs;
 316     short color_pair;
 317 
 318     if (!use_colors || !tty_clip (&y, &x, &rows, &cols))
 319         return;
 320 
 321     color = tty_maybe_map_color (color);
 322     color = g_array_index (mc_tty_ncurses_color_pair_and_attrs,
 323                            mc_tty_ncurses_color_pair_and_attrs_t, color)
 324                 .pair;
 325 
 326     ctext = g_malloc (sizeof (cchar_t) * (cols + 1));
 327 
 328     for (int row = 0; row < rows; row++)
 329     {
 330         mvin_wchnstr (y + row, x, ctext, cols);
 331 
 332         for (int col = 0; col < cols; col++)
 333         {
 334             getcchar (&ctext[col], wch, &attrs, &color_pair, NULL);
 335             setcchar (&ctext[col], wch, attrs, color, NULL);
 336         }
 337 
 338         mvadd_wchnstr (y + row, x, ctext, cols);
 339     }
 340 
 341     g_free (ctext);
 342 #else
 343     (void) y;
 344     (void) x;
 345     (void) rows;
 346     (void) cols;
 347     (void) color;
 348 #endif
 349 }
 350 
 351 /* --------------------------------------------------------------------------------------------- */

/* [previous][next][first][last][top][bottom][index][help]  */