Implemented configuration using libconfig

This commit is contained in:
Vftdan 2024-08-14 22:23:21 +02:00
parent a776a10d34
commit a0825d1d58
11 changed files with 307 additions and 21 deletions

View File

@ -1,5 +1,6 @@
DEPS = DEPS =
DEPS += libevdev DEPS += libevdev
DEPS += libconfig
CFLAGS = -Wall -Wextra CFLAGS = -Wall -Wextra
ifdef DEBUG ifdef DEBUG
CFLAGS += -Og -gdwarf-2 CFLAGS += -Og -gdwarf-2
@ -11,7 +12,7 @@ CPPFLAGS += $(shell pkg-config --cflags $(DEPS))
LDLIBS += $(shell pkg-config --libs $(DEPS)) LDLIBS += $(shell pkg-config --libs $(DEPS))
INTERP ?= INTERP ?=
MAIN = main 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) all: $(MAIN)

137
config.c Normal file
View File

@ -0,0 +1,137 @@
#include <string.h>
#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;
}

29
config.cfg Normal file
View File

@ -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

37
config.h Normal file
View File

@ -0,0 +1,37 @@
#ifndef CONFIG_H_
#define CONFIG_H_
#include <libconfig.h>
#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_ */

1
defs.h
View File

@ -10,6 +10,7 @@
// Assuming child type has a field for the base type // 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 // 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 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) #define DEBUG_PRINT_VALUE(x, fmt) fprintf(stderr, #x " = " fmt "\n", x); fflush(stderr)

View File

@ -88,12 +88,12 @@ graph_channel_init(GraphChannel * ch, GraphNode * start, size_t start_idx, Graph
} }
GraphNode * GraphNode *
graph_node_new(GraphNodeSpecification * spec) graph_node_new(GraphNodeSpecification * spec, GraphNodeConfig * config)
{ {
if (!spec || !spec->create) { if (!spec || !spec->create) {
return NULL; return NULL;
} }
return spec->create(spec); return spec->create(spec, config);
} }
void void

View File

@ -3,6 +3,7 @@
#include "events.h" #include "events.h"
#include "processing.h" #include "processing.h"
#include "config.h"
typedef struct graph_node GraphNode; typedef struct graph_node GraphNode;
typedef struct graph_channel GraphChannel; typedef struct graph_channel GraphChannel;
@ -28,14 +29,14 @@ struct graph_channel {
}; };
struct graph_node_specification { struct graph_node_specification {
GraphNode * (*create)(GraphNodeSpecification * self); GraphNode * (*create)(GraphNodeSpecification * self, GraphNodeConfig * config);
void (*destroy)(GraphNodeSpecification * self, GraphNode * target); void (*destroy)(GraphNodeSpecification * self, GraphNode * target);
void (*register_io)(GraphNodeSpecification * self, GraphNode * target, ProcessingState * state); void (*register_io)(GraphNodeSpecification * self, GraphNode * target, ProcessingState * state);
char *name; char *name;
}; };
void graph_channel_init(GraphChannel * ch, GraphNode * start, size_t start_idx, GraphNode * end, size_t end_idx); 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_delete(GraphNode * self);
void graph_node_register_io(GraphNode * self, ProcessingState * state); void graph_node_register_io(GraphNode * self, ProcessingState * state);
void graph_channel_list_init(GraphChannelList * lst); void graph_channel_list_init(GraphChannelList * lst);

87
main.c
View File

@ -3,6 +3,12 @@
#include "nodes/getchar.h" #include "nodes/getchar.h"
#include "nodes/evdev.h" #include "nodes/evdev.h"
GraphNodeSpecification *node_specifications[] = {
&nodespec_getchar,
&nodespec_print,
&nodespec_evdev,
};
int int
main(int argc, char ** argv) 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_input, 5);
io_subscription_list_init(&state.wait_output, 5); io_subscription_list_init(&state.wait_output, 5);
GraphNode * input_node = graph_node_new(&nodespec_getchar); config_t config_tree;
GraphNode * output_node = graph_node_new(&nodespec_print); FullConfig loaded_config;
GraphNode * evdev_node = graph_node_new(&nodespec_evdev); config_init(&config_tree);
GraphChannel chan[2]; config_read_file(&config_tree, "config.cfg");
graph_channel_init(&chan[0], input_node, 0, output_node, 0); config_set_auto_convert(&config_tree, CONFIG_TRUE);
graph_channel_init(&chan[1], evdev_node, 0, output_node, 1); if (!load_config(config_root_setting(&config_tree), &loaded_config)) {
graph_node_register_io(input_node, &state); perror("Failed to load config");
graph_node_register_io(output_node, &state); exit(1);
graph_node_register_io(evdev_node, &state); }
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) { while (true) {
process_iteration(&state); process_iteration(&state);
} }
graph_node_delete(evdev_node); for (ssize_t i = loaded_config.nodes.length - 1; i >= 0; --i) {
graph_node_delete(output_node); graph_node_delete(nodes[i]);
graph_node_delete(input_node); }
free(channels);
free(nodes);
config_destroy(&config_tree);
io_subscription_list_deinit(&state.wait_output); io_subscription_list_deinit(&state.wait_output);
io_subscription_list_deinit(&state.wait_input); io_subscription_list_deinit(&state.wait_input);

View File

@ -13,6 +13,7 @@ typedef struct {
IOHandling subscription; IOHandling subscription;
struct libevdev *dev; struct libevdev *dev;
int fd; int fd;
int namespace;
} EvdevGraphNode; } EvdevGraphNode;
static void static void
@ -67,13 +68,22 @@ handle_io(EventPositionBase * self, int fd, bool is_output)
} }
static GraphNode * static GraphNode *
create(GraphNodeSpecification * spec) create(GraphNodeSpecification * spec, GraphNodeConfig * config)
{ {
EvdevGraphNode * node = calloc(1, sizeof(EvdevGraphNode)); EvdevGraphNode * node = calloc(1, sizeof(EvdevGraphNode));
if (!node) { if (!node) {
return NULL; 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; int err;
if ((err = libevdev_new_from_fd(fd, &node->dev)) < 0) { if ((err = libevdev_new_from_fd(fd, &node->dev)) < 0) {
errno = -err; errno = -err;
@ -97,6 +107,7 @@ create(GraphNodeSpecification * spec)
}, },
.dev = node->dev, .dev = node->dev,
.fd = fd, .fd = fd,
.namespace = node->namespace,
}; };
return &node->as_GraphNode; return &node->as_GraphNode;
} }

View File

@ -6,6 +6,7 @@
typedef struct { typedef struct {
GraphNode as_GraphNode; GraphNode as_GraphNode;
IOHandling subscription; IOHandling subscription;
int32_t namespace;
} GetcharGraphNode; } GetcharGraphNode;
static void static void
@ -47,7 +48,7 @@ handle_io(EventPositionBase * self, int fd, bool is_output)
} }
static GraphNode * static GraphNode *
create(GraphNodeSpecification * spec) create(GraphNodeSpecification * spec, GraphNodeConfig * config)
{ {
GetcharGraphNode * node = calloc(1, sizeof(GetcharGraphNode)); GetcharGraphNode * node = calloc(1, sizeof(GetcharGraphNode));
if (!node) { if (!node) {
@ -68,7 +69,11 @@ create(GraphNodeSpecification * spec)
.handle_io = handle_io, .handle_io = handle_io,
.enabled = true, .enabled = true,
}, },
.namespace = 0,
}; };
if (config) {
config_setting_lookup_int(config->options, "namespace", &node->namespace);
}
return &node->as_GraphNode; return &node->as_GraphNode;
} }

View File

@ -27,8 +27,9 @@ handle_event(EventPositionBase * self, EventNode * event)
} }
static GraphNode * static GraphNode *
create(GraphNodeSpecification * spec) create(GraphNodeSpecification * spec, GraphNodeConfig * config)
{ {
(void) config;
GraphNode * node = calloc(1, sizeof(GraphNode)); GraphNode * node = calloc(1, sizeof(GraphNode));
if (!node) { if (!node) {
return node; return node;