From 76a433273ec60ab6c7c407901e9bd944cc8e4cc6 Mon Sep 17 00:00:00 2001 From: Daniel Zaoui Date: Thu, 4 Jan 2018 17:16:14 +0200 Subject: [PATCH] Introduce a new recorder not using LD_PRELOAD --- src/bin/.gitignore | 1 + src/bin/Makefile.am | 8 +- src/bin/recorder.c | 481 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 489 insertions(+), 1 deletion(-) create mode 100644 src/bin/recorder.c diff --git a/src/bin/.gitignore b/src/bin/.gitignore index 9dd964a..627dcbf 100644 --- a/src/bin/.gitignore +++ b/src/bin/.gitignore @@ -1,4 +1,5 @@ /exactness /exactness_helper /exactness_play +/exactness_record /exactness_canvas.eo.* diff --git a/src/bin/Makefile.am b/src/bin/Makefile.am index ed2d12f..089cea4 100644 --- a/src/bin/Makefile.am +++ b/src/bin/Makefile.am @@ -1,6 +1,6 @@ MAINTAINERCLEANFILES = Makefile.in -bin_PROGRAMS = exactness exactness_helper exactness_play +bin_PROGRAMS = exactness exactness_helper exactness_play exactness_record exactness_SOURCES = \ exactness.c \ @@ -13,6 +13,8 @@ exactness_helper_SOURCES = exactness_helper.c exactness_play_SOURCES = player.c ../lib/tsuite_file_data.c ../lib/tsuite_common.c +exactness_record_SOURCES = recorder.c ../lib/tsuite_file_data.c ../lib/tsuite_common.c + exactness_LDADD = \ @EFL_LIBS@ ../lib/libexactness_player.la @@ -21,6 +23,8 @@ exactness_helper_LDADD = \ exactness_play_LDADD = @EFL_LIBS@ +exactness_record_LDADD = @EFL_LIBS@ + exactness_CFLAGS = \ @EFL_CFLAGS@ \ -I$(top_srcdir)/src/lib \ @@ -30,3 +34,5 @@ exactness_CFLAGS = \ exactness_helper_CFLAGS = @EFL_CFLAGS@ -I$(top_srcdir)/src/lib exactness_play_CFLAGS = @EFL_CFLAGS@ -I$(top_srcdir)/src/lib + +exactness_record_CFLAGS = @EFL_CFLAGS@ -I$(top_srcdir)/src/lib diff --git a/src/bin/recorder.c b/src/bin/recorder.c new file mode 100644 index 0000000..f8805eb --- /dev/null +++ b/src/bin/recorder.c @@ -0,0 +1,481 @@ +#define _GNU_SOURCE 1 +#define EFL_EO_API_SUPPORT +#define EFL_BETA_API_SUPPORT +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "tsuite_file_data.h" +#include "exactness_private.h" + +#define MAX_PATH 1024 +#define SHOT_KEY_STR "F2" +#define SAVE_KEY_STR "F3" + +<<<<<<< HEAD +#include +#include "exactness_canvas.eo.h" + +static const char *_rec_filename = NULL; +======= +static Evas *(*_evas_new)(void) = NULL; +static const char *_out_filename = NULL; +>>>>>>> 0cce6b6... SqR +static const char *_test_name = NULL; +static int _verbose = 0; + +static Eina_List *_evas_list = NULL; +static int _last_evas_id = 0; + +static Lists_st *_events_list = NULL; + +static char *_shot_key = NULL; + +static void +_printf(int verbose, const char *fmt, ...) +{ + va_list ap; + if (!_verbose || verbose > _verbose) return; + + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); +} + +static Tsuite_Event_Type +_event_pointer_type_get(Efl_Pointer_Action t) +{ + switch(t) + { + case EFL_POINTER_ACTION_IN: return TSUITE_EVENT_MOUSE_IN; + case EFL_POINTER_ACTION_OUT: return TSUITE_EVENT_MOUSE_OUT; + case EFL_POINTER_ACTION_DOWN: return TSUITE_EVENT_MULTI_DOWN; + case EFL_POINTER_ACTION_UP: return TSUITE_EVENT_MULTI_UP; + case EFL_POINTER_ACTION_MOVE: return TSUITE_EVENT_MULTI_MOVE; + case EFL_POINTER_ACTION_WHEEL: return TSUITE_EVENT_MOUSE_WHEEL; + default: + return TSUITE_EVENT_NOT_SUPPORTED; + } +} + +static const char * +_event_name_get(Tsuite_Event_Type type) +{ + switch(type) + { + case TSUITE_EVENT_MOUSE_IN: return "Mouse In"; + case TSUITE_EVENT_MOUSE_OUT: return "Mouse Out"; + case TSUITE_EVENT_MOUSE_DOWN: return "Mouse Down"; + case TSUITE_EVENT_MOUSE_UP: return "Mouse Up"; + case TSUITE_EVENT_MOUSE_MOVE: return "Mouse Move"; + case TSUITE_EVENT_MOUSE_WHEEL: return "Mouse Wheel"; + case TSUITE_EVENT_MULTI_DOWN: return "Multi Down"; + case TSUITE_EVENT_MULTI_UP: return "Multi Up"; + case TSUITE_EVENT_MULTI_MOVE: return "Multi Move"; + case TSUITE_EVENT_KEY_DOWN: return "Key Down"; + case TSUITE_EVENT_KEY_UP: return "Key Up"; + case TSUITE_EVENT_KEY_DOWN_WITH_KEYCODE: return "Key Down with Keycode"; + case TSUITE_EVENT_KEY_UP_WITH_KEYCODE: return "Key Up with Keycode"; + case TSUITE_EVENT_TAKE_SHOT: return "Take shot"; + default: return NULL; + } +} + +static Eina_Bool +_is_hook_duplicate(const Variant_st *v, Tsuite_Event_Type ev_type, const void *info, int len) +{ + if (v->t.type == tsuite_event_mapping_type_str_get(ev_type) && + !memcmp(v->data, info, len)) return EINA_TRUE; + return EINA_FALSE; +} + +/* Adding variant to list, this list is later written to EET file */ +#define ADD_TO_LIST(EVT_TYPE, INFO) \ + do { /* This macro will add event to EET data list */ \ + if (_events_list) \ + { \ + const Variant_st *prev_v = eina_list_last_data_get(_events_list->variant_list); \ + if (!prev_v || !_is_hook_duplicate(prev_v, EVT_TYPE, &INFO, sizeof(INFO))) \ + { \ + _printf(1, "Recording %s\n", tsuite_event_mapping_type_str_get(EVT_TYPE)); \ + Variant_st *v = malloc(sizeof(Variant_st)); \ + v->data = malloc(sizeof(INFO)); \ + _variant_type_set(tsuite_event_mapping_type_str_get(EVT_TYPE), \ + &v->t, EINA_FALSE); \ + memcpy(v->data, &INFO, sizeof(INFO)); \ + _events_list->variant_list = eina_list_append(_events_list->variant_list, v); \ + } \ + } \ + } while (0) + +static int +_evas_id_get(Evas *e) +{ + return (intptr_t)efl_key_data_get(e, "__evas_id"); +} + +static void +_event_pointer_cb(void *data, const Efl_Event *event) +{ + Eo *eo_e = data; + Eo *evp = event->info; + if (!evp) return; + + int timestamp = efl_input_timestamp_get(evp); + int n_evas = _evas_id_get(eo_e); + Efl_Pointer_Action action = efl_input_pointer_action_get(evp); + Tsuite_Event_Type evt = _event_pointer_type_get(action); + + if (!timestamp) return; + + _printf(2, "Calling \"%s\" timestamp=<%u>\n", _event_name_get(evt), timestamp); + + switch (action) + { + case EFL_POINTER_ACTION_MOVE: + { + double rad = 0, radx = 0, rady = 0, pres = 0, ang = 0, fx = 0, fy = 0; + int tool = efl_input_pointer_tool_get(evp); + Eina_Position2D pos = efl_input_pointer_position_get(evp); + multi_move t = { tool, pos.x, pos.y, rad, radx, rady, pres, ang, fx, fy, timestamp, n_evas }; + if (t.n_evas >= 0) ADD_TO_LIST(evt, t); + break; + } + case EFL_POINTER_ACTION_DOWN: case EFL_POINTER_ACTION_UP: + { + double rad = 0, radx = 0, rady = 0, pres = 0, ang = 0, fx = 0, fy = 0; + int b = efl_input_pointer_button_get(evp); + int tool = efl_input_pointer_tool_get(evp); + Eina_Position2D pos = efl_input_pointer_position_get(evp); + Efl_Pointer_Flags flags = efl_input_pointer_button_flags_get(evp); + multi_event t = { tool, b, pos.x, pos.y, rad, radx, rady, pres, ang, + fx, fy, flags, timestamp, n_evas }; + if (t.n_evas >= 0) ADD_TO_LIST(evt, t); + break; + } + case EFL_POINTER_ACTION_IN: case EFL_POINTER_ACTION_OUT: + { + mouse_in_mouse_out t = { timestamp, n_evas }; + if (t.n_evas >= 0) ADD_TO_LIST(evt, t); + break; + } + case EFL_POINTER_ACTION_WHEEL: + { + Eina_Bool horiz = efl_input_pointer_wheel_horizontal_get(evp); + int z = efl_input_pointer_wheel_delta_get(evp); + mouse_wheel t = { horiz, z, timestamp, n_evas }; + if (t.n_evas >= 0) ADD_TO_LIST(evt, t); + break; + } + default: + break; + } +} + +static void +_event_key_cb(void *data, const Efl_Event *event) +{ + Efl_Input_Key *evk = event->info; + Eo *eo_e = data; + if (!evk) return; + const char *key = efl_input_key_name_get(evk); + int timestamp = efl_input_timestamp_get(evk); + unsigned int n_evas = _evas_id_get(eo_e); + Tsuite_Event_Type evt = TSUITE_EVENT_KEY_UP_WITH_KEYCODE; + + if (efl_input_key_pressed_get(evk)) + { + if (!strcmp(key, _shot_key)) + { + _printf(2, "Take Screenshot: %s timestamp=<%u>\n", __func__, timestamp); + take_screenshot t = { timestamp, n_evas }; + if (t.n_evas >= 0) ADD_TO_LIST(TSUITE_EVENT_TAKE_SHOT, t); + return; + } + if (!strcmp(key, SAVE_KEY_STR)) + { + if (_events_list) + write_events(_rec_filename, _events_list); + _printf(2, "Save events: %s timestamp=<%u>\n", __func__, timestamp); + return; + } + evt = TSUITE_EVENT_KEY_DOWN_WITH_KEYCODE; + } + else + { + if (!strcmp(key, _shot_key) || !strcmp(key, SAVE_KEY_STR)) return; + } + if (_events_list) + { /* Construct duplicate strings, free them when list if freed */ + key_down_key_up_with_keycode t; + t.timestamp = timestamp; + t.keyname = eina_stringshare_add(key); + t.key = eina_stringshare_add(efl_input_key_get(evk)); + t.string = eina_stringshare_add(efl_input_key_string_get(evk)); + t.compose = eina_stringshare_add(efl_input_key_compose_get(evk)); + t.keycode = efl_input_key_code_get(evk); + t.n_evas = n_evas; + if (t.n_evas >= 0) ADD_TO_LIST(evt, t); + } +} + +// note: "hold" event comes from above (elm), not below (ecore) +EFL_CALLBACKS_ARRAY_DEFINE(_event_pointer_callbacks, + { EFL_EVENT_POINTER_MOVE, _event_pointer_cb }, + { EFL_EVENT_POINTER_DOWN, _event_pointer_cb }, + { EFL_EVENT_POINTER_UP, _event_pointer_cb }, + { EFL_EVENT_POINTER_IN, _event_pointer_cb }, + { EFL_EVENT_POINTER_OUT, _event_pointer_cb }, + { EFL_EVENT_POINTER_WHEEL, _event_pointer_cb }, + { EFL_EVENT_FINGER_MOVE, _event_pointer_cb }, + { EFL_EVENT_FINGER_DOWN, _event_pointer_cb }, + { EFL_EVENT_FINGER_UP, _event_pointer_cb }, + { EFL_EVENT_KEY_DOWN, _event_key_cb }, + { EFL_EVENT_KEY_UP, _event_key_cb } + ) + +static Evas * +_my_evas_new(int w EINA_UNUSED, int h EINA_UNUSED) +{ + Evas *e; + if (!_evas_new) return NULL; + e = _evas_new(); + if (e) + { + _printf(1, "New Evas\n"); + _evas_list = eina_list_append(_evas_list, e); + efl_key_data_set(e, "__evas_id", (void *)(intptr_t)_last_evas_id++); + efl_event_callback_array_add(e, _event_pointer_callbacks(), e); + } + return e; +} + +static Eina_Bool +_prg_invoke(const char *full_path, int argc, char **argv) +{ + Eina_Value *ret__; + int real__; + + void (*efl_main)(void *data, const Efl_Event *ev); + int (*elm_main)(int argc, char **argv); + int (*c_main)(int argc, char **argv); + Eina_Module *h = eina_module_new(full_path); + if (!h || !eina_module_load(h)) + { + fprintf(stderr, "Failed loading %s.\n", full_path); + if (h) eina_module_free(h); + return EINA_FALSE; + } + efl_main = eina_module_symbol_get(h, "efl_main"); + elm_main = eina_module_symbol_get(h, "elm_main"); + c_main = eina_module_symbol_get(h, "main"); + _evas_new = eina_module_symbol_get(h, "evas_new"); + if (!_evas_new) + { + fprintf(stderr, "Failed loading symbol 'evas_new' from %s.\n", full_path); + eina_module_free(h); + return 1; + } + if (efl_main) + { + elm_init(argc, argv); + efl_event_callback_add(efl_main_loop_get(), EFL_LOOP_EVENT_ARGUMENTS, efl_main, NULL); + ret__ = efl_loop_begin(efl_main_loop_get()); + real__ = efl_loop_exit_code_process(ret__); + elm_shutdown(); + } + else if (elm_main) + { + elm_init(argc, argv); + real__ = elm_main(argc, argv); + elm_shutdown(); + } + else if (c_main) + { + real__ = c_main(argc, argv); + } + else + { + fprintf(stderr, "Failed loading symbol 'efl_main', 'elm_main' or 'main' from %s.\n", full_path); + eina_module_free(h); + real__ = 1; + } + return real__; +} + +static Eina_Stringshare * +_prg_full_path_guess(const char *prg) +{ + char full_path[MAX_PATH]; + if (strchr(prg, '/')) return eina_stringshare_add(prg); + char *paths = strdup(getenv("PATH")); + Eina_Stringshare *ret = NULL; + while (paths && *paths && !ret) + { + char *real_path; + char *colon = strchr(paths, ':'); + if (colon) *colon = '\0'; + + sprintf(full_path, "%s/%s", paths, prg); + real_path = ecore_file_realpath(full_path); + if (*real_path) + { + ret = eina_stringshare_add(real_path); + // check if executable + } + free(real_path); + + paths += strlen(paths); + if (colon) paths++; + } + return ret; +} + +static void +_print_usage(const char *progn, FILE *outf) +{ + fprintf(outf, "Usage: %s [options] [program]\n", progn); + fprintf(outf, "Options:\n" + " -t file.rec Name of the filename where to store the test\n" + " -v Verbose mode\n" + " -h Print this message and exit\n" + "\n"); +} + +static Eina_Bool +_mkdir(const char *dir) +{ + if (!ecore_file_exists(dir)) + { + const char *cur = dir + 1; + do + { + char *slash = strchr(cur, '/'); + if (slash) *slash = '\0'; + if (!ecore_file_exists(dir) && !ecore_file_mkdir(dir)) return EINA_FALSE; + if (slash) *slash = '/'; + if (slash) cur = slash + 1; + else cur = NULL; + } + while (cur); + } + return EINA_TRUE; +} + +int main(int argc, char **argv) +{ + int pret = 1; + + eina_init(); + + opterr = 0; + for (int opt; (opt = getopt(argc, argv, "+vt:h")) != -1;) + switch (opt) + { + case 0: + break; + case 't': + { + _rec_filename = eina_stringshare_add(optarg); + break; + } + case 'v': + { + _verbose++; + break; + } + case 'h': + { + _print_usage(argv[0], stdout); + pret = 0; + goto end; + } + default: + { + _print_usage(argv[0], stderr); + goto end; + } + } + + if (!argv[optind]) + { + fprintf(stderr, "no program specified\nUse -h for more information\n"); + goto end; + } + if (!_rec_filename) + { + fprintf(stderr, "no test file specified\n"); + goto end; + } + else + { + char *slash = strrchr(_rec_filename, '/'); + if (slash) _test_name = strdup(slash + 1); + else _test_name = strdup(_rec_filename); + char *dot = strrchr(_test_name, '.'); + if (dot) *dot = '\0'; + if (slash) + { + *slash = '\0'; + if (!_mkdir(_rec_filename)) + { + fprintf(stderr, "Can't create %s\n", _rec_filename); + goto end; + } + *slash = '/'; + } + } + + efl_object_init(); + evas_init(); + + /* Replace the current command line to hide the Exactness part */ + int len = argv[argc - 1] + strlen(argv[argc - 1]) - argv[optind]; + memcpy(argv[0], argv[optind], len); + memset(argv[0] + len, 0, _POSIX_PATH_MAX - len); + + for (int i = optind; i < argc; i++) + { + if (i != optind) + { + argv[i - optind] = argv[0] + (argv[i] - argv[optind]); + } + _printf(1, "%s ", argv[i - optind]); + } + _printf(1, "\n"); + + if (!_shot_key) _shot_key = getenv("TSUITE_SHOT_KEY"); + if (!_shot_key) _shot_key = SHOT_KEY_STR; + + if (!_events_list) + { + struct sysinfo s_info; + sysinfo(&s_info); + _events_list = calloc(1, sizeof(*_events_list)); + _events_list->first_timestamp = s_info.uptime * 1000; + _printf(2, "Uptime=<%u>\n", _events_list->first_timestamp); + } + + ecore_evas_callback_new_set(_my_evas_new); + _prg_invoke(_prg_full_path_guess(argv[0]), argc - optind, argv); + write_events(_rec_filename, _events_list); + free_events(_events_list, EINA_TRUE); + _events_list = NULL; + +end: + eina_shutdown(); + return pret; +}