#include #include #include #include #include #include #include #include #include #include int handle_udevice(char* details, int udevice_fd); int handle_line(char* line, int udevice_fd); int handle_mouse(char* details, int udevice_fd); int read_file(char* filename, int udevice_fd); int setup_udevice(); void teardown_udevice(int fd); void emit(int fd, int type, int code, int val) { struct input_event ie; ie.type = type; ie.code = code; ie.value = val; ie.time.tv_sec = 0; ie.time.tv_usec = 0; write(fd, &ie, sizeof(ie)); } int handle_keyboard(char* details, int udevice_fd) { int code, event_type, value; int matched = sscanf(details, "%d,%d,%d", &event_type, &code, &value); if(matched != 3) { printf("Didn't match enough values for a keyboard event.\n"); return 1; } // printf("Event type: %d, Code: %d, Value: %d\n", // event_type, code, value); if(event_type != 1) { printf("Not sure what to do with event type %d", event_type); return 1; } emit(udevice_fd, EV_KEY, code, value); emit(udevice_fd, EV_SYN, SYN_REPORT, 0); return 0; } int handle_line(char* line, int udevice_fd) { static time_t timer_seconds = 0; static long timer_nanos = 0; static time_t total_seconds = 0; static long total_nanos = 0; time_t seconds; long nanos; char type; char details[32]; struct timespec to_sleep; struct timespec remaining; int matched = sscanf(line, "%ld %ld,%c,%s\n", &seconds, &nanos, &type, details); if(matched == 0) { printf("Line '%s' appears incorrect. Exiting", line); return 1; } else if(matched < 4) { printf("Only matched %d", matched); return 1; } remaining.tv_sec = 0; remaining.tv_nsec = 0; to_sleep.tv_sec = seconds - timer_seconds; to_sleep.tv_nsec = nanos - timer_nanos; if(to_sleep.tv_nsec < 0) { --to_sleep.tv_sec; to_sleep.tv_nsec += 1000000000L; } // printf("Timer %ld %ld\n", timer_seconds, timer_nanos); // printf("Read %ld %ld\n", seconds, nanos); // printf("Sleep %ld %ld\n", to_sleep.tv_sec, to_sleep.tv_nsec); while(nanosleep(&to_sleep, &remaining) == -1) { printf("Sleep harder\n"); to_sleep.tv_sec = remaining.tv_sec; to_sleep.tv_nsec = remaining.tv_nsec; } if(remaining.tv_sec != 0 || remaining.tv_nsec != 0) { printf("oops, remaining.\n"); } total_seconds += to_sleep.tv_sec; total_nanos += to_sleep.tv_nsec; if(total_nanos > 1000000000L) { total_nanos -= 1000000000L; total_seconds += 1; } timer_seconds = seconds; timer_nanos = nanos; printf("%ld %ld\tslept %ld %ld\n", total_seconds, total_nanos, to_sleep.tv_sec, to_sleep.tv_nsec); if(type == 'k') { return handle_keyboard(details, udevice_fd); } else if(type == 'm') { return handle_mouse(details, udevice_fd); } else { printf("Unexpected type %c/n", type); return 1; } } int handle_mouse(char* details, int udevice_fd) { static int current_left = 0; static int current_middle = 0; static int current_right = 0; int left, middle, right, x, y; int matched = sscanf(details, "l%d,m%d,r%d,x%d,y%d", &left, &middle, &right, &x, &y); if(matched != 5) { printf("Failed to match enough data for a mouse event.\n"); return 1; } // printf("L: %d M: %d, R: %d, X: %d, Y: %d\n", // left, middle, right, x, y); /* Move the mouse diagonally, 5 units per axis */ if(x != 0) { emit(udevice_fd, EV_REL, REL_X, x); } if(y != 0) { emit(udevice_fd, EV_REL, REL_Y, -1 * y); } if(left != current_left) { emit(udevice_fd, EV_KEY, BTN_LEFT, left); current_left = left; } if(middle != current_middle) { emit(udevice_fd, EV_KEY, BTN_MIDDLE, middle); current_middle = middle; } if(right != current_right) { emit(udevice_fd, EV_KEY, BTN_RIGHT, right); current_right = right; } emit(udevice_fd, EV_SYN, SYN_REPORT, 0); return 0; } int main(int argc, char* argv[]) { if(argc < 2) { printf("Please provide a capture file."); exit(EXIT_FAILURE); } int result = 0; int udevice_fd = setup_udevice(); if(read_file(argv[1], udevice_fd)) { result = EXIT_FAILURE; } teardown_udevice(udevice_fd); return result; } int read_file(char* filename, int udevice_fd) { FILE* fp; char* line = NULL; size_t len = 0; ssize_t read; fp = fopen(filename, "r"); if (fp == NULL) { printf("Failed to open file %s: %d\n", filename, errno); return 1; } for(int i = 3; i > 0; i--) { fprintf(stderr, "playing back in %d seconds\n", i); sleep(1); } while((read = getline(&line, &len, fp)) != -1) { if(handle_line(line, udevice_fd)) { return 1; } } } int setup_udevice() { struct uinput_setup usetup; int fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK); /* enable mouse button left and relative events */ ioctl(fd, UI_SET_EVBIT, EV_KEY); // Add keyboard keys. We could do this individually but we're super // lazy and it appears a loop should work fine based on the linux/input-event-codes.h header for(int i = KEY_ESC; i <= KEY_MICMUTE; i++) { ioctl(fd, UI_SET_KEYBIT, i); } // Add mouse buttons ioctl(fd, UI_SET_KEYBIT, BTN_LEFT); ioctl(fd, UI_SET_KEYBIT, BTN_LEFT); ioctl(fd, UI_SET_EVBIT, EV_REL); ioctl(fd, UI_SET_RELBIT, REL_X); ioctl(fd, UI_SET_RELBIT, REL_Y); memset(&usetup, 0, sizeof(usetup)); usetup.id.bustype = BUS_USB; usetup.id.vendor = 0x1234; /* sample vendor */ usetup.id.product = 0x5678; /* sample product */ strcpy(usetup.name, "Playback mouses"); ioctl(fd, UI_DEV_SETUP, &usetup); ioctl(fd, UI_DEV_CREATE); /* * On UI_DEV_CREATE the kernel will create the device node for this * device. We are inserting a pause here so that userspace has time * to detect, initialize the new device, and can start listening to * the event, otherwise it will not notice the event we are about * to send. This pause is only needed in our example code! */ // sleep(1); return fd; } void teardown_udevice(int fd) { ioctl(fd, UI_DEV_DESTROY); close(fd); }