yambar/plugin.c
Daniel Eklöf d576802e49
modules/sway-xkb: new module, uses sway 'input' events to expose kbd layout
We subscribe to Sway's 'input' events, and use these to expose input
devices' active XKB layout.

The module is configured by specifying a list of 'identifiers'; these
are the input devices (keyboards, typically), that we'll be
monitoring. All other input devices are ignored.

'content' is a template, and the module will instantiate a dynlist
with a 'content' for each *existing* input found in the 'identifiers'
list.

We also monitor for device 'added' and 'removed' events, and update
our internal list of existing inputs.

This means the user can configure a set of identifiers, and only those
that are actually present will be displayed. If a device that is
listed in the 'identifiers' list is added, it will be displayed. If it
is removed, it will no longer be displayed.
2019-08-14 21:51:43 +02:00

221 lines
6.8 KiB
C

#include "plugin.h"
#include <string.h>
#include <dlfcn.h>
#define LOG_MODULE "plugin"
#define LOG_ENABLE_DBG 0
#include "log.h"
#include "config.h"
#include "tllist.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);
EXTERN_MODULE(alsa);
EXTERN_MODULE(backlight);
EXTERN_MODULE(battery);
EXTERN_MODULE(clock);
EXTERN_MODULE(i3);
EXTERN_MODULE(label);
EXTERN_MODULE(mpd);
EXTERN_MODULE(network);
EXTERN_MODULE(removables);
EXTERN_MODULE(sway_xkb);
EXTERN_MODULE(xkb);
EXTERN_MODULE(xwindow);
EXTERN_PARTICLE(empty);
EXTERN_PARTICLE(list);
EXTERN_PARTICLE(map);
EXTERN_PARTICLE(progress_bar);
EXTERN_PARTICLE(ramp);
EXTERN_PARTICLE(string);
EXTERN_DECORATION(background);
EXTERN_DECORATION(stack);
EXTERN_DECORATION(underline);
#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";
}
return NULL;
}
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)
REGISTER_CORE_MODULE(alsa, alsa);
REGISTER_CORE_MODULE(backlight, backlight);
REGISTER_CORE_MODULE(battery, battery);
REGISTER_CORE_MODULE(clock, clock);
REGISTER_CORE_MODULE(i3, i3);
REGISTER_CORE_MODULE(label, label);
REGISTER_CORE_MODULE(mpd, mpd);
REGISTER_CORE_MODULE(network, network);
REGISTER_CORE_MODULE(removables, removables);
REGISTER_CORE_MODULE(sway-xkb, sway_xkb);
#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_DECORATION(background, background);
REGISTER_CORE_DECORATION(stack, stack);
REGISTER_CORE_DECORATION(underline, underline);
#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;
}