#define _POSIX_C_SOURCE 200809l #define _DEFAULT_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #define test_bit(yalv, abs_b) ((((char *)abs_b)[yalv/8] & (1< 0) #define EVDEV_ROOT "/dev/input/" int main(int argc, char **argv) { (void)argc; (void)argv; DIR *dir = opendir(EVDEV_ROOT); if (!dir) err(1, "opendir:"); struct dirent *dp; struct stat st = {0}; int stylus = -1; while ((dp = readdir(dir)) != NULL) { if (fstatat(dirfd(dir), dp->d_name, &st, 0) == -1) err(1, "fstatat:"); if ((st.st_mode & S_IFMT) != S_IFCHR) continue; // blocking io int tmp_fd = openat(dirfd(dir), dp->d_name, O_RDWR); if (tmp_fd < 0) err(1, "openat:"); union { char b[EV_MAX]; char k[KEY_MAX]; } vec; if (ioctl(tmp_fd, EVIOCGBIT(0, EV_MAX), vec.b) < 0) err(1, "ioctl:"); if (!test_bit(EV_KEY, vec.b) || !test_bit(EV_ABS, vec.b)) continue; if (ioctl(tmp_fd, EVIOCGBIT(EV_KEY, KEY_MAX), vec.k) < 0) err(1, "ioctl:"); if (test_bit(BTN_STYLUS, vec.k) && test_bit(BTN_TOOL_RUBBER, vec.k)) { stylus = tmp_fd; break; } close(tmp_fd); } closedir(dir); if (stylus == -1) err(1, "no stylus input found"); int poll_set = epoll_create(1); if (poll_set < 0) err(1, "epoll_create:"); struct epoll_event poll_ev = { .events = EPOLLIN }; if (epoll_ctl(poll_set, EPOLL_CTL_ADD, stylus, &poll_ev) < 0) err(1, "epoll_ctl:"); struct input_event in_ev, out_ev; while (epoll_wait(poll_set, &poll_ev, 1, -1) != -1) { switch (poll_ev.events) { case EPOLLIN: if (read(stylus, &in_ev, sizeof(struct input_event)) < 0) err(1, "read:"); if (in_ev.code != BTN_STYLUS) break; out_ev = in_ev; out_ev.code = BTN_TOOL_RUBBER; if (write(stylus, &out_ev, sizeof(struct input_event)) < 0) err(1, "write:"); break; default: break; } } return 0; }