From a7a7660a49f964d0c76872e839a70db10ba8bcc0 Mon Sep 17 00:00:00 2001 From: Vladimir Panteleev Date: Sat, 10 Jan 2026 13:13:57 +0000 Subject: [PATCH] fix(swaybar/dbusmenu): cache icon lookups to avoid repeated filesystem access Add caching for icon lookups in menu items. Two new fields are added to the menu item struct: - icon_surface: cached icon surface loaded from icon_name - icon_lookup_done: tracks whether icon lookup has been attempted The icon is now looked up once and cached, rather than performing filesystem access every time the menu is drawn. The cached surface is properly cleaned up when the menu is destroyed. --- swaybar/tray/dbusmenu.c | 61 +++++++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 26 deletions(-) diff --git a/swaybar/tray/dbusmenu.c b/swaybar/tray/dbusmenu.c index 7665f83f2..360838817 100644 --- a/swaybar/tray/dbusmenu.c +++ b/swaybar/tray/dbusmenu.c @@ -60,6 +60,8 @@ struct swaybar_dbusmenu_menu_item { char *label; char *icon_name; cairo_surface_t *icon_data; + cairo_surface_t *icon_surface; // cached icon loaded from icon_name + bool icon_lookup_done; // true if we've already tried to look up icon_name enum menu_toggle_type toggle_type; bool enabled; bool visible; @@ -239,6 +241,9 @@ static void swaybar_dbusmenu_menu_destroy(struct swaybar_dbusmenu_menu *menu) { if (item->icon_data) { cairo_surface_destroy(item->icon_data); } + if (item->icon_surface) { + cairo_surface_destroy(item->icon_surface); + } free(item); } } @@ -404,36 +409,40 @@ static void draw_menu_items(cairo_t *cairo, struct swaybar_dbusmenu_menu *menu, icon_size = 2 * padding + size; cairo_set_source_u32(cairo, config->colors.focused_statusline); if (item->icon_name) { - list_t *icon_search_paths = create_list(); - list_cat(icon_search_paths, tray->basedirs); - if (sni->menu_icon_theme_paths) { - for (char **path = sni->menu_icon_theme_paths; *path; ++path) { - list_add(icon_search_paths, *path); + // Cache icon lookup to avoid repeated filesystem access + if (!item->icon_lookup_done) { + item->icon_lookup_done = true; + list_t *icon_search_paths = create_list(); + list_cat(icon_search_paths, tray->basedirs); + if (sni->menu_icon_theme_paths) { + for (char **path = sni->menu_icon_theme_paths; *path; ++path) { + list_add(icon_search_paths, *path); + } + } + if (sni->icon_theme_path) { + list_add(icon_search_paths, sni->icon_theme_path); + } + int min_size, max_size; + char *icon_path = + find_icon(tray->themes, icon_search_paths, item->icon_name, size, + config->icon_theme, &min_size, &max_size); + list_free(icon_search_paths); + + if (icon_path) { + item->icon_surface = load_image(icon_path); + free(icon_path); } } - if (sni->icon_theme_path) { - list_add(icon_search_paths, sni->icon_theme_path); - } - int min_size, max_size; - char *icon_path = - find_icon(tray->themes, icon_search_paths, item->icon_name, size, - config->icon_theme, &min_size, &max_size); - list_free(icon_search_paths); - if (icon_path) { - cairo_surface_t *icon = load_image(icon_path); - free(icon_path); - if (icon) { - cairo_surface_t *icon_scaled = - cairo_image_surface_scale(icon, size, size); - cairo_surface_destroy(icon); + if (item->icon_surface) { + cairo_surface_t *icon_scaled = + cairo_image_surface_scale(item->icon_surface, size, size); - cairo_set_source_surface(cairo, icon_scaled, x, y); - cairo_rectangle(cairo, x, y, size, size); - cairo_fill(cairo); - cairo_surface_destroy(icon_scaled); - is_icon_drawn = true; - } + cairo_set_source_surface(cairo, icon_scaled, x, y); + cairo_rectangle(cairo, x, y, size, size); + cairo_fill(cairo); + cairo_surface_destroy(icon_scaled); + is_icon_drawn = true; } } else if (item->icon_data) { cairo_surface_t *icon = cairo_image_surface_scale(item->icon_data, size, size);