summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFrediano Ziglio <fziglio@redhat.com>2015-08-03 20:53:35 +0100
committerFrediano Ziglio <fziglio@redhat.com>2015-08-03 20:53:35 +0100
commite8e04507d1fd3c34af9c3066671eb6bdddb43247 (patch)
tree7d85f4603ce1336a9aa78182e04f27bb13197f67
parent514085e1a4153c71a9492ff92239c4a5b1120cf0 (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.am5
-rw-r--r--configure.ac5
-rw-r--r--global.h7
-rw-r--r--play.c20
-rw-r--r--play.cpp131
-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
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 <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);
+ }
+ }
+
+}
diff --git a/record.c b/record.cpp
index e313e73..8cc2fd2 100644
--- a/record.c
+++ b/record.cpp
@@ -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: