From e8e04507d1fd3c34af9c3066671eb6bdddb43247 Mon Sep 17 00:00:00 2001 From: Frediano Ziglio Date: Mon, 3 Aug 2015 20:53:35 +0100 Subject: Start implementing saving to file and replay Save to a file the event. Compile some files (record and play) with C++ compiler. Replay some data (keyboard seems to work). Signed-off-by: Frediano Ziglio --- Makefile.am | 5 +- configure.ac | 5 ++ global.h | 7 ++ play.c | 20 ----- play.cpp | 131 ++++++++++++++++++++++++++++ record.c | 220 ----------------------------------------------- record.cpp | 275 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 421 insertions(+), 242 deletions(-) delete mode 100644 play.c create mode 100644 play.cpp delete mode 100644 record.c create mode 100644 record.cpp diff --git a/Makefile.am b/Makefile.am index 56e1da3..4f256a0 100644 --- a/Makefile.am +++ b/Makefile.am @@ -23,10 +23,11 @@ bin_PROGRAMS = spice-replay AM_CFLAGS = $(CWARNFLAGS) $(REPLAY_CFLAGS) spice_replay_LDADD = $(REPLAY_LIBS) +spice_replay_LINK = $(LINK) spice_replay_SOURCES = \ spice-replay.c \ - record.c \ - play.c \ + record.cpp \ + play.cpp \ xev.c \ test_xi2.c diff --git a/configure.ac b/configure.ac index b64ff96..e859faa 100644 --- a/configure.ac +++ b/configure.ac @@ -27,6 +27,11 @@ AC_CONFIG_SRCDIR([Makefile.am]) AC_CONFIG_HEADERS([config.h]) AC_USE_SYSTEM_EXTENSIONS +AC_PROG_CXX +AC_LANG_PUSH([C++]) +AX_APPEND_COMPILE_FLAGS([-fno-exceptions -fno-rtti -fcheck-new --no_rtti]) +AC_LANG_POP([C++]) + # Initialize Automake AM_INIT_AUTOMAKE([foreign dist-bzip2]) AM_MAINTAINER_MODE diff --git a/global.h b/global.h index 14edb58..ab27073 100644 --- a/global.h +++ b/global.h @@ -1,3 +1,6 @@ +#ifdef __cplusplus +extern "C" { +#endif extern Display *dpy; extern int verbose; @@ -19,3 +22,7 @@ is_xinput_event(XEvent *event) { return event->type == GenericEvent && event->xgeneric.extension == xi_opcode; } + +#ifdef __cplusplus +} +#endif diff --git a/play.c b/play.c deleted file mode 100644 index 119aa00..0000000 --- a/play.c +++ /dev/null @@ -1,20 +0,0 @@ -#include "config.h" -#include -#include -#include -#include - -#include "global.h" - -void play(const char *fn, Window win) -{ -#if 0 - if (win == 0) { - fprintf (stderr, "play mode require a window to be specified\n"); - exit(EXIT_FAILURE); - } -#endif - - fprintf (stderr, "playback mode still not supported\n"); - exit(EXIT_FAILURE); -} diff --git a/play.cpp b/play.cpp new file mode 100644 index 0000000..8c3ce44 --- /dev/null +++ b/play.cpp @@ -0,0 +1,131 @@ +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_XI2 +#include +#endif + +#include "global.h" + +static FILE *f_in = NULL; + +namespace { +class Record { +public: + Record() { + get_number(START_RECORD); + type = get_number(); + time = get_number(); + }; + unsigned get_type() const { return type; } + unsigned get_time() const { return time; } + ~Record() { get_number(END_RECORD); } ; + unsigned get_field(unsigned type) { + get_number(FIELD); + get_number(type); + return get_number(); + } +private: + unsigned type, time; + enum { START_RECORD = 1, END_RECORD = 2, FIELD = 3 }; + unsigned get_number(); + void get_number(unsigned); +}; +}; + +unsigned +Record::get_number() +{ + unsigned num; + if (fread(&num, sizeof(num), 1, f_in) != 1) { + fprintf (stderr, "end of file\n"); + exit(EXIT_FAILURE); + } + return num; +} + +void +Record::get_number(unsigned n) +{ + if (get_number() != n) { + fprintf (stderr, "expected %u in file position %lx\n", n, (unsigned long) ftell(f_in)); + exit(EXIT_FAILURE); + } +} + +void play(const char *fn, Window win) +{ + if (win == 0) { + fprintf (stderr, "play mode require a window to be specified\n"); + exit(EXIT_FAILURE); + } + + f_in = fopen(fn, "rb"); + if (!f_in) { + fprintf (stderr, "unable to open input file\n"); + exit(EXIT_FAILURE); + } + + unsigned prev_time = 0; + while (1) { + Record rec; + + unsigned tm = rec.get_time(); + if (prev_time) { + unsigned diff = tm - prev_time; + struct timespec ts = { diff / 1000u, diff % 1000u * 1000000u }; + nanosleep(&ts, NULL); + } + prev_time = tm; + + XEvent ev; + memset(&ev, 0, sizeof(ev)); + ev.xkey.display = dpy; + ev.xkey.window = win; + ev.xkey.time = CurrentTime; + ev.xkey.root = DefaultRootWindow(dpy); + + switch (rec.get_type()) { + case XI_KeyPress: + case XI_KeyRelease: + ev.type = rec.get_type() == XI_KeyPress ? KeyPress : KeyRelease; + ev.xkey.x = rec.get_field(1); + ev.xkey.x_root = ev.xkey.x; + ev.xkey.y = rec.get_field(2); + ev.xkey.y_root = ev.xkey.y; + ev.xkey.state = rec.get_field(3); + ev.xkey.keycode = rec.get_field(4); + ev.xkey.same_screen = True; + XSendEvent(dpy, win, False, 0, &ev); + XFlush(dpy); + break; + case XI_ButtonPress: + ev.type = ButtonPress; + goto mouse; + case XI_ButtonRelease: + ev.type = ButtonRelease; + goto mouse; + case XI_Motion: + ev.type = MotionNotify; + mouse: + ev.xkey.x = rec.get_field(1); + ev.xkey.x_root = ev.xkey.x; + ev.xkey.y = rec.get_field(2); + ev.xkey.y_root = ev.xkey.y; + ev.xkey.state = rec.get_field(3); + rec.get_field(4); + XSendEvent(dpy, win, False, 0, &ev); + XFlush(dpy); + break; + default: + fprintf (stderr, "unknown type %u\n", rec.get_type()); + exit(EXIT_FAILURE); + } + } + +} diff --git a/record.c b/record.c deleted file mode 100644 index e313e73..0000000 --- a/record.c +++ /dev/null @@ -1,220 +0,0 @@ -#include "config.h" -#include -#include -#include -#include -#include -#include -#include -#ifdef HAVE_XI2 -#include -#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; - -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_motion(XIDeviceEvent* ev) -{ - int mouse_x, mouse_y; - - mouse_x = (int) ev->root_x; - if (mouse_x < x || mouse_x - x >= width) - return; - mouse_y = (int) ev->root_y; - if (mouse_y < y || mouse_y - y >= height) - return; - if (inside) - printf("Mouse moved at (%d,%d)\n", mouse_x, mouse_y); -} - -static void -handle_xi_keys(XIDeviceEvent* event) -{ - printf("Got a key\n"); -} - -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) - handle_xi_keys(cookie->data); - break; - case XI_ButtonPress: - break; - case XI_ButtonRelease: - break; - case XI_Motion: - handle_motion(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); -} 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 +#include +#include +#include +#include +#include +#include +#ifdef HAVE_XI2 +#include +#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); +} -- cgit v1.2.3