I'm having some problems getting both sides of code using uinput
working.
Based on Getting started with uinput: the user level input subsystem[dead link; archived] I put together the following writer (minus error handling):
int main(int ac, char **av)
{
int fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK);
int ret = ioctl(fd, UI_SET_EVBIT, EV_ABS);
ret = ioctl(fd, UI_SET_ABSBIT, ABS_X);
struct uinput_user_dev uidev = {0};
snprintf(uidev.name, UINPUT_MAX_NAME_SIZE, "uinput-rotary");
uidev.absmin[ABS_X] = 0;
uidev.absmax[ABS_X] = 255;
ret = write(fd, &uidev, sizeof(uidev));
ret = ioctl(fd, UI_DEV_CREATE);
struct input_event ev = {0};
ev.type = EV_ABS;
ev.code = ABS_X;
ev.value = 42;
ret = write(fd, &ev, sizeof(ev));
getchar();
ret = ioctl(fd, UI_DEV_DESTROY);
return EXIT_SUCCESS;
}
That seems to work, at least the full input_event
structure seems to be written.
I then wrote the most naive reader of events I could come up with:
int main(int ac, char **av)
{
int fd = open(av[1], O_RDONLY);
char name[256] = "unknown";
ioctl(fd, EVIOCGNAME(sizeof(name)), name);
printf("reading from %s\n", name);
struct input_event ev = {0};
int ret = read(fd, &ev, sizeof(ev));
printf("Read an event! %i\n", ret);
printf("ev.time.tv_sec: %li\n", ev.time.tv_sec);
printf("ev.time.tv_usec: %li\n", ev.time.tv_usec);
printf("ev.type: %hi\n", ev.type);
printf("ev.code: %hi\n", ev.code);
printf("ev.value: %li\n", ev.value);
return EXIT_SUCCESS;
}
Unfortunately the reader side doesn't work at all; only manages to read 8 bytes each time, which isn't nearly a full input_event
structure.
What silly mistake am I making?
You should also be writing a sync event after the actual event. In your writer side code:
struct input_event ev = {0};
ev.type = EV_ABS;
ev.code = ABS_X;
ev.value = 42;
usleep(1500);
memset(&ev, 0, sizeof(ev));
ev.type = EV_SYN;
ev.code = 0;
ev.value = 0;
ret = write(fd, &ev, sizeof(ev));
getchar();
TL;DR: The kernel expects an EV_SYN
event of code SYN_REPORT
because individual events may be grouped together, i.e., when they happen at the same point in time.
You can think of it as the kernel interpreting a group of events rather than individual events as specified in struct input_event
. An EV_SYN
event delimits these groups of events with SYN_REPORT
as code, i.e., this provides a grouping mechanism for signaling events that occur simultaneously.
For example, imagine you touch with your finger on the surface of a touchpad:
Given the definition of struct input_event
in linux/input.h
:
struct input_event {
/* ... */
__u16 type; /* e.g., EV_ABS, EV_REL */
__u16 code; /* e.g., ABS_X for EV_ABS */
__s32 value; /* e.g., the value of the x coordinate for ABS_X */
};
It isn't possible to create a single EV_ABS
event that can hold both the codes ABS_X
and ABS_Y
, as well as the values for specifying the x and y coordinates, respectively – there is just a single code
member in struct input_event
1.
Instead, two EV_ABS
events2 will be created:
ABS_X
and the x coordinate as value
.ABS_Y
and the y coordinate as value
.It wouldn't be entirely correct to interpret these two events as two events separated in time, i.e., one that indicates a change of the x coordinate and another that occurs later in time and indicates a change of the y coordinate.
Instead of two in-time-separated events, these events should be grouped together and interpreted by the kernel as a single unit of input data change that indicates a change of both the x and y coordinates at the same time. This is why the EV_SYN
mechanism described above exists: by generating one EV_SYN
event with the SYN_REPORT
code just after these two EV_ABS
events (i.e., ABS_X
and ABS_Y
), we are able to group them as a unit.
1This is directly related to the fact that a struct input_event
is of fixed size and can't grow arbitrarily.
2Tecnically, more events may be created. For example, another event of type EV_KEY
with the code BTN_TOUCH
will likely be created. However, this is irrelevant for the point I want to make.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With