#define _DEFAULT_SOURCE /* for getopt() and optarg */ #include #include #include #include #include #include #include #include #include #define VERSION "0.0.1" #define DEFAULT_DELAY_MS 30 #define DEFAULT_KBD "/dev/input/by-id/usb-Marson_Marson_Keyboard_and_Mouse_Link_Ver:ps2120L-event-kbd" const char *self; int debugging = 0; /* -v */ const char *keyboard_dev = DEFAULT_KBD; /* -k */ int delay_ms = DEFAULT_DELAY_MS; /* -d */ /* variadic macros are apparently C99 and not just a GNU extension */ void logmsg(const char *prefix, const char *fmt, ...) { va_list ap; va_start(ap, fmt); fprintf(stderr, "%s: %s: ", self, prefix); vfprintf(stderr, fmt, ap); va_end(ap); fputc('\n', stderr); } #define die(fmt, ...) { logmsg("fatal", fmt, ##__VA_ARGS__); exit(1); }; #define warn(fmt, ...) { logmsg("warning", fmt, ##__VA_ARGS__); }; #define info(fmt, ...) { logmsg("info", fmt, ##__VA_ARGS__); }; #define debug(fmt, ...) { if(debugging) logmsg("debug", fmt, ##__VA_ARGS__); }; void set_self(const char *argv0) { char *p; self = argv0; p = strrchr(self, '/'); if(p) self = p + 1; } void print_help(void) { /* helptext comes from usage.c, which gets generated by mkusage.pl, from the content in the the .rst file. */ extern const char *helptext[]; const char **helpline; puts("marsond " VERSION ", by B. Watson , WTFPL"); puts("Usage:"); for(helpline = helptext; *helpline; helpline++) puts(*helpline); } void version(void) { puts(VERSION); } void parse_args(int argc, char **argv) { int opt; opterr = 0; if(argc >= 2) { if(strcmp(argv[1], "--help") == 0) { print_help(); exit(0); } else if(strcmp(argv[1], "--version") == 0) { version(); exit(0); } } while( (opt = getopt(argc, argv, "d:hk:vV")) != -1) { switch(opt) { case 'v': debugging++; break; case 'd': delay_ms = atoi(optarg); break; case 'k': keyboard_dev = optarg; break; case 'h': print_help(); exit(0); break; case 'V': version(); exit(0); break; case '?': die("invalid option (try --help)"); break; default: print_help(); exit(1); break; } } if(delay_ms < 1) die("invalid -d argument"); } void daemonize(void) { int pid; pid = fork(); if(pid < 0) { die("fork() failed"); } else if(pid) { /* parent */ exit(0); } /* we are the child here */ setsid(); pid = fork(); if(pid < 0) { die("fork() failed"); } else if(pid) { /* 2nd generation parent */ info("forked to background, PID %d\n", pid); exit(0); } /* we are the grandchild here */ chdir("/"); close(0); close(1); close(2); } int main(int argc, char **argv) { int infd, outfd, i; struct uinput_user_dev dev; struct input_event ev; set_self(argv[0]); debug("starting up"); parse_args(argc, argv); if((outfd = open("/dev/uinput", O_WRONLY | O_NONBLOCK)) >= 0) { debug("opened /dev/uinput"); } else { die("failed to open /dev/uinput: %s", strerror(errno)); } if((infd = open(keyboard_dev, O_RDONLY)) >= 0) { debug("opened %s", keyboard_dev); } else { die("failed to open %s: %s", keyboard_dev, strerror(errno)); } if(ioctl(infd, EVIOCGRAB, 1) >= 0) { debug("grabbed %s", keyboard_dev); } else { die("failed to grab %s: %s", keyboard_dev, strerror(errno)); } if(ioctl(outfd, UI_SET_EVBIT, EV_KEY) >= 0) { debug("UI_SET_EVBIT OK"); } else { die("UI_SET_EVBIT failed: %s", strerror(errno)); } for(i = 0; i < KEY_MAX; i++) { if(ioctl(outfd, UI_SET_KEYBIT, i) >= 0) { /* we don't wanna be *that* verbose */ } else { die("UI_SET_EVBIT failed: %s", strerror(errno)); } } debug("UI_SET_KEYBIT OK"); memset(&dev, 0, sizeof(dev)); snprintf(dev.name, UINPUT_MAX_NAME_SIZE, "marsond virtual keyboard"); dev.id.bustype = BUS_USB; dev.id.vendor = 0x69; dev.id.product = 0x666; dev.id.version = 1; if(write(outfd, &dev, sizeof(dev)) >= 0) { debug("wrote dev structure to /dev/uinput"); } else { die("write to /dev/uinput failed: %s", strerror(errno)); } if(ioctl(outfd, UI_DEV_CREATE) >= 0) { debug("created virtual keyboard device"); } else { die("UI_DEV_CREATE failed: %s", strerror(errno)); } if(!debugging) daemonize(); /* note: don't call die() unless debugging is true, since daemonize() closes our stderr (there's nowhere for the error message to print). */ while(1) { if(read(infd, &ev, sizeof(ev)) < 0) { if(debugging) die("read from %s failed: %s", keyboard_dev, strerror(errno)); } debug("event: type %d, code %d, value %d\n", ev.type, ev.code, ev.value); if(ev.type == EV_KEY && ev.code == KEY_ENTER && ev.value == 0) { debug("got Enter key release, delaying %d ms", delay_ms); usleep(delay_ms * 1000); } if(write(outfd, &ev, sizeof(ev)) < 0) { if(debugging) die("write to /dev/uinput failed: %s", strerror(errno)); } } exit(1); } /* for the A key: event: type 4, code 4, value 458756 event: type 1, code 30, value 1 event: type 0, code 0, value 0 event: type 4, code 4, value 458756 event: type 1, code 30, value 0 event: type 0, code 0, value 0 for Enter: event: type 4, code 4, value 458792 event: type 1, code 28, value 1 event: type 0, code 0, value 0 event: type 4, code 4, value 458792 event: type 1, code 28, value 0 event: type 0, code 0, value 0 */