summaryrefslogtreecommitdiff
path: root/record.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'record.cpp')
-rw-r--r--record.cpp275
1 files changed, 275 insertions, 0 deletions
diff --git a/record.cpp b/record.cpp
new file mode 100644
index 0000000..8cc2fd2
--- /dev/null
+++ b/record.cpp
@@ -0,0 +1,275 @@
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <time.h>
+#include <X11/Xlib.h>
+#include <X11/extensions/XInput.h>
+#ifdef HAVE_XI2
+#include <X11/extensions/XInput2.h>
+#endif
+
+#include "global.h"
+
+static FILE *f_out = NULL;
+static volatile int done = 0;
+static int has_focus = 0, inside = 0;
+static Window win = 0;
+static int x, y, width, height;
+
+namespace {
+class Record {
+public:
+ Record(unsigned int type) {
+ add_number(START_RECORD);
+ add_number(type);
+ add_time();
+ };
+ ~Record() { add_number(END_RECORD); } ;
+ void add_field(unsigned type, unsigned value) {
+ add_number(FIELD);
+ add_number(type);
+ add_number(value);
+ }
+private:
+ enum { START_RECORD = 1, END_RECORD = 2, FIELD = 3 };
+ void add_number(unsigned);
+ void add_time();
+};
+};
+
+void
+Record::add_number(unsigned num)
+{
+ fwrite(&num, sizeof(num), 1, f_out);
+}
+
+void
+Record::add_time()
+{
+ unsigned tm;
+ struct timespec ts;
+
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ tm = ts.tv_sec * 1000u + ts.tv_nsec / 1000000u;
+ add_number(tm);
+}
+
+static void
+sig_end(int sig)
+{
+ done = 1;
+}
+
+static void
+get_win_pos(void)
+{
+ int win_x, win_y;
+ Window child;
+ XWindowAttributes attr;
+ XGetWindowAttributes(dpy, win, &attr);
+
+ if (!XTranslateCoordinates(dpy, win, DefaultRootWindow(dpy), attr.x, attr.y, &win_x, &win_y, &child)) {
+ fprintf(stderr, "Error XTranslateCoordinates\n");
+ exit(EXIT_FAILURE);
+ }
+ x = win_x;
+ y = win_y;
+ width = attr.width;
+ height = attr.height;
+}
+
+static void
+handle_xi_mouse(XIDeviceEvent* ev)
+{
+ int mouse_x = (int) ev->root_x;
+ if (mouse_x < x || mouse_x - x >= width)
+ return;
+
+ int mouse_y = (int) ev->root_y;
+ if (mouse_y < y || mouse_y - y >= height)
+ return;
+
+ if (!inside)
+ return;
+
+ unsigned mask = 0;
+ for (unsigned n = 0; n < ev->buttons.mask_len && n < 4; ++n)
+ mask |= ev->buttons.mask[n] << (n*8u);
+
+ Record r(ev->evtype);
+ r.add_field(1, mouse_x);
+ r.add_field(2, mouse_y);
+ r.add_field(3, ev->mods.effective);
+ r.add_field(4, mask);
+
+ if (verbose == 1)
+ printf("Mouse moved at (%d,%d)\n", mouse_x, mouse_y);
+}
+
+static void
+handle_xi_keys(XIDeviceEvent* ev)
+{
+ Record r(ev->evtype);
+ r.add_field(1, ev->root_x);
+ r.add_field(2, ev->root_y);
+ r.add_field(3, ev->mods.effective);
+ r.add_field(4, ev->detail);
+}
+
+static void
+handle_xinput(XGenericEventCookie *cookie)
+{
+ if (verbose >= 2)
+ xinput_dump(dpy, cookie);
+
+ switch (cookie->evtype) {
+ case XI_KeyPress:
+ case XI_KeyRelease:
+ if (has_focus) {
+ xinput_dump(dpy, cookie);
+ handle_xi_keys((XIDeviceEvent*) cookie->data);
+ }
+ break;
+ case XI_ButtonPress:
+ case XI_ButtonRelease:
+ case XI_Motion:
+ handle_xi_mouse((XIDeviceEvent*) cookie->data);
+ break;
+ /* don't care about these */
+ case XI_RawKeyPress:
+ case XI_RawKeyRelease:
+ case XI_RawButtonPress:
+ case XI_RawButtonRelease:
+ case XI_RawTouchBegin:
+ case XI_RawTouchUpdate:
+ case XI_RawTouchEnd:
+ case XI_RawMotion:
+ break;
+ default:
+ if (verbose == 1)
+ xinput_dump(dpy, cookie);
+ break;
+ }
+}
+
+static void
+handle_ConfigureNotify(XEvent *event)
+{
+ XConfigureEvent *e = &event->xconfigure;
+
+ if (e->window == win) {
+ x = e->x;
+ y = e->y;
+ width = e->width;
+ height = e->height;
+ }
+}
+
+static void
+handle_xev(XEvent *ev)
+{
+ if (verbose >= 2)
+ xev_dump(ev);
+
+ switch (ev->type) {
+ case FocusIn:
+ if (ev->xfocus.window == win)
+ has_focus = 1;
+ break;
+ case FocusOut:
+ if (ev->xfocus.window == win)
+ has_focus = 0;
+ break;
+ case EnterNotify:
+ if (ev->xcrossing.window == win)
+ inside = 1;
+ break;
+ case LeaveNotify:
+ if (ev->xcrossing.window == win)
+ inside = 0;
+ break;
+ case DestroyNotify:
+ if (ev->xdestroywindow.window == win)
+ done = 1;
+ break;
+ case ConfigureNotify:
+ handle_ConfigureNotify(ev);
+ break;
+ /* don't care */
+ case KeyPress:
+ case KeyRelease:
+ case ButtonPress:
+ case ButtonRelease:
+ case MotionNotify:
+ case ColormapNotify:
+ break;
+ case KeymapNotify:
+ case Expose:
+ case GraphicsExpose:
+ case NoExpose:
+ case VisibilityNotify:
+ case CreateNotify: /* see if our client was created ? */
+ case UnmapNotify:
+ case MapNotify:
+ case MapRequest:
+ case ReparentNotify:
+ case ConfigureRequest:
+ case GravityNotify:
+ case ResizeRequest:
+ case CirculateNotify:
+ case CirculateRequest:
+ case PropertyNotify: /* handle status change (visible/not) ? */
+ case SelectionClear:
+ case SelectionRequest:
+ case SelectionNotify:
+ case ClientMessage:
+ case MappingNotify:
+ if (verbose == 1)
+ xev_dump(ev);
+ break;
+ }
+}
+
+void record(const char *fn_out, Window w)
+{
+ if (w == 0) {
+ fprintf (stderr, "record mode require a window to be specified\n");
+ exit(EXIT_FAILURE);
+ }
+ win = w;
+
+ f_out = fopen(fn_out, "wb");
+ if (!f_out) {
+ fprintf (stderr, "unable to open output file\n");
+ exit(EXIT_FAILURE);
+ }
+
+ xev_init(win);
+ xinput_init(dpy, win);
+
+ /* TODO get focus and if mouse inside */
+ get_win_pos();
+
+ signal(SIGINT, sig_end);
+ signal(SIGTERM, sig_end);
+
+ while (!done) {
+ XEvent event;
+
+ XNextEvent (dpy, &event);
+
+ if (is_xinput_event(&event)) {
+ XGenericEventCookie *cookie = &event.xcookie;
+ if (XGetEventData(dpy, cookie)) {
+ handle_xinput(cookie);
+ XFreeEventData(dpy, cookie);
+ }
+ } else {
+ handle_xev(&event);
+ }
+ }
+
+ fclose(f_out);
+}