summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNathanael Sensfelder <SpamShield0@MultiAgentSystems.org>2019-12-23 15:44:19 +0100
committerNathanael Sensfelder <SpamShield0@MultiAgentSystems.org>2019-12-23 15:44:19 +0100
commit390576c3839ee7abb845e27b7267de45495e6b2f (patch)
treec481c37c868ccc65a3476f60b17369b21a90b79b /src/device
parent4355548f79375a62bb5e3bb5695190d48e4c0bc3 (diff)
Starting to turn relabsd into a proper daemon...
Diffstat (limited to 'src/device')
-rw-r--r--src/device/axis/axis_name.c302
-rw-r--r--src/device/physical_device.c276
-rw-r--r--src/device/virtual_device.c272
3 files changed, 850 insertions, 0 deletions
diff --git a/src/device/axis/axis_name.c b/src/device/axis/axis_name.c
new file mode 100644
index 0000000..c18a28f
--- /dev/null
+++ b/src/device/axis/axis_name.c
@@ -0,0 +1,302 @@
+/**** LIBEVDEV ****************************************************************/
+#include <libevdev/libevdev.h>
+
+/**** RELABSD *****************************************************************/
+#include <relabsd/debug.h>
+
+#include <relabsd/util/string.h>
+
+#include <relabsd/device/axis.h>
+
+/******************************************************************************/
+/**** LOCAL FUNCTIONS *********************************************************/
+/******************************************************************************/
+
+/******************************************************************************/
+/**** EXPORTED FUNCTIONS ******************************************************/
+/******************************************************************************/
+enum relabsd_axis_name relabsd_axis_parse_name
+(
+ const char name [const restrict static 1]
+)
+{
+ if (RELABSD_STRING_EQUALS("X", name))
+ {
+ return RELABSD_X;
+ }
+ else if (RELABSD_STRING_EQUALS("Y", name))
+ {
+ return RELABSD_Y;
+ }
+ else if (RELABSD_STRING_EQUALS("Z", name))
+ {
+ return RELABSD_Z;
+ }
+ else if (RELABSD_STRING_EQUALS("RX", name))
+ {
+ return RELABSD_RX;
+ }
+ else if (RELABSD_STRING_EQUALS("RY", name))
+ {
+ return RELABSD_RY;
+ }
+ else if (RELABSD_STRING_EQUALS("RZ", name))
+ {
+ return RELABSD_RZ;
+ }
+ else if (RELABSD_STRING_EQUALS("WL", name))
+ {
+ return RELABSD_WHEEL;
+ }
+ else if (RELABSD_STRING_EQUALS("MC", name))
+ {
+ return RELABSD_MISC;
+ }
+
+ return RELABSD_UNKNOWN;
+}
+
+const char * relabsd_axis_name_to_string (const enum relabsd_axis_name e)
+{
+ switch (e)
+ {
+ case RELABSD_X:
+ return "X";
+
+ case RELABSD_Y:
+ return "Y";
+
+ case RELABSD_Z:
+ return "Z";
+
+ case RELABSD_RX:
+ return "RX";
+
+ case RELABSD_RY:
+ return "RY";
+
+ case RELABSD_RZ:
+ return "RZ";
+
+ case RELABSD_WHEEL:
+ return "WL";
+
+ case RELABSD_MISC:
+ return "MC";
+
+ case RELABSD_UNKNOWN:
+ return "??";
+
+ default:
+ break;
+ }
+
+ RELABSD_S_PROG_ERROR("relabsd_axis_to_name is missing at least 1 case.");
+
+ return "..";
+}
+
+enum relabsd_axis_name relabsd_axis_name_and_evdev_abs_from_evdev_rel
+(
+ const unsigned int rel_code,
+ unsigned int abs_code [const restrict static 1]
+)
+{
+ switch (rel_code)
+ {
+ case REL_X:
+ *abs_code = ABS_X;
+ return RELABSD_X;
+
+ case REL_Y:
+ *abs_code = ABS_Y;
+ return RELABSD_Y;
+
+ case REL_Z:
+ *abs_code = ABS_Z;
+ return RELABSD_Z;
+
+ case REL_RX:
+ *abs_code = ABS_RX;
+ return RELABSD_RX;
+
+ case REL_RY:
+ *abs_code = ABS_RY;
+ return RELABSD_RY;
+
+ case REL_RZ:
+ *abs_code = ABS_RZ;
+ return RELABSD_RZ;
+
+ case REL_WHEEL:
+ *abs_code = ABS_WHEEL;
+ return RELABSD_WHEEL;
+
+ case REL_MISC:
+ *abs_code = ABS_MISC;
+ return RELABSD_MISC;
+
+ default:
+ return RELABSD_UNKNOWN;
+ }
+}
+
+unsigned int relabsd_axis_name_to_evdev_rel (const enum relabsd_axis_name e)
+{
+ switch (e)
+ {
+ case RELABSD_X:
+ return REL_X;
+
+ case RELABSD_Y:
+ return REL_Y;
+
+ case RELABSD_Z:
+ return REL_Z;
+
+ case RELABSD_RX:
+ return REL_RX;
+
+ case RELABSD_RY:
+ return REL_RY;
+
+ case RELABSD_RZ:
+ return REL_RZ;
+
+ case RELABSD_WHEEL:
+ return REL_WHEEL;
+
+ case RELABSD_MISC:
+ return REL_MISC;
+
+ case RELABSD_UNKNOWN:
+ RELABSD_S_PROG_ERROR
+ (
+ "relabsd_axis_name_to_evdev_rel(RELABSD_UNKNOWN) is forbidden."
+ );
+ return REL_MAX;
+
+ default:
+ break;
+ }
+
+ RELABSD_S_PROG_ERROR
+ (
+ "relabsd_axis_name_to_evdev_rel is missing at least 1 case."
+ );
+
+ return REL_MAX;
+}
+
+unsigned int relabsd_axis_name_to_evdev_abs (const enum relabsd_axis_name e)
+{
+ switch (e)
+ {
+ case RELABSD_X:
+ return ABS_X;
+
+ case RELABSD_Y:
+ return ABS_Y;
+
+ case RELABSD_Z:
+ return ABS_Z;
+
+ case RELABSD_RX:
+ return ABS_RX;
+
+ case RELABSD_RY:
+ return ABS_RY;
+
+ case RELABSD_RZ:
+ return ABS_RZ;
+
+ case RELABSD_WHEEL:
+ return ABS_WHEEL;
+
+ case RELABSD_MISC:
+ return ABS_MISC;
+
+ case RELABSD_UNKNOWN:
+ RELABSD_S_PROG_ERROR
+ (
+ "relabsd_axis_to_abs(RELABSD_UNKNOWN) is forbidden."
+ );
+ return ABS_MAX;
+
+ default:
+ break;
+ }
+
+ RELABSD_S_PROG_ERROR("relabsd_axis_to_abs is missing at least 1 case.");
+
+ return REL_MAX;
+}
+
+/*
+ * Returns the relabsd_axis equivalent of a EV_REL/EV_ABS code.
+ */
+enum relabsd_axis_name relabsd_axis_name_from_evdev_rel (const unsigned int rel)
+{
+ switch (rel)
+ {
+ case REL_X:
+ return RELABSD_X;
+
+ case REL_Y:
+ return RELABSD_Y;
+
+ case REL_Z:
+ return RELABSD_Z;
+
+ case REL_RX:
+ return RELABSD_RX;
+
+ case REL_RY:
+ return RELABSD_RY;
+
+ case REL_RZ:
+ return RELABSD_RZ;
+
+ case REL_WHEEL:
+ return RELABSD_WHEEL;
+
+ case REL_MISC:
+ return RELABSD_MISC;
+
+ default:
+ return RELABSD_UNKNOWN;
+ }
+}
+
+enum relabsd_axis_name relabsd_axis_name_from_evdev_abs (const unsigned int abs)
+{
+ switch (abs)
+ {
+ case ABS_X:
+ return RELABSD_X;
+
+ case ABS_Y:
+ return RELABSD_Y;
+
+ case ABS_Z:
+ return RELABSD_Z;
+
+ case ABS_RX:
+ return RELABSD_RX;
+
+ case ABS_RY:
+ return RELABSD_RY;
+
+ case ABS_RZ:
+ return RELABSD_RZ;
+
+ case ABS_WHEEL:
+ return RELABSD_WHEEL;
+
+ case ABS_MISC:
+ return RELABSD_MISC;
+
+ default:
+ return RELABSD_UNKNOWN;
+ }
+}
diff --git a/src/device/physical_device.c b/src/device/physical_device.c
new file mode 100644
index 0000000..a518750
--- /dev/null
+++ b/src/device/physical_device.c
@@ -0,0 +1,276 @@
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <libevdev/libevdev.h>
+
+#include "error.h"
+#include "axis.h"
+#include "config.h"
+
+#include "input.h"
+
+/*
+ * Ensures that the input device has enabled the EV_REL axes mentioned
+ * in the configuration file.
+ *
+ * Returns -1 on (fatal) error,
+ * 0 all configured axes are accounted for.
+ */
+static int check_for_axes
+(
+ const struct libevdev * const dev,
+ const struct relabsd_config * const conf
+)
+{
+ int i, device_is_valid;
+ unsigned int rel_code;
+
+ device_is_valid = 1;
+
+ for (i = RELABSD_VALID_AXES_COUNT; i --> 0;)
+ {
+ if (conf->axis[i].enabled)
+ {
+ rel_code = relabsd_axis_to_rel((enum relabsd_axis) i);
+
+ if (!libevdev_has_event_code(dev, EV_REL, rel_code))
+ {
+ RELABSD_FATAL
+ (
+ "Input device has no relative %s axis, yet the configuration "
+ "file asks to convert it.",
+ relabsd_axis_to_name((enum relabsd_axis) i)
+ );
+
+ device_is_valid = 0;
+ }
+ }
+ }
+
+ return (device_is_valid - 1);
+}
+
+/*
+ * Ensures that the input device is compatible with the config file.
+ *
+ * Returns -1 on (fatal) error,
+ * 0 is the device is compatible.
+ */
+static int device_is_compatible
+(
+ const struct libevdev * const dev,
+ const struct relabsd_config * const conf
+)
+{
+ if (!libevdev_has_event_type(dev, EV_REL))
+ {
+ RELABSD_S_FATAL("Input device has no relative axis.");
+
+ return -1;
+ }
+
+ if (check_for_axes(dev, conf) < 0)
+ {
+ return -1;
+ }
+
+ return 0;
+}
+
+int relabsd_physical_device_open
+(
+ struct relabsd_physical_device * const input,
+ const struct relabsd_config * const conf
+)
+{
+ RELABSD_S_DEBUG(RELABSD_DEBUG_PROGRAM_FLOW, "Opening input device...");
+
+ input->fd = open(conf->input_file, O_RDONLY);
+
+ if (input->fd < 0)
+ {
+ RELABSD_FATAL
+ (
+ "Could not open device '%s' in read only mode: %s.",
+ conf->input_file,
+ strerror(errno)
+ );
+
+ return -1;
+ }
+
+ if
+ (
+ libevdev_new_from_fd(input->fd, &(input->dev)) < 0
+ )
+ {
+ RELABSD_FATAL
+ (
+ "libevdev could not open '%s': %s.",
+ conf->input_file,
+ strerror(errno)
+ );
+
+ close(input->fd);
+
+ return -1;
+ }
+
+ if (device_is_compatible(input->dev, conf) < 0)
+ {
+ return -1;
+ }
+
+ return 0;
+}
+
+void relabsd_physical_device_close (const struct relabsd_physical_device * const input)
+{
+ RELABSD_S_DEBUG(RELABSD_DEBUG_PROGRAM_FLOW, "Closing input device...");
+
+ libevdev_free(input->dev);
+ close(input->fd);
+}
+
+int relabsd_physical_device_read
+(
+ const struct relabsd_physical_device * const input,
+ unsigned int * const input_type,
+ unsigned int * const input_code,
+ int * const input_value
+)
+{
+ int rc;
+ struct input_event event;
+
+ /*
+ if (libevdev_has_event_pending(input->dev) == 0)
+ {
+ return -1;
+ }
+ */
+ rc =
+ libevdev_next_event
+ (
+ input->dev,
+ (LIBEVDEV_READ_FLAG_NORMAL | LIBEVDEV_READ_FLAG_BLOCKING),
+ &event
+ );
+
+ /* TODO: Look into LIBEVDEV_READ_STATUS_SYNC, handle it. */
+ /*
+ if (rc == LIBEVDEV_READ_STATUS_SYNC)
+ {
+ handle_syn_dropped(input->dev);
+ }
+ else
+ */
+ if (rc != LIBEVDEV_READ_STATUS_SUCCESS)
+ {
+ RELABSD_WARNING("[INPUT] Could not get next event: %s.", strerror(-rc));
+
+ return -1;
+ }
+
+ RELABSD_DEBUG
+ (
+ RELABSD_DEBUG_REAL_EVENTS,
+ "Valid event received: {type = %s; code = %s; value = %d}.",
+ libevdev_event_type_get_name(event.type),
+ libevdev_event_code_get_name(event.type, event.code),
+ event.value
+ );
+
+ *input_type = event.type;
+ *input_code = event.code;
+ *input_value = event.value;
+
+ return 0;
+}
+
+int relabsd_physical_device_wait_for_next_event
+(
+ const struct relabsd_physical_device * const input,
+ const struct relabsd_config * const config
+)
+{
+ int ready_fds;
+ const int old_errno = errno;
+ fd_set ready_to_read;
+ struct timeval curr_timeout;
+
+ FD_ZERO(&ready_to_read);
+ FD_SET(input->fd, &ready_to_read);
+
+ /* call to select may alter timeout */
+ memcpy
+ (
+ (void *) &(curr_timeout),
+ (const void *) &(config->timeout),
+ sizeof(struct timeval)
+ );
+
+ errno = 0;
+
+ RELABSD_S_ERROR
+ (
+ "Waiting for input to be ready..."
+ );
+
+ ready_fds = select
+ (
+ (input->fd + 1),
+ &ready_to_read,
+ (fd_set *) NULL,
+ (fd_set *) NULL,
+ (input->timed_out) ? NULL : &(curr_timeout)
+ );
+
+ if (errno != 0)
+ {
+ RELABSD_ERROR
+ (
+ "Unable to wait for timeout: %s (errno: %d).",
+ strerror(errno),
+ errno
+ );
+
+ if (errno == EINTR)
+ {
+ /* Signal interruption? */
+ }
+ else
+ {
+ /* TODO: error message */
+ }
+
+ errno = old_errno;
+
+ return -1;
+ }
+
+ if (ready_fds == -1)
+ {
+ /* TODO: error message */
+
+ RELABSD_S_ERROR
+ (
+ "Unable to wait for timeout, yet errno was not set to anything."
+ );
+
+ errno = old_errno;
+
+ return -1;
+ }
+
+ RELABSD_ERROR
+ (
+ "Input is ready, ready_fds = %d", ready_fds
+ );
+
+ errno = old_errno;
+
+ return ready_fds;
+}
diff --git a/src/device/virtual_device.c b/src/device/virtual_device.c
new file mode 100644
index 0000000..19d1097
--- /dev/null
+++ b/src/device/virtual_device.c
@@ -0,0 +1,272 @@
+
+
+static void replace_rel_axes
+(
+ struct relabsd_virtual_device * const dev,
+ const struct relabsd_config * const config
+)
+{
+ int i;
+ struct input_absinfo absinfo;
+ unsigned int abs_code, rel_code;
+
+ for (i = RELABSD_VALID_AXES_COUNT; i --> 0;)
+ {
+ if (config->axis[i].enabled)
+ {
+ rel_code = relabsd_axis_to_rel((enum relabsd_axis) i);
+ abs_code = relabsd_axis_to_abs((enum relabsd_axis) i);
+
+ relabsd_config_get_absinfo(config, (enum relabsd_axis) i, &absinfo);
+ libevdev_disable_event_code(dev->dev, EV_REL, rel_code);
+ libevdev_enable_event_code(dev->dev, EV_ABS, abs_code, &absinfo);
+ }
+ }
+
+}
+
+static int rename_device
+(
+ struct libevdev * const dev,
+ const struct relabsd_config * const config
+)
+{
+ size_t new_name_size;
+ char * new_name;
+ const char * real_name;
+
+ /* +2: One for the \0, one for the space between prefix and 'real_name'. */
+ new_name_size = strlen(RELABSD_DEVICE_PREFIX) + 2;
+
+ if (config->device_name == (char *) NULL)
+ {
+ /* XXX
+ * "The name is never NULL but it may be the empty string."
+ * I'm assuming that since they use the term 'string', it is \0
+ * terminated.
+ */
+ real_name = libevdev_get_name(dev);
+ }
+ else
+ {
+ real_name = config->device_name;
+ }
+
+ new_name_size += strlen(real_name);
+
+ new_name = (char *) calloc(new_name_size, sizeof(char));
+
+ if (new_name == (char *) NULL)
+ {
+ RELABSD_ERROR
+ (
+ "Attempt at allocating memory to create the virtual device's name "
+ "failed: %s.",
+ strerror(errno)
+ );
+
+ /* This frees whatever came from 'libevdev_get_name'. */
+ libevdev_set_name(dev, RELABSD_DEVICE_PREFIX);
+
+ return -1;
+ }
+
+ if
+ (
+ snprintf
+ (
+ new_name,
+ new_name_size,
+ "%s %s",
+ RELABSD_DEVICE_PREFIX,
+ real_name
+ )
+ != ((int) (new_name_size - 1))
+ )
+ {
+ /* This makes for a great message when strerror(errno) returns SUCCESS. */
+ RELABSD_ERROR
+ (
+ "Something unexpected happened while renaming the virtual device: %s.",
+ strerror(errno)
+ );
+
+ /* This frees whatever came from 'libevdev_get_name'. */
+ libevdev_set_name(dev, RELABSD_DEVICE_PREFIX);
+
+ free((void *) new_name);
+
+ return -1;
+ }
+
+ /* This frees whatever came from 'libevdev_get_name'. */
+ libevdev_set_name(dev, new_name);
+ free((void *) new_name);
+
+ return 0;
+}
+
+int relabsd_virtual_device_create
+(
+ struct relabsd_virtual_device * const dev,
+ const struct relabsd_config * const config
+)
+{
+ int fd;
+
+ RELABSD_S_DEBUG(RELABSD_DEBUG_PROGRAM_FLOW, "Creating virtual device...");
+
+ fd = open(config->input_file, O_RDONLY);
+
+ if (fd < 0)
+ {
+ RELABSD_FATAL
+ (
+ "Could not open device '%s' in read only mode: %s.",
+ config->input_file,
+ strerror(errno)
+ );
+
+ return -1;
+ }
+
+ if (libevdev_new_from_fd(fd, &(dev->dev)) < 0)
+ {
+ RELABSD_FATAL
+ (
+ "libevdev could not open '%s': '%s'.",
+ config->input_file,
+ strerror(errno)
+ );
+
+ close(fd);
+
+ return -1;
+ }
+
+ if (rename_device(dev->dev, config) < 0)
+ {
+ libevdev_free(dev->dev);
+ close(fd);
+
+ return -1;
+ }
+
+ libevdev_enable_event_type(dev->dev, EV_ABS);
+
+ replace_rel_axes(dev, config);
+
+ if
+ (
+ libevdev_uinput_create_from_device
+ (
+ dev->dev,
+ /* See top of the file. */
+ RELABSD_UINPUT_OPEN_MANAGED,
+ &(dev->uidev)
+ )
+ < 0
+ )
+ {
+ RELABSD_FATAL("Could not create relabsd device: %s.", strerror(errno));
+
+ libevdev_free(dev->dev);
+
+ close(fd);
+
+ return -1;
+ }
+
+ close(fd);
+
+ return 0;
+}
+
+void relabsd_virtual_device_destroy (const struct relabsd_virtual_device * const dev)
+{
+ RELABSD_S_DEBUG(RELABSD_DEBUG_PROGRAM_FLOW, "Destroying virtual device...");
+
+ libevdev_uinput_destroy(dev->uidev);
+ libevdev_free(dev->dev);
+}
+
+int relabsd_virtual_device_write_evdev_event
+(
+ const struct relabsd_virtual_device * const dev,
+ unsigned int const type,
+ unsigned int const code,
+ int const value
+)
+{
+ RELABSD_DEBUG
+ (
+ RELABSD_DEBUG_VIRTUAL_EVENTS,
+ "Sending event: {type = %s; code = %s; value = %d}.",
+ libevdev_event_type_get_name(type),
+ libevdev_event_code_get_name(type, code),
+ value
+ );
+
+ /*
+ * We'll also send the 'EV_SYN' events when we receive them from the input
+ * device.
+ * OPTIMIZE: prevent 'EV_SYN' from being sent if we haven't sent any new
+ * values. (It might not be worth it though)
+ */
+ if (libevdev_uinput_write_event(dev->uidev, type, code, value) == 0)
+ {
+ /* FIXME:
+ * Why does activating the timeout trigger the EV_KEYS event to not be
+ * followed by EV_SYN?
+ */
+ if (type == EV_KEY)
+ {
+ libevdev_uinput_write_event
+ (
+ dev->uidev,
+ EV_SYN,
+ SYN_REPORT,
+ 0
+ );
+ }
+
+ return 0;
+ }
+
+ return -1;
+}
+
+void relabsd_virtual_device_set_axes_to_zero
+(
+ const struct relabsd_virtual_device * const dev,
+ const struct relabsd_config * const config
+)
+{
+ int i;
+
+ for (i = 0; i < RELABSD_VALID_AXES_COUNT; ++i)
+ {
+ if (config->axis[i].enabled)
+ {
+ relabsd_virtual_device_write_evdev_event
+ (
+ dev,
+ EV_ABS,
+ relabsd_axis_to_abs((enum relabsd_axis) i),
+ 0
+ );
+ }
+ }
+
+ /*
+ * Also send a SYN event when the axes have been modified.
+ */
+ libevdev_uinput_write_event
+ (
+ dev->uidev,
+ EV_SYN,
+ SYN_REPORT,
+ 0
+ );
+}
+