Implemented configuration using libconfig
This commit is contained in:
parent
a776a10d34
commit
a0825d1d58
3
Makefile
3
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)
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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
1
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)
|
||||
|
||||
|
|
4
graph.c
4
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
|
||||
|
|
5
graph.h
5
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);
|
||||
|
|
87
main.c
87
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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue