rec.c - randomcrap - random crap programs of varying quality
 (HTM) git clone git://git.codemadness.org/randomcrap
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
       rec.c (15340B)
       ---
            1 /* serialize and replay keyboard and mouse events
            2    Dependencies: Xlib, Xtst, XInput2, etc
            3    Some code adapted from the examples in xorg-xinput/src/ test.c and test_xi2.c.
            4    Compile: cc -o main main.c -lX11 -lXtst -lXi -lrt -D_GNU_SOURCE -Wall */
            5 
            6 #include <sys/time.h>
            7 
            8 #include <X11/extensions/XInput.h>
            9 #include <X11/extensions/XInput2.h>
           10 #include <X11/extensions/XTest.h>
           11 
           12 #include <X11/Xlib.h>
           13 
           14 #include <err.h>
           15 #include <errno.h>
           16 #include <poll.h>
           17 #include <signal.h>
           18 #include <stdio.h>
           19 #include <string.h>
           20 #include <stdlib.h>
           21 #include <time.h>
           22 #include <unistd.h>
           23 
           24 enum EventType { EventNone = 0, EventMotion, EventKeyPress, EventKeyRelease,
           25                  EventButtonPress, EventButtonRelease, EventSleep };
           26 
           27 struct event {
           28         enum EventType evtype;
           29         int button; /* button or key */
           30         int x, y; /* mouse x, y */
           31         float sleeptime;
           32 };
           33 
           34 volatile sig_atomic_t state_sigcont = 0, state_sighup = 0, state_sigabrt = 0;
           35 volatile sig_atomic_t state_sigusr1 = 0, state_sigusr2 = 0;
           36 
           37 Window win, root;
           38 Display *dpy;
           39 int screen;
           40 int xi_opcode;
           41 
           42 int isrecording = 0;
           43 int isreplaying = 0;
           44 int isrunning = 0;
           45 int xfd;
           46 
           47 struct event *events = NULL;
           48 size_t nevents = 0;
           49 
           50 const char *replay_file = NULL;
           51 
           52 /* configuration */
           53 int replay_usebuttons = 1; /* use button events? */
           54 int replay_usemotion = 1;  /* use motion events? */
           55 int replay_usekeys = 1;    /* use keyboard events */
           56 int replay_usesleep = 1;   /* use timings/sleep for replaying? */
           57 
           58 int record_usebuttons = 1; /* use button events? */
           59 int record_usemotion = 1;  /* use motion events? */
           60 int record_usekeys = 1;    /* use keyboard events */
           61 int record_usesleep = 1;   /* use timings/sleep for recording? */
           62 
           63 int replay_key = 134;      /* Super_R */
           64 int replay_repeat = 0;     /* repeat replay? */
           65 int record_key = 127;      /* pause/rec key on my keyboard */
           66 int timefirstevent = 1;    /* start timer at first event or since program startup */
           67 int exit_key = 96;         /* F12 */
           68 
           69 /* button states */
           70 char buttons[32], keys[256];
           71 
           72 void replay_events(struct event *events, size_t nevents);
           73 
           74 int
           75 process_event(struct event *ev)
           76 {
           77         unsigned long delay = CurrentTime; /* no delay */
           78         unsigned long keydelay = 1; /* delay atleast some time (ms) to process the key */
           79 
           80         switch (ev->evtype) {
           81         case EventMotion:
           82                 if (!replay_usemotion)
           83                         return 0;
           84                 XTestFakeMotionEvent(dpy, -1, ev->x, ev->y, delay);
           85                 XSync(dpy, False);
           86                 return 1;
           87         case EventKeyPress:
           88                 if (!replay_usekeys)
           89                         return 0;
           90                 XTestFakeKeyEvent(dpy, ev->button, 1, keydelay);
           91                 XSync(dpy, False);
           92                 return 1;
           93         case EventKeyRelease:
           94                 if (!replay_usekeys)
           95                         return 0;
           96                 XTestFakeKeyEvent(dpy, ev->button, 0, keydelay);
           97                 XSync(dpy, False);
           98                 return 1;
           99         case EventButtonPress:
          100                 if (!replay_usebuttons)
          101                         return 0;
          102                 XTestFakeButtonEvent(dpy, ev->button, 1, delay);
          103                 XSync(dpy, False);
          104                 return 1;
          105         case EventButtonRelease:
          106                 if (!replay_usebuttons)
          107                         return 0;
          108                 XTestFakeButtonEvent(dpy, ev->button, 0, delay);
          109                 XSync(dpy, False);
          110                 return 1;
          111         case EventSleep:
          112                 if (!replay_usesleep)
          113                         return 0;
          114                 usleep(ev->sleeptime * 1000000);
          115                 return 2; /* not an X11 event */
          116         default:
          117                 return 0;
          118         }
          119 }
          120 
          121 int
          122 line2event(const char *line, struct event *ev)
          123 {
          124         char *p, *end;
          125 
          126         if (strncmp(line, "motion ", strlen("motion ")) == 0) {
          127                 if (isrecording && !record_usemotion)
          128                         return 0;
          129 
          130                 ev->evtype = EventMotion;
          131 
          132                 /* parse arguments */
          133                 p = strchr(line, ' ');
          134                 ev->x = strtol(p, &end, 10);
          135 
          136                 p = end;
          137                 ev->y = strtol(p, &end, 10);
          138                 return 1;
          139         } else if (strncmp(line, "keypress ", strlen("keypress ")) == 0 ||
          140                    strncmp(line, "keyrelease ", strlen("keyrelease ")) == 0) {
          141 
          142                 if (isrecording && !record_usekeys)
          143                         return 0;
          144 
          145                 ev->evtype = line[3] == 'p' ? EventKeyPress : EventKeyRelease;
          146 
          147                 /* parse arguments */
          148                 p = strchr(line, ' ');
          149                 ev->button = strtol(p, &end, 10);
          150 
          151                 return 1;
          152         } else if (strncmp(line, "press ", strlen("press ")) == 0 ||
          153                    strncmp(line, "release ", strlen("release ")) == 0) {
          154                 if (isrecording && !record_usebuttons)
          155                         return 0;
          156 
          157                 ev->evtype = line[0] == 'p' ? EventButtonPress : EventButtonRelease;
          158 
          159                 /* parse arguments */
          160                 p = strchr(line, ' ');
          161                 ev->button = strtol(p, &end, 10);
          162 
          163                 return 1;
          164         } else if (strncmp(line, "sleep ", strlen("sleep ")) == 0) {
          165                 if (isrecording && !record_usesleep)
          166                         return 0;
          167 
          168                 ev->evtype = EventSleep;
          169 
          170                 /* parse arguments */
          171                 p = strchr(line, ' '); /* there is always a space */
          172                 ev->sleeptime = strtof(p, &end);
          173                 return 2;
          174         }
          175         return -1;
          176 }
          177 
          178 int
          179 xevent2event(XGenericEventCookie *cookie, struct event *ev)
          180 {
          181         XIDeviceEvent *dev_event;
          182 
          183         dev_event = (XIDeviceEvent *)(cookie->data);
          184 
          185         /* NOTE: the "detail" attribute is "the button number, key code, touch ID, or 0." */
          186         switch (cookie->evtype) {
          187         /* device event */
          188         case XI_ButtonPress:
          189                 ev->evtype = EventButtonPress;
          190                 ev->button = dev_event->detail;
          191                 return 1;
          192         case XI_ButtonRelease:
          193                 ev->evtype = EventButtonRelease;
          194                 ev->button = dev_event->detail;
          195                 return 1;
          196         case XI_KeyPress:
          197                 ev->evtype = EventKeyPress;
          198                 ev->button = dev_event->detail;
          199                 return 1;
          200         case XI_KeyRelease:
          201                 ev->evtype = EventKeyRelease;
          202                 ev->button = dev_event->detail;
          203                 return 1;
          204         case XI_Motion:
          205                 ev->evtype = EventMotion;
          206                 ev->x = (int)dev_event->root_x;
          207                 ev->y = (int)dev_event->root_y;
          208                 return 1;
          209         }
          210         return 0;
          211 }
          212 
          213 int
          214 filterevent(struct event *ev)
          215 {
          216         switch (ev->evtype) {
          217         case EventButtonPress:
          218                 if (ev->button < 0 || ev->button >= sizeof(buttons) / sizeof(buttons[0]))
          219                         break;
          220                 buttons[ev->button] = 1;
          221                 break;
          222         case EventButtonRelease:
          223                 if (ev->button < 0 || ev->button >= sizeof(buttons) / sizeof(buttons[0]))
          224                         break;
          225                 /* ignore if button was not registered as pressed beforehand */
          226                 if (buttons[ev->button] == 0)
          227                         return 1;
          228                 buttons[ev->button] = 0;
          229                 break;
          230         case EventKeyPress:
          231                 if (ev->button < 0 || ev->button >= sizeof(keys) / sizeof(keys[0]))
          232                         break;
          233                 keys[ev->button] = 1;
          234                 break;
          235         case EventKeyRelease:
          236                 if (ev->button < 0 || ev->button >= sizeof(keys) / sizeof(keys[0]))
          237                         break;
          238                 /* ignore if button was not registered as pressed beforehand */
          239                 if (keys[ev->button] == 0)
          240                         return 1;
          241                 keys[ev->button] = 0;
          242                 break;
          243         default:
          244                 break;
          245         }
          246         return 0;
          247 }
          248 
          249 int
          250 event2line(struct event *ev)
          251 {
          252         switch (ev->evtype) {
          253         case EventButtonPress:
          254                 if (isreplaying && !replay_usebuttons)
          255                         break;
          256                 printf("press %d\n", ev->button);
          257                 return 1;
          258         case EventButtonRelease:
          259                 if (isreplaying && !replay_usebuttons)
          260                         break;
          261                 printf("release %d\n", ev->button);
          262                 return 1;
          263         case EventKeyPress:
          264                 if (isreplaying && !replay_usekeys)
          265                         break;
          266                 printf("keypress %d\n", ev->button);
          267                 return 1;
          268         case EventKeyRelease:
          269                 if (isreplaying && !replay_usekeys)
          270                         break;
          271                 printf("keyrelease %d\n", ev->button);
          272                 return 1;
          273         case EventMotion:
          274                 if (isreplaying && !replay_usemotion)
          275                         break;
          276                 printf("motion %d %d\n", ev->x, ev->y);
          277                 return 1;
          278         case EventSleep:
          279                 if (isreplaying && !replay_usesleep)
          280                         break;
          281                 printf("sleep %f\n", ev->sleeptime);
          282                 return 1;
          283         default:
          284                 break;
          285         }
          286         return 0;
          287 }
          288 
          289 void
          290 read_events_from_file(const char *path)
          291 {
          292         struct event ev;
          293         FILE *fp;
          294         char *line = NULL;
          295         size_t linesiz = 0;
          296         ssize_t n;
          297         int r;
          298 
          299         if ((fp = fopen(path, "rb")) == NULL)
          300                 err(1, "fopen: %s", path);
          301 
          302         nevents = 0;
          303         while ((n = getline(&line, &linesiz, fp)) > 0) {
          304                 if (n && line[n - 1] == '\n')
          305                         line[--n] = '\0';
          306 
          307                 if ((r = line2event(line, &ev)) > 0) {
          308                         /* add event */
          309                         if (!(events = realloc(events, (nevents + 1) * sizeof(ev))))
          310                                 err(1, "realloc");
          311                         memcpy(&events[nevents], &ev, sizeof(ev));
          312                         nevents++;
          313                 }
          314         }
          315         if (ferror(fp))
          316                 err(1, "getline");
          317         free(line);
          318 }
          319 
          320 /* fake events of buttons that are still pressed. This prevents some annoying issue when the
          321    recording is stopped and later replayed the button or key states or held */
          322 void
          323 fake_release_button_states(void)
          324 {
          325         struct event ev;
          326         int i;
          327 
          328         ev.x = 0;
          329         ev.y = 0;
          330         ev.sleeptime = 0;
          331 
          332         ev.evtype = EventButtonRelease;
          333         for (i = 0; i < sizeof(buttons) / sizeof(buttons[0]); i++) {
          334                 if (!buttons[i])
          335                         continue;
          336                 ev.button = i;
          337                 event2line(&ev);
          338         }
          339 
          340         ev.evtype = EventKeyRelease;
          341         for (i = 0; i < sizeof(keys) / sizeof(keys[0]); i++) {
          342                 if (!keys[i])
          343                         continue;
          344                 ev.button = i;
          345                 event2line(&ev);
          346         }
          347 }
          348 
          349 void
          350 record_stop(void)
          351 {
          352         if (!isrecording)
          353                 return;
          354 
          355         isrecording = 0;
          356 
          357         fake_release_button_states();
          358         fputs("recording stopped\n", stderr);
          359 }
          360 
          361 void
          362 record_reset(void)
          363 {
          364         /* start new buffer for recording */
          365         free(events);
          366         events = NULL;
          367         nevents = 0;
          368 }
          369 
          370 void
          371 record_start(void)
          372 {
          373         isrecording = 1;
          374 
          375         record_reset();
          376 
          377         /* reset currently pressed button states */
          378         memset(buttons, 0, sizeof(buttons));
          379         memset(keys, 0, sizeof(keys));
          380 
          381         fputs("recording started\n", stderr);
          382 }
          383 
          384 void
          385 replay_stop(void)
          386 {
          387         if (!isreplaying)
          388                 return;
          389 
          390         isreplaying = 0;
          391         fputs("replay stopped\n", stderr);
          392 }
          393 
          394 void
          395 replay_start(void)
          396 {
          397         isreplaying = 1;
          398         fputs("replay started\n", stderr);
          399 
          400         replay_events(events, nevents);
          401         if (isreplaying)
          402                 replay_stop();
          403 }
          404 
          405 int
          406 eventpoll(void)
          407 {
          408         struct pollfd pfds[1];
          409         int r, timeout = 16; /* timeout in ms */
          410 
          411         pfds[0].fd = xfd;
          412         pfds[0].events = POLLIN | POLLHUP | POLLNVAL;
          413         pfds[0].revents = 0;
          414 
          415         if ((r = poll(pfds, 1, timeout)) == -1) {
          416                 /* interruptions by signals are OK */
          417                 if (errno != EINTR)
          418                         err(1, "poll");
          419         }
          420         return r;
          421 }
          422 
          423 /* check for a X11 event or handle a received signal */
          424 int
          425 eventloop(int dopoll)
          426 {
          427         if (state_sigusr1) {
          428                 state_sigusr1 = 0;
          429                 record_start();
          430         }
          431         if (state_sigusr2) {
          432                 state_sigusr2 = 0;
          433                 record_stop();
          434         }
          435         if (state_sigcont) {
          436                 state_sigcont = 0;
          437                 replay_start();
          438         }
          439         if (state_sigabrt) {
          440                 state_sigabrt = 0;
          441                 replay_stop();
          442         }
          443         if (state_sighup) {
          444                 state_sighup = 0;
          445                 record_reset();
          446                 read_events_from_file(replay_file);
          447         }
          448         if (XPending(dpy))
          449                 return 1;
          450         if (dopoll)
          451                 eventpoll();
          452 
          453         return 0;
          454 }
          455 
          456 void
          457 handle_event(XEvent *xev)
          458 {
          459         struct event ev;
          460         XGenericEventCookie *cookie;
          461         XIDeviceEvent *dev_event;
          462 
          463         cookie = (XGenericEventCookie *)&(xev->xcookie);
          464         if (cookie->type == GenericEvent &&
          465             cookie->extension == xi_opcode &&
          466             XGetEventData(dpy, cookie)) {
          467                 dev_event = (XIDeviceEvent *)(cookie->data);
          468 
          469                 switch (cookie->evtype) {
          470                 case XI_KeyRelease:
          471                         /* exit immediately */
          472                         if (dev_event->detail == exit_key) {
          473                                 isrecording = 0;
          474                                 isreplaying = 0;
          475                                 isrunning = 0;
          476                                 goto endevent;
          477                         } else if (dev_event->detail == record_key) {
          478                                 if (isreplaying)
          479                                         replay_stop();
          480                                 if (!isrecording)
          481                                         record_start();
          482                                 else
          483                                         record_stop();
          484                                 goto endevent;
          485                         } else if (dev_event->detail == replay_key) {
          486                                 if (isrecording)
          487                                         record_stop();
          488                                 if (!isreplaying)
          489                                         replay_start();
          490                                 else
          491                                         replay_stop();
          492                                 goto endevent;
          493                         }
          494                         break;
          495                 case XI_KeyPress:
          496                         if (dev_event->detail == exit_key ||
          497                             dev_event->detail == record_key ||
          498                             dev_event->detail == replay_key)
          499                                 goto endevent;
          500                         break;
          501                 }
          502 
          503                 if (isrecording && xevent2event(cookie, &ev) > 0 && !filterevent(&ev)) {
          504                         if (event2line(&ev)) {
          505                                 fflush(stdout); /* force flushing the stream line buffer */
          506 
          507                                 /* add event */
          508                                 if (!(events = realloc(events, (nevents + 1) * sizeof(ev))))
          509                                         err(1, "realloc");
          510                                 memcpy(&events[nevents], &ev, sizeof(ev));
          511                                 nevents++;
          512                         }
          513                 }
          514 endevent:
          515                 XFreeEventData(dpy, cookie);
          516         }
          517 }
          518 
          519 void
          520 xinput_setup(void)
          521 {
          522         int xi_major = 2, xi_minor = 0;
          523         XIEventMask masks[2];
          524         XIEventMask *m;
          525         XDeviceInfo *devs;
          526         Status status;
          527         /* bits storage for event masks */
          528         unsigned char nmask[XIMaskLen(XI_LASTEVENT)];
          529         int i, event, error, ndevs;
          530 
          531         if (!XQueryExtension(dpy, "XInputExtension", &xi_opcode, &event, &error))
          532                 errx(1, "X Input extension not available");
          533 
          534         status = XIQueryVersion(dpy, &xi_major, &xi_minor);
          535         switch (status) {
          536         case Success:
          537                 break;
          538         case BadRequest:
          539                 errx(1, "no XI2 support. (%d.%d only)", xi_major, xi_minor);
          540         default:
          541                 errx(1, "XI2: unknown error");
          542         }
          543 
          544         memset(nmask, 0, sizeof(nmask));
          545 
          546         /* Select for motion events */
          547         if (record_usebuttons) {
          548                 XISetMask(nmask, XI_ButtonPress);
          549                 XISetMask(nmask, XI_ButtonRelease);
          550         }
          551         /* always need key events for activation */
          552         XISetMask(nmask, XI_KeyPress);
          553         XISetMask(nmask, XI_KeyRelease);
          554         if (record_usemotion)
          555                 XISetMask(nmask, XI_Motion);
          556         m = &masks[0];
          557         /* m->deviceid = XIAllDevices; */ /* normal events requires XIAllDevices (not XIAllMasterDevices) */
          558         m->mask_len = sizeof(nmask);
          559         m->mask = nmask;
          560 
          561         /* select events individually per device, this allows to ignore certain devices */
          562         if ((devs = XListInputDevices(dpy, &ndevs))) {
          563                 for (i = 0; i < ndevs; i++) {
          564                         m->deviceid = devs[i].id;
          565                         if (strstr(devs[i].name, " XTEST "))
          566                                 continue; /* ignore XTEST devices */
          567                         XISelectEvents(dpy, root, masks, 1);
          568                         XSync(dpy, False);
          569                 }
          570                 XFreeDeviceList(devs);
          571         }
          572 }
          573 
          574 void
          575 replay_events(struct event *events, size_t nevents)
          576 {
          577         XEvent xev;
          578         size_t i;
          579 
          580         do {
          581                 for (i = 0; i < nevents && isreplaying; i++) {
          582                         process_event(&events[i]);
          583 
          584                         while (eventloop(0)) {
          585                                 XNextEvent(dpy, &xev);
          586                                 handle_event(&xev);
          587                         }
          588                 }
          589         } while (replay_repeat);
          590 }
          591 
          592 void
          593 run(void)
          594 {
          595         struct event ev;
          596         XEvent xev;
          597         struct timespec tc, tp;
          598         struct timeval tv1, tv2, tvr;
          599         int isfirst;
          600         float sleeptime;
          601 
          602         /* initial clock / timing */
          603         if (!timefirstevent) {
          604                 clock_gettime(CLOCK_MONOTONIC, &tc);
          605                 memcpy(&tp, &tc, sizeof(tp));
          606         }
          607 
          608         isfirst = 1;
          609         isrunning = 1; /* why are you running? */
          610         while (isrunning) {
          611                 if (!eventloop(1))
          612                         continue;
          613                 XNextEvent(dpy, &xev);
          614 
          615                 /* timing relative to previous event */
          616                 memcpy(&tp, &tc, sizeof(tp));
          617                 clock_gettime(CLOCK_MONOTONIC, &tc);
          618 
          619                 if (timefirstevent && isfirst) {
          620                         memcpy(&tp, &tc, sizeof(tp));
          621                         isfirst = 0;
          622                 }
          623 
          624                 TIMESPEC_TO_TIMEVAL(&tv1, &tp);
          625                 TIMESPEC_TO_TIMEVAL(&tv2, &tc);
          626                 timersub(&tv2, &tv1, &tvr);
          627                 if (record_usesleep) {
          628                         sleeptime = tvr.tv_sec + (tvr.tv_usec / 1000000.0);
          629                         if (sleeptime > 0.0) {
          630                                 ev.evtype = EventSleep;
          631                                 ev.sleeptime = sleeptime;
          632 
          633                                 if (isrecording && event2line(&ev)) {
          634                                         fflush(stdout); /* force flushing the stream line buffer */
          635 
          636                                         /* add event */
          637                                         if (!(events = realloc(events, (nevents + 1) * sizeof(ev))))
          638                                                 err(1, "realloc");
          639                                         memcpy(&events[nevents], &ev, sizeof(ev));
          640                                         nevents++;
          641                                 }
          642                         }
          643                 }
          644 
          645                 handle_event(&xev);
          646         }
          647 }
          648 
          649 /* prevent errors from ever stopping the program */
          650 int
          651 xerror(Display *dpy, XErrorEvent *ee)
          652 {
          653         return 0;
          654 }
          655 
          656 void
          657 sighandler(int signo)
          658 {
          659         switch (signo) {
          660         case SIGABRT: state_sigabrt = 1; break;
          661         case SIGCONT: state_sigcont = 1; break;
          662         case SIGHUP:  state_sighup = 1;  break;
          663         case SIGUSR1: state_sigusr1 = 1; break;
          664         case SIGUSR2: state_sigusr2 = 1; break;
          665         }
          666 }
          667 
          668 int
          669 main(int argc, char **argv)
          670 {
          671         struct sigaction sa = { 0 };
          672 
          673         sa.sa_flags = SA_RESTART;
          674         sa.sa_handler = sighandler;
          675 
          676         sigaction(SIGABRT, &sa, NULL);
          677         sigaction(SIGCONT, &sa, NULL);
          678         sigaction(SIGHUP, &sa, NULL);
          679         sigaction(SIGUSR1, &sa, NULL);
          680         sigaction(SIGUSR2, &sa, NULL);
          681 
          682         if ((dpy = XOpenDisplay(NULL)) == NULL)
          683                 errx(1, "XOpenDisplay");
          684 
          685         screen = DefaultScreen(dpy);
          686         root = RootWindow(dpy, screen);
          687         win = root;
          688         if ((xfd = ConnectionNumber(dpy)) == -1)
          689                 errx(1, "No file descriptor returned from ConnectionNumber()");
          690 
          691         XSetErrorHandler(xerror);
          692 
          693         xinput_setup();
          694 
          695         if (argc > 1) {
          696                 replay_file = argv[1];
          697                 read_events_from_file(replay_file);
          698                 replay_start();
          699                 replay_stop();
          700                 run(); /* keep running */
          701         } else {
          702                 run(); /* keep running */
          703         }
          704 
          705         XCloseDisplay(dpy);
          706 
          707         return 0;
          708 }