diff --git a/Makefile b/Makefile index 07c0e22..fdeeae8 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -DEPS = +DEPS = libevdev CFLAGS = -Wall -Wextra ifdef DEBUG CFLAGS += -Og -gdwarf-2 @@ -10,7 +10,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 +OBJS = main.o events.o processing.o graph.o nodes/getchar.o nodes/print.o nodes/evdev.o all: $(MAIN) diff --git a/main.c b/main.c index a7d0002..d16d43c 100644 --- a/main.c +++ b/main.c @@ -1,6 +1,7 @@ #include "processing.h" #include "nodes/print.h" #include "nodes/getchar.h" +#include "nodes/evdev.h" int main(int argc, char ** argv) @@ -17,15 +18,19 @@ main(int argc, char ** argv) GraphNode * input_node = graph_node_new(&nodespec_getchar); GraphNode * output_node = graph_node_new(&nodespec_print); - GraphChannel chan; - graph_channel_init(&chan, input_node, 0, output_node, 0); + 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); while (true) { process_iteration(&state); } + graph_node_delete(evdev_node); graph_node_delete(output_node); graph_node_delete(input_node); diff --git a/nodes/evdev.c b/nodes/evdev.c new file mode 100644 index 0000000..d56c8a4 --- /dev/null +++ b/nodes/evdev.c @@ -0,0 +1,129 @@ +#include +#include +#include +#include +#include +#include +#include +#include "evdev.h" +#include "../processing.h" + +typedef struct { + GraphNode as_GraphNode; + IOHandling subscription; + struct libevdev *dev; + int fd; +} EvdevGraphNode; + +static void +handle_io(EventPositionBase * self, int fd, bool is_output) +{ + (void) is_output; + (void) fd; + EvdevGraphNode * node = DOWNCAST(EvdevGraphNode, GraphNode, DOWNCAST(GraphNode, EventPositionBase, self)); + int err = 0; + AbsoluteTime realtime; + clock_gettime(CLOCK_REALTIME, &realtime.absolute); + AbsoluteTime monotime = get_current_time(); + RelativeTime realtime_adj = absolute_time_sub_absolute(realtime, monotime); + while (!err) { + struct input_event buf; + err = libevdev_next_event(node->dev, LIBEVDEV_READ_FLAG_NORMAL, &buf); + if (err) { + if (err == -EAGAIN || err == 1) { + break; + } + node->subscription.enabled = false; + if (err < 0) { + errno = -err; + perror("Failed to read evdev event"); + } + break; + } + realtime.absolute.tv_sec = buf.time.tv_sec; + realtime.absolute.tv_nsec = buf.time.tv_usec * (long) 1000; + monotime = absolute_time_sub_relative(realtime, realtime_adj); + EventData data = { + .code = { + .ns = 1, + .value = (((int32_t) buf.type) << 16) | buf.code, + }, + .ttl = 100, + .priority = 10, + .payload = buf.value, + .modifiers = EMPTY_MODIFIER_SET, + .time = monotime, + }; + for (size_t i = 0; i < node->as_GraphNode.outputs.length; ++i) { + EventNode *ev = event_create(&data); + if (!ev) { + perror("Failed to create event"); + break; + } + ev->position = &node->as_GraphNode.outputs.elements[i]->as_EventPositionBase; + } + } +} + +static GraphNode * +create(GraphNodeSpecification * spec) +{ + EvdevGraphNode * node = calloc(1, sizeof(EvdevGraphNode)); + if (!node) { + return NULL; + } + int fd = open("/dev/input/event7", O_RDONLY | O_NONBLOCK); + int err; + if ((err = libevdev_new_from_fd(fd, &node->dev)) < 0) { + errno = -err; + free(node); + return NULL; + } + *node = (EvdevGraphNode) { + .as_GraphNode = { + .as_EventPositionBase = { + .handle_event = NULL, + .waiting_new_event = false, + }, + .specification = spec, + .inputs = EMPTY_GRAPH_CHANNEL_LIST, + .outputs = EMPTY_GRAPH_CHANNEL_LIST, + }, + .subscription = { + .self = &node->as_GraphNode.as_EventPositionBase, + .handle_io = handle_io, + .enabled = true, + }, + .dev = node->dev, + .fd = fd, + }; + return &node->as_GraphNode; +} + +static void destroy +(GraphNodeSpecification * self, GraphNode * target) +{ + (void) self; + EvdevGraphNode * node = DOWNCAST(EvdevGraphNode, GraphNode, target); + if (node->dev) { + libevdev_free(node->dev); + node->dev = NULL; + close(node->fd); + } + free(target); +} + +static void +register_io(GraphNodeSpecification * self, GraphNode * target, ProcessingState * state) +{ + (void) self; + EvdevGraphNode * node = DOWNCAST(EvdevGraphNode, GraphNode, target); + io_subscription_list_add(&state->wait_input, node->fd, &node->subscription); +} + +GraphNodeSpecification nodespec_evdev = (GraphNodeSpecification) { + .create = &create, + .destroy = &destroy, + .register_io = ®ister_io, + .name = "evdev", +}; diff --git a/nodes/evdev.h b/nodes/evdev.h new file mode 100644 index 0000000..bde6ba3 --- /dev/null +++ b/nodes/evdev.h @@ -0,0 +1,8 @@ +#ifndef NODES_EVDEV_H_ +#define NODES_EVDEV_H_ + +#include "../graph.h" + +extern GraphNodeSpecification nodespec_evdev; + +#endif /* end of include guard: NODES_EVDEV_H_ */