Merge branch 'modules-as-plugins'

This commit is contained in:
Daniel Eklöf 2019-01-12 22:24:10 +01:00
commit 297ff512b3
31 changed files with 696 additions and 613 deletions

View file

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.9) cmake_minimum_required(VERSION 3.13)
project(f00bar C) project(f00bar C)
set(CMAKE_C_STANDARD_REQUIRED ON) set(CMAKE_C_STANDARD_REQUIRED ON)
@ -19,11 +19,6 @@ pkg_check_modules(FONTCONFIG REQUIRED fontconfig) # Core
pkg_check_modules(CAIRO REQUIRED cairo cairo-xcb cairo-ft) # Core pkg_check_modules(CAIRO REQUIRED cairo cairo-xcb cairo-ft) # Core
pkg_check_modules(YAML REQUIRED yaml-0.1) # Core (configuration) pkg_check_modules(YAML REQUIRED yaml-0.1) # Core (configuration)
pkg_check_modules(XCB_XKB REQUIRED xcb-xkb) # Module/xkb
pkg_check_modules(JSON REQUIRED json-c) # Module/i3
pkg_check_modules(UDEV REQUIRED libudev) # Module/battery
pkg_check_modules(MPD REQUIRED libmpdclient) # Module/mpd
pkg_check_modules(ALSA REQUIRED alsa) # Module/alsa
add_executable(f00bar add_executable(f00bar
bar.c bar.h bar.c bar.h
@ -35,6 +30,7 @@ add_executable(f00bar
main.c main.c
module.c module.h module.c module.h
particle.c particle.h particle.c particle.h
plugin.c plugin.h
tag.c tag.h tag.c tag.h
xcb.c xcb.h xcb.c xcb.h
yml.c yml.h yml.c yml.h
@ -50,32 +46,19 @@ add_executable(f00bar
particles/progress-bar.c particles/progress-bar.h particles/progress-bar.c particles/progress-bar.h
particles/ramp.c particles/ramp.h particles/ramp.c particles/ramp.h
particles/string.c particles/string.h particles/string.c particles/string.h
modules/alsa.c modules/alsa.h
modules/backlight.c modules/backlight.h
modules/battery.c modules/battery.h
modules/clock.c modules/clock.h
modules/i3.c modules/i3.h
modules/label.c modules/label.h
modules/mpd.c modules/mpd.h
modules/network.c modules/network.h
modules/removables.c modules/removables.h
modules/xkb.c modules/xkb.h
modules/xwindow.c modules/xwindow.h
) )
# TODO: directory global
target_compile_definitions(f00bar PRIVATE _GNU_SOURCE) target_compile_definitions(f00bar PRIVATE _GNU_SOURCE)
# Make global symbols in f00bar visible to dlopen:ed plugins
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -rdynamic")
target_compile_options(f00bar PRIVATE target_compile_options(f00bar PRIVATE
${XCB_CFLAGS_OTHER} ${XCB_CFLAGS_OTHER}
${FONTCONFIG_CFLAGS_OTHER} ${FONTCONFIG_CFLAGS_OTHER}
${CAIRO_CFLAGS_OTHER} ${CAIRO_CFLAGS_OTHER}
${YAML_CFLAGS_OTHER} ${YAML_CFLAGS_OTHER}
${XCB_XKB_CFLAGS_OTHER}
${JSON_CFLAGS_OTHER}
${UDEV_CFLAGS_OTHER}
${MPD_CFLAGS_OTHER}
${ALSA_CFLAGS_OTHER}
) )
target_include_directories(f00bar PRIVATE target_include_directories(f00bar PRIVATE
@ -83,22 +66,27 @@ target_include_directories(f00bar PRIVATE
${FONTCONFIG_INCLUDE_DIRS} ${FONTCONFIG_INCLUDE_DIRS}
${CAIRO_INCLUDE_DIRS} ${CAIRO_INCLUDE_DIRS}
${YAML_INCLUDE_DIRS} ${YAML_INCLUDE_DIRS}
${XCB_XKB_INCLUDE_DIRS}
${JSON_INCLUDE_DIRS}
${UDEV_INCLUDE_DIRS}
${MPD_INCLUDE_DIRS}
${ALSA_INCLUDE_DIRS}
) )
target_link_libraries(f00bar target_link_libraries(f00bar
${CMAKE_THREAD_LIBS_INIT} ${CMAKE_THREAD_LIBS_INIT}
${CMAKE_DL_LIBS}
${XCB_LIBRARIES} ${XCB_LIBRARIES}
${FONTCONFIG_LIBRARIES} ${FONTCONFIG_LIBRARIES}
${CAIRO_LIBRARIES} ${CAIRO_LIBRARIES}
${YAML_LIBRARIES} ${YAML_LIBRARIES}
${XCB_XKB_LIBRARIES}
${JSON_LIBRARIES}
${UDEV_LIBRARIES}
${MPD_LIBRARIES}
${ALSA_LIBRARIES}
) )
add_library(module-sdk INTERFACE)
target_compile_definitions(module-sdk INTERFACE _GNU_SOURCE)
target_compile_options(module-sdk INTERFACE ${CAIRO_CFLAGS_OTHER})
target_include_directories(module-sdk INTERFACE ${CAIRO_INCLUDE_DIRS})
target_link_libraries(module-sdk INTERFACE ${CMAKE_THREAD_LIBS_INIT})
set_property(TARGET f00bar PROPERTY INSTALL_RPATH \$ORIGIN/../lib/f00bar)
set_property(TARGET f00bar PROPERTY BUILD_RPATH \$ORIGIN/modules)
install(TARGETS f00bar DESTINATION bin)
add_subdirectory(modules)

View file

@ -1,4 +1,4 @@
#include "config-verify.h" #include "config.h"
#include <string.h> #include <string.h>
#include <assert.h> #include <assert.h>
@ -6,31 +6,11 @@
#define LOG_MODULE "config:verify" #define LOG_MODULE "config:verify"
#define LOG_ENABLE_DBG 0 #define LOG_ENABLE_DBG 0
#include "log.h" #include "log.h"
#include "plugin.h"
#include "tllist.h" #include "tllist.h"
typedef tll(const char *) keychain_t; const char *
conf_err_prefix(const keychain_t *chain, const struct yml_node *node)
struct attr_info {
const char *name;
bool required;
bool (*verify)(keychain_t *chain, const struct yml_node *node);
};
static keychain_t *
chain_push(keychain_t *chain, const char *key)
{
tll_push_back(*chain, key);
return chain;
}
static void
chain_pop(keychain_t *chain)
{
tll_pop_back(*chain);
}
static const char *
err_prefix(const keychain_t *chain, const struct yml_node *node)
{ {
static char msg[4096]; static char msg[4096];
int idx = 0; int idx = 0;
@ -46,36 +26,36 @@ err_prefix(const keychain_t *chain, const struct yml_node *node)
return msg; return msg;
} }
static bool bool
verify_string(keychain_t *chain, const struct yml_node *node) conf_verify_string(keychain_t *chain, const struct yml_node *node)
{ {
const char *s = yml_value_as_string(node); const char *s = yml_value_as_string(node);
if (s == NULL) { if (s == NULL) {
LOG_ERR("%s: value must be a string", err_prefix(chain, node)); LOG_ERR("%s: value must be a string", conf_err_prefix(chain, node));
return false; return false;
} }
return true; return true;
} }
static bool bool
verify_int(keychain_t *chain, const struct yml_node *node) conf_verify_int(keychain_t *chain, const struct yml_node *node)
{ {
if (yml_value_is_int(node)) if (yml_value_is_int(node))
return true; return true;
LOG_ERR("%s: value is not an integer: '%s'", LOG_ERR("%s: value is not an integer: '%s'",
err_prefix(chain, node), yml_value_as_string(node)); conf_err_prefix(chain, node), yml_value_as_string(node));
return false; return false;
} }
static bool bool
verify_enum(keychain_t *chain, const struct yml_node *node, conf_verify_enum(keychain_t *chain, const struct yml_node *node,
const char *values[], size_t count) const char *values[], size_t count)
{ {
const char *s = yml_value_as_string(node); const char *s = yml_value_as_string(node);
if (s == NULL) { if (s == NULL) {
LOG_ERR("%s: value must be a string", err_prefix(chain, node)); LOG_ERR("%s: value must be a string", conf_err_prefix(chain, node));
return false; return false;
} }
@ -84,19 +64,19 @@ verify_enum(keychain_t *chain, const struct yml_node *node,
return true; return true;
} }
LOG_ERR("%s: value must be one of:", err_prefix(chain, node)); LOG_ERR("%s: value must be one of:", conf_err_prefix(chain, node));
for (size_t i = 0; i < count; i++) for (size_t i = 0; i < count; i++)
LOG_ERR(" %s", values[i]); LOG_ERR(" %s", values[i]);
return false; return false;
} }
static bool bool
verify_dict(keychain_t *chain, const struct yml_node *node, conf_verify_dict(keychain_t *chain, const struct yml_node *node,
const struct attr_info info[], size_t count) const struct attr_info info[], size_t count)
{ {
if (!yml_is_dict(node)) { if (!yml_is_dict(node)) {
LOG_ERR("%s: must be a dictionary", err_prefix(chain, node)); LOG_ERR("%s: must be a dictionary", conf_err_prefix(chain, node));
return false; return false;
} }
@ -109,7 +89,7 @@ verify_dict(keychain_t *chain, const struct yml_node *node,
{ {
const char *key = yml_value_as_string(it.key); const char *key = yml_value_as_string(it.key);
if (key == NULL) { if (key == NULL) {
LOG_ERR("%s: key must be a string", err_prefix(chain, it.key)); LOG_ERR("%s: key must be a string", conf_err_prefix(chain, it.key));
return false; return false;
} }
@ -123,7 +103,7 @@ verify_dict(keychain_t *chain, const struct yml_node *node,
} }
if (attr == NULL) { if (attr == NULL) {
LOG_ERR("%s: invalid key: %s", err_prefix(chain, it.key), key); LOG_ERR("%s: invalid key: %s", conf_err_prefix(chain, it.key), key);
return false; return false;
} }
@ -139,19 +119,19 @@ verify_dict(keychain_t *chain, const struct yml_node *node,
if (!info[i].required || exists[i]) if (!info[i].required || exists[i])
continue; continue;
LOG_ERR("%s: missing required key: %s", err_prefix(chain, node), info[i].name); LOG_ERR("%s: missing required key: %s", conf_err_prefix(chain, node), info[i].name);
return false; return false;
} }
return true; return true;
} }
static bool bool
verify_color(keychain_t *chain, const struct yml_node *node) conf_verify_color(keychain_t *chain, const struct yml_node *node)
{ {
const char *s = yml_value_as_string(node); const char *s = yml_value_as_string(node);
if (s == NULL) { if (s == NULL) {
LOG_ERR("%s: value must be a string", err_prefix(chain, node)); LOG_ERR("%s: value must be a string", conf_err_prefix(chain, node));
return false; return false;
} }
@ -160,7 +140,7 @@ verify_color(keychain_t *chain, const struct yml_node *node)
if (strlen(s) != 8 || v != 4) { if (strlen(s) != 8 || v != 4) {
LOG_ERR("%s: value must be a color ('rrggbbaa', e.g ff00ffff)", LOG_ERR("%s: value must be a color ('rrggbbaa', e.g ff00ffff)",
err_prefix(chain, node)); conf_err_prefix(chain, node));
return false; return false;
} }
@ -168,14 +148,14 @@ verify_color(keychain_t *chain, const struct yml_node *node)
} }
static bool bool
verify_font(keychain_t *chain, const struct yml_node *node) conf_verify_font(keychain_t *chain, const struct yml_node *node)
{ {
static const struct attr_info attrs[] = { static const struct attr_info attrs[] = {
{"family", true, &verify_string}, {"family", true, &conf_verify_string},
}; };
return verify_dict(chain, node, attrs, sizeof(attrs) / sizeof(attrs[0])); return conf_verify_dict(chain, node, attrs, sizeof(attrs) / sizeof(attrs[0]));
} }
static bool verify_decoration(keychain_t *chain, const struct yml_node *node); static bool verify_decoration(keychain_t *chain, const struct yml_node *node);
@ -184,7 +164,7 @@ static bool
verify_decoration_stack(keychain_t *chain, const struct yml_node *node) verify_decoration_stack(keychain_t *chain, const struct yml_node *node)
{ {
if (!yml_is_list(node)) { if (!yml_is_list(node)) {
LOG_ERR("%s: must be a list of decorations", err_prefix(chain, node)); LOG_ERR("%s: must be a list of decorations", conf_err_prefix(chain, node));
return false; return false;
} }
@ -206,7 +186,7 @@ verify_decoration(keychain_t *chain, const struct yml_node *node)
if (yml_dict_length(node) != 1) { if (yml_dict_length(node) != 1) {
LOG_ERR("%s: decoration must be a dictionary with a single key; " LOG_ERR("%s: decoration must be a dictionary with a single key; "
"the name of the particle", err_prefix(chain, node)); "the name of the particle", conf_err_prefix(chain, node));
return false; return false;
} }
@ -216,7 +196,7 @@ verify_decoration(keychain_t *chain, const struct yml_node *node)
const char *deco_name = yml_value_as_string(deco); const char *deco_name = yml_value_as_string(deco);
if (deco_name == NULL) { if (deco_name == NULL) {
LOG_ERR("%s: decoration name must be a string", err_prefix(chain, deco)); LOG_ERR("%s: decoration name must be a string", conf_err_prefix(chain, deco));
return false; return false;
} }
@ -227,12 +207,12 @@ verify_decoration(keychain_t *chain, const struct yml_node *node)
} }
static const struct attr_info background[] = { static const struct attr_info background[] = {
{"color", true, &verify_color}, {"color", true, &conf_verify_color},
}; };
static const struct attr_info underline[] = { static const struct attr_info underline[] = {
{"size", true, &verify_int}, {"size", true, &conf_verify_int},
{"color", true, &verify_color}, {"color", true, &conf_verify_color},
}; };
static const struct { static const struct {
@ -248,7 +228,7 @@ verify_decoration(keychain_t *chain, const struct yml_node *node)
if (strcmp(decos[i].name, deco_name) != 0) if (strcmp(decos[i].name, deco_name) != 0)
continue; continue;
if (!verify_dict(chain_push(chain, deco_name), if (!conf_verify_dict(chain_push(chain, deco_name),
values, decos[i].attrs, decos[i].count)) values, decos[i].attrs, decos[i].count))
{ {
return false; return false;
@ -259,12 +239,10 @@ verify_decoration(keychain_t *chain, const struct yml_node *node)
} }
LOG_ERR( LOG_ERR(
"%s: invalid decoration name: %s", err_prefix(chain, deco), deco_name); "%s: invalid decoration name: %s", conf_err_prefix(chain, deco), deco_name);
return false; return false;
} }
static bool verify_particle(keychain_t *chain, const struct yml_node *node);
static bool static bool
verify_list_items(keychain_t *chain, const struct yml_node *node) verify_list_items(keychain_t *chain, const struct yml_node *node)
{ {
@ -274,7 +252,7 @@ verify_list_items(keychain_t *chain, const struct yml_node *node)
it.node != NULL; it.node != NULL;
yml_list_next(&it)) yml_list_next(&it))
{ {
if (!verify_particle(chain, it.node)) if (!conf_verify_particle(chain, it.node))
return false; return false;
} }
@ -287,7 +265,7 @@ verify_map_values(keychain_t *chain, const struct yml_node *node)
if (!yml_is_dict(node)) { if (!yml_is_dict(node)) {
LOG_ERR( LOG_ERR(
"%s: must be a dictionary of workspace-name: particle mappings", "%s: must be a dictionary of workspace-name: particle mappings",
err_prefix(chain, node)); conf_err_prefix(chain, node));
return false; return false;
} }
@ -297,12 +275,11 @@ verify_map_values(keychain_t *chain, const struct yml_node *node)
{ {
const char *key = yml_value_as_string(it.key); const char *key = yml_value_as_string(it.key);
if (key == NULL) { if (key == NULL) {
LOG_ERR("%s: key must be a string (a i3 workspace name)", LOG_ERR("%s: key must be a string", conf_err_prefix(chain, it.key));
err_prefix(chain, it.key));
return false; return false;
} }
if (!verify_particle(chain_push(chain, key), it.value)) if (!conf_verify_particle(chain_push(chain, key), it.value))
return false; return false;
chain_pop(chain); chain_pop(chain);
@ -312,13 +289,13 @@ verify_map_values(keychain_t *chain, const struct yml_node *node)
} }
static bool static bool
verify_particle_dictionary(keychain_t *chain, const struct yml_node *node) conf_verify_particle_dictionary(keychain_t *chain, const struct yml_node *node)
{ {
assert(yml_is_dict(node)); assert(yml_is_dict(node));
if (yml_dict_length(node) != 1) { if (yml_dict_length(node) != 1) {
LOG_ERR("%s: particle must be a dictionary with a single key; " LOG_ERR("%s: particle must be a dictionary with a single key; "
"the name of the particle", err_prefix(chain, node)); "the name of the particle", conf_err_prefix(chain, node));
return false; return false;
} }
@ -328,15 +305,15 @@ verify_particle_dictionary(keychain_t *chain, const struct yml_node *node)
const char *particle_name = yml_value_as_string(particle); const char *particle_name = yml_value_as_string(particle);
if (particle_name == NULL) { if (particle_name == NULL) {
LOG_ERR("%s: particle name must be a string", err_prefix(chain, particle)); LOG_ERR("%s: particle name must be a string", conf_err_prefix(chain, particle));
return false; return false;
} }
#define COMMON_ATTRS \ #define COMMON_ATTRS \
{"margin", false, &verify_int}, \ {"margin", false, &conf_verify_int}, \
{"left-margin", false, &verify_int}, \ {"left-margin", false, &conf_verify_int}, \
{"right-margin", false, &verify_int}, \ {"right-margin", false, &conf_verify_int}, \
{"on-click", false, &verify_string}, {"on-click", false, &conf_verify_string},
static const struct attr_info empty[] = { static const struct attr_info empty[] = {
COMMON_ATTRS COMMON_ATTRS
@ -344,42 +321,42 @@ verify_particle_dictionary(keychain_t *chain, const struct yml_node *node)
static const struct attr_info list[] = { static const struct attr_info list[] = {
{"items", true, &verify_list_items}, {"items", true, &verify_list_items},
{"spacing", false, &verify_int}, {"spacing", false, &conf_verify_int},
{"left-spacing", false, &verify_int}, {"left-spacing", false, &conf_verify_int},
{"right-spacing", false, &verify_int}, {"right-spacing", false, &conf_verify_int},
COMMON_ATTRS COMMON_ATTRS
}; };
static const struct attr_info map[] = { static const struct attr_info map[] = {
{"tag", true, &verify_string}, {"tag", true, &conf_verify_string},
{"values", true, &verify_map_values}, {"values", true, &verify_map_values},
{"default", false, &verify_particle}, {"default", false, &conf_verify_particle},
COMMON_ATTRS COMMON_ATTRS
}; };
static const struct attr_info progress_bar[] = { static const struct attr_info progress_bar[] = {
{"tag", true, &verify_string}, {"tag", true, &conf_verify_string},
{"length", true, &verify_int}, {"length", true, &conf_verify_int},
/* TODO: make these optional? Default to empty */ /* TODO: make these optional? Default to empty */
{"start", true, &verify_particle}, {"start", true, &conf_verify_particle},
{"end", true, &verify_particle}, {"end", true, &conf_verify_particle},
{"fill", true, &verify_particle}, {"fill", true, &conf_verify_particle},
{"empty", true, &verify_particle}, {"empty", true, &conf_verify_particle},
{"indicator", true, &verify_particle}, {"indicator", true, &conf_verify_particle},
COMMON_ATTRS COMMON_ATTRS
}; };
static const struct attr_info ramp[] = { static const struct attr_info ramp[] = {
{"tag", true, &verify_string}, {"tag", true, &conf_verify_string},
{"items", true, &verify_list_items}, {"items", true, &verify_list_items},
COMMON_ATTRS COMMON_ATTRS
}; };
static const struct attr_info string[] = { static const struct attr_info string[] = {
{"text", true, &verify_string}, {"text", true, &conf_verify_string},
{"max", false, &verify_int}, {"max", false, &conf_verify_int},
{"font", false, &verify_font}, {"font", false, &conf_verify_font},
{"foreground", false, &verify_color}, {"foreground", false, &conf_verify_color},
{"deco", false, &verify_decoration}, {"deco", false, &verify_decoration},
COMMON_ATTRS COMMON_ATTRS
}; };
@ -403,7 +380,7 @@ verify_particle_dictionary(keychain_t *chain, const struct yml_node *node)
if (strcmp(particles[i].name, particle_name) != 0) if (strcmp(particles[i].name, particle_name) != 0)
continue; continue;
if (!verify_dict(chain_push(chain, particle_name), values, if (!conf_verify_dict(chain_push(chain, particle_name), values,
particles[i].attrs, particles[i].count)) particles[i].attrs, particles[i].count))
{ {
return false; return false;
@ -414,60 +391,31 @@ verify_particle_dictionary(keychain_t *chain, const struct yml_node *node)
} }
LOG_ERR( LOG_ERR(
"%s: invalid particle name: %s", err_prefix(chain, particle), particle_name); "%s: invalid particle name: %s", conf_err_prefix(chain, particle), particle_name);
return false; return false;
} }
static bool bool
verify_particle(keychain_t *chain, const struct yml_node *node) conf_verify_particle(keychain_t *chain, const struct yml_node *node)
{ {
if (yml_is_dict(node)) if (yml_is_dict(node))
return verify_particle_dictionary(chain, node); return conf_verify_particle_dictionary(chain, node);
else if (yml_is_list(node)) else if (yml_is_list(node))
return verify_list_items(chain, node); return verify_list_items(chain, node);
else { else {
LOG_ERR("%s: particle must be either a dictionary or a list", LOG_ERR("%s: particle must be either a dictionary or a list",
err_prefix(chain, node)); conf_err_prefix(chain, node));
return false; return false;
} }
} }
static bool
verify_i3_content(keychain_t *chain, const struct yml_node *node)
{
if (!yml_is_dict(node)) {
LOG_ERR(
"%s: must be a dictionary of workspace-name: particle mappings",
err_prefix(chain, node));
return false;
}
for (struct yml_dict_iter it = yml_dict_iter(node);
it.key != NULL;
yml_dict_next(&it))
{
const char *key = yml_value_as_string(it.key);
if (key == NULL) {
LOG_ERR("%s: key must be a string (a i3 workspace name)",
err_prefix(chain, it.key));
return false;
}
if (!verify_particle(chain_push(chain, key), it.value))
return false;
chain_pop(chain);
}
return true;
}
static bool static bool
verify_module(keychain_t *chain, const struct yml_node *node) verify_module(keychain_t *chain, const struct yml_node *node)
{ {
if (!yml_is_dict(node) || yml_dict_length(node) != 1) { if (!yml_is_dict(node) || yml_dict_length(node) != 1) {
LOG_ERR("%s: module must be a dictionary with a single key; " LOG_ERR("%s: module must be a dictionary with a single key; "
"the name of the module", err_prefix(chain, node)); "the name of the module", conf_err_prefix(chain, node));
return false; return false;
} }
@ -477,122 +425,30 @@ verify_module(keychain_t *chain, const struct yml_node *node)
const char *mod_name = yml_value_as_string(module); const char *mod_name = yml_value_as_string(module);
if (mod_name == NULL) { if (mod_name == NULL) {
LOG_ERR("%s: module name must be a string", err_prefix(chain, module)); LOG_ERR("%s: module name must be a string", conf_err_prefix(chain, module));
return false; return false;
} }
static const struct attr_info alsa[] = { const struct module_info *info = plugin_load_module(mod_name);
{"card", true, &verify_string}, if (info == NULL) {
{"mixer", true, &verify_string}, LOG_ERR(
{"content", true, &verify_particle}, "%s: invalid module name: %s", conf_err_prefix(chain, node), mod_name);
{"anchors", false, NULL}, return false;
};
static const struct attr_info backlight[] = {
{"name", true, &verify_string},
{"content", true, &verify_particle},
{"anchors", false, NULL},
};
static const struct attr_info battery[] = {
{"name", true, &verify_string},
{"poll-interval", false, &verify_int},
{"content", true, &verify_particle},
{"anchors", false, NULL},
};
static const struct attr_info clock[] = {
{"date-format", false, &verify_string},
{"time-format", false, &verify_string},
{"content", true, &verify_particle},
{"anchors", false, NULL},
};
static const struct attr_info label[] = {
{"content", true, &verify_particle},
{"anchors", false, NULL},
};
static const struct attr_info mpd[] = {
{"host", true, &verify_string},
{"port", false, &verify_int},
{"content", true, &verify_particle},
{"anchors", false, NULL},
};
static const struct attr_info i3[] = {
{"spacing", false, &verify_int},
{"left-spacing", false, &verify_int},
{"right-spacing", false, &verify_int},
{"content", true, &verify_i3_content},
{"anchors", false, NULL},
};
static const struct attr_info network[] = {
{"name", true, &verify_string},
{"content", true, &verify_particle},
{"anchors", false, NULL},
};
static const struct attr_info removables[] = {
{"spacing", false, &verify_int},
{"left-spacing", false, &verify_int},
{"right-spacing", false, &verify_int},
{"content", true, &verify_particle},
{"anchors", false, NULL},
};
static const struct attr_info xkb[] = {
{"content", true, &verify_particle},
{"anchors", false, NULL},
};
static const struct attr_info xwindow[] = {
{"content", true, &verify_particle},
{"anchors", false, NULL},
};
static const struct {
const char *name;
const struct attr_info *attrs;
size_t count;
} modules[] = {
{"alsa", alsa, sizeof(alsa) / sizeof(alsa[0])},
{"backlight", backlight, sizeof(backlight) / sizeof(backlight[0])},
{"battery", battery, sizeof(battery) / sizeof(battery[0])},
{"clock", clock, sizeof(clock) / sizeof(clock[0])},
{"i3", i3, sizeof(i3) / sizeof(i3[0])},
{"label", label, sizeof(label) / sizeof(label[0])},
{"mpd", mpd, sizeof(mpd) / sizeof(mpd[0])},
{"network", network, sizeof(network) / sizeof(network[0])},
{"removables", removables, sizeof(removables) / sizeof(removables[0])},
{"xkb", xkb, sizeof(xkb) / sizeof(xkb[0])},
{"xwindow", xwindow, sizeof(xwindow) / sizeof(xwindow[0])},
};
for (size_t i = 0; i < sizeof(modules) / sizeof(modules[0]); i++) {
if (strcmp(modules[i].name, mod_name) != 0)
continue;
if (!verify_dict(chain_push(chain, mod_name), values,
modules[i].attrs, modules[i].count))
{
return false;
}
chain_pop(chain);
return true;
} }
LOG_ERR("%s: invalid module name: %s", err_prefix(chain, module), mod_name); if (!conf_verify_dict(chain_push(chain, mod_name), values,
return false; info->attrs, info->attr_count))
return false;
chain_pop(chain);
return true;
} }
static bool static bool
verify_module_list(keychain_t *chain, const struct yml_node *node) verify_module_list(keychain_t *chain, const struct yml_node *node)
{ {
if (!yml_is_list(node)) { if (!yml_is_list(node)) {
LOG_ERR("%s: must be a list of modules", err_prefix(chain, node)); LOG_ERR("%s: must be a list of modules", conf_err_prefix(chain, node));
return false; return false;
} }
@ -611,21 +467,21 @@ static bool
verify_bar_border(keychain_t *chain, const struct yml_node *node) verify_bar_border(keychain_t *chain, const struct yml_node *node)
{ {
static const struct attr_info attrs[] = { static const struct attr_info attrs[] = {
{"width", true, &verify_int}, {"width", true, &conf_verify_int},
{"color", true, &verify_color}, {"color", true, &conf_verify_color},
}; };
return verify_dict(chain, node, attrs, sizeof(attrs) / sizeof(attrs[0])); return conf_verify_dict(chain, node, attrs, sizeof(attrs) / sizeof(attrs[0]));
} }
static bool static bool
verify_bar_location(keychain_t *chain, const struct yml_node *node) verify_bar_location(keychain_t *chain, const struct yml_node *node)
{ {
return verify_enum(chain, node, (const char *[]){"top", "bottom"}, 2); return conf_verify_enum(chain, node, (const char *[]){"top", "bottom"}, 2);
} }
bool bool
config_verify_bar(const struct yml_node *bar) conf_verify_bar(const struct yml_node *bar)
{ {
if (!yml_is_dict(bar)) { if (!yml_is_dict(bar)) {
LOG_ERR("bar is not a dictionary"); LOG_ERR("bar is not a dictionary");
@ -636,27 +492,27 @@ config_verify_bar(const struct yml_node *bar)
chain_push(&chain, "bar"); chain_push(&chain, "bar");
static const struct attr_info attrs[] = { static const struct attr_info attrs[] = {
{"height", true, &verify_int}, {"height", true, &conf_verify_int},
{"location", true, &verify_bar_location}, {"location", true, &verify_bar_location},
{"background", true, &verify_color}, {"background", true, &conf_verify_color},
{"spacing", false, &verify_int}, {"spacing", false, &conf_verify_int},
{"left-spacing", false, &verify_int}, {"left-spacing", false, &conf_verify_int},
{"right-spacing", false, &verify_int}, {"right-spacing", false, &conf_verify_int},
{"margin", false, &verify_int}, {"margin", false, &conf_verify_int},
{"left_margin", false, &verify_int}, {"left_margin", false, &conf_verify_int},
{"right_margin", false, &verify_int}, {"right_margin", false, &conf_verify_int},
{"border", false, &verify_bar_border}, {"border", false, &verify_bar_border},
{"font", false, &verify_font}, {"font", false, &conf_verify_font},
{"left", false, &verify_module_list}, {"left", false, &verify_module_list},
{"center", false, &verify_module_list}, {"center", false, &verify_module_list},
{"right", false, &verify_module_list}, {"right", false, &verify_module_list},
}; };
bool ret = verify_dict(&chain, bar, attrs, sizeof(attrs) / sizeof(attrs[0])); bool ret = conf_verify_dict(&chain, bar, attrs, sizeof(attrs) / sizeof(attrs[0]));
tll_free(chain); tll_free(chain);
return ret; return ret;
} }

View file

@ -1,5 +1,44 @@
#pragma once #pragma once
#include <stdbool.h> #include <stdbool.h>
#include "tllist.h"
#include "yml.h" #include "yml.h"
bool config_verify_bar(const struct yml_node *bar); typedef tll(const char *) keychain_t;
struct attr_info {
const char *name;
bool required;
bool (*verify)(keychain_t *chain, const struct yml_node *node);
};
static inline keychain_t *
chain_push(keychain_t *chain, const char *key)
{
tll_push_back(*chain, key);
return chain;
}
static inline void
chain_pop(keychain_t *chain)
{
tll_pop_back(*chain);
}
const char *conf_err_prefix(
const keychain_t *chain, const struct yml_node *node);
bool conf_verify_string(keychain_t *chain, const struct yml_node *node);
bool conf_verify_int(keychain_t *chain, const struct yml_node *node);
bool conf_verify_enum(keychain_t *chain, const struct yml_node *node,
const char *values[], size_t count);
bool conf_verify_dict(keychain_t *chain, const struct yml_node *node,
const struct attr_info info[], size_t count);
bool conf_verify_color(keychain_t *chain, const struct yml_node *node);
bool conf_verify_font(keychain_t *chain, const struct yml_node *node);
bool conf_verify_particle(keychain_t *chain, const struct yml_node *node);

230
config.c
View file

@ -21,19 +21,8 @@
#include "particles/string.h" #include "particles/string.h"
#include "module.h" #include "module.h"
#include "modules/alsa.h"
#include "modules/backlight.h"
#include "modules/battery.h"
#include "modules/clock.h"
#include "modules/i3.h"
#include "modules/label.h"
#include "modules/mpd.h"
#include "modules/network.h"
#include "modules/removables.h"
#include "modules/xkb.h"
#include "modules/xwindow.h"
#include "config-verify.h" #include "config-verify.h"
#include "plugin.h"
static uint8_t static uint8_t
hex_nibble(char hex) hex_nibble(char hex)
@ -179,9 +168,6 @@ particle_string_from_config(const struct yml_node *node,
fg_color, left_margin, right_margin, on_click_template); fg_color, left_margin, right_margin, on_click_template);
} }
static struct particle * particle_from_config(
const struct yml_node *node, const struct font *parent_font);
static struct particle * static struct particle *
particle_list_from_config(const struct yml_node *node, particle_list_from_config(const struct yml_node *node,
const struct font *parent_font, const struct font *parent_font,
@ -191,8 +177,8 @@ particle_list_from_config(const struct yml_node *node,
const struct yml_node *items = yml_get_value(node, "items"); const struct yml_node *items = yml_get_value(node, "items");
const struct yml_node *spacing = yml_get_value(node, "spacing"); const struct yml_node *spacing = yml_get_value(node, "spacing");
const struct yml_node *_left_spacing = yml_get_value(node, "left_spacing"); const struct yml_node *_left_spacing = yml_get_value(node, "left-spacing");
const struct yml_node *_right_spacing = yml_get_value(node, "right_spacing"); const struct yml_node *_right_spacing = yml_get_value(node, "right-spacing");
int left_spacing = spacing != NULL ? yml_value_as_int(spacing) : int left_spacing = spacing != NULL ? yml_value_as_int(spacing) :
_left_spacing != NULL ? yml_value_as_int(_left_spacing) : 0; _left_spacing != NULL ? yml_value_as_int(_left_spacing) : 0;
@ -207,7 +193,7 @@ particle_list_from_config(const struct yml_node *node,
it.node != NULL; it.node != NULL;
yml_list_next(&it), idx++) yml_list_next(&it), idx++)
{ {
parts[idx] = particle_from_config(it.node, parent_font); parts[idx] = conf_to_particle(it.node, parent_font);
} }
return particle_list_new( return particle_list_new(
@ -233,11 +219,11 @@ particle_map_from_config(const struct yml_node *node,
yml_dict_next(&it), idx++) yml_dict_next(&it), idx++)
{ {
particle_map[idx].tag_value = yml_value_as_string(it.key); particle_map[idx].tag_value = yml_value_as_string(it.key);
particle_map[idx].particle = particle_from_config(it.value, parent_font); particle_map[idx].particle = conf_to_particle(it.value, parent_font);
} }
struct particle *default_particle = def != NULL struct particle *default_particle = def != NULL
? particle_from_config(def, parent_font) ? conf_to_particle(def, parent_font)
: NULL; : NULL;
return particle_map_new( return particle_map_new(
@ -262,7 +248,7 @@ particle_ramp_from_config(const struct yml_node *node,
it.node != NULL; it.node != NULL;
yml_list_next(&it), idx++) yml_list_next(&it), idx++)
{ {
parts[idx] = particle_from_config(it.node, parent_font); parts[idx] = conf_to_particle(it.node, parent_font);
} }
return particle_ramp_new( return particle_ramp_new(
@ -287,11 +273,11 @@ particle_progress_bar_from_config(const struct yml_node *node,
return particle_progress_bar_new( return particle_progress_bar_new(
yml_value_as_string(tag), yml_value_as_string(tag),
yml_value_as_int(length), yml_value_as_int(length),
particle_from_config(start, parent_font), conf_to_particle(start, parent_font),
particle_from_config(end, parent_font), conf_to_particle(end, parent_font),
particle_from_config(fill, parent_font), conf_to_particle(fill, parent_font),
particle_from_config(empty, parent_font), conf_to_particle(empty, parent_font),
particle_from_config(indicator, parent_font), conf_to_particle(indicator, parent_font),
left_margin, right_margin, on_click_template); left_margin, right_margin, on_click_template);
} }
@ -307,14 +293,14 @@ particle_simple_list_from_config(const struct yml_node *node,
it.node != NULL; it.node != NULL;
yml_list_next(&it), idx++) yml_list_next(&it), idx++)
{ {
parts[idx] = particle_from_config(it.node, parent_font); parts[idx] = conf_to_particle(it.node, parent_font);
} }
return particle_list_new(parts, count, 0, 2, 0, 0, NULL); return particle_list_new(parts, count, 0, 2, 0, 0, NULL);
} }
static struct particle * struct particle *
particle_from_config(const struct yml_node *node, const struct font *parent_font) conf_to_particle(const struct yml_node *node, const struct font *parent_font)
{ {
if (yml_is_list(node)) if (yml_is_list(node))
return particle_simple_list_from_config(node, parent_font); return particle_simple_list_from_config(node, parent_font);
@ -323,9 +309,9 @@ particle_from_config(const struct yml_node *node, const struct font *parent_font
const char *type = yml_value_as_string(pair.key); const char *type = yml_value_as_string(pair.key);
const struct yml_node *margin = yml_get_value(pair.value, "margin"); const struct yml_node *margin = yml_get_value(pair.value, "margin");
const struct yml_node *left_margin = yml_get_value(pair.value, "left_margin"); const struct yml_node *left_margin = yml_get_value(pair.value, "left-margin");
const struct yml_node *right_margin = yml_get_value(pair.value, "right_margin"); const struct yml_node *right_margin = yml_get_value(pair.value, "right-margin");
const struct yml_node *on_click = yml_get_value(pair.value, "on_click"); const struct yml_node *on_click = yml_get_value(pair.value, "on-click");
int left = margin != NULL ? yml_value_as_int(margin) : int left = margin != NULL ? yml_value_as_int(margin) :
left_margin != NULL ? yml_value_as_int(left_margin) : 0; left_margin != NULL ? yml_value_as_int(left_margin) : 0;
@ -365,154 +351,10 @@ particle_from_config(const struct yml_node *node, const struct font *parent_font
return ret; return ret;
} }
static struct module *
module_label_from_config(const struct yml_node *node, const struct font *parent_font)
{
const struct yml_node *c = yml_get_value(node, "content");
return module_label(particle_from_config(c, parent_font));
}
static struct module *
module_clock_from_config(const struct yml_node *node, const struct font *parent_font)
{
const struct yml_node *c = yml_get_value(node, "content");
const struct yml_node *date_format = yml_get_value(node, "date-format");
const struct yml_node *time_format = yml_get_value(node, "time-format");
return module_clock(
particle_from_config(c, parent_font),
date_format != NULL ? yml_value_as_string(date_format) : "%x",
time_format != NULL ? yml_value_as_string(time_format) : "%H:%M");
}
static struct module *
module_xwindow_from_config(const struct yml_node *node, const struct font *parent_font)
{
const struct yml_node *c = yml_get_value(node, "content");
return module_xwindow(particle_from_config(c, parent_font));
}
static struct module *
module_i3_from_config(const struct yml_node *node, const struct font *parent_font)
{
const struct yml_node *c = yml_get_value(node, "content");
const struct yml_node *spacing = yml_get_value(node, "spacing");
const struct yml_node *left_spacing = yml_get_value(node, "left_spacing");
const struct yml_node *right_spacing = yml_get_value(node, "right_spacing");
int left = spacing != NULL ? yml_value_as_int(spacing) :
left_spacing != NULL ? yml_value_as_int(left_spacing) : 0;
int right = spacing != NULL ? yml_value_as_int(spacing) :
right_spacing != NULL ? yml_value_as_int(right_spacing) : 0;
struct i3_workspaces workspaces[yml_dict_length(c)];
size_t idx = 0;
for (struct yml_dict_iter it = yml_dict_iter(c);
it.key != NULL;
yml_dict_next(&it), idx++)
{
workspaces[idx].name = yml_value_as_string(it.key);
workspaces[idx].content = particle_from_config(it.value, parent_font);
}
return module_i3(workspaces, yml_dict_length(c), left, right);
}
static struct module *
module_battery_from_config(const struct yml_node *node,
const struct font *parent_font)
{
const struct yml_node *c = yml_get_value(node, "content");
const struct yml_node *name = yml_get_value(node, "name");
const struct yml_node *poll_interval = yml_get_value(node, "poll_interval");
return module_battery(
yml_value_as_string(name),
particle_from_config(c, parent_font),
poll_interval != NULL ? yml_value_as_int(poll_interval) : 60);
}
static struct module *
module_xkb_from_config(const struct yml_node *node,
const struct font *parent_font)
{
const struct yml_node *c = yml_get_value(node, "content");
return module_xkb(particle_from_config(c, parent_font));
}
static struct module *
module_backlight_from_config(const struct yml_node *node,
const struct font *parent_font)
{
const struct yml_node *name = yml_get_value(node, "name");
const struct yml_node *c = yml_get_value(node, "content");
return module_backlight(
yml_value_as_string(name), particle_from_config(c, parent_font));
}
static struct module *
module_mpd_from_config(const struct yml_node *node,
const struct font *parent_font)
{
const struct yml_node *host = yml_get_value(node, "host");
const struct yml_node *port = yml_get_value(node, "port");
const struct yml_node *c = yml_get_value(node, "content");
return module_mpd(
yml_value_as_string(host),
port != NULL ? yml_value_as_int(port) : 0,
particle_from_config(c, parent_font));
}
static struct module *
module_network_from_config(const struct yml_node *node,
const struct font *parent_font)
{
const struct yml_node *name = yml_get_value(node, "name");
const struct yml_node *content = yml_get_value(node, "content");
return module_network(
yml_value_as_string(name), particle_from_config(content, parent_font));
}
static struct module *
module_removables_from_config(const struct yml_node *node,
const struct font *parent_font)
{
const struct yml_node *content = yml_get_value(node, "content");
const struct yml_node *spacing = yml_get_value(node, "spacing");
const struct yml_node *left_spacing = yml_get_value(node, "left_spacing");
const struct yml_node *right_spacing = yml_get_value(node, "right_spacing");
int left = spacing != NULL ? yml_value_as_int(spacing) :
left_spacing != NULL ? yml_value_as_int(left_spacing) : 0;
int right = spacing != NULL ? yml_value_as_int(spacing) :
right_spacing != NULL ? yml_value_as_int(right_spacing) : 0;
return module_removables(
particle_from_config(content, parent_font), left, right);
}
static struct module *
module_alsa_from_config(const struct yml_node *node,
const struct font *parent_font)
{
const struct yml_node *card = yml_get_value(node, "card");
const struct yml_node *mixer = yml_get_value(node, "mixer");
const struct yml_node *content = yml_get_value(node, "content");
return module_alsa(
yml_value_as_string(card),
yml_value_as_string(mixer),
particle_from_config(content, parent_font));
}
struct bar * struct bar *
conf_to_bar(const struct yml_node *bar) conf_to_bar(const struct yml_node *bar)
{ {
if (!config_verify_bar(bar)) if (!conf_verify_bar(bar))
return NULL; return NULL;
struct bar_config conf = {0}; struct bar_config conf = {0};
@ -539,11 +381,11 @@ conf_to_bar(const struct yml_node *bar)
if (spacing != NULL) if (spacing != NULL)
conf.left_spacing = conf.right_spacing = yml_value_as_int(spacing); conf.left_spacing = conf.right_spacing = yml_value_as_int(spacing);
const struct yml_node *left_spacing = yml_get_value(bar, "left_spacing"); const struct yml_node *left_spacing = yml_get_value(bar, "left-spacing");
if (left_spacing != NULL) if (left_spacing != NULL)
conf.left_spacing = yml_value_as_int(left_spacing); conf.left_spacing = yml_value_as_int(left_spacing);
const struct yml_node *right_spacing = yml_get_value(bar, "right_spacing"); const struct yml_node *right_spacing = yml_get_value(bar, "right-spacing");
if (right_spacing != NULL) if (right_spacing != NULL)
conf.right_spacing = yml_value_as_int(right_spacing); conf.right_spacing = yml_value_as_int(right_spacing);
@ -551,11 +393,11 @@ conf_to_bar(const struct yml_node *bar)
if (margin != NULL) if (margin != NULL)
conf.left_margin = conf.right_margin = yml_value_as_int(margin); conf.left_margin = conf.right_margin = yml_value_as_int(margin);
const struct yml_node *left_margin = yml_get_value(bar, "left_margin"); const struct yml_node *left_margin = yml_get_value(bar, "left-margin");
if (left_margin != NULL) if (left_margin != NULL)
conf.left_margin = yml_value_as_int(left_margin); conf.left_margin = yml_value_as_int(left_margin);
const struct yml_node *right_margin = yml_get_value(bar, "right_margin"); const struct yml_node *right_margin = yml_get_value(bar, "right-margin");
if (right_margin != NULL) if (right_margin != NULL)
conf.right_margin = yml_value_as_int(right_margin); conf.right_margin = yml_value_as_int(right_margin);
@ -599,30 +441,8 @@ conf_to_bar(const struct yml_node *bar)
struct yml_dict_iter m = yml_dict_iter(it.node); struct yml_dict_iter m = yml_dict_iter(it.node);
const char *mod_name = yml_value_as_string(m.key); const char *mod_name = yml_value_as_string(m.key);
if (strcmp(mod_name, "label") == 0) const struct module_info *info = plugin_load_module(mod_name);
mods[idx] = module_label_from_config(m.value, font); mods[idx] = info->from_conf(m.value, font);
else if (strcmp(mod_name, "clock") == 0)
mods[idx] = module_clock_from_config(m.value, font);
else if (strcmp(mod_name, "xwindow") == 0)
mods[idx] = module_xwindow_from_config(m.value, font);
else if (strcmp(mod_name, "i3") == 0)
mods[idx] = module_i3_from_config(m.value, font);
else if (strcmp(mod_name, "battery") == 0)
mods[idx] = module_battery_from_config(m.value, font);
else if (strcmp(mod_name, "xkb") == 0)
mods[idx] = module_xkb_from_config(m.value, font);
else if (strcmp(mod_name, "backlight") == 0)
mods[idx] = module_backlight_from_config(m.value, font);
else if (strcmp(mod_name, "mpd") == 0)
mods[idx] = module_mpd_from_config(m.value, font);
else if (strcmp(mod_name, "network") == 0)
mods[idx] = module_network_from_config(m.value, font);
else if (strcmp(mod_name, "removables") == 0)
mods[idx] = module_removables_from_config(m.value, font);
else if (strcmp(mod_name, "alsa") == 0)
mods[idx] = module_alsa_from_config(m.value, font);
else
assert(false);
} }
if (i == 0) { if (i == 0) {

View file

@ -1,7 +1,16 @@
#pragma once #pragma once
#include "bar.h" #include "bar.h"
#include "yml.h"
#include "font.h" #include "font.h"
#include "particle.h"
#include "yml.h"
bool conf_verify_bar(const struct yml_node *bar);
struct bar *conf_to_bar(const struct yml_node *bar); struct bar *conf_to_bar(const struct yml_node *bar);
/*
* Utility functions, for e.g. modules
*/
struct particle * conf_to_particle(
const struct yml_node *node, const struct font *parent_font);

View file

@ -3,12 +3,22 @@
#include <threads.h> #include <threads.h>
#include <cairo.h> #include <cairo.h>
#include "config-verify.h"
#include "particle.h" #include "particle.h"
#include "tag.h" #include "tag.h"
#include "yml.h"
struct bar; struct bar;
struct module; struct module;
struct module_info {
struct module *(*from_conf)(const struct yml_node *node,
const struct font *parent_font);
size_t attr_count; /* TODO: remove, NULL-terminate attr list instead */
const struct attr_info attrs[];
};
struct module_run_context { struct module_run_context {
struct module *module; struct module *module;
int ready_fd; int ready_fd;

69
modules/CMakeLists.txt Normal file
View file

@ -0,0 +1,69 @@
cmake_minimum_required(VERSION 3.13)
pkg_check_modules(ALSA REQUIRED alsa)
add_library(alsa MODULE alsa.c)
target_compile_options(alsa PRIVATE ${ALSA_CFLAGS_OTHER})
target_include_directories(alsa PRIVATE ${ALSA_INCLUDE_DIRS})
target_link_libraries(alsa module-sdk ${ALSA_LIBRARIES})
pkg_check_modules(UDEV REQUIRED libudev)
add_library(backlight MODULE backlight.c)
target_compile_options(backlight PRIVATE ${UDEV_CFLAGS_OTHER})
target_include_directories(backlight PRIVATE ${UDEV_INCLUDE_DIRS})
target_link_libraries(backlight module-sdk ${UDEV_LIBRARIES})
add_library(battery MODULE battery.c)
target_compile_options(battery PRIVATE ${UDEV_CFLAGS_OTHER})
target_include_directories(battery PRIVATE ${UDEV_INCLUDE_DIRS})
target_link_libraries(battery module-sdk ${UDEV_LIBRARIES})
add_library(clock MODULE clock.c)
target_link_libraries(clock module-sdk)
pkg_check_modules(JSON REQUIRED json-c)
add_library(i3 MODULE i3.c)
target_compile_options(i3 PRIVATE ${JSON_CFLAGS_OTHER})
target_include_directories(i3 PRIVATE ${JSON_INCLUDE_DIRS})
target_link_libraries(i3 module-sdk ${JSON_LIBRARIES})
add_library(label MODULE label.c)
target_link_libraries(label module-sdk)
pkg_check_modules(MPD REQUIRED libmpdclient)
add_library(mpd MODULE mpd.c)
target_compile_options(mpd PRIVATE ${MPD_CFLAGS_OTHER})
target_include_directories(mpd PRIVATE ${MPD_INCLUDE_DIRS})
target_link_libraries(mpd module-sdk ${MPD_LIBRARIES})
add_library(network MODULE network.c)
target_link_libraries(network module-sdk)
add_library(removables MODULE removables.c)
target_compile_options(removables PRIVATE ${UDEV_CFLAGS_OTHER})
target_include_directories(removables PRIVATE ${UDEV_INCLUDE_DIRS})
target_link_libraries(removables module-sdk ${UDEV_LIBRARIES})
pkg_check_modules(XCB_XKB REQUIRED xcb-xkb)
add_library(xkb MODULE xkb.c)
target_compile_options(xkb PRIVATE ${XCB_XKB_CFLAGS_OTHER})
target_include_directories(xkb PRIVATE ${XCB_XKB_INCLUDE_DIRS})
target_link_libraries(xkb module-sdk ${XCB_XKB_LIBRARIES})
add_library(xwindow MODULE xwindow.c)
target_link_libraries(xwindow module-sdk)
install(
TARGETS
alsa
backlight
battery
clock
i3
label
mpd
network
removables
xkb
xwindow
DESTINATION lib/f00bar)

View file

@ -1,5 +1,3 @@
#include "alsa.h"
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -9,6 +7,7 @@
#define LOG_ENABLE_DBG 0 #define LOG_ENABLE_DBG 0
#include "../log.h" #include "../log.h"
#include "../bar.h" #include "../bar.h"
#include "../config.h"
#include "../tllist.h" #include "../tllist.h"
struct private { struct private {
@ -249,8 +248,8 @@ run(struct module_run_context *ctx)
return 0; return 0;
} }
struct module * static struct module *
module_alsa(const char *card, const char *mixer, struct particle *label) alsa_new(const char *card, const char *mixer, struct particle *label)
{ {
struct private *priv = malloc(sizeof(*priv)); struct private *priv = malloc(sizeof(*priv));
priv->label = label; priv->label = label;
@ -267,3 +266,28 @@ module_alsa(const char *card, const char *mixer, struct particle *label)
mod->content = &content; mod->content = &content;
return mod; return mod;
} }
static struct module *
from_conf(const struct yml_node *node, const struct font *parent_font)
{
const struct yml_node *card = yml_get_value(node, "card");
const struct yml_node *mixer = yml_get_value(node, "mixer");
const struct yml_node *content = yml_get_value(node, "content");
return alsa_new(
yml_value_as_string(card),
yml_value_as_string(mixer),
conf_to_particle(content, parent_font));
}
const struct module_info module_info = {
.from_conf = &from_conf,
.attr_count = 4,
.attrs = {
{"card", true, &conf_verify_string},
{"mixer", true, &conf_verify_string},
{"content", true, &conf_verify_particle},
{"anchors", false, NULL},
{NULL, false, NULL}
},
};

View file

@ -1,7 +0,0 @@
#pragma once
#include "../module.h"
#include "../particle.h"
struct module *module_alsa(
const char *card, const char *mixer, struct particle *label);

View file

@ -1,5 +1,3 @@
#include "backlight.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -15,6 +13,7 @@
#define LOG_MODULE "battery" #define LOG_MODULE "battery"
#include "../log.h" #include "../log.h"
#include "../bar.h" #include "../bar.h"
#include "../config.h"
struct private { struct private {
struct particle *label; struct particle *label;
@ -198,8 +197,8 @@ run(struct module_run_context *ctx)
return 0; return 0;
} }
struct module * static struct module *
module_backlight(const char *device, struct particle *label) backlight_new(const char *device, struct particle *label)
{ {
struct private *m = malloc(sizeof(*m)); struct private *m = malloc(sizeof(*m));
m->label = label; m->label = label;
@ -214,3 +213,24 @@ module_backlight(const char *device, struct particle *label)
mod->content = &content; mod->content = &content;
return mod; return mod;
} }
static struct module *
from_conf(const struct yml_node *node, const struct font *parent_font)
{
const struct yml_node *name = yml_get_value(node, "name");
const struct yml_node *c = yml_get_value(node, "content");
return backlight_new(
yml_value_as_string(name), conf_to_particle(c, parent_font));
}
const struct module_info module_info = {
.from_conf = &from_conf,
.attr_count = 3,
.attrs = {
{"name", true, &conf_verify_string},
{"content", true, &conf_verify_particle},
{"anchors", false, NULL},
{NULL, false, NULL},
},
};

View file

@ -1,6 +0,0 @@
#pragma once
#include "../module.h"
#include "../particle.h"
struct module *module_backlight(const char *device, struct particle *label);

View file

@ -1,5 +1,3 @@
#include "battery.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -16,6 +14,7 @@
#define LOG_MODULE "battery" #define LOG_MODULE "battery"
#include "../log.h" #include "../log.h"
#include "../bar.h" #include "../bar.h"
#include "../config.h"
enum state { STATE_FULL, STATE_CHARGING, STATE_DISCHARGING }; enum state { STATE_FULL, STATE_CHARGING, STATE_DISCHARGING };
@ -326,9 +325,8 @@ out:
return ret; return ret;
} }
struct module * static struct module *
module_battery(const char *battery, struct particle *label, battery_new(const char *battery, struct particle *label, int poll_interval_secs)
int poll_interval_secs)
{ {
struct private *m = malloc(sizeof(*m)); struct private *m = malloc(sizeof(*m));
m->label = label; m->label = label;
@ -344,3 +342,28 @@ module_battery(const char *battery, struct particle *label,
mod->content = &content; mod->content = &content;
return mod; return mod;
} }
static struct module *
from_conf(const struct yml_node *node, const struct font *parent_font)
{
const struct yml_node *c = yml_get_value(node, "content");
const struct yml_node *name = yml_get_value(node, "name");
const struct yml_node *poll_interval = yml_get_value(node, "poll-interval");
return battery_new(
yml_value_as_string(name),
conf_to_particle(c, parent_font),
poll_interval != NULL ? yml_value_as_int(poll_interval) : 60);
}
const struct module_info module_info = {
.from_conf = &from_conf,
.attr_count = 4,
.attrs = {
{"name", true, &conf_verify_string},
{"poll-interval", false, &conf_verify_int},
{"content", true, &conf_verify_particle},
{"anchors", false, NULL},
{NULL, false, NULL},
},
};

View file

@ -1,7 +0,0 @@
#pragma once
#include "../module.h"
#include "../particle.h"
struct module *module_battery(
const char *battery, struct particle *label, int poll_interval_secs);

View file

@ -1,4 +1,3 @@
#include "clock.h"
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <time.h> #include <time.h>
@ -7,6 +6,7 @@
#include <poll.h> #include <poll.h>
#include "../bar.h" #include "../bar.h"
#include "../config.h"
struct private { struct private {
struct particle *label; struct particle *label;
@ -78,9 +78,8 @@ run(struct module_run_context *ctx)
return 0; return 0;
} }
struct module * static struct module *
module_clock(struct particle *label, clock_new(struct particle *label, const char *date_format, const char *time_format)
const char *date_format, const char *time_format)
{ {
struct private *m = malloc(sizeof(*m)); struct private *m = malloc(sizeof(*m));
m->label = label; m->label = label;
@ -94,3 +93,28 @@ module_clock(struct particle *label,
mod->content = &content; mod->content = &content;
return mod; return mod;
} }
static struct module *
from_conf(const struct yml_node *node, const struct font *parent_font)
{
const struct yml_node *c = yml_get_value(node, "content");
const struct yml_node *date_format = yml_get_value(node, "date-format");
const struct yml_node *time_format = yml_get_value(node, "time-format");
return clock_new(
conf_to_particle(c, parent_font),
date_format != NULL ? yml_value_as_string(date_format) : "%x",
time_format != NULL ? yml_value_as_string(time_format) : "%H:%M");
}
const struct module_info module_info = {
.from_conf = &from_conf,
.attr_count = 4,
.attrs = {
{"date-format", false, &conf_verify_string},
{"time-format", false, &conf_verify_string},
{"content", true, &conf_verify_particle},
{"anchors", false, NULL},
{NULL, false, NULL},
},
};

View file

@ -1,7 +0,0 @@
#pragma once
#include "../module.h"
#include "../particle.h"
struct module *module_clock(
struct particle *label, const char *date_format, const char *time_format);

View file

@ -1,5 +1,3 @@
#include "i3.h"
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
@ -21,6 +19,7 @@
#define LOG_ENABLE_DBG 0 #define LOG_ENABLE_DBG 0
#include "../log.h" #include "../log.h"
#include "../bar.h" #include "../bar.h"
#include "../config.h"
#include "../particles/dynlist.h" #include "../particles/dynlist.h"
@ -611,9 +610,15 @@ content(struct module *mod)
particles, particle_count, m->left_spacing, m->right_spacing); particles, particle_count, m->left_spacing, m->right_spacing);
} }
struct module * /* Maps workspace name to a content particle. */
module_i3(struct i3_workspaces workspaces[], size_t workspace_count, struct i3_workspaces {
int left_spacing, int right_spacing) const char *name;
struct particle *content;
};
static struct module *
i3_new(struct i3_workspaces workspaces[], size_t workspace_count,
int left_spacing, int right_spacing)
{ {
struct private *m = malloc(sizeof(*m)); struct private *m = malloc(sizeof(*m));
@ -638,3 +643,73 @@ module_i3(struct i3_workspaces workspaces[], size_t workspace_count,
mod->content = &content; mod->content = &content;
return mod; return mod;
} }
static struct module *
from_conf(const struct yml_node *node, const struct font *parent_font)
{
const struct yml_node *c = yml_get_value(node, "content");
const struct yml_node *spacing = yml_get_value(node, "spacing");
const struct yml_node *left_spacing = yml_get_value(node, "left-spacing");
const struct yml_node *right_spacing = yml_get_value(node, "right-spacing");
int left = spacing != NULL ? yml_value_as_int(spacing) :
left_spacing != NULL ? yml_value_as_int(left_spacing) : 0;
int right = spacing != NULL ? yml_value_as_int(spacing) :
right_spacing != NULL ? yml_value_as_int(right_spacing) : 0;
struct i3_workspaces workspaces[yml_dict_length(c)];
size_t idx = 0;
for (struct yml_dict_iter it = yml_dict_iter(c);
it.key != NULL;
yml_dict_next(&it), idx++)
{
workspaces[idx].name = yml_value_as_string(it.key);
workspaces[idx].content = conf_to_particle(it.value, parent_font);
}
return i3_new(workspaces, yml_dict_length(c), left, right);
}
static bool
verify_content(keychain_t *chain, const struct yml_node *node)
{
if (!yml_is_dict(node)) {
LOG_ERR(
"%s: must be a dictionary of workspace-name: particle mappings",
conf_err_prefix(chain, node));
return false;
}
for (struct yml_dict_iter it = yml_dict_iter(node);
it.key != NULL;
yml_dict_next(&it))
{
const char *key = yml_value_as_string(it.key);
if (key == NULL) {
LOG_ERR("%s: key must be a string (a i3 workspace name)",
conf_err_prefix(chain, it.key));
return false;
}
if (!conf_verify_particle(chain_push(chain, key), it.value))
return false;
chain_pop(chain);
}
return true;
}
const struct module_info module_info = {
.from_conf = &from_conf,
.attr_count = 5,
.attrs = {
{"spacing", false, &conf_verify_int},
{"left-spacing", false, &conf_verify_int},
{"right-spacing", false, &conf_verify_int},
{"content", true, &verify_content},
{"anchors", false, NULL},
{NULL, false, NULL},
},
};

View file

@ -1,14 +0,0 @@
#pragma once
#include "../module.h"
#include "../particle.h"
/* Maps workspace name to a content particle. */
struct i3_workspaces {
const char *name;
struct particle *content;
};
struct module *module_i3(
struct i3_workspaces workspaces[], size_t workspace_count,
int left_spacing, int right_spacing);

View file

@ -1,10 +1,10 @@
#include "label.h"
#include <stdlib.h> #include <stdlib.h>
#include <assert.h> #include <assert.h>
#include <poll.h> #include <poll.h>
#include "../config.h"
struct private { struct private {
struct particle *label; struct particle *label;
}; };
@ -32,8 +32,8 @@ run(struct module_run_context *ctx)
return 0; return 0;
} }
struct module * static struct module *
module_label(struct particle *label) label_new(struct particle *label)
{ {
struct private *m = malloc(sizeof(*m)); struct private *m = malloc(sizeof(*m));
m->label = label; m->label = label;
@ -45,3 +45,20 @@ module_label(struct particle *label)
mod->content = &content; mod->content = &content;
return mod; return mod;
} }
static struct module *
from_conf(const struct yml_node *node, const struct font *parent_font)
{
const struct yml_node *c = yml_get_value(node, "content");
return label_new(conf_to_particle(c, parent_font));
}
const struct module_info module_info = {
.from_conf = &from_conf,
.attr_count = 2,
.attrs = {
{"content", true, &conf_verify_particle},
{"anchors", false, NULL},
{NULL, false, NULL},
},
};

View file

@ -1,6 +0,0 @@
#pragma once
#include "../module.h"
#include "../particle.h"
struct module *module_label(struct particle *label);

View file

@ -1,5 +1,3 @@
#include "mpd.h"
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
@ -18,6 +16,7 @@
#define LOG_ENABLE_DBG 0 #define LOG_ENABLE_DBG 0
#include "../log.h" #include "../log.h"
#include "../bar.h" #include "../bar.h"
#include "../config.h"
enum state { enum state {
STATE_OFFLINE = 1000, STATE_OFFLINE = 1000,
@ -451,8 +450,8 @@ refresh_in(struct module *mod, long milli_seconds)
return r == 0; return r == 0;
} }
struct module * static struct module *
module_mpd(const char *host, uint16_t port, struct particle *label) mpd_new(const char *host, uint16_t port, struct particle *label)
{ {
struct private *priv = malloc(sizeof(*priv)); struct private *priv = malloc(sizeof(*priv));
priv->host = strdup(host); priv->host = strdup(host);
@ -478,3 +477,28 @@ module_mpd(const char *host, uint16_t port, struct particle *label)
mod->refresh_in = &refresh_in; mod->refresh_in = &refresh_in;
return mod; return mod;
} }
static struct module *
from_conf(const struct yml_node *node, const struct font *parent_font)
{
const struct yml_node *host = yml_get_value(node, "host");
const struct yml_node *port = yml_get_value(node, "port");
const struct yml_node *c = yml_get_value(node, "content");
return mpd_new(
yml_value_as_string(host),
port != NULL ? yml_value_as_int(port) : 0,
conf_to_particle(c, parent_font));
}
const struct module_info module_info = {
.from_conf = &from_conf,
.attr_count = 4,
.attrs = {
{"host", true, &conf_verify_string},
{"port", false, &conf_verify_int},
{"content", true, &conf_verify_particle},
{"anchors", false, NULL},
{NULL, false, NULL},
},
};

View file

@ -1,9 +0,0 @@
#pragma once
#include <stdint.h>
#include "../module.h"
#include "../particle.h"
struct module *module_mpd(
const char *host, uint16_t port, struct particle *label);

View file

@ -1,5 +1,3 @@
#include "network.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -17,8 +15,9 @@
#define LOG_MODULE "network" #define LOG_MODULE "network"
#define LOG_ENABLE_DBG 0 #define LOG_ENABLE_DBG 0
#include "../log.h" #include "../log.h"
#include "../module.h"
#include "../bar.h" #include "../bar.h"
#include "../config.h"
#include "../module.h"
#include "../tllist.h" #include "../tllist.h"
struct af_addr { struct af_addr {
@ -141,6 +140,8 @@ netlink_connect(void)
.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR, .nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR,
}; };
LOG_WARN("nl_pid_value = 0x%08x", addr.nl_pid);
if (bind(sock, (const struct sockaddr *)&addr, sizeof(addr)) == -1) { if (bind(sock, (const struct sockaddr *)&addr, sizeof(addr)) == -1) {
LOG_ERRNO("failed to bind netlink socket"); LOG_ERRNO("failed to bind netlink socket");
close(sock); close(sock);
@ -509,8 +510,8 @@ run(struct module_run_context *ctx)
return 0; return 0;
} }
struct module * static struct module *
module_network(const char *iface, struct particle *label) network_new(const char *iface, struct particle *label)
{ {
struct private *priv = malloc(sizeof(*priv)); struct private *priv = malloc(sizeof(*priv));
priv->iface = strdup(iface); priv->iface = strdup(iface);
@ -531,3 +532,24 @@ module_network(const char *iface, struct particle *label)
mod->content = &content; mod->content = &content;
return mod; return mod;
} }
static struct module *
from_conf(const struct yml_node *node, const struct font *parent_font)
{
const struct yml_node *name = yml_get_value(node, "name");
const struct yml_node *content = yml_get_value(node, "content");
return network_new(
yml_value_as_string(name), conf_to_particle(content, parent_font));
}
const struct module_info module_info = {
.from_conf = &from_conf,
.attr_count = 3,
.attrs = {
{"name", true, &conf_verify_string},
{"content", true, &conf_verify_particle},
{"anchors", false, NULL},
{NULL, false, NULL},
},
};

View file

@ -1,6 +0,0 @@
#pragma once
#include "../module.h"
#include "../particle.h"
struct module *module_network(const char *iface, struct particle *label);

View file

@ -1,5 +1,3 @@
#include "removables.h"
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <stdint.h> #include <stdint.h>
@ -18,8 +16,9 @@
#define LOG_ENABLE_DBG 0 #define LOG_ENABLE_DBG 0
#include "../log.h" #include "../log.h"
#include "../bar.h" #include "../bar.h"
#include "../tllist.h" #include "../config.h"
#include "../particles/dynlist.h" #include "../particles/dynlist.h"
#include "../tllist.h"
typedef tll(char *) mount_point_list_t; typedef tll(char *) mount_point_list_t;
@ -544,8 +543,8 @@ run(struct module_run_context *ctx)
return 0; return 0;
} }
struct module * static struct module *
module_removables(struct particle *label, int left_spacing, int right_spacing) removables_new(struct particle *label, int left_spacing, int right_spacing)
{ {
struct private *priv = malloc(sizeof(*priv)); struct private *priv = malloc(sizeof(*priv));
priv->label = label; priv->label = label;
@ -560,3 +559,33 @@ module_removables(struct particle *label, int left_spacing, int right_spacing)
mod->content = &content; mod->content = &content;
return mod; return mod;
} }
static struct module *
from_conf(const struct yml_node *node, const struct font *parent_font)
{
const struct yml_node *content = yml_get_value(node, "content");
const struct yml_node *spacing = yml_get_value(node, "spacing");
const struct yml_node *left_spacing = yml_get_value(node, "left-spacing");
const struct yml_node *right_spacing = yml_get_value(node, "right-spacing");
int left = spacing != NULL ? yml_value_as_int(spacing) :
left_spacing != NULL ? yml_value_as_int(left_spacing) : 0;
int right = spacing != NULL ? yml_value_as_int(spacing) :
right_spacing != NULL ? yml_value_as_int(right_spacing) : 0;
return removables_new(
conf_to_particle(content, parent_font), left, right);
}
const struct module_info module_info = {
.from_conf = &from_conf,
.attr_count = 5,
.attrs = {
{"spacing", false, &conf_verify_int},
{"left-spacing", false, &conf_verify_int},
{"right-spacing", false, &conf_verify_int},
{"content", true, &conf_verify_particle},
{"anchors", false, NULL},
{NULL, false, NULL},
},
};

View file

@ -1,7 +0,0 @@
#pragma once
#include "../module.h"
#include "../particle.h"
struct module *module_removables(
struct particle *label, int left_spacing, int right_spacing);

View file

@ -1,5 +1,3 @@
#include "xkb.h"
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <assert.h> #include <assert.h>
@ -13,6 +11,7 @@
#define LOG_MODULE "xkb" #define LOG_MODULE "xkb"
#include "../log.h" #include "../log.h"
#include "../bar.h" #include "../bar.h"
#include "../config.h"
#include "../xcb.h" #include "../xcb.h"
struct layout { struct layout {
@ -436,8 +435,8 @@ run(struct module_run_context *ctx)
return ret; return ret;
} }
struct module * static struct module *
module_xkb(struct particle *label) xkb_new(struct particle *label)
{ {
struct private *m = malloc(sizeof(*m)); struct private *m = malloc(sizeof(*m));
m->label = label; m->label = label;
@ -452,3 +451,20 @@ module_xkb(struct particle *label)
mod->content = &content; mod->content = &content;
return mod; return mod;
} }
static struct module *
from_conf(const struct yml_node *node, const struct font *parent_font)
{
const struct yml_node *c = yml_get_value(node, "content");
return xkb_new(conf_to_particle(c, parent_font));
}
const struct module_info module_info = {
.from_conf = &from_conf,
.attr_count = 2,
.attrs = {
{"content", true, &conf_verify_particle},
{"anchors", false, NULL},
{NULL, false, NULL},
},
};

View file

@ -1,5 +0,0 @@
#pragma once
#include "../module.h"
#include "../particle.h"
struct module *module_xkb(struct particle *label);

View file

@ -1,5 +1,3 @@
#include "xwindow.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -17,6 +15,7 @@
#define LOG_MODULE "xkb" #define LOG_MODULE "xkb"
#include "../log.h" #include "../log.h"
#include "../bar.h" #include "../bar.h"
#include "../config.h"
#include "../xcb.h" #include "../xcb.h"
struct private { struct private {
@ -301,8 +300,8 @@ destroy(struct module *mod)
module_default_destroy(mod); module_default_destroy(mod);
} }
struct module * static struct module *
module_xwindow(struct particle *label) xwindow_new(struct particle *label)
{ {
struct private *m = calloc(1, sizeof(*m)); struct private *m = calloc(1, sizeof(*m));
m->label = label; m->label = label;
@ -314,3 +313,20 @@ module_xwindow(struct particle *label)
mod->content = &content; mod->content = &content;
return mod; return mod;
} }
static struct module *
from_conf(const struct yml_node *node, const struct font *parent_font)
{
const struct yml_node *c = yml_get_value(node, "content");
return xwindow_new(conf_to_particle(c, parent_font));
}
const struct module_info module_info = {
.from_conf = &from_conf,
.attr_count = 2,
.attrs = {
{"content", true, &conf_verify_particle},
{"anchors", false, NULL},
{NULL, false, NULL},
},
};

View file

@ -1,6 +0,0 @@
#pragma once
#include "../module.h"
#include "../particle.h"
struct module *module_xwindow(struct particle *label);

77
plugin.c Normal file
View file

@ -0,0 +1,77 @@
#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"
struct plugin {
char *name;
void *lib;
const void *sym;
};
static tll(struct plugin) plugins = tll_init();
static void
free_plugin(struct plugin plug)
{
dlerror();
dlclose(plug.lib);
const char *dl_error = dlerror();
if (dl_error != NULL)
LOG_ERR("%s: dlclose(): %s", plug.name, dl_error);
free(plug.name);
}
static void __attribute__((destructor))
fini(void)
{
tll_free_and_free(plugins, free_plugin);
}
const struct module_info *
plugin_load_module(const char *name)
{
char path[128];
snprintf(path, sizeof(path), "lib%s.so", name);
/* Have we already loaded it? */
tll_foreach(plugins, plug) {
if (strcmp(plug->item.name, name) == 0) {
LOG_DBG("%s already loaded: %p", name, plug->item.lib);
assert(plug->item.sym != NULL);
return plug->item.sym;
}
}
/* Not loaded - do it now */
void *lib = dlopen(path, RTLD_LOCAL | RTLD_NOW);
LOG_DBG("%s: dlopened to %p", name, lib);
if (lib == NULL) {
LOG_ERR("%s: dlopen: %s", name, dlerror());
return NULL;
}
tll_push_back(plugins, ((struct plugin){strdup(name), lib}));
struct plugin *plug = &tll_back(plugins);
dlerror(); /* Clear previous error */
plug->sym = dlsym(lib, "module_info");
const char *dlsym_error = dlerror();
if (dlsym_error != NULL) {
LOG_ERR("%s: dlsym: %s", name, dlsym_error);
return NULL;
}
assert(plug->sym != NULL);
return plug->sym;
}

5
plugin.h Normal file
View file

@ -0,0 +1,5 @@
#pragma once
#include "module.h"
const struct module_info *plugin_load_module(const char *name);