sway

i3-compatible Wayland compositor
git clone https://git.awy.one/sway
Log | Files | Refs | README | LICENSE

commit 3749fa5e17bb7da63b20d9969bf6e084f074ad95
parent 623901a0d2a3472791b17d8b5572a7708c7ccc85
Author: Vladimir Panteleev <git@cy.md>
Date:   Sat, 10 Jan 2026 13:13:57 +0000

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.

Diffstat:
Mswaybar/tray/dbusmenu.c | 65+++++++++++++++++++++++++++++++++++++----------------------------
1 file changed, 37 insertions(+), 28 deletions(-)

diff --git 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,37 +409,41 @@ 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) { - 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); - - 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; + 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 (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; + } } else if (item->icon_data) { cairo_surface_t *icon = cairo_image_surface_scale(item->icon_data, size, size); cairo_set_source_surface(cairo, icon, x, y);