From 0e55613d60e90408068ca7de042e5f13a87248ea Mon Sep 17 00:00:00 2001 From: e-tho <128100160+e-tho@users.noreply.github.com> Date: Mon, 16 Jun 2025 02:28:56 +0200 Subject: [PATCH 1/4] [Dmenu][Script] Add support for fallback icons This adds support for fallback icons in dmenu mode using comma-separated values in the icon metadata. When the primary icon is not found, subsequent icons in the list will be tried until one is successfully loaded. Example usage: "Firefox\0icon\x1ffirefox,web-browser,application-x-executable" --- doc/rofi-script.5.markdown | 4 ++-- source/modes/dmenu.c | 24 ++++++++++++++++++++++-- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/doc/rofi-script.5.markdown b/doc/rofi-script.5.markdown index 9d5d41c38..35a4fcfdd 100644 --- a/doc/rofi-script.5.markdown +++ b/doc/rofi-script.5.markdown @@ -138,7 +138,7 @@ For example: The following options are supported: -- **icon**: Set the icon for that row. +- **icon**: Set the icon for that row. Multiple fallback icons can be specified using comma-separated values. - **display**: Replace the displayed string. (Original string will still be used for filtering) @@ -158,7 +158,7 @@ The following options are supported: multiple entries can be passed using the `\x1f` separator. ```bash - echo -en "aap\0icon\x1ffolder\x1finfo\x1ftest\n" + echo -en "aap\0icon\x1ffolder,inode-directory\x1finfo\x1ftest\n" ``` ## Executing external program diff --git a/source/modes/dmenu.c b/source/modes/dmenu.c index 65b1ef2fa..951eeb8ac 100644 --- a/source/modes/dmenu.c +++ b/source/modes/dmenu.c @@ -724,12 +724,32 @@ static cairo_surface_t *dmenu_get_icon(const Mode *sw, if (dr->icon_name == NULL) { return NULL; } - uint32_t uid = dr->icon_fetch_uid = + if (strchr(dr->icon_name, ',') == NULL) { + uint32_t uid = dr->icon_fetch_uid = rofi_icon_fetcher_query(dr->icon_name, height); dr->icon_fetch_size = height; dr->icon_fetch_scale = scale; - return rofi_icon_fetcher_get(uid); + return rofi_icon_fetcher_get(uid); + } else { + cairo_surface_t *icon_surface = NULL; + char *icon_name_copy = g_strdup(dr->icon_name); + char *icon_iter = icon_name_copy; + char *icon = NULL; + // Try each icon in the comma-separated list until one is found + while ((icon = strsep(&icon_iter, ",")) != NULL) { + uint32_t uid = rofi_icon_fetcher_query(icon, height); + icon_surface = rofi_icon_fetcher_get(uid); + if (icon_surface != NULL) { + dr->icon_fetch_uid = uid; + dr->icon_fetch_size = height; + dr->icon_fetch_scale = scale; + break; + } + } + g_free(icon_name_copy); + return icon_surface; + } } static void dmenu_finish(DmenuModePrivateData *pd, RofiViewState *state, From 83ae375a4197a327fd6adbfbac133b45adad9920 Mon Sep 17 00:00:00 2001 From: e-tho <128100160+e-tho@users.noreply.github.com> Date: Mon, 16 Jun 2025 14:24:23 +0200 Subject: [PATCH 2/4] [Dmenu][Script] Refactor icon fallback logic to avoid redundancy --- source/modes/dmenu.c | 41 +++++++++++++++++------------------------ 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/source/modes/dmenu.c b/source/modes/dmenu.c index 951eeb8ac..82f5d0c6c 100644 --- a/source/modes/dmenu.c +++ b/source/modes/dmenu.c @@ -724,32 +724,25 @@ static cairo_surface_t *dmenu_get_icon(const Mode *sw, if (dr->icon_name == NULL) { return NULL; } - if (strchr(dr->icon_name, ',') == NULL) { - uint32_t uid = dr->icon_fetch_uid = - rofi_icon_fetcher_query(dr->icon_name, height); - dr->icon_fetch_size = height; - dr->icon_fetch_scale = scale; - - return rofi_icon_fetcher_get(uid); - } else { - cairo_surface_t *icon_surface = NULL; - char *icon_name_copy = g_strdup(dr->icon_name); - char *icon_iter = icon_name_copy; - char *icon = NULL; - // Try each icon in the comma-separated list until one is found - while ((icon = strsep(&icon_iter, ",")) != NULL) { - uint32_t uid = rofi_icon_fetcher_query(icon, height); - icon_surface = rofi_icon_fetcher_get(uid); - if (icon_surface != NULL) { - dr->icon_fetch_uid = uid; - dr->icon_fetch_size = height; - dr->icon_fetch_scale = scale; - break; - } + cairo_surface_t *icon_surface = NULL; + char *icon_name_copy = g_strdup(dr->icon_name); + char *icon_iter = icon_name_copy; + char *icon = NULL; + + // Try each icon in the comma-separated list until one is found + while ((icon = strsep(&icon_iter, ",")) != NULL) { + uint32_t uid = rofi_icon_fetcher_query(icon, height); + icon_surface = rofi_icon_fetcher_get(uid); + if (icon_surface != NULL) { + dr->icon_fetch_uid = uid; + dr->icon_fetch_size = height; + dr->icon_fetch_scale = scale; + break; } - g_free(icon_name_copy); - return icon_surface; } + + g_free(icon_name_copy); + return icon_surface; } static void dmenu_finish(DmenuModePrivateData *pd, RofiViewState *state, From 2553171072cfe5642c65457f80b739cb9dde3e9a Mon Sep 17 00:00:00 2001 From: e-tho <128100160+e-tho@users.noreply.github.com> Date: Fri, 27 Jun 2025 18:47:25 +0200 Subject: [PATCH 3/4] [Dmenu] Fix async icon fetching --- include/modes/dmenuscriptshared.h | 2 ++ source/modes/dmenu.c | 36 ++++++++++++++++++++++++------- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/include/modes/dmenuscriptshared.h b/include/modes/dmenuscriptshared.h index d6cf0715b..041219c29 100644 --- a/include/modes/dmenuscriptshared.h +++ b/include/modes/dmenuscriptshared.h @@ -18,6 +18,8 @@ typedef struct { uint32_t icon_fetch_uid; uint32_t icon_fetch_size; guint icon_fetch_scale; + /** Current fallback icon index being tried */ + int icon_fallback_index; /** Hidden meta keywords. */ char *meta; diff --git a/source/modes/dmenu.c b/source/modes/dmenu.c index 82f5d0c6c..c896d7bac 100644 --- a/source/modes/dmenu.c +++ b/source/modes/dmenu.c @@ -137,6 +137,7 @@ static void read_add_block(DmenuModePrivateData *pd, Block **block, char *data, (*block)->values[(*block)->length].icon_fetch_uid = 0; (*block)->values[(*block)->length].icon_fetch_size = 0; (*block)->values[(*block)->length].icon_fetch_scale = 0; + (*block)->values[(*block)->length].icon_fallback_index = 0; (*block)->values[(*block)->length].icon_name = NULL; (*block)->values[(*block)->length].meta = NULL; (*block)->values[(*block)->length].info = NULL; @@ -169,6 +170,7 @@ static void read_add(DmenuModePrivateData *pd, char *data, gsize len) { pd->cmd_list[pd->cmd_list_length].icon_fetch_uid = 0; pd->cmd_list[pd->cmd_list_length].icon_fetch_size = 0; pd->cmd_list[pd->cmd_list_length].icon_fetch_scale = 0; + pd->cmd_list[pd->cmd_list_length].icon_fallback_index = 0; pd->cmd_list[pd->cmd_list_length].icon_name = NULL; pd->cmd_list[pd->cmd_list_length].display = NULL; pd->cmd_list[pd->cmd_list_length].meta = NULL; @@ -724,25 +726,43 @@ static cairo_surface_t *dmenu_get_icon(const Mode *sw, if (dr->icon_name == NULL) { return NULL; } - cairo_surface_t *icon_surface = NULL; + + if (dr->icon_fetch_uid > 0) { + cairo_surface_t *surface = NULL; + gboolean query_done = rofi_icon_fetcher_get_ex(dr->icon_fetch_uid, &surface); + + if (surface != NULL) { + return surface; + } else if (query_done) { + dr->icon_fallback_index++; + dr->icon_fetch_uid = 0; + } else { + return NULL; + } + } + char *icon_name_copy = g_strdup(dr->icon_name); char *icon_iter = icon_name_copy; - char *icon = NULL; + char *current_icon = NULL; + int current_index = 0; // Try each icon in the comma-separated list until one is found - while ((icon = strsep(&icon_iter, ",")) != NULL) { - uint32_t uid = rofi_icon_fetcher_query(icon, height); - icon_surface = rofi_icon_fetcher_get(uid); - if (icon_surface != NULL) { - dr->icon_fetch_uid = uid; + while ((current_icon = strsep(&icon_iter, ",")) != NULL) { + if (current_index == dr->icon_fallback_index) { + dr->icon_fetch_uid = rofi_icon_fetcher_query(current_icon, height); dr->icon_fetch_size = height; dr->icon_fetch_scale = scale; break; } + current_index++; + } + + if (current_icon == NULL) { + dr->icon_fetch_uid = 0; } g_free(icon_name_copy); - return icon_surface; + return NULL; } static void dmenu_finish(DmenuModePrivateData *pd, RofiViewState *state, From 074573e0042849a656713cc9dc2d542208a02517 Mon Sep 17 00:00:00 2001 From: e-tho <128100160+e-tho@users.noreply.github.com> Date: Wed, 24 Sep 2025 17:08:07 +0200 Subject: [PATCH 4/4] [Dmenu][Script] Unify dmenu and script mode icon fallback logic --- include/modes/dmenuscriptshared.h | 2 +- source/modes/dmenu.c | 27 ++++++++----------- source/modes/script.c | 45 ++++++++++++++++++++++++------- 3 files changed, 48 insertions(+), 26 deletions(-) diff --git a/include/modes/dmenuscriptshared.h b/include/modes/dmenuscriptshared.h index 041219c29..1c706ceb2 100644 --- a/include/modes/dmenuscriptshared.h +++ b/include/modes/dmenuscriptshared.h @@ -13,7 +13,7 @@ typedef struct { char *display; /** Icon name to display. */ - char *icon_name; + char **icon_name; /** Async icon fetch handler. */ uint32_t icon_fetch_uid; uint32_t icon_fetch_size; diff --git a/source/modes/dmenu.c b/source/modes/dmenu.c index c896d7bac..8debf17e2 100644 --- a/source/modes/dmenu.c +++ b/source/modes/dmenu.c @@ -488,7 +488,7 @@ static void dmenu_mode_free(Mode *sw) { for (size_t i = 0; i < pd->cmd_list_length; i++) { if (pd->cmd_list[i].entry) { g_free(pd->cmd_list[i].entry); - g_free(pd->cmd_list[i].icon_name); + g_strfreev(pd->cmd_list[i].icon_name); g_free(pd->cmd_list[i].display); g_free(pd->cmd_list[i].meta); g_free(pd->cmd_list[i].info); @@ -741,27 +741,22 @@ static cairo_surface_t *dmenu_get_icon(const Mode *sw, } } - char *icon_name_copy = g_strdup(dr->icon_name); - char *icon_iter = icon_name_copy; char *current_icon = NULL; - int current_index = 0; - - // Try each icon in the comma-separated list until one is found - while ((current_icon = strsep(&icon_iter, ",")) != NULL) { - if (current_index == dr->icon_fallback_index) { - dr->icon_fetch_uid = rofi_icon_fetcher_query(current_icon, height); - dr->icon_fetch_size = height; - dr->icon_fetch_scale = scale; - break; - } - current_index++; + if (dr->icon_name && dr->icon_fallback_index >= 0) { + int icon_count = g_strv_length(dr->icon_name); + if (dr->icon_fallback_index < icon_count) { + current_icon = dr->icon_name[dr->icon_fallback_index]; + } } + if ( current_icon ){ + dr->icon_fetch_uid = rofi_icon_fetcher_query(current_icon, height); + dr->icon_fetch_size = height; + dr->icon_fetch_scale = scale; - if (current_icon == NULL) { + } else { dr->icon_fetch_uid = 0; } - g_free(icon_name_copy); return NULL; } diff --git a/source/modes/script.c b/source/modes/script.c index 1db6d8780..c959c2531 100644 --- a/source/modes/script.c +++ b/source/modes/script.c @@ -94,7 +94,8 @@ void dmenuscript_parse_entry_extras(G_GNUC_UNUSED Mode *sw, *(extra) = NULL; *(extra + 1) = NULL; if (strcasecmp(key, "icon") == 0) { - entry->icon_name = value; + entry->icon_name = g_strsplit(value,",", -1); + g_free(value); } else if (strcasecmp(key, "display") == 0) { entry->display = value; } else if (strcasecmp(key, "meta") == 0) { @@ -409,7 +410,7 @@ static void script_mode_destroy(Mode *sw) { if (rmpd != NULL) { for (unsigned int i = 0; i < rmpd->cmd_list_length; i++) { g_free(rmpd->cmd_list[i].entry); - g_free(rmpd->cmd_list[i].icon_name); + g_strfreev(rmpd->cmd_list[i].icon_name); g_free(rmpd->cmd_list[i].display); g_free(rmpd->cmd_list[i].meta); } @@ -518,20 +519,46 @@ static cairo_surface_t *script_get_icon(const Mode *sw, unsigned int height) { ScriptModePrivateData *pd = (ScriptModePrivateData *)mode_get_private_data(sw); + const guint scale = display_scale(); + g_return_val_if_fail(pd->cmd_list != NULL, NULL); DmenuScriptEntry *dr = &(pd->cmd_list[selected_line]); if (dr->icon_name == NULL) { return NULL; } - if (dr->icon_fetch_uid > 0 && dr->icon_fetch_size == height && - dr->icon_fetch_scale == scale) { - return rofi_icon_fetcher_get(dr->icon_fetch_uid); + + if (dr->icon_fetch_uid > 0) { + cairo_surface_t *surface = NULL; + gboolean query_done = rofi_icon_fetcher_get_ex(dr->icon_fetch_uid, &surface); + + if (surface != NULL) { + return surface; + } else if (query_done) { + dr->icon_fallback_index++; + dr->icon_fetch_uid = 0; + } else { + return NULL; + } + } + + char *current_icon = NULL; + if (dr->icon_name && dr->icon_fallback_index >= 0) { + int icon_count = g_strv_length(dr->icon_name); + if (dr->icon_fallback_index < icon_count) { + current_icon = dr->icon_name[dr->icon_fallback_index]; + } + } + if ( current_icon ){ + dr->icon_fetch_uid = rofi_icon_fetcher_query(current_icon, height); + dr->icon_fetch_size = height; + dr->icon_fetch_scale = scale; + + } else { + dr->icon_fetch_uid = 0; } - dr->icon_fetch_uid = rofi_icon_fetcher_query(dr->icon_name, height); - dr->icon_fetch_size = height; - dr->icon_fetch_scale = scale; - return rofi_icon_fetcher_get(dr->icon_fetch_uid); + + return NULL; } #include "mode-private.h"