mirror of
https://github.com/swaywm/sway.git
synced 2026-06-15 14:33:54 -04:00
swaybar/tray: replace icon lookup with libsfdo
Drops the in-tree icon theme parser and lookup logic in favor of libsfdo-icon, which implements the icon theme spec. Absolute icon paths are handled in swaybar itself with an access() probe, since libsfdo intentionally doesn't deal with them. Pixmap fallback still kicks in if load_image() fails on the absolute path. KDE's IconThemePath is honored via sfdo_icon_theme_load_from() with a per-SNI override theme that gets dropped when the property changes. The min/max size cache per SNI is replaced by re-lookup when the rendered size changes; libsfdo caches the parsed theme internally and reads gtk's icon-theme.cache files, so this is cheap. Theme switching at runtime is preserved via a new tray_reload_icon_theme() called from parse_bar_config(). The old code worked here implicitly because all themes were preloaded. Closes: https://github.com/swaywm/sway/issues/8607
This commit is contained in:
parent
c857ca3a97
commit
81c31b0c4d
11 changed files with 175 additions and 597 deletions
|
|
@ -1,15 +1,17 @@
|
|||
#include <arpa/inet.h>
|
||||
#include <cairo.h>
|
||||
#include <limits.h>
|
||||
#include <sfdo-icon.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include "config.h"
|
||||
#include "swaybar/bar.h"
|
||||
#include "swaybar/config.h"
|
||||
#include "swaybar/image.h"
|
||||
#include "swaybar/input.h"
|
||||
#include "swaybar/tray/host.h"
|
||||
#include "swaybar/tray/icon.h"
|
||||
#include "swaybar/tray/item.h"
|
||||
#include "swaybar/tray/tray.h"
|
||||
#include "cairo_util.h"
|
||||
|
|
@ -28,7 +30,7 @@ static bool sni_ready(struct swaybar_sni *sni) {
|
|||
|
||||
static void set_sni_dirty(struct swaybar_sni *sni) {
|
||||
if (sni_ready(sni)) {
|
||||
sni->target_size = sni->min_size = sni->max_size = 0; // invalidate previous icon
|
||||
sni->target_size = sni->icon_size = 0; // invalidate previous icon
|
||||
set_bar_dirty(sni->tray->bar);
|
||||
}
|
||||
}
|
||||
|
|
@ -161,6 +163,13 @@ static int get_property_callback(sd_bus_message *msg, void *data,
|
|||
}
|
||||
}
|
||||
|
||||
// IconThemePath may change at runtime; the cached override theme is
|
||||
// keyed on the old path, so drop it.
|
||||
if (strcmp(prop, "IconThemePath") == 0) {
|
||||
sfdo_icon_theme_destroy(sni->icon_theme_override);
|
||||
sni->icon_theme_override = NULL;
|
||||
}
|
||||
|
||||
if (strcmp(prop, "Status") == 0 || (sni->status && (sni->status[0] == 'N' ?
|
||||
prop[0] == 'A' : has_prefix(prop, "Icon")))) {
|
||||
set_sni_dirty(sni);
|
||||
|
|
@ -313,6 +322,7 @@ void destroy_sni(struct swaybar_sni *sni) {
|
|||
}
|
||||
|
||||
cairo_surface_destroy(sni->icon);
|
||||
sfdo_icon_theme_destroy(sni->icon_theme_override);
|
||||
free(sni->watcher_id);
|
||||
free(sni->service);
|
||||
free(sni->path);
|
||||
|
|
@ -415,25 +425,88 @@ static enum hotspot_event_handling icon_hotspot_callback(
|
|||
return HOTSPOT_PROCESS;
|
||||
}
|
||||
|
||||
static void reload_sni(struct swaybar_sni *sni, char *icon_theme,
|
||||
// Some apps send "foo.png" as an icon name. The icon theme spec doesn't
|
||||
// allow extensions in lookups; strip a known one before passing to libsfdo.
|
||||
static size_t name_len_without_extension(const char *name) {
|
||||
size_t len = strlen(name);
|
||||
if (len >= 4 && name[len - 4] == '.') {
|
||||
const char *ext = &name[len - 3];
|
||||
if (strcmp(ext, "png") == 0 || strcmp(ext, "svg") == 0 ||
|
||||
strcmp(ext, "xpm") == 0) {
|
||||
return len - 4;
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static struct sfdo_icon_theme *get_sni_icon_theme(struct swaybar_sni *sni,
|
||||
const char *theme_name) {
|
||||
if (!sni->icon_theme_path) {
|
||||
return sni->tray->icon_theme;
|
||||
}
|
||||
if (sni->icon_theme_override) {
|
||||
return sni->icon_theme_override;
|
||||
}
|
||||
struct sfdo_string basedir = {
|
||||
.data = sni->icon_theme_path,
|
||||
.len = strlen(sni->icon_theme_path),
|
||||
};
|
||||
int options = SFDO_ICON_THEME_LOAD_OPTION_ALLOW_MISSING |
|
||||
SFDO_ICON_THEME_LOAD_OPTION_RELAXED;
|
||||
sni->icon_theme_override = sfdo_icon_theme_load_from(sni->tray->icon_ctx,
|
||||
theme_name, &basedir, 1, options);
|
||||
if (!sni->icon_theme_override) {
|
||||
sway_log(SWAY_DEBUG, "%s: failed to load theme '%s' from '%s', "
|
||||
"falling back to global theme", sni->watcher_id,
|
||||
theme_name ? theme_name : "(default)", sni->icon_theme_path);
|
||||
return sni->tray->icon_theme;
|
||||
}
|
||||
return sni->icon_theme_override;
|
||||
}
|
||||
|
||||
static char *lookup_icon_path(struct swaybar_sni *sni, const char *icon_name,
|
||||
const char *theme_name, int target_size) {
|
||||
// Absolute paths are outside the XDG icon theme spec; handle directly
|
||||
if (icon_name[0] == '/') {
|
||||
return access(icon_name, R_OK) == 0 ? strdup(icon_name) : NULL;
|
||||
}
|
||||
|
||||
struct sfdo_icon_theme *theme = get_sni_icon_theme(sni, theme_name);
|
||||
if (!theme) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int options = SFDO_ICON_THEME_LOOKUP_OPTIONS_DEFAULT;
|
||||
#if !HAVE_GDK_PIXBUF
|
||||
options |= SFDO_ICON_THEME_LOOKUP_OPTION_NO_SVG;
|
||||
#endif
|
||||
size_t name_len = name_len_without_extension(icon_name);
|
||||
struct sfdo_icon_file *file = sfdo_icon_theme_lookup(theme, icon_name,
|
||||
name_len, target_size, 1, options);
|
||||
if (!file || file == SFDO_ICON_FILE_INVALID) {
|
||||
return NULL;
|
||||
}
|
||||
char *path = strdup(sfdo_icon_file_get_path(file, NULL));
|
||||
sfdo_icon_file_destroy(file);
|
||||
return path;
|
||||
}
|
||||
|
||||
static void reload_sni(struct swaybar_sni *sni, const char *icon_theme,
|
||||
int target_size) {
|
||||
char *icon_name = sni->status[0] == 'N' ?
|
||||
sni->attention_icon_name : sni->icon_name;
|
||||
if (icon_name) {
|
||||
list_t *icon_search_paths = create_list();
|
||||
list_cat(icon_search_paths, sni->tray->basedirs);
|
||||
if (sni->icon_theme_path) {
|
||||
list_add(icon_search_paths, sni->icon_theme_path);
|
||||
}
|
||||
char *icon_path = find_icon(sni->tray->themes, icon_search_paths,
|
||||
icon_name, target_size, icon_theme,
|
||||
&sni->min_size, &sni->max_size);
|
||||
list_free(icon_search_paths);
|
||||
char *icon_path = lookup_icon_path(sni, icon_name, icon_theme,
|
||||
target_size);
|
||||
if (icon_path) {
|
||||
cairo_surface_destroy(sni->icon);
|
||||
sni->icon = load_image(icon_path);
|
||||
free(icon_path);
|
||||
return;
|
||||
if (sni->icon) {
|
||||
return;
|
||||
}
|
||||
// load_image() failed (unsupported format, file vanished,
|
||||
// etc); fall through to pixmap fallback if available.
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -463,11 +536,10 @@ uint32_t render_sni(cairo_t *cairo, struct swaybar_output *output, double *x,
|
|||
int padding = output->bar->config->tray_padding;
|
||||
int target_size = height - 2*padding;
|
||||
if (target_size != sni->target_size && sni_ready(sni)) {
|
||||
// check if another icon should be loaded
|
||||
if (target_size < sni->min_size || target_size > sni->max_size) {
|
||||
if (target_size != sni->icon_size) {
|
||||
reload_sni(sni, output->bar->config->icon_theme, target_size);
|
||||
sni->icon_size = target_size;
|
||||
}
|
||||
|
||||
sni->target_size = target_size;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue