From a0825d1d5854d56f9f3f892f05a5f71697949e1d Mon Sep 17 00:00:00 2001 From: Vftdan Date: Wed, 14 Aug 2024 22:23:21 +0200 Subject: [PATCH] Implemented configuration using libconfig --- Makefile | 3 +- config.c | 137 ++++++++++++++++++++++++++++++++++++++++++++++++ config.cfg | 29 ++++++++++ config.h | 37 +++++++++++++ defs.h | 1 + graph.c | 4 +- graph.h | 5 +- main.c | 87 +++++++++++++++++++++++++----- nodes/evdev.c | 15 +++++- nodes/getchar.c | 7 ++- nodes/print.c | 3 +- 11 files changed, 307 insertions(+), 21 deletions(-) create mode 100644 config.c create mode 100644 config.cfg create mode 100644 config.h diff --git a/Makefile b/Makefile index e6703c9..7feaec3 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,6 @@ DEPS = DEPS += libevdev +DEPS += libconfig CFLAGS = -Wall -Wextra ifdef DEBUG CFLAGS += -Og -gdwarf-2 @@ -11,7 +12,7 @@ CPPFLAGS += $(shell pkg-config --cflags $(DEPS)) LDLIBS += $(shell pkg-config --libs $(DEPS)) INTERP ?= MAIN = main -OBJS = main.o events.o processing.o graph.o nodes/getchar.o nodes/print.o nodes/evdev.o +OBJS = main.o events.o processing.o graph.o config.o nodes/getchar.o nodes/print.o nodes/evdev.o all: $(MAIN) diff --git a/config.c b/config.c new file mode 100644 index 0000000..9ccc3f2 --- /dev/null +++ b/config.c @@ -0,0 +1,137 @@ +#include +#include "config.h" + +static GraphNodeConfig +load_single_node_config(const config_setting_t *config_member) +{ + GraphNodeConfig result = { + .name = NULL, + .type = NULL, + .options = NULL, + }; + if (!config_member) { + return result; + } + result.name = config_setting_name(config_member); + if (!result.name) { + config_setting_lookup_string(config_member, "namd", &result.name); + } + config_setting_lookup_string(config_member, "type", &result.type); + result.options = config_setting_get_member(config_member, "options"); + return result; +} + +static GraphNodeConfigSection +load_nodes_section(const config_setting_t *config_section) +{ + GraphNodeConfigSection result = { + .length = 0, + .items = NULL, + }; + if (!config_section) { + return result; + } + ssize_t length = config_setting_length(config_section); + if (length <= 0) { + return result; + } + result.items = calloc(length, sizeof(GraphNodeConfig)); + if (!result.items) { + return result; + } + for (ssize_t i = 0; i < length; ++i) { + result.items[i] = load_single_node_config(config_setting_get_elem(config_section, i)); + } + result.length = length; + return result; +} + +inline static void +load_channel_end_config(const config_setting_t *config_member, const char **name_ptr, size_t *index_ptr) +{ + ssize_t length = config_setting_length(config_member); + if (length < 1) { + return; + } + if (length == 1) { + config_setting_t *kv = config_setting_get_elem(config_member, 0); + if (!kv) { + return; + } + *name_ptr = config_setting_name(config_member); + *index_ptr = config_setting_get_int64(config_member); + return; + } + *name_ptr = config_setting_get_string_elem(config_member, 0); + *index_ptr = config_setting_get_int64_elem(config_member, 1); +} + +static GraphChannelConfig +load_single_channel_config(const config_setting_t *config_member) +{ + GraphChannelConfig result = { + .from = {NULL, 0}, + .to = {NULL, 0}, + }; + if (!config_member) { + return result; + } + config_setting_t *ends[2]; + bool flip = true; + const char *flip_keys[2] = {"to", "from"}; + for (int i = 0; i < 2; ++i) { + if (!(ends[i] = config_setting_get_elem(config_member, i))) { + return result; + } + const char *name = config_setting_name(ends[i]); + if (!name || strcmp(name, flip_keys[i]) != 0) { + flip = false; + } + } + if (flip) { + config_setting_t *tmp = ends[0]; + ends[0] = ends[1]; + ends[1] = tmp; + } + load_channel_end_config(ends[0], &result.from.name, &result.from.index); + load_channel_end_config(ends[1], &result.to.name, &result.to.index); + return result; +} + +static GraphChannelConfigSection +load_channels_section(const config_setting_t *config_section) +{ + GraphChannelConfigSection result = { + .length = 0, + .items = NULL, + }; + if (!config_section) { + return result; + } + ssize_t length = config_setting_length(config_section); + if (length <= 0) { + return result; + } + result.items = calloc(length, sizeof(GraphChannelConfig)); + if (!result.items) { + return result; + } + for (ssize_t i = 0; i < length; ++i) { + result.items[i] = load_single_channel_config(config_setting_get_elem(config_section, i)); + } + result.length = length; + return result; +} + +bool +load_config(const config_setting_t *config_root, FullConfig *config) +{ + if (!config_root || !config) { + return false; + } + const config_setting_t *node_config = config_setting_get_member(config_root, "nodes"); + const config_setting_t *channel_config = config_setting_get_member(config_root, "channels"); + config->nodes = load_nodes_section(node_config); + config->channels = load_channels_section(channel_config); + return true; +} diff --git a/config.cfg b/config.cfg new file mode 100644 index 0000000..5181dbd --- /dev/null +++ b/config.cfg @@ -0,0 +1,29 @@ +nodes = { + stdin = { + type = "getchar"; + options = { + namespace = 0; + }; + }; + print = { + type = "print"; + options = {}; + }; + clickpad = { + type = "evdev"; + options = { + file = "/dev/input/event7"; + namespace = 1; + }; + }; +}; + +channels = ({ + from: ("stdin", 0); + to: ("print", 0); +}, { + from: ("clickpad", 0); + to: ("print", 1); +}); + +// vim: ft=libconfig diff --git a/config.h b/config.h new file mode 100644 index 0000000..07f1597 --- /dev/null +++ b/config.h @@ -0,0 +1,37 @@ +#ifndef CONFIG_H_ +#define CONFIG_H_ + +#include +#include "defs.h" + +typedef struct { + const char *name; + const char *type; + config_setting_t *options; +} GraphNodeConfig; + +typedef struct { + size_t length; + GraphNodeConfig *items; +} GraphNodeConfigSection; + +typedef struct { + struct { + const char *name; + size_t index; + } from, to; +} GraphChannelConfig; + +typedef struct { + size_t length; + GraphChannelConfig *items; +} GraphChannelConfigSection; + +typedef struct { + GraphNodeConfigSection nodes; + GraphChannelConfigSection channels; +} FullConfig; + +bool load_config(const config_setting_t *config_root, FullConfig *config); + +#endif /* end of include guard: CONFIG_H_ */ diff --git a/defs.h b/defs.h index b47a6c7..82c3f92 100644 --- a/defs.h +++ b/defs.h @@ -10,6 +10,7 @@ // Assuming child type has a field for the base type // So for structs it is usually actual downcast, but for unions it is an upcast #define DOWNCAST(contype, basename, ptr) containerof(ptr, contype, as_##basename) +#define lengthof(arr) (sizeof(arr) / sizeof(*arr)) #define DEBUG_PRINT_VALUE(x, fmt) fprintf(stderr, #x " = " fmt "\n", x); fflush(stderr) diff --git a/graph.c b/graph.c index 8a17aaf..7c13fb7 100644 --- a/graph.c +++ b/graph.c @@ -88,12 +88,12 @@ graph_channel_init(GraphChannel * ch, GraphNode * start, size_t start_idx, Graph } GraphNode * -graph_node_new(GraphNodeSpecification * spec) +graph_node_new(GraphNodeSpecification * spec, GraphNodeConfig * config) { if (!spec || !spec->create) { return NULL; } - return spec->create(spec); + return spec->create(spec, config); } void diff --git a/graph.h b/graph.h index 3f8c5e5..25cf6e6 100644 --- a/graph.h +++ b/graph.h @@ -3,6 +3,7 @@ #include "events.h" #include "processing.h" +#include "config.h" typedef struct graph_node GraphNode; typedef struct graph_channel GraphChannel; @@ -28,14 +29,14 @@ struct graph_channel { }; struct graph_node_specification { - GraphNode * (*create)(GraphNodeSpecification * self); + GraphNode * (*create)(GraphNodeSpecification * self, GraphNodeConfig * config); void (*destroy)(GraphNodeSpecification * self, GraphNode * target); void (*register_io)(GraphNodeSpecification * self, GraphNode * target, ProcessingState * state); char *name; }; void graph_channel_init(GraphChannel * ch, GraphNode * start, size_t start_idx, GraphNode * end, size_t end_idx); -GraphNode *graph_node_new(GraphNodeSpecification * spec); +GraphNode *graph_node_new(GraphNodeSpecification * spec, GraphNodeConfig * config); void graph_node_delete(GraphNode * self); void graph_node_register_io(GraphNode * self, ProcessingState * state); void graph_channel_list_init(GraphChannelList * lst); diff --git a/main.c b/main.c index d16d43c..2cfa696 100644 --- a/main.c +++ b/main.c @@ -3,6 +3,12 @@ #include "nodes/getchar.h" #include "nodes/evdev.h" +GraphNodeSpecification *node_specifications[] = { + &nodespec_getchar, + &nodespec_print, + &nodespec_evdev, +}; + int main(int argc, char ** argv) { @@ -16,23 +22,80 @@ main(int argc, char ** argv) io_subscription_list_init(&state.wait_input, 5); io_subscription_list_init(&state.wait_output, 5); - GraphNode * input_node = graph_node_new(&nodespec_getchar); - GraphNode * output_node = graph_node_new(&nodespec_print); - GraphNode * evdev_node = graph_node_new(&nodespec_evdev); - GraphChannel chan[2]; - graph_channel_init(&chan[0], input_node, 0, output_node, 0); - graph_channel_init(&chan[1], evdev_node, 0, output_node, 1); - graph_node_register_io(input_node, &state); - graph_node_register_io(output_node, &state); - graph_node_register_io(evdev_node, &state); + config_t config_tree; + FullConfig loaded_config; + config_init(&config_tree); + config_read_file(&config_tree, "config.cfg"); + config_set_auto_convert(&config_tree, CONFIG_TRUE); + if (!load_config(config_root_setting(&config_tree), &loaded_config)) { + perror("Failed to load config"); + exit(1); + } + + GraphNode **nodes = calloc(loaded_config.nodes.length, sizeof(GraphNode*)); + for (size_t i = 0; i < loaded_config.nodes.length; ++i) { + const char* type_name = loaded_config.nodes.items[i].type; + if (!type_name) { + fprintf(stderr, "No node type for node %ld \"%s\"\n", i, loaded_config.nodes.items[i].name); + exit(1); + } + GraphNodeSpecification *spec = NULL; + for (size_t j = 0; j < lengthof(node_specifications); ++j) { + if (strcmp(type_name, node_specifications[j]->name) == 0) { + spec = node_specifications[j]; + break; + } + } + if (!spec) { + fprintf(stderr, "Unknown node type \"%s\" for node %ld \"%s\"\n", type_name, i, loaded_config.nodes.items[i].name); + exit(1); + } + if (!(nodes[i] = graph_node_new(spec, &loaded_config.nodes.items[i]))) { + perror("Failed to create node"); + fprintf(stderr, "%ld \"%s\"\n", i, loaded_config.nodes.items[i].name); + exit(1); + } + } + + GraphChannel *channels = calloc(loaded_config.channels.length, sizeof(GraphChannel)); + for (size_t i = 0; i < loaded_config.channels.length; ++i) { + const char *node_names[2]; + GraphNode *end_nodes[2] = {NULL, NULL}; + node_names[0] = loaded_config.channels.items[i].from.name; + node_names[1] = loaded_config.channels.items[i].to.name; + for (int j = 0; j < 2; ++j) { + for (size_t k = 0; k < loaded_config.nodes.length; ++k) { + if (strcmp(loaded_config.nodes.items[k].name, node_names[j]) == 0) { + end_nodes[j] = nodes[k]; + break; + } + } + if (!end_nodes[j]) { + fprintf(stderr, "No node named \"%s\"\n", node_names[j]); + exit(1); + } + } + graph_channel_init(&channels[i], + end_nodes[0], loaded_config.channels.items[i].from.index, + end_nodes[1], loaded_config.channels.items[i].to.index + ); + } + + for (size_t i = 0; i < loaded_config.nodes.length; ++i) { + graph_node_register_io(nodes[i], &state); + } while (true) { process_iteration(&state); } - graph_node_delete(evdev_node); - graph_node_delete(output_node); - graph_node_delete(input_node); + for (ssize_t i = loaded_config.nodes.length - 1; i >= 0; --i) { + graph_node_delete(nodes[i]); + } + free(channels); + free(nodes); + + config_destroy(&config_tree); io_subscription_list_deinit(&state.wait_output); io_subscription_list_deinit(&state.wait_input); diff --git a/nodes/evdev.c b/nodes/evdev.c index da2197c..9e065f0 100644 --- a/nodes/evdev.c +++ b/nodes/evdev.c @@ -13,6 +13,7 @@ typedef struct { IOHandling subscription; struct libevdev *dev; int fd; + int namespace; } EvdevGraphNode; static void @@ -67,13 +68,22 @@ handle_io(EventPositionBase * self, int fd, bool is_output) } static GraphNode * -create(GraphNodeSpecification * spec) +create(GraphNodeSpecification * spec, GraphNodeConfig * config) { EvdevGraphNode * node = calloc(1, sizeof(EvdevGraphNode)); if (!node) { return NULL; } - int fd = open("/dev/input/event7", O_RDONLY | O_NONBLOCK); + const char *filename = NULL; + if (config) { + config_setting_lookup_int(config->options, "namespace", &node->namespace); + config_setting_lookup_string(config->options, "file", &filename); + } + if (filename == NULL) { + free(node); + return NULL; + } + int fd = open(filename, O_RDONLY | O_NONBLOCK); int err; if ((err = libevdev_new_from_fd(fd, &node->dev)) < 0) { errno = -err; @@ -97,6 +107,7 @@ create(GraphNodeSpecification * spec) }, .dev = node->dev, .fd = fd, + .namespace = node->namespace, }; return &node->as_GraphNode; } diff --git a/nodes/getchar.c b/nodes/getchar.c index da55c66..4bfff2f 100644 --- a/nodes/getchar.c +++ b/nodes/getchar.c @@ -6,6 +6,7 @@ typedef struct { GraphNode as_GraphNode; IOHandling subscription; + int32_t namespace; } GetcharGraphNode; static void @@ -47,7 +48,7 @@ handle_io(EventPositionBase * self, int fd, bool is_output) } static GraphNode * -create(GraphNodeSpecification * spec) +create(GraphNodeSpecification * spec, GraphNodeConfig * config) { GetcharGraphNode * node = calloc(1, sizeof(GetcharGraphNode)); if (!node) { @@ -68,7 +69,11 @@ create(GraphNodeSpecification * spec) .handle_io = handle_io, .enabled = true, }, + .namespace = 0, }; + if (config) { + config_setting_lookup_int(config->options, "namespace", &node->namespace); + } return &node->as_GraphNode; } diff --git a/nodes/print.c b/nodes/print.c index 1512672..4da2aa3 100644 --- a/nodes/print.c +++ b/nodes/print.c @@ -27,8 +27,9 @@ handle_event(EventPositionBase * self, EventNode * event) } static GraphNode * -create(GraphNodeSpecification * spec) +create(GraphNodeSpecification * spec, GraphNodeConfig * config) { + (void) config; GraphNode * node = calloc(1, sizeof(GraphNode)); if (!node) { return node;