Compare commits

..

5 Commits

Author SHA1 Message Date
Vftdan e2ea8fed1e Add "documentation" field to node specification
Use it as node type help message
2024-08-18 15:26:54 +02:00
Vftdan 8b55223b9d Small fixes
- Fix parseable string of MODOP_TOGGLE
 - Actually set the configured namespace in nodes/evdev
 - Ensure non-negative payload in nodes/getchar
2024-08-18 15:23:49 +02:00
Vftdan 01bf0a9608 Implement command-line arguments 2024-08-18 14:45:53 +02:00
Vftdan 5969a1df8e Expose module registry 2024-08-18 14:44:39 +02:00
Vftdan 68a189dba1 Automatically call module registry cleanup on exit 2024-08-18 14:43:19 +02:00
12 changed files with 122 additions and 10 deletions

View File

@ -33,6 +33,7 @@ struct graph_node_specification {
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;
char *documentation;
}; };
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);

84
main.c
View File

@ -1,12 +1,89 @@
#include <getopt.h>
#include <stdio.h>
#include "processing.h" #include "processing.h"
#include "hash_table.h" #include "hash_table.h"
#include "module_registry.h" #include "module_registry.h"
union __attribute__((transparent_union)) option_ident {
enum {
NCOPT_BASE = 0xFF,
NCOPT_MODULE_HELP,
} as_nonchar;
int as_int;
// No "as_char" field, because it can cause problems on big-endian CPUs
};
int int
main(int argc, char ** argv) main(int argc, char ** argv)
{ {
(void)argc; const char* config_filename = "config.cfg";
(void)argv;
while (true) {
static const struct option long_options [] = {
{"config", required_argument, NULL, 'c'},
{"help", no_argument, NULL, 'h'},
{"list-modules", no_argument, NULL, 'l'},
{"module-help", required_argument, NULL, NCOPT_MODULE_HELP},
{NULL, 0, NULL, 0}};
static const char help_fstring[] =
"Usage: %s <[options...]>\n"
"Options:\n"
"\t--config <filename>, -c <filename> read configuration from <filename>\n"
"\t instead of ./config.cfg\n"
"\t--help, -h show this message\n"
"\t--list-modules, -l list currently loaded node types\n"
"\t--module-help <name> print help information provided for node type <name>\n"
;
union option_ident opt = {.as_int = getopt_long(argc, argv, "c:hl", long_options, NULL)};
if (opt.as_int < 0) {
break;
}
switch (opt.as_int) {
case 'c':
config_filename = optarg;
break;
case 'h':
printf(help_fstring, argv[0]);
return 0;
case 'l':
{
const GraphNodeSpecificationRegistry * reg = get_graph_node_specification_registy();
for (size_t i = 0; i < reg->capacity; ++i) {
if (!reg->key_array[i].key.bytes) {
continue;
}
printf("%.*s\n", (int) reg->key_array[i].key.length, reg->key_array[i].key.bytes);
}
};
return 0;
case NCOPT_MODULE_HELP:
{
GraphNodeSpecification *spec = lookup_graph_node_specification(optarg);
if (!spec) {
fprintf(stderr, "Unknown node type \"%s\"\n", optarg);
return 1;
}
const char* module_help = spec->documentation;
if (module_help) {
printf("Help for node type \"%s\":\n", optarg);
printf("%s\n", module_help);
} else {
printf("No help provided for node type \"%s\"\n", optarg);
}
};
return 0;
default:
fprintf(stderr, "Unexpected option ");
if ((unsigned int) opt.as_int <= 0xFF) {
fprintf(stderr, "'%c'\n", (char) opt.as_int);
} else {
fprintf(stderr, "%d\n", opt.as_int);
}
return 1;
}
}
ProcessingState state = (ProcessingState) { ProcessingState state = (ProcessingState) {
.wait_delay = NULL, .wait_delay = NULL,
@ -18,7 +95,7 @@ main(int argc, char ** argv)
config_t config_tree; config_t config_tree;
FullConfig loaded_config; FullConfig loaded_config;
config_init(&config_tree); config_init(&config_tree);
config_read_file(&config_tree, "config.cfg"); config_read_file(&config_tree, config_filename);
config_set_auto_convert(&config_tree, CONFIG_TRUE); config_set_auto_convert(&config_tree, CONFIG_TRUE);
if (!load_config(config_root_setting(&config_tree), &loaded_config)) { if (!load_config(config_root_setting(&config_tree), &loaded_config)) {
perror("Failed to load config"); perror("Failed to load config");
@ -92,6 +169,5 @@ main(int argc, char ** argv)
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);
destroy_graph_node_specification_registry();
return 0; return 0;
} }

View File

@ -199,7 +199,7 @@ modifier_operation_parse(const char* name)
if (strcmp(name, "reset") == 0) { if (strcmp(name, "reset") == 0) {
return MODOP_UNSET; return MODOP_UNSET;
} }
if (strcmp(name, "togggle") == 0) { if (strcmp(name, "toggle") == 0) {
return MODOP_TOGGLE; return MODOP_TOGGLE;
} }
return -1; return -1;

View File

@ -1,7 +1,6 @@
#include "module_registry.h" #include "module_registry.h"
#include "hash_table.h"
static TYPED_HASH_TABLE(GraphNodeSpecification*) registry; static GraphNodeSpecificationRegistry registry;
static bool initialized = false; static bool initialized = false;
static void static void
@ -34,7 +33,14 @@ lookup_graph_node_specification(const char * name)
return registry.value_array[idx]; return registry.value_array[idx];
} }
void const GraphNodeSpecificationRegistry *
get_graph_node_specification_registy()
{
ensure_initialized();
return &registry;
}
__attribute__((destructor)) void
destroy_graph_node_specification_registry() destroy_graph_node_specification_registry()
{ {
ensure_initialized(); ensure_initialized();

View File

@ -2,9 +2,13 @@
#define MODULE_REGISTRY_H_ #define MODULE_REGISTRY_H_
#include "graph.h" #include "graph.h"
#include "hash_table.h"
typedef TYPED_HASH_TABLE(GraphNodeSpecification*) GraphNodeSpecificationRegistry;
void register_graph_node_specification(GraphNodeSpecification * spec); void register_graph_node_specification(GraphNodeSpecification * spec);
GraphNodeSpecification * lookup_graph_node_specification(const char * name); GraphNodeSpecification * lookup_graph_node_specification(const char * name);
void destroy_graph_node_specification_registry(); void destroy_graph_node_specification_registry();
const GraphNodeSpecificationRegistry * get_graph_node_specification_registy();
#endif /* end of include guard: MODULE_REGISTRY_H_ */ #endif /* end of include guard: MODULE_REGISTRY_H_ */

View File

@ -47,7 +47,7 @@ handle_io(EventPositionBase * self, int fd, bool is_output)
monotime = absolute_time_sub_relative(realtime, realtime_adj); monotime = absolute_time_sub_relative(realtime, realtime_adj);
EventData data = { EventData data = {
.code = { .code = {
.ns = 1, .ns = node->namespace,
.major = buf.type, .major = buf.type,
.minor = buf.code, .minor = buf.code,
}, },
@ -139,6 +139,10 @@ GraphNodeSpecification nodespec_evdev = (GraphNodeSpecification) {
.destroy = &destroy, .destroy = &destroy,
.register_io = &register_io, .register_io = &register_io,
.name = "evdev", .name = "evdev",
.documentation = "Reads evdev events of the specified device\nDoes not accept events\nSends events on all connectors with major code, minor code, payload respectively set to evdev event type, code, value"
"\nOption 'namespace' (optional): set namespace for the generated events"
"\nOption 'file' (required): device file to read events from (like '/dev/input/eventN'), the process must have sufficient privileges to read the file"
,
}; };
MODULE_CONSTRUCTOR(init) MODULE_CONSTRUCTOR(init)

View File

@ -29,7 +29,7 @@ handle_io(EventPositionBase * self, int fd, bool is_output)
}, },
.ttl = 100, .ttl = 100,
.priority = 10, .priority = 10,
.payload = buf[0], .payload = (unsigned char) buf[0],
.modifiers = EMPTY_MODIFIER_SET, .modifiers = EMPTY_MODIFIER_SET,
.time = get_current_time(), .time = get_current_time(),
}; };
@ -95,6 +95,9 @@ GraphNodeSpecification nodespec_getchar = (GraphNodeSpecification) {
.destroy = &destroy, .destroy = &destroy,
.register_io = &register_io, .register_io = &register_io,
.name = "getchar", .name = "getchar",
.documentation = "Converts stdin bytes to events\nDoes not accept events\nSends events on all connectors with major and minor codes (0, 1) and the read byte as payload"
"\nOption 'namespace' (optional): set namespace for the generated events"
,
}; };
MODULE_CONSTRUCTOR(init) MODULE_CONSTRUCTOR(init)

View File

@ -91,6 +91,10 @@ GraphNodeSpecification nodespec_modifiers = (GraphNodeSpecification) {
.destroy = &destroy, .destroy = &destroy,
.register_io = NULL, .register_io = NULL,
.name = "modifiers", .name = "modifiers",
.documentation = "Sets/unsets/toggles modifiers in an event\nAccepts events on any connector\nSends events on all connectors"
"\nOption 'operation' (required): the operation to apply to the event modifier set ('set'/'unset'/'toggle')"
"\nOption 'modifiers' (required): collection of integers --- the set of modifiers to operate on"
,
}; };
MODULE_CONSTRUCTOR(init) MODULE_CONSTRUCTOR(init)

View File

@ -94,6 +94,13 @@ GraphNodeSpecification nodespec_modify_predicate = (GraphNodeSpecification) {
.destroy = &destroy, .destroy = &destroy,
.register_io = NULL, .register_io = NULL,
.name = "modify_predicate", .name = "modify_predicate",
.documentation = "Changes 'enabled' and 'inverted' flags of a predicate\nAccepts events on any connector\nDoes not send events"
"\nOption 'target' (required): the predicate to modify"
"\nOption 'enable_on' (optional): the predicate, satisfying events of which set 'enabled' flag of the target predicate to 1"
"\nOption 'disable_on' (optional): the predicate, satisfying events of which set 'enabled' flag of the target predicate to 0"
"\nOption 'invert_on' (optional): the predicate, satisfying events of which set 'inverted' flag of the target predicate to 1"
"\nOption 'uninvert_on' (optional): the predicate, satisfying events of which set 'inverted' flag of the target predicate to 0"
,
}; };
MODULE_CONSTRUCTOR(init) MODULE_CONSTRUCTOR(init)

View File

@ -60,6 +60,8 @@ GraphNodeSpecification nodespec_print = (GraphNodeSpecification) {
.destroy = &destroy, .destroy = &destroy,
.register_io = NULL, .register_io = NULL,
.name = "print", .name = "print",
.documentation = "Prints received events\nAccepts events on any connector\nDoes not send events"
,
}; };
MODULE_CONSTRUCTOR(init) MODULE_CONSTRUCTOR(init)

View File

@ -88,6 +88,9 @@ GraphNodeSpecification nodespec_router = (GraphNodeSpecification) {
.destroy = &destroy, .destroy = &destroy,
.register_io = NULL, .register_io = NULL,
.name = "router", .name = "router",
.documentation = "Conditionally copies the received events\nAccepts events on any connector\nSends events on all connectors with configured predicates"
"\nOption 'predicates' (required): collection of predicates in the order of output connectors from zero, a received event is copied to the given connector iff it satisfies the predicate"
,
}; };
MODULE_CONSTRUCTOR(init) MODULE_CONSTRUCTOR(init)

View File

@ -53,6 +53,8 @@ GraphNodeSpecification nodespec_tee = (GraphNodeSpecification) {
.destroy = &destroy, .destroy = &destroy,
.register_io = NULL, .register_io = NULL,
.name = "tee", .name = "tee",
.documentation = "Copies the received events\nAccepts events on any connector\nSends events on all connectors"
,
}; };
MODULE_CONSTRUCTOR(init) MODULE_CONSTRUCTOR(init)