From 17dc5071fdfb99784e17bc5936aa18de21e7d056 Mon Sep 17 00:00:00 2001 From: myrslint <206005528+myrslint@users.noreply.github.com> Date: Wed, 30 Apr 2025 02:32:07 +0000 Subject: [PATCH] Extends former behavior to include loading of icons if they are specified as an absolute path. Fixes size selection for SVG (scalable) icons relying on whose nominal size read via gdk-pixbuf loader may not correctly indicate that they can be scaled to neatly fill the available scale e.g., symbolic icons from Adwaita specify a nominal size of 16x16. --- common/sfdo.c | 23 ++++++++++++++++------- include/swaybar/image.h | 2 +- swaybar/image.c | 20 ++++++++++++++++++-- swaybar/tray/item.c | 28 +++++++++++++++------------- 4 files changed, 50 insertions(+), 23 deletions(-) diff --git a/common/sfdo.c b/common/sfdo.c index a31c87698..e2e685650 100644 --- a/common/sfdo.c +++ b/common/sfdo.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -6,16 +7,24 @@ #include "log.h" #include "sfdo.h" +// this extends libsfdo's behavior to also handle icons specified as absolute paths char *sfdo_icon_lookup_extended(struct sfdo *sfdo, char *icon_name, int target_size, int scale) { - int lookup_options = SFDO_ICON_THEME_LOOKUP_OPTIONS_DEFAULT; - struct sfdo_icon_file *icon_file = \ - sfdo_icon_theme_lookup(sfdo->icon_theme, icon_name, SFDO_NT, \ - target_size, scale, lookup_options); char *icon_path = NULL; - if (icon_file && icon_file != SFDO_ICON_FILE_INVALID) { - icon_path = strdup(sfdo_icon_file_get_path(icon_file, NULL)); + if (icon_name[0] == '/') { + struct stat sb; + if (!stat(icon_name, &sb)) { + icon_path = strdup(icon_name); + } + } else { + int lookup_options = SFDO_ICON_THEME_LOOKUP_OPTIONS_DEFAULT; + struct sfdo_icon_file *icon_file = \ + sfdo_icon_theme_lookup(sfdo->icon_theme, icon_name, SFDO_NT, \ + target_size, scale, lookup_options); + if (icon_file && icon_file != SFDO_ICON_FILE_INVALID) { + icon_path = strdup(sfdo_icon_file_get_path(icon_file, NULL)); + } + sfdo_icon_file_destroy(icon_file); } - sfdo_icon_file_destroy(icon_file); return icon_path; } diff --git a/include/swaybar/image.h b/include/swaybar/image.h index 53a210dd7..332e81152 100644 --- a/include/swaybar/image.h +++ b/include/swaybar/image.h @@ -2,6 +2,6 @@ #define _SWAYBAR_IMAGE_H #include -cairo_surface_t *load_image(const char *path); +cairo_surface_t *load_image(const char *path, int target_size, int scale); #endif diff --git a/swaybar/image.c b/swaybar/image.c index ed24b9f99..f3ba2082d 100644 --- a/swaybar/image.c +++ b/swaybar/image.c @@ -104,11 +104,27 @@ static cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf( } #endif // HAVE_GDK_PIXBUF -cairo_surface_t *load_image(const char *path) { +cairo_surface_t *load_image(const char *path, int target_size, int scale) { cairo_surface_t *image; #if HAVE_GDK_PIXBUF GError *err = NULL; - GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(path, &err); + GdkPixbuf *pixbuf = NULL; + // svg images should be loaded at target size. the size read from an svg + // file is only nominal and can lead to an image too small for the avaialble + // space compared to bitmap icons selected at the nearest available size + int i = strlen(path) - 1; + // this is naive and assumes ascii, utf-8, or another encoding + // that encodes these letters as single bytes + if ((i > 2) && + (path[i] == 'g' || path[i] == 'G') && \ + (path[i - 1] == 'v' || path[i - 1] == 'V') && \ + (path[i - 2] == 's' || path[i - 2] == 'S') && \ + (path[i - 3] == '.' )) { + pixbuf = gdk_pixbuf_new_from_file_at_scale(path, -1, target_size, \ + true, &err); + } else { + pixbuf = gdk_pixbuf_new_from_file(path, &err); + } if (!pixbuf) { sway_log(SWAY_ERROR, "Failed to load background image (%s).", err->message); diff --git a/swaybar/tray/item.c b/swaybar/tray/item.c index 455feda4a..e7b8d4231 100644 --- a/swaybar/tray/item.c +++ b/swaybar/tray/item.c @@ -421,8 +421,7 @@ static enum hotspot_event_handling icon_hotspot_callback( return HOTSPOT_PROCESS; } -static void reload_sni(struct swaybar_sni *sni, char *icon_theme, - int target_size) { +static void reload_sni(struct swaybar_sni *sni, char *icon_theme, int target_size) { char *icon_name = sni->status[0] == 'N' ? sni->attention_icon_name : sni->icon_name; if (icon_name) { @@ -437,20 +436,15 @@ static void reload_sni(struct swaybar_sni *sni, char *icon_theme, icon_name, target_size, icon_theme, &sni->min_size, &sni->max_size); #else + // TODO: at some point we will need to make this scaling-aware + int scale = 1; struct sfdo *sfdo = sni->tray->bar->config->sfdo; if (sfdo) { - int lookup_options = SFDO_ICON_THEME_LOOKUP_OPTIONS_DEFAULT; - int scale = 1; - struct sfdo_icon_file *icon_file = \ - sfdo_icon_theme_lookup(sfdo->icon_theme, icon_name, SFDO_NT, \ - target_size, scale, lookup_options); - if (!icon_file || icon_file == SFDO_ICON_FILE_INVALID) { - sway_log(SWAY_ERROR, "sfdo: icon %s invalid or not found in theme %s at size %d", \ + icon_path = sfdo_icon_lookup_extended(sfdo, icon_name, target_size, scale); + if (!icon_path) { + sway_log(SWAY_DEBUG, "sfdo: icon %s invalid or not found in theme %s at size %d", \ icon_name, icon_theme, target_size); - } else { - icon_path = strdup(sfdo_icon_file_get_path(icon_file, NULL)); } - sfdo_icon_file_destroy(icon_file); } #endif #if !HAVE_LIBSFDO @@ -458,9 +452,17 @@ static void reload_sni(struct swaybar_sni *sni, char *icon_theme, #endif if (icon_path) { cairo_surface_destroy(sni->icon); - sni->icon = load_image(icon_path); + sni->icon = load_image(icon_path, target_size, scale); free(icon_path); return; + } else { + // the :( icon won't be drawn for a missing icon and whichever old icon was + // loaded will persist if this is not done. one might not have noticed this + // for tray items that have only one icon loaded only once, successfully or + // unsuccessfully. items with multiple icons, such as fcitx5 or other input + // method frameworks make the problem apparent + free(sni->icon); + sni->icon = NULL; } }