yambar/plugin.c
Jordan Isaacs 6113f9b94e
particles/icon: init
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.
2024-07-23 22:58:23 -07:00

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