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 }