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; } }