Rewrite kobo color inputs
This commit is contained in:
parent
27f18d0a45
commit
83ba5981cc
6 changed files with 287 additions and 9 deletions
|
@ -2,5 +2,5 @@ project('kobo-color-inputdev', 'c',
|
||||||
version : '0.1',
|
version : '0.1',
|
||||||
default_options : ['warning_level=3'])
|
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)
|
install : true)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{ pkgs ? import <nixpkgs> { } }:
|
{ pkgs ? import <nixpkgs> { } }:
|
||||||
pkgs.mkShell {
|
pkgs.mkShell {
|
||||||
packages = with pkgs; [ libevent clang-tools ];
|
packages = with pkgs; [ libinput evtest clang-tools ];
|
||||||
inputsFrom = [ (pkgs.callPackage ./default.nix { }) ];
|
inputsFrom = [ (pkgs.callPackage ./default.nix { }) ];
|
||||||
}
|
}
|
||||||
|
|
102
src/device.c
Normal file
102
src/device.c
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
#include "logger.h"
|
||||||
|
#include <assert.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <linux/input-event-codes.h>
|
||||||
|
#include <linux/input.h>
|
||||||
|
#include <linux/uinput.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
5
src/device.h
Normal file
5
src/device.h
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
#pragma once
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
int setup(FILE **dev);
|
||||||
|
int cleanup(FILE *dev);
|
17
src/logger.h
Normal file
17
src/logger.h
Normal file
|
@ -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)
|
168
src/main.c
168
src/main.c
|
@ -1,12 +1,166 @@
|
||||||
|
#include "device.h"
|
||||||
|
#include "logger.h"
|
||||||
|
#include <assert.h>
|
||||||
|
#include <linux/input-event-codes.h>
|
||||||
|
#include <linux/input.h>
|
||||||
|
#include <stdbool.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#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) {
|
return 1;
|
||||||
if(argc != 1) {
|
|
||||||
printf("%s takes no arguments.\n", argv[0]);
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue