mirror of
https://codeberg.org/dnkl/yambar.git
synced 2025-04-20 11:35:42 +02:00
Introduce a new icon particle. It follows the icon spec (https://specifications.freedesktop.org/icon-theme-spec/latest/index.html). Rendering logic is taken from fuzzel (using nanosvg + libpng), while loading logic is taken from sway. Standard usage is with `use-tag = false` which expands the provided string template and then loads the string as the icon name. There are settings to manually override the base paths, themes, etc. The second usage which is required for tray support is a special icon tag that transfers raw pixmaps. With `use-tag = true` it first expands the string, and then uses that output to find an icon pixmap tag. To reduce memory usage, themes are reference counted so they can be passed down the configuration stack without having to load them in multiple times. For programmability, a fallback particle can be specified if no icon/tag is found `fallback: ...`. And the new icon pixmap tag can be existence checked in map conditions using `+{tag_name}`. Future work to be done in follow up diffs: 1. Icon caching. Currently performs an icon lookup on each instantiation & a render on each refresh. 2. Theme caching. Changing theme directories results in a new "theme collection" being created resulting in the possibility of duplicated theme loading.
321 lines
10 KiB
C
321 lines
10 KiB
C
#include "plugin.h"
|
|
|
|
#include <dlfcn.h>
|
|
#include <string.h>
|
|
|
|
#include <tllist.h>
|
|
|
|
#define LOG_MODULE "plugin"
|
|
#define LOG_ENABLE_DBG 0
|
|
#include "config.h"
|
|
#include "log.h"
|
|
|
|
#if !defined(CORE_PLUGINS_AS_SHARED_LIBRARIES)
|
|
|
|
#define EXTERN_MODULE(plug_name) \
|
|
extern const struct module_iface module_##plug_name##_iface; \
|
|
extern bool plug_name##_verify_conf(keychain_t *chain, const struct yml_node *node); \
|
|
extern struct module *plug_name##_from_conf(const struct yml_node *node, struct conf_inherit inherited);
|
|
|
|
#define EXTERN_PARTICLE(plug_name) \
|
|
extern const struct particle_iface particle_##plug_name##_iface; \
|
|
extern bool plug_name##_verify_conf(keychain_t *chain, const struct yml_node *node); \
|
|
extern struct particle *plug_name##_from_conf(const struct yml_node *node, struct particle *common);
|
|
|
|
#define EXTERN_DECORATION(plug_name) \
|
|
extern const struct deco_iface deco_##plug_name##_iface; \
|
|
extern bool plug_name##_verify_conf(keychain_t *chain, const struct yml_node *node); \
|
|
extern struct deco *plug_name##_from_conf(const struct yml_node *node);
|
|
|
|
#if defined(HAVE_PLUGIN_alsa)
|
|
EXTERN_MODULE(alsa);
|
|
#endif
|
|
#if defined(HAVE_PLUGIN_backlight)
|
|
EXTERN_MODULE(backlight);
|
|
#endif
|
|
#if defined(HAVE_PLUGIN_battery)
|
|
EXTERN_MODULE(battery);
|
|
#endif
|
|
#if defined(HAVE_PLUGIN_clock)
|
|
EXTERN_MODULE(clock);
|
|
#endif
|
|
#if defined(HAVE_PLUGIN_cpu)
|
|
EXTERN_MODULE(cpu);
|
|
#endif
|
|
#if defined(HAVE_PLUGIN_disk_io)
|
|
EXTERN_MODULE(disk_io);
|
|
#endif
|
|
#if defined(HAVE_PLUGIN_dwl)
|
|
EXTERN_MODULE(dwl);
|
|
#endif
|
|
#if defined(HAVE_PLUGIN_foreign_toplevel)
|
|
EXTERN_MODULE(foreign_toplevel);
|
|
#endif
|
|
#if defined(HAVE_PLUGIN_mem)
|
|
EXTERN_MODULE(mem);
|
|
#endif
|
|
#if defined(HAVE_PLUGIN_mpd)
|
|
EXTERN_MODULE(mpd);
|
|
#endif
|
|
#if defined(HAVE_PLUGIN_i3)
|
|
EXTERN_MODULE(i3);
|
|
#endif
|
|
#if defined(HAVE_PLUGIN_label)
|
|
EXTERN_MODULE(label);
|
|
#endif
|
|
#if defined(HAVE_PLUGIN_network)
|
|
EXTERN_MODULE(network);
|
|
#endif
|
|
#if defined(HAVE_PLUGIN_pipewire)
|
|
EXTERN_MODULE(pipewire);
|
|
#endif
|
|
#if defined(HAVE_PLUGIN_pulse)
|
|
EXTERN_MODULE(pulse);
|
|
#endif
|
|
#if defined(HAVE_PLUGIN_removables)
|
|
EXTERN_MODULE(removables);
|
|
#endif
|
|
#if defined(HAVE_PLUGIN_river)
|
|
EXTERN_MODULE(river);
|
|
#endif
|
|
#if defined(HAVE_PLUGIN_script)
|
|
EXTERN_MODULE(script);
|
|
#endif
|
|
#if defined(HAVE_PLUGIN_sway_xkb)
|
|
EXTERN_MODULE(sway_xkb);
|
|
#endif
|
|
#if defined(HAVE_PLUGIN_xkb)
|
|
EXTERN_MODULE(xkb);
|
|
#endif
|
|
#if defined(HAVE_PLUGIN_xwindow)
|
|
EXTERN_MODULE(xwindow);
|
|
#endif
|
|
|
|
EXTERN_PARTICLE(empty);
|
|
EXTERN_PARTICLE(list);
|
|
EXTERN_PARTICLE(map);
|
|
EXTERN_PARTICLE(progress_bar);
|
|
EXTERN_PARTICLE(ramp);
|
|
EXTERN_PARTICLE(string);
|
|
EXTERN_PARTICLE(icon);
|
|
|
|
EXTERN_DECORATION(background);
|
|
EXTERN_DECORATION(border);
|
|
EXTERN_DECORATION(stack);
|
|
EXTERN_DECORATION(underline);
|
|
EXTERN_DECORATION(overline);
|
|
|
|
#undef EXTERN_DECORATION
|
|
#undef EXTERN_PARTICLE
|
|
#undef EXTERN_MODULE
|
|
|
|
#endif
|
|
|
|
static tll(struct plugin) plugins = tll_init();
|
|
|
|
static const char *
|
|
type2str(enum plugin_type type)
|
|
{
|
|
switch (type) {
|
|
case PLUGIN_MODULE:
|
|
return "module";
|
|
case PLUGIN_PARTICLE:
|
|
return "particle";
|
|
case PLUGIN_DECORATION:
|
|
return "decoration";
|
|
}
|
|
|
|
assert(false && "invalid type");
|
|
return "";
|
|
}
|
|
|
|
static void __attribute__((constructor)) init(void)
|
|
{
|
|
#if !defined(CORE_PLUGINS_AS_SHARED_LIBRARIES)
|
|
|
|
#define REGISTER_CORE_PLUGIN(plug_name, func_prefix, plug_type) \
|
|
do { \
|
|
tll_push_back(plugins, ((struct plugin){ \
|
|
.name = strdup(#plug_name), \
|
|
.type = (plug_type), \
|
|
.lib = RTLD_DEFAULT, \
|
|
})); \
|
|
} while (0)
|
|
|
|
#define REGISTER_CORE_MODULE(plug_name, func_prefix) \
|
|
do { \
|
|
REGISTER_CORE_PLUGIN(plug_name, func_prefix, PLUGIN_MODULE); \
|
|
tll_back(plugins).module = &module_##func_prefix##_iface; \
|
|
} while (0)
|
|
#define REGISTER_CORE_PARTICLE(plug_name, func_prefix) \
|
|
do { \
|
|
REGISTER_CORE_PLUGIN(plug_name, func_prefix, PLUGIN_PARTICLE); \
|
|
tll_back(plugins).particle = &particle_##func_prefix##_iface; \
|
|
} while (0)
|
|
#define REGISTER_CORE_DECORATION(plug_name, func_prefix) \
|
|
do { \
|
|
REGISTER_CORE_PLUGIN(plug_name, func_prefix, PLUGIN_DECORATION); \
|
|
tll_back(plugins).decoration = &deco_##func_prefix##_iface; \
|
|
} while (0)
|
|
|
|
#if defined(HAVE_PLUGIN_alsa)
|
|
REGISTER_CORE_MODULE(alsa, alsa);
|
|
#endif
|
|
#if defined(HAVE_PLUGIN_backlight)
|
|
REGISTER_CORE_MODULE(backlight, backlight);
|
|
#endif
|
|
#if defined(HAVE_PLUGIN_battery)
|
|
REGISTER_CORE_MODULE(battery, battery);
|
|
#endif
|
|
#if defined(HAVE_PLUGIN_clock)
|
|
REGISTER_CORE_MODULE(clock, clock);
|
|
#endif
|
|
#if defined(HAVE_PLUGIN_cpu)
|
|
REGISTER_CORE_MODULE(cpu, cpu);
|
|
#endif
|
|
#if defined(HAVE_PLUGIN_disk_io)
|
|
REGISTER_CORE_MODULE(disk-io, disk_io);
|
|
#endif
|
|
#if defined(HAVE_PLUGIN_dwl)
|
|
REGISTER_CORE_MODULE(dwl, dwl);
|
|
#endif
|
|
#if defined(HAVE_PLUGIN_foreign_toplevel)
|
|
REGISTER_CORE_MODULE(foreign-toplevel, foreign_toplevel);
|
|
#endif
|
|
#if defined(HAVE_PLUGIN_mem)
|
|
REGISTER_CORE_MODULE(mem, mem);
|
|
#endif
|
|
#if defined(HAVE_PLUGIN_mpd)
|
|
REGISTER_CORE_MODULE(mpd, mpd);
|
|
#endif
|
|
#if defined(HAVE_PLUGIN_i3)
|
|
REGISTER_CORE_MODULE(i3, i3);
|
|
#endif
|
|
#if defined(HAVE_PLUGIN_label)
|
|
REGISTER_CORE_MODULE(label, label);
|
|
#endif
|
|
#if defined(HAVE_PLUGIN_network)
|
|
REGISTER_CORE_MODULE(network, network);
|
|
#endif
|
|
#if defined(HAVE_PLUGIN_pipewire)
|
|
REGISTER_CORE_MODULE(pipewire, pipewire);
|
|
#endif
|
|
#if defined(HAVE_PLUGIN_pulse)
|
|
REGISTER_CORE_MODULE(pulse, pulse);
|
|
#endif
|
|
#if defined(HAVE_PLUGIN_removables)
|
|
REGISTER_CORE_MODULE(removables, removables);
|
|
#endif
|
|
#if defined(HAVE_PLUGIN_river)
|
|
REGISTER_CORE_MODULE(river, river);
|
|
#endif
|
|
#if defined(HAVE_PLUGIN_script)
|
|
REGISTER_CORE_MODULE(script, script);
|
|
#endif
|
|
#if defined(HAVE_PLUGIN_sway_xkb)
|
|
REGISTER_CORE_MODULE(sway-xkb, sway_xkb);
|
|
#endif
|
|
#if defined(HAVE_PLUGIN_xkb)
|
|
REGISTER_CORE_MODULE(xkb, xkb);
|
|
#endif
|
|
#if defined(HAVE_PLUGIN_xwindow)
|
|
REGISTER_CORE_MODULE(xwindow, xwindow);
|
|
#endif
|
|
|
|
REGISTER_CORE_PARTICLE(empty, empty);
|
|
REGISTER_CORE_PARTICLE(list, list);
|
|
REGISTER_CORE_PARTICLE(map, map);
|
|
REGISTER_CORE_PARTICLE(progress-bar, progress_bar);
|
|
REGISTER_CORE_PARTICLE(ramp, ramp);
|
|
REGISTER_CORE_PARTICLE(string, string);
|
|
REGISTER_CORE_PARTICLE(icon, icon);
|
|
|
|
REGISTER_CORE_DECORATION(background, background);
|
|
REGISTER_CORE_DECORATION(border, border);
|
|
REGISTER_CORE_DECORATION(stack, stack);
|
|
REGISTER_CORE_DECORATION(underline, underline);
|
|
REGISTER_CORE_DECORATION(overline, overline);
|
|
|
|
#undef REGISTER_CORE_DECORATION
|
|
#undef REGISTER_CORE_PARTICLE
|
|
#undef REGISTER_CORE_PLUGIN
|
|
|
|
#endif /* !CORE_PLUGINS_AS_SHARED_LIBRARIES */
|
|
}
|
|
|
|
static void
|
|
free_plugin(struct plugin plug)
|
|
{
|
|
dlerror();
|
|
|
|
if (plug.lib != NULL)
|
|
dlclose(plug.lib);
|
|
|
|
const char *dl_error = dlerror();
|
|
if (dl_error != NULL)
|
|
LOG_ERR("%s: %s: dlclose(): %s", type2str(plug.type), plug.name, dl_error);
|
|
|
|
free(plug.name);
|
|
}
|
|
|
|
static void __attribute__((destructor)) fini(void) { tll_free_and_free(plugins, free_plugin); }
|
|
|
|
const struct plugin *
|
|
plugin_load(const char *name, enum plugin_type type)
|
|
{
|
|
tll_foreach(plugins, plug)
|
|
{
|
|
if (plug->item.type == type && strcmp(plug->item.name, name) == 0) {
|
|
LOG_DBG("%s: %s already loaded: %p", type2str(type), name, plug->item.lib);
|
|
assert(plug->item.dummy != NULL);
|
|
return &plug->item;
|
|
}
|
|
}
|
|
|
|
char path[128];
|
|
snprintf(path, sizeof(path), "%s_%s.so", type2str(type), name);
|
|
|
|
/* Not loaded - do it now */
|
|
void *lib = dlopen(path, RTLD_LOCAL | RTLD_NOW);
|
|
LOG_DBG("%s: %s: dlopened to %p", type2str(type), name, lib);
|
|
|
|
if (lib == NULL) {
|
|
LOG_ERR("%s: %s: dlopen: %s", type2str(type), name, dlerror());
|
|
return NULL;
|
|
}
|
|
|
|
tll_push_back(plugins, ((struct plugin){strdup(name), type, lib, {NULL}}));
|
|
struct plugin *plug = &tll_back(plugins);
|
|
|
|
dlerror(); /* Clear previous error */
|
|
plug->dummy = dlsym(lib, "iface");
|
|
|
|
const char *dl_error = dlerror();
|
|
if (dl_error != NULL) {
|
|
LOG_ERR("%s: %s: dlsym: %s", type2str(type), name, dl_error);
|
|
return NULL;
|
|
}
|
|
|
|
return plug;
|
|
}
|
|
|
|
const struct module_iface *
|
|
plugin_load_module(const char *name)
|
|
{
|
|
const struct plugin *plug = plugin_load(name, PLUGIN_MODULE);
|
|
return plug != NULL ? plug->module : NULL;
|
|
}
|
|
|
|
const struct particle_iface *
|
|
plugin_load_particle(const char *name)
|
|
{
|
|
const struct plugin *plug = plugin_load(name, PLUGIN_PARTICLE);
|
|
return plug != NULL ? plug->particle : NULL;
|
|
}
|
|
|
|
const struct deco_iface *
|
|
plugin_load_deco(const char *name)
|
|
{
|
|
const struct plugin *plug = plugin_load(name, PLUGIN_DECORATION);
|
|
return plug != NULL ? plug->decoration : NULL;
|
|
}
|