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)
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(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
bar.c bar.h
@ -35,6 +30,7 @@ add_executable(f00bar
main.c
module.c module.h
particle.c particle.h
plugin.c plugin.h
tag.c tag.h
xcb.c xcb.h
yml.c yml.h
@ -50,32 +46,19 @@ add_executable(f00bar
particles/progress-bar.c particles/progress-bar.h
particles/ramp.c particles/ramp.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)
# 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
${XCB_CFLAGS_OTHER}
${FONTCONFIG_CFLAGS_OTHER}
${CAIRO_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
@ -83,22 +66,27 @@ target_include_directories(f00bar PRIVATE
${FONTCONFIG_INCLUDE_DIRS}
${CAIRO_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
${CMAKE_THREAD_LIBS_INIT}
${CMAKE_DL_LIBS}
${XCB_LIBRARIES}
${FONTCONFIG_LIBRARIES}
${CAIRO_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 <assert.h>
@ -6,31 +6,11 @@
#define LOG_MODULE "config:verify"
#define LOG_ENABLE_DBG 0
#include "log.h"
#include "plugin.h"
#include "tllist.h"
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 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)
const char *
conf_err_prefix(const keychain_t *chain, const struct yml_node *node)
{
static char msg[4096];
int idx = 0;
@ -46,36 +26,36 @@ err_prefix(const keychain_t *chain, const struct yml_node *node)
return msg;
}
static bool
verify_string(keychain_t *chain, const struct yml_node *node)
bool
conf_verify_string(keychain_t *chain, const struct yml_node *node)
{
const char *s = yml_value_as_string(node);
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 true;
}
static bool
verify_int(keychain_t *chain, const struct yml_node *node)
bool
conf_verify_int(keychain_t *chain, const struct yml_node *node)
{
if (yml_value_is_int(node))
return true;
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;
}
static bool
verify_enum(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)
{
const char *s = yml_value_as_string(node);
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;
}
@ -84,19 +64,19 @@ verify_enum(keychain_t *chain, const struct yml_node *node,
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++)
LOG_ERR(" %s", values[i]);
return false;
}
static bool
verify_dict(keychain_t *chain, const struct yml_node *node,
bool
conf_verify_dict(keychain_t *chain, const struct yml_node *node,
const struct attr_info info[], size_t count)
{
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;
}
@ -109,7 +89,7 @@ verify_dict(keychain_t *chain, const struct yml_node *node,
{
const char *key = yml_value_as_string(it.key);
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;
}
@ -123,7 +103,7 @@ verify_dict(keychain_t *chain, const struct yml_node *node,
}
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;
}
@ -139,19 +119,19 @@ verify_dict(keychain_t *chain, const struct yml_node *node,
if (!info[i].required || exists[i])
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 true;
}
static bool
verify_color(keychain_t *chain, const struct yml_node *node)
bool
conf_verify_color(keychain_t *chain, const struct yml_node *node)
{
const char *s = yml_value_as_string(node);
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;
}
@ -160,7 +140,7 @@ verify_color(keychain_t *chain, const struct yml_node *node)
if (strlen(s) != 8 || v != 4) {
LOG_ERR("%s: value must be a color ('rrggbbaa', e.g ff00ffff)",
err_prefix(chain, node));
conf_err_prefix(chain, node));
return false;
}
@ -168,14 +148,14 @@ verify_color(keychain_t *chain, const struct yml_node *node)
}
static bool
verify_font(keychain_t *chain, const struct yml_node *node)
bool
conf_verify_font(keychain_t *chain, const struct yml_node *node)
{
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);
@ -184,7 +164,7 @@ static bool
verify_decoration_stack(keychain_t *chain, const struct yml_node *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;
}
@ -206,7 +186,7 @@ verify_decoration(keychain_t *chain, const struct yml_node *node)
if (yml_dict_length(node) != 1) {
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;
}
@ -216,7 +196,7 @@ verify_decoration(keychain_t *chain, const struct yml_node *node)
const char *deco_name = yml_value_as_string(deco);
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;
}
@ -227,12 +207,12 @@ verify_decoration(keychain_t *chain, const struct yml_node *node)
}
static const struct attr_info background[] = {
{"color", true, &verify_color},
{"color", true, &conf_verify_color},
};
static const struct attr_info underline[] = {
{"size", true, &verify_int},
{"color", true, &verify_color},
{"size", true, &conf_verify_int},
{"color", true, &conf_verify_color},
};
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)
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))
{
return false;
@ -259,12 +239,10 @@ verify_decoration(keychain_t *chain, const struct yml_node *node)
}
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;
}
static bool verify_particle(keychain_t *chain, const struct yml_node *node);
static bool
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;
yml_list_next(&it))
{
if (!verify_particle(chain, it.node))
if (!conf_verify_particle(chain, it.node))
return false;
}
@ -287,7 +265,7 @@ verify_map_values(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));
conf_err_prefix(chain, node));
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);
if (key == NULL) {
LOG_ERR("%s: key must be a string (a i3 workspace name)",
err_prefix(chain, it.key));
LOG_ERR("%s: key must be a string", conf_err_prefix(chain, it.key));
return false;
}
if (!verify_particle(chain_push(chain, key), it.value))
if (!conf_verify_particle(chain_push(chain, key), it.value))
return false;
chain_pop(chain);
@ -312,13 +289,13 @@ verify_map_values(keychain_t *chain, const struct yml_node *node)
}
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));
if (yml_dict_length(node) != 1) {
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;
}
@ -328,15 +305,15 @@ verify_particle_dictionary(keychain_t *chain, const struct yml_node *node)
const char *particle_name = yml_value_as_string(particle);
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;
}
#define COMMON_ATTRS \
{"margin", false, &verify_int}, \
{"left-margin", false, &verify_int}, \
{"right-margin", false, &verify_int}, \
{"on-click", false, &verify_string},
{"margin", false, &conf_verify_int}, \
{"left-margin", false, &conf_verify_int}, \
{"right-margin", false, &conf_verify_int}, \
{"on-click", false, &conf_verify_string},
static const struct attr_info empty[] = {
COMMON_ATTRS
@ -344,42 +321,42 @@ verify_particle_dictionary(keychain_t *chain, const struct yml_node *node)
static const struct attr_info list[] = {
{"items", true, &verify_list_items},
{"spacing", false, &verify_int},
{"left-spacing", false, &verify_int},
{"right-spacing", false, &verify_int},
{"spacing", false, &conf_verify_int},
{"left-spacing", false, &conf_verify_int},
{"right-spacing", false, &conf_verify_int},
COMMON_ATTRS
};
static const struct attr_info map[] = {
{"tag", true, &verify_string},
{"tag", true, &conf_verify_string},
{"values", true, &verify_map_values},
{"default", false, &verify_particle},
{"default", false, &conf_verify_particle},
COMMON_ATTRS
};
static const struct attr_info progress_bar[] = {
{"tag", true, &verify_string},
{"length", true, &verify_int},
{"tag", true, &conf_verify_string},
{"length", true, &conf_verify_int},
/* TODO: make these optional? Default to empty */
{"start", true, &verify_particle},
{"end", true, &verify_particle},
{"fill", true, &verify_particle},
{"empty", true, &verify_particle},
{"indicator", true, &verify_particle},
{"start", true, &conf_verify_particle},
{"end", true, &conf_verify_particle},
{"fill", true, &conf_verify_particle},
{"empty", true, &conf_verify_particle},
{"indicator", true, &conf_verify_particle},
COMMON_ATTRS
};
static const struct attr_info ramp[] = {
{"tag", true, &verify_string},
{"tag", true, &conf_verify_string},
{"items", true, &verify_list_items},
COMMON_ATTRS
};
static const struct attr_info string[] = {
{"text", true, &verify_string},
{"max", false, &verify_int},
{"font", false, &verify_font},
{"foreground", false, &verify_color},
{"text", true, &conf_verify_string},
{"max", false, &conf_verify_int},
{"font", false, &conf_verify_font},
{"foreground", false, &conf_verify_color},
{"deco", false, &verify_decoration},
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)
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))
{
return false;
@ -414,60 +391,31 @@ verify_particle_dictionary(keychain_t *chain, const struct yml_node *node)
}
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;
}
static bool
verify_particle(keychain_t *chain, const struct yml_node *node)
bool
conf_verify_particle(keychain_t *chain, const struct yml_node *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))
return verify_list_items(chain, node);
else {
LOG_ERR("%s: particle must be either a dictionary or a list",
err_prefix(chain, node));
conf_err_prefix(chain, node));
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
verify_module(keychain_t *chain, const struct yml_node *node)
{
if (!yml_is_dict(node) || yml_dict_length(node) != 1) {
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;
}
@ -477,122 +425,30 @@ verify_module(keychain_t *chain, const struct yml_node *node)
const char *mod_name = yml_value_as_string(module);
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;
}
static const struct attr_info alsa[] = {
{"card", true, &verify_string},
{"mixer", true, &verify_string},
{"content", true, &verify_particle},
{"anchors", false, NULL},
};
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))
{
const struct module_info *info = plugin_load_module(mod_name);
if (info == NULL) {
LOG_ERR(
"%s: invalid module name: %s", conf_err_prefix(chain, node), mod_name);
return false;
}
if (!conf_verify_dict(chain_push(chain, mod_name), values,
info->attrs, info->attr_count))
return false;
chain_pop(chain);
return true;
}
LOG_ERR("%s: invalid module name: %s", err_prefix(chain, module), mod_name);
return false;
}
static bool
verify_module_list(keychain_t *chain, const struct yml_node *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;
}
@ -611,21 +467,21 @@ static bool
verify_bar_border(keychain_t *chain, const struct yml_node *node)
{
static const struct attr_info attrs[] = {
{"width", true, &verify_int},
{"color", true, &verify_color},
{"width", true, &conf_verify_int},
{"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
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
config_verify_bar(const struct yml_node *bar)
conf_verify_bar(const struct yml_node *bar)
{
if (!yml_is_dict(bar)) {
LOG_ERR("bar is not a dictionary");
@ -636,27 +492,27 @@ config_verify_bar(const struct yml_node *bar)
chain_push(&chain, "bar");
static const struct attr_info attrs[] = {
{"height", true, &verify_int},
{"height", true, &conf_verify_int},
{"location", true, &verify_bar_location},
{"background", true, &verify_color},
{"background", true, &conf_verify_color},
{"spacing", false, &verify_int},
{"left-spacing", false, &verify_int},
{"right-spacing", false, &verify_int},
{"spacing", false, &conf_verify_int},
{"left-spacing", false, &conf_verify_int},
{"right-spacing", false, &conf_verify_int},
{"margin", false, &verify_int},
{"left_margin", false, &verify_int},
{"right_margin", false, &verify_int},
{"margin", false, &conf_verify_int},
{"left_margin", false, &conf_verify_int},
{"right_margin", false, &conf_verify_int},
{"border", false, &verify_bar_border},
{"font", false, &verify_font},
{"font", false, &conf_verify_font},
{"left", false, &verify_module_list},
{"center", 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);
return ret;
}

View file

@ -1,5 +1,44 @@
#pragma once
#include <stdbool.h>
#include "tllist.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 "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 "plugin.h"
static uint8_t
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);
}
static struct particle * particle_from_config(
const struct yml_node *node, const struct font *parent_font);
static struct particle *
particle_list_from_config(const struct yml_node *node,
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 *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");
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 = spacing != NULL ? yml_value_as_int(spacing) :
_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;
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(
@ -233,11 +219,11 @@ particle_map_from_config(const struct yml_node *node,
yml_dict_next(&it), idx++)
{
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
? particle_from_config(def, parent_font)
? conf_to_particle(def, parent_font)
: NULL;
return particle_map_new(
@ -262,7 +248,7 @@ particle_ramp_from_config(const struct yml_node *node,
it.node != NULL;
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(
@ -287,11 +273,11 @@ particle_progress_bar_from_config(const struct yml_node *node,
return particle_progress_bar_new(
yml_value_as_string(tag),
yml_value_as_int(length),
particle_from_config(start, parent_font),
particle_from_config(end, parent_font),
particle_from_config(fill, parent_font),
particle_from_config(empty, parent_font),
particle_from_config(indicator, parent_font),
conf_to_particle(start, parent_font),
conf_to_particle(end, parent_font),
conf_to_particle(fill, parent_font),
conf_to_particle(empty, parent_font),
conf_to_particle(indicator, parent_font),
left_margin, right_margin, on_click_template);
}
@ -307,14 +293,14 @@ particle_simple_list_from_config(const struct yml_node *node,
it.node != NULL;
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);
}
static struct particle *
particle_from_config(const struct yml_node *node, const struct font *parent_font)
struct particle *
conf_to_particle(const struct yml_node *node, const struct font *parent_font)
{
if (yml_is_list(node))
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 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 *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 *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 *on_click = yml_get_value(pair.value, "on-click");
int left = margin != NULL ? yml_value_as_int(margin) :
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;
}
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 *
conf_to_bar(const struct yml_node *bar)
{
if (!config_verify_bar(bar))
if (!conf_verify_bar(bar))
return NULL;
struct bar_config conf = {0};
@ -539,11 +381,11 @@ conf_to_bar(const struct yml_node *bar)
if (spacing != NULL)
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)
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)
conf.right_spacing = yml_value_as_int(right_spacing);
@ -551,11 +393,11 @@ conf_to_bar(const struct yml_node *bar)
if (margin != NULL)
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)
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)
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);
const char *mod_name = yml_value_as_string(m.key);
if (strcmp(mod_name, "label") == 0)
mods[idx] = module_label_from_config(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);
const struct module_info *info = plugin_load_module(mod_name);
mods[idx] = info->from_conf(m.value, font);
}
if (i == 0) {

View file

@ -1,7 +1,16 @@
#pragma once
#include "bar.h"
#include "yml.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);
/*
* 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 <cairo.h>
#include "config-verify.h"
#include "particle.h"
#include "tag.h"
#include "yml.h"
struct bar;
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 *module;
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 <string.h>
@ -9,6 +7,7 @@
#define LOG_ENABLE_DBG 0
#include "../log.h"
#include "../bar.h"
#include "../config.h"
#include "../tllist.h"
struct private {
@ -249,8 +248,8 @@ run(struct module_run_context *ctx)
return 0;
}
struct module *
module_alsa(const char *card, const char *mixer, struct particle *label)
static struct module *
alsa_new(const char *card, const char *mixer, struct particle *label)
{
struct private *priv = malloc(sizeof(*priv));
priv->label = label;
@ -267,3 +266,28 @@ module_alsa(const char *card, const char *mixer, struct particle *label)
mod->content = &content;
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 <stdlib.h>
#include <string.h>
@ -15,6 +13,7 @@
#define LOG_MODULE "battery"
#include "../log.h"
#include "../bar.h"
#include "../config.h"
struct private {
struct particle *label;
@ -198,8 +197,8 @@ run(struct module_run_context *ctx)
return 0;
}
struct module *
module_backlight(const char *device, struct particle *label)
static struct module *
backlight_new(const char *device, struct particle *label)
{
struct private *m = malloc(sizeof(*m));
m->label = label;
@ -214,3 +213,24 @@ module_backlight(const char *device, struct particle *label)
mod->content = &content;
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 <stdlib.h>
#include <string.h>
@ -16,6 +14,7 @@
#define LOG_MODULE "battery"
#include "../log.h"
#include "../bar.h"
#include "../config.h"
enum state { STATE_FULL, STATE_CHARGING, STATE_DISCHARGING };
@ -326,9 +325,8 @@ out:
return ret;
}
struct module *
module_battery(const char *battery, struct particle *label,
int poll_interval_secs)
static struct module *
battery_new(const char *battery, struct particle *label, int poll_interval_secs)
{
struct private *m = malloc(sizeof(*m));
m->label = label;
@ -344,3 +342,28 @@ module_battery(const char *battery, struct particle *label,
mod->content = &content;
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 <string.h>
#include <time.h>
@ -7,6 +6,7 @@
#include <poll.h>
#include "../bar.h"
#include "../config.h"
struct private {
struct particle *label;
@ -78,9 +78,8 @@ run(struct module_run_context *ctx)
return 0;
}
struct module *
module_clock(struct particle *label,
const char *date_format, const char *time_format)
static struct module *
clock_new(struct particle *label, const char *date_format, const char *time_format)
{
struct private *m = malloc(sizeof(*m));
m->label = label;
@ -94,3 +93,28 @@ module_clock(struct particle *label,
mod->content = &content;
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 <stdio.h>
#include <string.h>
@ -21,6 +19,7 @@
#define LOG_ENABLE_DBG 0
#include "../log.h"
#include "../bar.h"
#include "../config.h"
#include "../particles/dynlist.h"
@ -611,8 +610,14 @@ content(struct module *mod)
particles, particle_count, m->left_spacing, m->right_spacing);
}
struct module *
module_i3(struct i3_workspaces workspaces[], size_t workspace_count,
/* Maps workspace name to a content particle. */
struct i3_workspaces {
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));
@ -638,3 +643,73 @@ module_i3(struct i3_workspaces workspaces[], size_t workspace_count,
mod->content = &content;
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 <assert.h>
#include <poll.h>
#include "../config.h"
struct private {
struct particle *label;
};
@ -32,8 +32,8 @@ run(struct module_run_context *ctx)
return 0;
}
struct module *
module_label(struct particle *label)
static struct module *
label_new(struct particle *label)
{
struct private *m = malloc(sizeof(*m));
m->label = label;
@ -45,3 +45,20 @@ module_label(struct particle *label)
mod->content = &content;
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 <stdio.h>
#include <string.h>
@ -18,6 +16,7 @@
#define LOG_ENABLE_DBG 0
#include "../log.h"
#include "../bar.h"
#include "../config.h"
enum state {
STATE_OFFLINE = 1000,
@ -451,8 +450,8 @@ refresh_in(struct module *mod, long milli_seconds)
return r == 0;
}
struct module *
module_mpd(const char *host, uint16_t port, struct particle *label)
static struct module *
mpd_new(const char *host, uint16_t port, struct particle *label)
{
struct private *priv = malloc(sizeof(*priv));
priv->host = strdup(host);
@ -478,3 +477,28 @@ module_mpd(const char *host, uint16_t port, struct particle *label)
mod->refresh_in = &refresh_in;
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 <stdlib.h>
#include <string.h>
@ -17,8 +15,9 @@
#define LOG_MODULE "network"
#define LOG_ENABLE_DBG 0
#include "../log.h"
#include "../module.h"
#include "../bar.h"
#include "../config.h"
#include "../module.h"
#include "../tllist.h"
struct af_addr {
@ -141,6 +140,8 @@ netlink_connect(void)
.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) {
LOG_ERRNO("failed to bind netlink socket");
close(sock);
@ -509,8 +510,8 @@ run(struct module_run_context *ctx)
return 0;
}
struct module *
module_network(const char *iface, struct particle *label)
static struct module *
network_new(const char *iface, struct particle *label)
{
struct private *priv = malloc(sizeof(*priv));
priv->iface = strdup(iface);
@ -531,3 +532,24 @@ module_network(const char *iface, struct particle *label)
mod->content = &content;
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 <stdio.h>
#include <stdint.h>
@ -18,8 +16,9 @@
#define LOG_ENABLE_DBG 0
#include "../log.h"
#include "../bar.h"
#include "../tllist.h"
#include "../config.h"
#include "../particles/dynlist.h"
#include "../tllist.h"
typedef tll(char *) mount_point_list_t;
@ -544,8 +543,8 @@ run(struct module_run_context *ctx)
return 0;
}
struct module *
module_removables(struct particle *label, int left_spacing, int right_spacing)
static struct module *
removables_new(struct particle *label, int left_spacing, int right_spacing)
{
struct private *priv = malloc(sizeof(*priv));
priv->label = label;
@ -560,3 +559,33 @@ module_removables(struct particle *label, int left_spacing, int right_spacing)
mod->content = &content;
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 <string.h>
#include <assert.h>
@ -13,6 +11,7 @@
#define LOG_MODULE "xkb"
#include "../log.h"
#include "../bar.h"
#include "../config.h"
#include "../xcb.h"
struct layout {
@ -436,8 +435,8 @@ run(struct module_run_context *ctx)
return ret;
}
struct module *
module_xkb(struct particle *label)
static struct module *
xkb_new(struct particle *label)
{
struct private *m = malloc(sizeof(*m));
m->label = label;
@ -452,3 +451,20 @@ module_xkb(struct particle *label)
mod->content = &content;
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 <stdlib.h>
#include <string.h>
@ -17,6 +15,7 @@
#define LOG_MODULE "xkb"
#include "../log.h"
#include "../bar.h"
#include "../config.h"
#include "../xcb.h"
struct private {
@ -301,8 +300,8 @@ destroy(struct module *mod)
module_default_destroy(mod);
}
struct module *
module_xwindow(struct particle *label)
static struct module *
xwindow_new(struct particle *label)
{
struct private *m = calloc(1, sizeof(*m));
m->label = label;
@ -314,3 +313,20 @@ module_xwindow(struct particle *label)
mod->content = &content;
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);