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