[vlc-commits] [Git][videolan/vlc][master] ncurses: make colors configurable

Steve Lhomme (@robUx4) gitlab at videolan.org
Sat May 30 11:15:48 UTC 2026



Steve Lhomme pushed to branch master at VideoLAN / VLC


Commits:
88f2cd80 by Piotr Danecki at 2026-05-30T11:05:05+00:00
ncurses: make colors configurable

Signed-off-by: Piotr Danecki <i3riced at mailfence.com>

- - - - -


1 changed file:

- modules/gui/ncurses.c


Changes:

=====================================
modules/gui/ncurses.c
=====================================
@@ -85,7 +85,11 @@ vlc_module_begin ()
     set_callbacks(Open, Close)
     add_shortcut("curses")
     add_directory("browse-dir", NULL, BROWSE_TEXT, BROWSE_LONGTEXT)
-vlc_module_end ()
+
+    add_string("ncurses-colors", "", N_("ncurses colors config"), N_("configures the ncurses color palette to provided rgb values, avaiable colors: black, red, green, yellow, blue, magenta, cyan, white. Example: ncurses-colors=yellow=FF8000,cyan=00E5CC. Note: Do not use ' or \" in configuration files!"))
+    add_string("ncurses-theme", "", N_("ncurses theme config"), N_("configures what UI elements use what colors from the palette, avaiable elements: title, box, status, info, error, warning, debug, category, folder, progress. Example: ncurses-theme=title=yellow:black,box=cyan:black,playlist=cyan:black-yellow:black. Note: Do not use ' or \" in configuration files!"))
+
+vlc_module_end()
 
 #include "eject.c"
 
@@ -123,9 +127,6 @@ enum
 {
     C_DEFAULT = 0,
     C_TITLE,
-    C_PLAYLIST_1,
-    C_PLAYLIST_2,
-    C_PLAYLIST_3,
     C_BOX,
     C_STATUS,
     C_INFO,
@@ -134,37 +135,10 @@ enum
     C_DEBUG,
     C_CATEGORY,
     C_FOLDER,
+    C_PROGRESS,
     /* XXX: new elements here ! */
 
-    C_MAX
-};
-
-/* Available colors: BLACK RED GREEN YELLOW BLUE MAGENTA CYAN WHITE */
-static const struct { short f; short b; } color_pairs[] =
-{
-    /* element */       /* foreground*/ /* background*/
-    [C_TITLE]       = { COLOR_YELLOW,   COLOR_BLACK },
-
-    /* jamaican playlist, for rastafari sisters & brothers! */
-    [C_PLAYLIST_1]  = { COLOR_GREEN,    COLOR_BLACK },
-    [C_PLAYLIST_2]  = { COLOR_YELLOW,   COLOR_BLACK },
-    [C_PLAYLIST_3]  = { COLOR_RED,      COLOR_BLACK },
-
-    /* used in DrawBox() */
-    [C_BOX]         = { COLOR_CYAN,     COLOR_BLACK },
-    /* Source: State, Position, Volume, Chapters, etc...*/
-    [C_STATUS]      = { COLOR_BLUE,     COLOR_BLACK },
-
-    /* VLC messages, keep the order from highest priority to lowest */
-    [C_INFO]        = { COLOR_BLACK,    COLOR_WHITE },
-    [C_ERROR]       = { COLOR_RED,      COLOR_BLACK },
-    [C_WARNING]     = { COLOR_YELLOW,   COLOR_BLACK },
-    [C_DEBUG]       = { COLOR_WHITE,    COLOR_BLACK },
-
-    /* Category title: help, info, metadata */
-    [C_CATEGORY]    = { COLOR_MAGENTA,  COLOR_BLACK },
-    /* Folder (BOX_BROWSE) */
-    [C_FOLDER]      = { COLOR_RED,      COLOR_BLACK },
+    C_PLAYLIST,
 };
 
 struct dir_entry_t
@@ -175,6 +149,11 @@ struct dir_entry_t
 
 typedef struct VLC_VECTOR(char const *) pl_item_names;
 
+typedef struct short_color
+{
+    short r, g, b;
+} short_color;
+
 struct intf_sys_t
 {
     vlc_thread_t    thread;
@@ -182,10 +161,9 @@ struct intf_sys_t
 
     bool            color;
 
-    /* rgb values for the color yellow */
-    short           yellow_r;
-    short           yellow_g;
-    short           yellow_b;
+    /* rgb values for all of the colors */
+    short_color     system_colors[8];
+    size_t          playlist_colors;
 
     int             box_type;
     int             previous_box_type;
@@ -424,6 +402,222 @@ static void SearchPlaylist(intf_sys_t *sys)
     }
 }
 
+/****************************************************************************
+ * Config
+ ****************************************************************************/
+
+static const struct { short f; short b; } default_color_pairs[] =
+{
+    /* element */       /* foreground*/ /* background*/
+    [C_TITLE]       = { COLOR_YELLOW,   COLOR_BLACK },
+
+    /* used in DrawBox() */
+    [C_BOX]         = { COLOR_CYAN,     COLOR_BLACK },
+    /* Source: State, Position, Volume, Chapters, etc...*/
+    [C_STATUS]      = { COLOR_BLUE,     COLOR_BLACK },
+
+    /* VLC messages, keep the order from highest priority to lowest */
+    [C_INFO]        = { COLOR_BLACK,    COLOR_WHITE },
+    [C_ERROR]       = { COLOR_RED,      COLOR_BLACK },
+    [C_WARNING]     = { COLOR_YELLOW,   COLOR_BLACK },
+    [C_DEBUG]       = { COLOR_WHITE,    COLOR_BLACK },
+
+    /* Category title: help, info, metadata */
+    [C_CATEGORY]    = { COLOR_MAGENTA,  COLOR_BLACK },
+    /* Folder (BOX_BROWSE) */
+    [C_FOLDER]      = { COLOR_RED,      COLOR_BLACK },
+
+    /* progress bar */
+    [C_PROGRESS]    = { COLOR_WHITE,    COLOR_WHITE},
+
+    /* jamaican playlist, for rastafari sisters & brothers! */
+    [C_PLAYLIST]    = { COLOR_GREEN,    COLOR_BLACK },
+    [C_PLAYLIST + 1]= { COLOR_YELLOW,   COLOR_BLACK },
+    [C_PLAYLIST + 2]= { COLOR_RED,      COLOR_BLACK },
+};
+
+static const char * color_names[] = {
+  "black",
+  "red",
+  "green",
+  "yellow",
+  "blue",
+  "magenta",
+  "cyan",
+  "white"
+};
+
+static int get_color_id_from_string(const char * string) {
+    if (!string) {
+        return -1;
+    }
+    for (int i = 0; i < 8; i++)
+        if (strcmp(string, color_names[i]) == 0)
+        return i;
+    return -1;
+}
+
+static const char * element_names[] = {
+  [C_TITLE] = "title",
+
+  [C_BOX] = "box",
+  [C_STATUS] = "status",
+
+  [C_INFO] = "info",
+  [C_ERROR] = "error",
+  [C_WARNING] = "warning",
+  [C_DEBUG] = "debug",
+
+  [C_CATEGORY] = "category",
+  [C_FOLDER] = "folder",
+
+  [C_PROGRESS] = "progress",
+};
+
+static int get_element_id_from_string(const char * string) {
+    if (!string) {
+        return -1;
+    }
+    for (int i = C_DEFAULT + 1; i < C_PLAYLIST; i++)
+        if (strcmp(string, element_names[i]) == 0)
+            return i;
+    return -1;
+}
+
+static void load_colors_config(intf_thread_t *intf) {
+    init_color(COLOR_YELLOW, 960, 500, 0); /* YELLOW -> ORANGE */
+    config_chain_t *p_config_chain = NULL;
+
+    char * psz_in = config_GetPsz("ncurses-colors");
+    if (!psz_in) {
+        return;
+    }
+
+    config_ChainParseOptions(&p_config_chain, psz_in);
+    config_chain_t *current = p_config_chain;
+    while (current) {
+        if (!current->psz_name) {
+            msg_Err(intf, "Couldn't load color name");
+            goto end;
+        }
+        if (!current->psz_value) {
+            msg_Err(intf, "Couldn't load color value");
+            goto end;
+        }
+        unsigned int color;
+        if (sscanf(current->psz_value, "%x", &color) != 1) {
+            msg_Err(intf, "Invalid color value %s", current->psz_value);
+            goto end;
+        }
+        int r = ((color >> 16) & 0xFF) * 1000 / 255;
+        int g = ((color >> 8) & 0xFF) * 1000 / 255;
+        int b = (color & 0xFF) * 1000 / 255;
+        int color_id = get_color_id_from_string(current->psz_name);
+        if (color_id == -1) {
+            msg_Err(intf, "Invalid color name %s", current->psz_name);
+            goto end;
+        }
+        init_color(color_id, r, g, b);
+    end:
+        current = current->p_next;
+    }
+    config_ChainDestroy(p_config_chain);
+    free(psz_in);
+}
+
+static int parse_color_pair(intf_thread_t *intf, const char *token,
+                             int *fg, int *bg)
+{
+    char fg_buf[8], bg_buf[8];
+
+    if (sscanf(token, "%7[a-z]:%7[a-z]", fg_buf, bg_buf) != 2) {
+        msg_Err(intf, "Invalid color pair '%s', expected 'fg:bg'", token);
+        return -1;
+    }
+
+    *fg = get_color_id_from_string(fg_buf);
+    if (*fg == -1) {
+        msg_Err(intf, "Unknown foreground color '%s'", fg_buf);
+        return -1;
+    }
+
+    *bg = get_color_id_from_string(bg_buf);
+    if (*bg == -1) {
+        msg_Err(intf, "Unknown background color '%s'", bg_buf);
+        return -1;
+    }
+
+    return 0;
+}
+
+static void load_pairs_config(intf_thread_t *intf) {
+    for (int i = C_DEFAULT + 1; i < (int)ARRAY_SIZE(default_color_pairs); i++) {
+        init_pair(i, default_color_pairs[i].f, default_color_pairs[i].b);
+    }
+
+    config_chain_t *p_config_chain = NULL;
+
+    char * psz_in = config_GetPsz("ncurses-theme");
+    if (!psz_in) {
+        return;
+    }
+    config_ChainParseOptions(&p_config_chain, psz_in);
+    config_chain_t *current = p_config_chain;
+    while (current) {
+        if (!current->psz_name) {
+            msg_Err(intf, "Couldn't load color name");
+            goto end_theme;
+        }
+        if (!current->psz_value) {
+            msg_Err(intf, "Couldn't load color value");
+            goto end_theme;
+        }
+
+        if (strcmp(current->psz_name, "playlist") == 0) {
+            char * safe_ptr = NULL;
+            char * current_token = strtok_r(current->psz_value, "-", &safe_ptr);
+            int playlist_id = 0;
+            while (current_token) {
+                int fg_color;
+                int bg_color;
+
+                if (parse_color_pair(intf, current_token, &fg_color, &bg_color) == -1)
+                    goto end_theme;
+                init_pair(C_PLAYLIST + playlist_id, fg_color, bg_color);
+
+                playlist_id++;
+                current_token = strtok_r(NULL, "-", &safe_ptr);
+
+                if (C_PLAYLIST + playlist_id >= COLOR_PAIRS) {
+                    msg_Err(intf, "Too many playlist colors");
+                    goto end_theme;
+                }
+            }
+            if (playlist_id != 0)
+                intf->p_sys->playlist_colors = playlist_id;
+        }
+        else {
+            int fg_color;
+            int bg_color;
+
+            if (parse_color_pair(intf, current->psz_value, &fg_color, &bg_color) == -1)
+                goto end_theme;
+
+            int element_id = get_element_id_from_string(current->psz_name);
+            if (element_id == -1) {
+                msg_Err(intf, "Invalid ui element %s", current->psz_name);
+                goto end_theme;
+            }
+
+            init_pair(element_id, fg_color, bg_color);
+        }
+    end_theme:
+        current = current->p_next;
+    }
+    config_ChainDestroy(p_config_chain);
+    free(psz_in);
+}
+
 /****************************************************************************
  * Drawing
  ****************************************************************************/
@@ -439,13 +633,15 @@ static void start_color_and_pairs(intf_thread_t *intf)
     }
 
     start_color();
-    for (int i = C_DEFAULT + 1; i < C_MAX; i++)
-        init_pair(i, color_pairs[i].f, color_pairs[i].b);
+    sys->playlist_colors = 3;
+    load_pairs_config(intf);
 
     /* untested, in all my terminals, !can_change_color() --funman */
     if (can_change_color()) {
-        color_content(COLOR_YELLOW, &sys->yellow_r, &sys->yellow_g, &sys->yellow_b);
-        init_color(COLOR_YELLOW, 960, 500, 0); /* YELLOW -> ORANGE */
+        for (int i = 0; i < 8 && i < COLORS; i++) {
+            color_content(i, &sys->system_colors[i].r, &sys->system_colors[i].g, &sys->system_colors[i].b);
+        }
+        load_colors_config(intf);
     }
 }
 
@@ -487,15 +683,6 @@ static void DrawEmptyLine(int y, int x, int w)
     mvhline(y, x, ' ', w);
 }
 
-static void DrawLine(int y, int x, int w)
-{
-    if (w <= 0) return;
-
-    attrset(A_REVERSE);
-    mvhline(y, x, ' ', w);
-    attroff(A_REVERSE);
-}
-
 static void mvnprintw(int y, int x, int w, const char *p_fmt, ...)
 {
     va_list  vl_args;
@@ -854,8 +1041,9 @@ static int DrawPlaylist(intf_thread_t *intf)
 
     for (size_t i = 0; i < sys->pl_item_names.size; i++)
     {
-        if (sys->color)
-            color_set(i%3 + C_PLAYLIST_1, NULL);
+        if (sys->color) {
+            color_set((sys->playlist_colors > 0 ? i%sys->playlist_colors : 1) + C_PLAYLIST, NULL);
+        }
 
         MainBoxWrite(sys, i, "%c %s",
                 (ssize_t)i == cur_idx ? '>' : ' ',
@@ -1013,7 +1201,13 @@ static int DrawStatus(intf_thread_t *intf)
     DrawBox(y++, 1, sys->color, ""); /* position slider */
     DrawEmptyLine(y, 1, COLS-2);
     if (vlc_player_IsStarted(player))
-        DrawLine(y, 1, (int)((COLS-2) * vlc_player_GetPosition(player)));
+    {
+        if (sys->color)
+            color_set(C_PROGRESS, NULL);
+        DrawEmptyLine(y, 1, (int)((COLS-2) * vlc_player_GetPosition(player)));
+        if (sys->color)
+            color_set(C_DEFAULT, NULL);
+    }
     y += 2; /* skip slider and box */
 
     vlc_playlist_Unlock(playlist);
@@ -1766,8 +1960,12 @@ static void Close(vlc_object_t *p_this)
     free(sys->current_dir);
 
     if (can_change_color())
-        /* Restore yellow to its original color */
-        init_color(COLOR_YELLOW, sys->yellow_r, sys->yellow_g, sys->yellow_b);
+    {
+        /* Restore colors to theirs original colors */
+        for (int i = 0; i < 8 && i < COLORS; i++) {
+            init_color(i, sys->system_colors[i].r, sys->system_colors[i].g, sys->system_colors[i].b);
+        }
+    }
 
     endwin();   /* Close the ncurses interface */
 



View it on GitLab: https://code.videolan.org/videolan/vlc/-/commit/88f2cd806ffe968ef019915cd28bbab2473c96ed

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/commit/88f2cd806ffe968ef019915cd28bbab2473c96ed
You're receiving this email because of your account on code.videolan.org. Manage all notifications: https://code.videolan.org/-/profile/notifications | Help: https://code.videolan.org/help




More information about the vlc-commits mailing list