diff --git a/meson.build b/meson.build index d904a53..ab5947e 100644 --- a/meson.build +++ b/meson.build @@ -2,5 +2,5 @@ project('kobo-color-inputdev', 'c', version : '0.1', default_options : ['warning_level=3']) -exe = executable('kobo-color-inputdev', 'src/main.c', +main = executable('kobo-color-inputdev', ['src/main.c', './src/device.c'], install : true) diff --git a/shell.nix b/shell.nix index da14068..7a14c38 100644 --- a/shell.nix +++ b/shell.nix @@ -1,5 +1,5 @@ { pkgs ? import { } }: pkgs.mkShell { - packages = with pkgs; [ libevent clang-tools ]; + packages = with pkgs; [ libinput evtest clang-tools ]; inputsFrom = [ (pkgs.callPackage ./default.nix { }) ]; } diff --git a/src/device.c b/src/device.c new file mode 100644 index 0000000..fc077d6 --- /dev/null +++ b/src/device.c @@ -0,0 +1,102 @@ +#include "logger.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct uinput_abs_setup ABS_SETUP_X = {.code = ABS_X, + .absinfo = { + 0, + .maximum = 1680, + .resolution = 6, + }}; + +struct uinput_abs_setup ABS_SETUP_Y = {.code = ABS_Y, + .absinfo = { + 0, + .maximum = 1264, + .resolution = 8, + }}; + +struct uinput_abs_setup ABS_SETUP_PRESSURE = {.code = ABS_PRESSURE, + .absinfo = { + 0, + .maximum = 4095, + }}; + +struct uinput_abs_setup ABS_SETUP_TILT_X = {.code = ABS_TILT_X, + .absinfo = { + 0, + .minimum = -60, + .maximum = 60, + }}; + +struct uinput_abs_setup ABS_SETUP_TILT_Y = {.code = ABS_TILT_Y, + .absinfo = { + 0, + .minimum = -60, + .maximum = 60, + }}; + +struct uinput_abs_setup ABS_SETUP_DISTANCE = {.code = ABS_DISTANCE, + .absinfo = { + 0, + .maximum = 0xf, + }}; + +int setup(FILE **device) { + struct uinput_setup usetup; + int fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK); + if (fd < 0) { + char *error = strerror(errno); + logf("Error opening /dev/uinput: %s", error); + return 1; + } + + /* + * The ioctls below will enable the device that is about to be + * created, to pass key events, in this case the space key. + */ + ioctl(fd, UI_SET_EVBIT, EV_SYN); + ioctl(fd, UI_SET_EVBIT, EV_KEY); + ioctl(fd, UI_SET_EVBIT, EV_ABS); + + ioctl(fd, UI_ABS_SETUP, &ABS_SETUP_X); + ioctl(fd, UI_ABS_SETUP, &ABS_SETUP_Y); + ioctl(fd, UI_ABS_SETUP, &ABS_SETUP_PRESSURE); + ioctl(fd, UI_ABS_SETUP, &ABS_SETUP_TILT_X); + ioctl(fd, UI_ABS_SETUP, &ABS_SETUP_TILT_Y); + ioctl(fd, UI_ABS_SETUP, &ABS_SETUP_DISTANCE); + + ioctl(fd, UI_SET_KEYBIT, BTN_TOUCH); + ioctl(fd, UI_SET_KEYBIT, BTN_STYLUS); + ioctl(fd, UI_SET_KEYBIT, BTN_STYLUS2); + + memset(&usetup, 0, sizeof(usetup)); + usetup.id.bustype = BUS_VIRTUAL; + usetup.id.vendor = 0x1234; /* sample vendor */ + usetup.id.product = 0x5678; /* sample product */ + strcpy(usetup.name, "Elan Touchscreen"); + + ioctl(fd, UI_DEV_SETUP, &usetup); + ioctl(fd, UI_DEV_CREATE); + + FILE *file = fdopen(fd, "w"); + *device = file; + + return 0; +} + +void cleanup(FILE *device) { + fflush(device); + + ioctl(fileno(device), UI_DEV_DESTROY); + + fclose(device); +} diff --git a/src/device.h b/src/device.h new file mode 100644 index 0000000..8a7bb1d --- /dev/null +++ b/src/device.h @@ -0,0 +1,5 @@ +#pragma once +#include + +int setup(FILE **dev); +int cleanup(FILE *dev); diff --git a/src/logger.h b/src/logger.h new file mode 100644 index 0000000..8bdccf6 --- /dev/null +++ b/src/logger.h @@ -0,0 +1,17 @@ +#pragma once + +#define BLK "\x1B[0;30m" +#define RED "\x1B[0;31m" +#define GRN "\x1B[0;32m" +#define YEL "\x1B[0;33m" +#define BLU "\x1B[0;34m" +#define MAG "\x1B[0;35m" +#define CYN "\x1B[0;36m" +#define WHT "\x1B[0;37m" +#define RESET "\x1B[0m" + +#define logf(x, ...) \ + fprintf(stderr, "%s%s%s:%s%d%s: " x "\n", BLU, __FILE_NAME__, WHT, RED, \ + __LINE__, RESET, __VA_ARGS__); + +#define log(x) logf("%s", x) diff --git a/src/main.c b/src/main.c index 6366a2f..5727789 100644 --- a/src/main.c +++ b/src/main.c @@ -1,12 +1,166 @@ +#include "device.h" +#include "logger.h" +#include +#include +#include +#include #include +#include +#include -#define PROJECT_NAME "kobo-color-inputdev" +int read_input_event(struct input_event *ie) { + if (fread(ie, sizeof(struct input_event), 1, stdin) != 1) { + if (feof(stdin)) { + return EOF; + } else { + char *err = strerror(ferror(stdin)); + logf("Error reading input event! %s", err); -int main(int argc, char **argv) { - if(argc != 1) { - printf("%s takes no arguments.\n", argv[0]); - return 1; + return 1; } - printf("This is project %s.\n", PROJECT_NAME); - return 0; + } + return 0; +} + +int read_to_match(struct input_event *ie, int type, int code, int value) { + int res; + while (!(res = read_input_event(ie) && (type == ie->type) && + (code == -1 || code == ie->code) && + (value == -1 || value == ie->value))) { + }; + return res; +} + +int forward_event(FILE *dev, struct input_event *ie) { + if (fwrite(ie, sizeof(struct input_event), 1, dev) != 1) { + if (feof(dev)) { + return EOF; + } else { + char *err = strerror(ferror(dev)); + logf("Error writing input event(type: %x, code: %x, value: %x)! %s", + ie->type, ie->code, ie->value, err); + + return 1; + } + } + if (ie->type == EV_SYN) { + fflush(dev); + } + return 0; +} + +bool rewrite_abs_pen_event(struct input_event *ie) { + assert(ie->type == EV_ABS); + + switch (ie->code) { + // Rewrite the event codes + case ABS_MT_POSITION_X: + ie->code = ABS_X; + break; + case ABS_MT_POSITION_Y: + ie->code = ABS_Y; + break; + case ABS_MT_PRESSURE: + ie->code = ABS_PRESSURE; + break; + case ABS_MT_DISTANCE: + ie->code = ABS_DISTANCE; + // Passthrough events: + case ABS_TILT_X: + case ABS_TILT_Y: + break; + // Ignore events without logging + case ABS_MT_TRACKING_ID: + case ABS_MT_TOOL_TYPE: + case ABS_MT_TOUCH_MAJOR: + case ABS_MT_TOUCH_MINOR: + // Custom kobo event codes, extracted from headers at: + // https://github.com/kobolabs/Kobo-Reader/blob/master/hw/mt8113-libraC_vision7/ + // ABS_AZIMUTH + case 0x1d: + // ABS_TIP_MIN + case 0x2a: + // ABS_TIP_MAX + case 0x29: + return false; + default: + logf("Unknown EV_ABS! code: %x, value: %x", ie->code, ie->value); + return false; + } + return true; +} + +int rewrite_key_pen_event(struct input_event *ie) { + assert(ie->type == EV_KEY); + switch (ie->code) { + case BTN_TOUCH: + case BTN_STYLUS: + case BTN_STYLUS2: + return true; + // Known events we don't want to forward + case KEY_BATTERY: + return false; + // Uknown events + default: + logf("Unknown EV_KEY! code: %x, value: %x", ie->code, ie->value); + return false; + } + return 0; +} + +int rewrite_syn_pen_event(struct input_event *ie) { + assert(ie->type == EV_SYN); + if (ie->code == SYN_MT_REPORT) + ie->code = SYN_REPORT; + + return true; +} + +bool rewrite_pen_event(struct input_event *ie) { + switch (ie->type) { + case EV_ABS: + return rewrite_abs_pen_event(ie); + case EV_KEY: + return rewrite_key_pen_event(ie); + case EV_SYN: + return rewrite_syn_pen_event(ie); + default: + break; + } + return false; +} + +int process_events(FILE *dev) { + while (true) { + int res; + struct input_event ie; + while ((!(res = read_input_event(&ie))) && + (ie.type != EV_ABS && (ie.code != ABS_MT_TOOL_TYPE) && + (ie.value != MT_TOOL_PEN))) { + } + if (res == EOF) + return EXIT_SUCCESS; + if (res != 0) + return EXIT_FAILURE; + + while (!(res = read_input_event(&ie) && (ie.type == EV_ABS) && + (ie.code == ABS_MT_TOOL_TYPE) && + (ie.value != MT_TOOL_PEN))) { + if (rewrite_pen_event(&ie)) + forward_event(dev, &ie); + }; + if (res != 0) + return EXIT_FAILURE; + } +} + +int main(void) { + FILE *dev; + int res = setup(&dev); + if (res) { + return EXIT_FAILURE; + } + res = process_events(dev); + cleanup(dev); + return EXIT_SUCCESS; }