diff options
Diffstat (limited to 'record.cpp')
-rw-r--r-- | record.cpp | 275 |
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); +} |