symkey/playback.cpp

262 lines
6.4 KiB
C++

#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <linux/uinput.h>
#include <sys/stat.h>
static volatile int is_running = 1;
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 sigint_handler(int dummy);
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 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;
to_sleep.tv_nsec = 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) {
if(!is_running) {
return 0;
}
perror("nanosleep error");
printf("Attempted %ld.%ld sleep\n", to_sleep.tv_sec, to_sleep.tv_nsec);
printf("Need %ld.%ld more seconds for total sleep\n", remaining.tv_sec, remaining.tv_nsec);
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;
}
// 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[]) {
int repeat = 1;
if(argc < 2) {
printf("Please provide a capture file.");
exit(EXIT_FAILURE);
}
if(argc == 3) {
int matched = sscanf(argv[2], "%d", &repeat);
if(matched != 1) {
fprintf(stderr, "Failed to read repeat value.\n");
exit(EXIT_FAILURE);
}
printf("Repeating %d times\n", repeat);
}
signal(SIGINT, sigint_handler);
for(int i = 3; i > 0; i--) {
fprintf(stderr, "Playing back in %d seconds\n", i);
sleep(1);
}
int result = 0;
int udevice_fd = setup_udevice();
for(int i = 0; is_running && i < repeat; i++) {
fprintf(stderr, "Repeat %d/%d\n", i+1, repeat);
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;
}
while(is_running && (read = getline(&line, &len, fp)) != -1) {
if(handle_line(line, udevice_fd)) {
return 1;
}
}
fclose(fp);
return 0;
}
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_RIGHT);
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 sigint_handler(int dummy) {
is_running = 0;
}
void teardown_udevice(int fd) {
ioctl(fd, UI_DEV_DESTROY);
close(fd);
}