diff options
author | Frediano Ziglio <fziglio@redhat.com> | 2015-08-03 20:53:35 +0100 |
---|---|---|
committer | Frediano Ziglio <fziglio@redhat.com> | 2015-08-03 20:53:35 +0100 |
commit | e8e04507d1fd3c34af9c3066671eb6bdddb43247 (patch) | |
tree | 7d85f4603ce1336a9aa78182e04f27bb13197f67 | |
parent | 514085e1a4153c71a9492ff92239c4a5b1120cf0 (diff) |
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 <fziglio@redhat.com>
-rw-r--r-- | Makefile.am | 5 | ||||
-rw-r--r-- | configure.ac | 5 | ||||
-rw-r--r-- | global.h | 7 | ||||
-rw-r--r-- | play.c | 20 | ||||
-rw-r--r-- | play.cpp | 131 | ||||
-rw-r--r-- | record.cpp (renamed from record.c) | 83 |
6 files changed, 215 insertions, 36 deletions
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 @@ -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 @@ -1,20 +0,0 @@ -#include "config.h" -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <X11/Xlib.h> - -#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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.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_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); + } + } + +} @@ -2,8 +2,8 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <getopt.h> #include <signal.h> +#include <time.h> #include <X11/Xlib.h> #include <X11/extensions/XInput.h> #ifdef HAVE_XI2 @@ -18,6 +18,44 @@ 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) { @@ -43,24 +81,41 @@ get_win_pos(void) } static void -handle_motion(XIDeviceEvent* ev) +handle_xi_mouse(XIDeviceEvent* ev) { - int mouse_x, mouse_y; - - mouse_x = (int) ev->root_x; + int mouse_x = (int) ev->root_x; if (mouse_x < x || mouse_x - x >= width) return; - mouse_y = (int) ev->root_y; + + int mouse_y = (int) ev->root_y; if (mouse_y < y || mouse_y - y >= height) return; - if (inside) + + 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* event) +handle_xi_keys(XIDeviceEvent* ev) { - printf("Got a key\n"); + 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 @@ -72,15 +127,15 @@ handle_xinput(XGenericEventCookie *cookie) switch (cookie->evtype) { case XI_KeyPress: case XI_KeyRelease: - if (has_focus) - handle_xi_keys(cookie->data); + if (has_focus) { + xinput_dump(dpy, cookie); + handle_xi_keys((XIDeviceEvent*) cookie->data); + } break; case XI_ButtonPress: - break; case XI_ButtonRelease: - break; case XI_Motion: - handle_motion(cookie->data); + handle_xi_mouse((XIDeviceEvent*) cookie->data); break; /* don't care about these */ case XI_RawKeyPress: |