Skip to content

Commit

Permalink
replay: recording of the user input
Browse files Browse the repository at this point in the history
This records user input (keyboard and mouse events) in record mode and replays
these input events in replay mode.

Signed-off-by: Pavel Dovgalyuk <[email protected]>
Message-Id: <[email protected]>
Signed-off-by: Paolo Bonzini <[email protected]>
Signed-off-by: Pavel Dovgalyuk <[email protected]>
  • Loading branch information
Dovgalyuk authored and bonzini committed Nov 6, 2015
1 parent 4c27b85 commit ee31299
Show file tree
Hide file tree
Showing 7 changed files with 232 additions and 8 deletions.
4 changes: 4 additions & 0 deletions include/sysemu/replay.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,5 +112,9 @@ void replay_disable_events(void);
bool replay_events_enabled(void);
/*! Adds bottom half event to the queue */
void replay_bh_schedule_event(QEMUBH *bh);
/*! Adds input event to the queue */
void replay_input_event(QemuConsole *src, InputEvent *evt);
/*! Adds input sync event to the queue */
void replay_input_sync_event(void);

#endif
2 changes: 2 additions & 0 deletions include/ui/input.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ void qemu_input_handler_bind(QemuInputHandlerState *s,
const char *device_id, int head,
Error **errp);
void qemu_input_event_send(QemuConsole *src, InputEvent *evt);
void qemu_input_event_send_impl(QemuConsole *src, InputEvent *evt);
void qemu_input_event_sync(void);
void qemu_input_event_sync_impl(void);

InputEvent *qemu_input_event_new_key(KeyValue *key, bool down);
void qemu_input_event_send_key(QemuConsole *src, KeyValue *key, bool down);
Expand Down
1 change: 1 addition & 0 deletions replay/Makefile.objs
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ common-obj-y += replay.o
common-obj-y += replay-internal.o
common-obj-y += replay-events.o
common-obj-y += replay-time.o
common-obj-y += replay-input.o
33 changes: 33 additions & 0 deletions replay/replay-events.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "sysemu/replay.h"
#include "replay-internal.h"
#include "block/aio.h"
#include "ui/input.h"

typedef struct Event {
ReplayAsyncEventKind event_kind;
Expand All @@ -39,6 +40,13 @@ static void replay_run_event(Event *event)
case REPLAY_ASYNC_EVENT_BH:
aio_bh_call(event->opaque);
break;
case REPLAY_ASYNC_EVENT_INPUT:
qemu_input_event_send_impl(NULL, (InputEvent *)event->opaque);
qapi_free_InputEvent((InputEvent *)event->opaque);
break;
case REPLAY_ASYNC_EVENT_INPUT_SYNC:
qemu_input_event_sync_impl();
break;
default:
error_report("Replay: invalid async event ID (%d) in the queue",
event->event_kind);
Expand Down Expand Up @@ -131,6 +139,16 @@ void replay_bh_schedule_event(QEMUBH *bh)
}
}

void replay_add_input_event(struct InputEvent *event)
{
replay_add_event(REPLAY_ASYNC_EVENT_INPUT, event, NULL, 0);
}

void replay_add_input_sync_event(void)
{
replay_add_event(REPLAY_ASYNC_EVENT_INPUT_SYNC, NULL, NULL, 0);
}

static void replay_save_event(Event *event, int checkpoint)
{
if (replay_mode != REPLAY_MODE_PLAY) {
Expand All @@ -144,6 +162,11 @@ static void replay_save_event(Event *event, int checkpoint)
case REPLAY_ASYNC_EVENT_BH:
replay_put_qword(event->id);
break;
case REPLAY_ASYNC_EVENT_INPUT:
replay_save_input_event(event->opaque);
break;
case REPLAY_ASYNC_EVENT_INPUT_SYNC:
break;
default:
error_report("Unknown ID %d of replay event", read_event_kind);
exit(1);
Expand Down Expand Up @@ -187,6 +210,16 @@ static Event *replay_read_event(int checkpoint)
read_id = replay_get_qword();
}
break;
case REPLAY_ASYNC_EVENT_INPUT:
event = g_malloc0(sizeof(Event));
event->event_kind = read_event_kind;
event->opaque = replay_read_input_event();
return event;
case REPLAY_ASYNC_EVENT_INPUT_SYNC:
event = g_malloc0(sizeof(Event));
event->event_kind = read_event_kind;
event->opaque = 0;
return event;
default:
error_report("Unknown ID %d of replay event", read_event_kind);
exit(1);
Expand Down
160 changes: 160 additions & 0 deletions replay/replay-input.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
/*
* replay-input.c
*
* Copyright (c) 2010-2015 Institute for System Programming
* of the Russian Academy of Sciences.
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*
*/

#include "qemu-common.h"
#include "sysemu/replay.h"
#include "replay-internal.h"
#include "qemu/notify.h"
#include "ui/input.h"
#include "qapi/qmp-output-visitor.h"
#include "qapi/qmp-input-visitor.h"
#include "qapi-visit.h"

static InputEvent *qapi_clone_InputEvent(InputEvent *src)
{
QmpOutputVisitor *qov;
QmpInputVisitor *qiv;
Visitor *ov, *iv;
QObject *obj;
InputEvent *dst = NULL;

qov = qmp_output_visitor_new();
ov = qmp_output_get_visitor(qov);
visit_type_InputEvent(ov, &src, NULL, &error_abort);
obj = qmp_output_get_qobject(qov);
qmp_output_visitor_cleanup(qov);
if (!obj) {
return NULL;
}

qiv = qmp_input_visitor_new(obj);
iv = qmp_input_get_visitor(qiv);
visit_type_InputEvent(iv, &dst, NULL, &error_abort);
qmp_input_visitor_cleanup(qiv);
qobject_decref(obj);

return dst;
}

void replay_save_input_event(InputEvent *evt)
{
replay_put_dword(evt->type);

switch (evt->type) {
case INPUT_EVENT_KIND_KEY:
replay_put_dword(evt->u.key->key->type);

switch (evt->u.key->key->type) {
case KEY_VALUE_KIND_NUMBER:
replay_put_qword(evt->u.key->key->u.number);
replay_put_byte(evt->u.key->down);
break;
case KEY_VALUE_KIND_QCODE:
replay_put_dword(evt->u.key->key->u.qcode);
replay_put_byte(evt->u.key->down);
break;
case KEY_VALUE_KIND_MAX:
/* keep gcc happy */
break;
}
break;
case INPUT_EVENT_KIND_BTN:
replay_put_dword(evt->u.btn->button);
replay_put_byte(evt->u.btn->down);
break;
case INPUT_EVENT_KIND_REL:
replay_put_dword(evt->u.rel->axis);
replay_put_qword(evt->u.rel->value);
break;
case INPUT_EVENT_KIND_ABS:
replay_put_dword(evt->u.abs->axis);
replay_put_qword(evt->u.abs->value);
break;
case INPUT_EVENT_KIND_MAX:
/* keep gcc happy */
break;
}
}

InputEvent *replay_read_input_event(void)
{
InputEvent evt;
KeyValue keyValue;
InputKeyEvent key;
key.key = &keyValue;
InputBtnEvent btn;
InputMoveEvent rel;
InputMoveEvent abs;

evt.type = replay_get_dword();
switch (evt.type) {
case INPUT_EVENT_KIND_KEY:
evt.u.key = &key;
evt.u.key->key->type = replay_get_dword();

switch (evt.u.key->key->type) {
case KEY_VALUE_KIND_NUMBER:
evt.u.key->key->u.number = replay_get_qword();
evt.u.key->down = replay_get_byte();
break;
case KEY_VALUE_KIND_QCODE:
evt.u.key->key->u.qcode = (QKeyCode)replay_get_dword();
evt.u.key->down = replay_get_byte();
break;
case KEY_VALUE_KIND_MAX:
/* keep gcc happy */
break;
}
break;
case INPUT_EVENT_KIND_BTN:
evt.u.btn = &btn;
evt.u.btn->button = (InputButton)replay_get_dword();
evt.u.btn->down = replay_get_byte();
break;
case INPUT_EVENT_KIND_REL:
evt.u.rel = &rel;
evt.u.rel->axis = (InputAxis)replay_get_dword();
evt.u.rel->value = replay_get_qword();
break;
case INPUT_EVENT_KIND_ABS:
evt.u.abs = &abs;
evt.u.abs->axis = (InputAxis)replay_get_dword();
evt.u.abs->value = replay_get_qword();
break;
case INPUT_EVENT_KIND_MAX:
/* keep gcc happy */
break;
}

return qapi_clone_InputEvent(&evt);
}

void replay_input_event(QemuConsole *src, InputEvent *evt)
{
if (replay_mode == REPLAY_MODE_PLAY) {
/* Nothing */
} else if (replay_mode == REPLAY_MODE_RECORD) {
replay_add_input_event(qapi_clone_InputEvent(evt));
} else {
qemu_input_event_send_impl(src, evt);
}
}

void replay_input_sync_event(void)
{
if (replay_mode == REPLAY_MODE_PLAY) {
/* Nothing */
} else if (replay_mode == REPLAY_MODE_RECORD) {
replay_add_input_sync_event();
} else {
qemu_input_event_sync_impl();
}
}
13 changes: 13 additions & 0 deletions replay/replay-internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ enum ReplayEvents {

enum ReplayAsyncEventKind {
REPLAY_ASYNC_EVENT_BH,
REPLAY_ASYNC_EVENT_INPUT,
REPLAY_ASYNC_EVENT_INPUT_SYNC,
REPLAY_ASYNC_COUNT
};

Expand Down Expand Up @@ -124,4 +126,15 @@ void replay_save_events(int checkpoint);
/*! Read events from the file into the input queue */
void replay_read_events(int checkpoint);

/* Input events */

/*! Saves input event to the log */
void replay_save_input_event(InputEvent *evt);
/*! Reads input event from the log */
InputEvent *replay_read_input_event(void);
/*! Adds input event to the queue */
void replay_add_input_event(struct InputEvent *event);
/*! Adds input sync event to the queue */
void replay_add_input_sync_event(void);

#endif
27 changes: 19 additions & 8 deletions ui/input.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "trace.h"
#include "ui/input.h"
#include "ui/console.h"
#include "sysemu/replay.h"

struct QemuInputHandlerState {
DeviceState *dev;
Expand Down Expand Up @@ -300,14 +301,10 @@ static void qemu_input_queue_sync(struct QemuInputEventQueueHead *queue)
QTAILQ_INSERT_TAIL(queue, item, node);
}

void qemu_input_event_send(QemuConsole *src, InputEvent *evt)
void qemu_input_event_send_impl(QemuConsole *src, InputEvent *evt)
{
QemuInputHandlerState *s;

if (!runstate_is_running() && !runstate_check(RUN_STATE_SUSPENDED)) {
return;
}

qemu_input_event_trace(src, evt);

/* pre processing */
Expand All @@ -324,14 +321,19 @@ void qemu_input_event_send(QemuConsole *src, InputEvent *evt)
s->events++;
}

void qemu_input_event_sync(void)
void qemu_input_event_send(QemuConsole *src, InputEvent *evt)
{
QemuInputHandlerState *s;

if (!runstate_is_running() && !runstate_check(RUN_STATE_SUSPENDED)) {
return;
}

replay_input_event(src, evt);
}

void qemu_input_event_sync_impl(void)
{
QemuInputHandlerState *s;

trace_input_event_sync();

QTAILQ_FOREACH(s, &handlers, node) {
Expand All @@ -345,6 +347,15 @@ void qemu_input_event_sync(void)
}
}

void qemu_input_event_sync(void)
{
if (!runstate_is_running() && !runstate_check(RUN_STATE_SUSPENDED)) {
return;
}

replay_input_sync_event();
}

InputEvent *qemu_input_event_new_key(KeyValue *key, bool down)
{
InputEvent *evt = g_new0(InputEvent, 1);
Expand Down

0 comments on commit ee31299

Please sign in to comment.