From 9a8078cb799d2a34f1c5682c2e42dbdf19d2ee57 Mon Sep 17 00:00:00 2001 From: Daniel Zaoui Date: Mon, 24 Dec 2018 22:51:52 +0200 Subject: [PATCH] Exactness: code importation Differential Revision: https://phab.enlightenment.org/D7590 --- meson.build | 1 + src/bin/exactness/.gitignore | 5 + src/bin/exactness/exactness.c | 675 ++++++++++ src/bin/exactness/injector.c | 483 ++++++++ src/bin/exactness/inspect.c | 1653 +++++++++++++++++++++++++ src/bin/exactness/meson.build | 42 + src/bin/exactness/player.c | 1393 +++++++++++++++++++++ src/bin/exactness/player_entry.edc | 932 ++++++++++++++ src/bin/exactness/recorder.c | 531 ++++++++ src/lib/exactness/Exactness.h | 268 ++++ src/lib/exactness/exactness_private.h | 10 + src/lib/exactness/legacy_file.c | 876 +++++++++++++ src/lib/exactness/meson.build | 31 + src/lib/exactness/unit.c | 424 +++++++ 14 files changed, 7324 insertions(+) create mode 100644 src/bin/exactness/.gitignore create mode 100644 src/bin/exactness/exactness.c create mode 100644 src/bin/exactness/injector.c create mode 100644 src/bin/exactness/inspect.c create mode 100644 src/bin/exactness/meson.build create mode 100644 src/bin/exactness/player.c create mode 100644 src/bin/exactness/player_entry.edc create mode 100644 src/bin/exactness/recorder.c create mode 100644 src/lib/exactness/Exactness.h create mode 100644 src/lib/exactness/exactness_private.h create mode 100644 src/lib/exactness/legacy_file.c create mode 100644 src/lib/exactness/meson.build create mode 100644 src/lib/exactness/unit.c diff --git a/meson.build b/meson.build index 5ee96f0f09..fb77c43570 100644 --- a/meson.build +++ b/meson.build @@ -335,6 +335,7 @@ subprojects = [ ['elua' ,['elua'] , false, true, true, false, true, false, ['eina', 'luajit'], []], ['ecore_wayland' ,['wl-deprecated'] , false, true, false, false, false, false, ['eina'], []], ['ecore_drm' ,['drm-deprecated'] , false, true, false, false, false, false, ['eina'], []], +['exactness' ,[] , false, true, true, false, false, false, ['eina, evas, eet'], []], ] # We generate Efl_Config.h and config.h later, they will be available here diff --git a/src/bin/exactness/.gitignore b/src/bin/exactness/.gitignore new file mode 100644 index 0000000000..2760101039 --- /dev/null +++ b/src/bin/exactness/.gitignore @@ -0,0 +1,5 @@ +/exactness +/exactness_inject +/exactness_inspect +/exactness_play +/exactness_record diff --git a/src/bin/exactness/exactness.c b/src/bin/exactness/exactness.c new file mode 100644 index 0000000000..06c6097cf5 --- /dev/null +++ b/src/bin/exactness/exactness.c @@ -0,0 +1,675 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include + +#include "exactness_private.h" + +#ifdef _WIN32 +# include /* mkdir */ +#endif + +#define SCHEDULER_CMD_SIZE 1024 + +#define ORIG_SUBDIR "orig" +#define CURRENT_SUBDIR "current" + +#define EXACTNESS_PATH_MAX 1024 + +#define BUF_SIZE 1024 + +typedef struct +{ + EINA_INLIST; + char *name; + const char *command; +} List_Entry; + +typedef enum +{ + RUN_SIMULATION, + RUN_PLAY, + RUN_INIT +} Run_Mode; + +static unsigned short _running_jobs = 0, _max_jobs = 1; +static Eina_List *_base_dirs = NULL; +static char *_dest_dir; +static char *_wrap_command = NULL, *_fonts_dir = NULL; +static int _verbose = 0; +static Eina_Bool _scan_objs = EINA_FALSE, _disable_screenshots = EINA_FALSE, _stabilize_shots = EINA_FALSE; + +static Run_Mode _mode; +static List_Entry *_next_test_to_run = NULL; +static unsigned int _tests_executed = 0; + +static Eina_List *_errors; +static Eina_List *_compare_errors; + +static Eina_Bool _job_consume(); + +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 Exactness_Image * +_image_load(const char *filename) +{ + int w, h; + Evas_Load_Error err; + Ecore_Evas *ee = ecore_evas_new(NULL, 0, 0, 100, 100, NULL); + Eo *e = ecore_evas_get(ee); + + Eo *img = evas_object_image_add(e); + evas_object_image_file_set(img, filename, NULL); + err = evas_object_image_load_error_get(img); + if (err != EVAS_LOAD_ERROR_NONE) + { + fprintf(stderr, "could not load image '%s'. error string is \"%s\"\n", + filename, evas_load_error_str(err)); + return NULL; + } + + Exactness_Image *ex_img = malloc(sizeof(*ex_img)); + int len; + evas_object_image_size_get(img, &w, &h); + ex_img->w = w; + ex_img->h = h; + len = w * h * 4; + ex_img->pixels = malloc(len); + memcpy(ex_img->pixels, evas_object_image_data_get(img, EINA_FALSE), len); + + ecore_evas_free(ee); + return ex_img; +} + +static void +_image_save(Exactness_Image *ex_img, const char *output) +{ + Ecore_Evas *ee; + Eo *e, *img; + ee = ecore_evas_new(NULL, 0, 0, 100, 100, NULL); + e = ecore_evas_get(ee); + img = evas_object_image_add(e); + evas_object_image_size_set(img, ex_img->w, ex_img->h); + evas_object_image_data_set(img, ex_img->pixels); + evas_object_image_save(img, output, NULL, NULL); + ecore_evas_free(ee); +} + +static Eina_Bool +_file_compare(const char *orig_dir, const char *ent_name) +{ + Eina_Bool result = EINA_FALSE; + Exactness_Image *img1, *img2, *imgO = NULL; + char *filename1 = alloca(strlen(orig_dir) + strlen(ent_name) + 20); + char *filename2 = alloca(strlen(_dest_dir) + strlen(ent_name) + 20); + sprintf(filename1, "%s/%s", orig_dir, ent_name); + sprintf(filename2, "%s/%s/%s", _dest_dir, CURRENT_SUBDIR, ent_name); + + img1 = _image_load(filename1); + img2 = _image_load(filename2); + + if (exactness_image_compare(img1, img2, &imgO)) + { + char *buf = alloca(strlen(_dest_dir) + strlen(ent_name)); + sprintf(buf, "%s/%s/comp_%s", _dest_dir, CURRENT_SUBDIR, ent_name); + _image_save(imgO, buf); + _compare_errors = eina_list_append(_compare_errors, strdup(ent_name)); + result = EINA_TRUE; + } + exactness_image_free(img1); + exactness_image_free(img2); + exactness_image_free(imgO); + return result; +} + +static void +_exu_imgs_unpack(const char *exu_path, const char *dir, const char *ent_name) +{ + Exactness_Unit *unit = exactness_unit_file_read(exu_path); + Exactness_Image *img; + Eina_List *itr; + Ecore_Evas *ee = ecore_evas_new(NULL, 0, 0, 100, 100, NULL); + Eo *e = ecore_evas_get(ee); + int n = 1; + if (!unit) return; + EINA_LIST_FOREACH(unit->imgs, itr, img) + { + Eo *o = evas_object_image_add(e); + char *filename = alloca(strlen(dir) + strlen(ent_name) + 20); + snprintf(filename, EXACTNESS_PATH_MAX, "%s/%s%c%.3d.png", + dir, ent_name, SHOT_DELIMITER, n++); + evas_object_image_size_set(o, img->w, img->h); + evas_object_image_data_set(o, img->pixels); + if (!evas_object_image_save(o, filename, NULL, NULL)) + { + printf("Cannot save widget to <%s>\n", filename); + } + efl_del(o); + } + efl_del(e); + ecore_evas_free(ee); +} + +static void +_run_test_compare(const List_Entry *ent) +{ + char *path = alloca(EXACTNESS_PATH_MAX); + char *origdir = alloca(strlen(_dest_dir) + 20); + const char *base_dir; + Eina_List *itr; + int n = 1, nb_fails = 0; + printf("STATUS %s: COMPARE\n", ent->name); + EINA_LIST_FOREACH(_base_dirs, itr, base_dir) + { + sprintf(path, "%s/%s.exu", base_dir, ent->name); + if (ecore_file_exists(path)) + { + char *currentdir; + sprintf(origdir, "%s/%s/%s", _dest_dir, CURRENT_SUBDIR, ORIG_SUBDIR); + mkdir(origdir, 0744); + _exu_imgs_unpack(path, origdir, ent->name); + sprintf(path, "%s/%s/%s.exu", _dest_dir, CURRENT_SUBDIR, ent->name); + currentdir = alloca(strlen(_dest_dir) + 20); + sprintf(currentdir, "%s/%s", _dest_dir, CURRENT_SUBDIR); + _exu_imgs_unpack(path, currentdir, ent->name); + goto found; + } + else + { + sprintf(path, "%s/%s.rec", base_dir, ent->name); + if (ecore_file_exists(path)) + { + sprintf(origdir, "%s/%s", _dest_dir, ORIG_SUBDIR); + goto found; + } + } + } +found: + do + { + sprintf(path, "%s/%s%c%.3d.png", origdir, ent->name, SHOT_DELIMITER, n); + if (ecore_file_exists(path)) + { + sprintf(path, "%s%c%.3d.png", ent->name, SHOT_DELIMITER, n); + if (_file_compare(origdir, path)) nb_fails++; + } + else break; + n++; + } while (EINA_TRUE); + if (!nb_fails) + printf("STATUS %s: END - SUCCESS\n", ent->name); + else + printf("STATUS %s: END - FAIL (%d/%d)\n", ent->name, nb_fails, n - 1); +} + +#define CONFIG "ELM_SCALE=1 ELM_FINGER_SIZE=10 " +static Eina_Bool +_run_command_prepare(const List_Entry *ent, char *buf) +{ + char scn_path[EXACTNESS_PATH_MAX]; + Eina_Strbuf *sbuf; + const char *base_dir; + Eina_List *itr; + Eina_Bool is_exu; + EINA_LIST_FOREACH(_base_dirs, itr, base_dir) + { + is_exu = EINA_TRUE; + sprintf(scn_path, "%s/%s.exu", base_dir, ent->name); + if (ecore_file_exists(scn_path)) goto ok; + else + { + is_exu = EINA_FALSE; + sprintf(scn_path, "%s/%s.rec", base_dir, ent->name); + if (ecore_file_exists(scn_path)) goto ok; + } + } + fprintf(stderr, "Test %s not found in the provided base directories\n", ent->name); + return EINA_FALSE; +ok: + sbuf = eina_strbuf_new(); + printf("STATUS %s: START\n", ent->name); + eina_strbuf_append_printf(sbuf, + "%s exactness_play %s %s%s %s%.*s %s%s%s-t '%s' ", + _wrap_command ? _wrap_command : "", + _mode == RUN_SIMULATION ? "-s" : "", + _fonts_dir ? "-f " : "", _fonts_dir ? _fonts_dir : "", + _verbose ? "-" : "", _verbose, "vvvvvvvvvv", + _scan_objs ? "--scan-objects " : "", + _disable_screenshots ? "--disable-screenshots " : "", + _stabilize_shots ? "--stabilize-shots " : "", + scn_path + ); + if (is_exu) + { + if (_mode == RUN_PLAY) + eina_strbuf_append_printf(sbuf, "-o '%s/%s/%s.exu' ", _dest_dir, CURRENT_SUBDIR, ent->name); + if (_mode == RUN_INIT) + eina_strbuf_append_printf(sbuf, "-o '%s' ", scn_path); + } + else + { + if (_mode == RUN_PLAY) + eina_strbuf_append_printf(sbuf, "-o '%s/%s' ", _dest_dir, CURRENT_SUBDIR); + if (_mode == RUN_INIT) + eina_strbuf_append_printf(sbuf, "-o '%s/%s' ", _dest_dir, ORIG_SUBDIR); + } + if (ent->command) + { + eina_strbuf_append(sbuf, "-- "); + eina_strbuf_append(sbuf, CONFIG); + eina_strbuf_append(sbuf, ent->command); + } + strncpy(buf, eina_strbuf_string_get(sbuf), SCHEDULER_CMD_SIZE-1); + eina_strbuf_free(sbuf); + _printf(1, "Command: %s\n", buf); + return EINA_TRUE; +} + +static void +_job_compare(void *data) +{ + _run_test_compare(data); + + _running_jobs--; + _job_consume(); + /* If all jobs are done. */ + if (!_running_jobs) ecore_main_loop_quit(); +} + +static Eina_Bool +_job_deleted_cb(void *data EINA_UNUSED, int type EINA_UNUSED, void *event) +{ + Ecore_Exe_Event_Del *msg = (Ecore_Exe_Event_Del *) event; + List_Entry *ent = ecore_exe_data_get(msg->exe); + + if ((msg->exit_code != 0) || (msg->exit_signal != 0)) + { + _errors = eina_list_append(_errors, ent); + } + + if (_mode == RUN_PLAY) + { + ecore_job_add(_job_compare, ent); + } + else + { + _running_jobs--; + _job_consume(); + if (!_running_jobs) ecore_main_loop_quit(); + } + + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_job_consume() +{ + static Ecore_Event_Handler *job_del_callback_handler = NULL; + char buf[SCHEDULER_CMD_SIZE]; + List_Entry *ent = _next_test_to_run; + + if (_running_jobs == _max_jobs) return EINA_FALSE; + if (!ent) return EINA_FALSE; + + if (_run_command_prepare(ent, buf)) + { + _running_jobs++; + _tests_executed++; + + if (!job_del_callback_handler) + { + job_del_callback_handler = ecore_event_handler_add(ECORE_EXE_EVENT_DEL, + _job_deleted_cb, NULL); + } + + if (!ecore_exe_pipe_run(buf, ECORE_EXE_TERM_WITH_PARENT, ent)) + { + fprintf(stderr, "Failed executing test '%s'\n", ent->name); + } + } + _next_test_to_run = EINA_INLIST_CONTAINER_GET( + EINA_INLIST_GET(ent)->next, List_Entry); + + + return EINA_TRUE; +} + +static void +_scheduler_run() +{ + while (_job_consume()); +} + +static List_Entry * +_list_file_load(const char *filename) +{ + List_Entry *ret = NULL; + char buf[BUF_SIZE] = ""; + FILE *file; + file = fopen(filename, "r"); + if (!file) + { + perror("Failed opening list file"); + return NULL; + } + + while (fgets(buf, BUF_SIZE, file)) + { + /* Skip comment/empty lines. */ + if ((*buf == '#') || (*buf == '\n') || (!*buf)) + continue; + + char *tmp; + List_Entry *cur = calloc(1, sizeof(*cur)); + cur->name = strdup(buf); + + /* Set the command to the second half and put a \0 in between. */ + tmp = strchr(cur->name, ' '); + if (tmp) + { + *tmp = '\0'; + cur->command = tmp + 1; + } + else + { + /* FIXME: error. */ + cur->command = ""; + } + + /* Replace the newline char with a \0. */ + tmp = strchr(cur->command, '\n'); + if (tmp) + { + *tmp = '\0'; + } + + ret = EINA_INLIST_CONTAINER_GET( + eina_inlist_append(EINA_INLIST_GET(ret), EINA_INLIST_GET(cur)), + List_Entry); + } + + return ret; +} + +static void +_list_file_free(List_Entry *list) +{ + while (list) + { + List_Entry *ent = list; + list = EINA_INLIST_CONTAINER_GET(EINA_INLIST_GET(list)->next, + List_Entry); + + free(ent->name); + free(ent); + /* we don't free ent->command because it's allocated together. */ + } +} + +static int +_errors_sort_cb(List_Entry *a, List_Entry *b) +{ + return strcmp(a->name, b->name); +} + +static const Ecore_Getopt optdesc = { + "exactness", + "%prog [options] <-r|-p|-i|-s> ", + PACKAGE_VERSION, + "(C) 2013 Enlightenment", + "BSD", + "A pixel perfect test suite for EFL based applications.", + 0, + { + ECORE_GETOPT_APPEND('b', "base-dir", "The location of the exu/rec files.", ECORE_GETOPT_TYPE_STR), + ECORE_GETOPT_STORE_STR('o', "output", "The location of the images."), + ECORE_GETOPT_STORE_STR('w', "wrap", "Use a custom command to launch the tests (e.g valgrind)."), + ECORE_GETOPT_STORE_USHORT('j', "jobs", "The number of jobs to run in parallel."), + ECORE_GETOPT_STORE_TRUE('p', "play", "Run in play mode."), + ECORE_GETOPT_STORE_TRUE('i', "init", "Run in init mode."), + ECORE_GETOPT_STORE_TRUE('s', "simulation", "Run in simulation mode."), + ECORE_GETOPT_STORE_TRUE(0, "scan-objects", "Extract information of all the objects at every shot."), + ECORE_GETOPT_STORE_TRUE(0, "disable-screenshots", "Disable screenshots."), + ECORE_GETOPT_STORE_STR('f', "fonts-dir", "Specify a directory of the fonts that should be used."), + ECORE_GETOPT_STORE_TRUE(0, "stabilize-shots", "Wait for the frames to be stable before taking the shots."), + ECORE_GETOPT_COUNT('v', "verbose", "Turn verbose messages on."), + + ECORE_GETOPT_LICENSE('L', "license"), + ECORE_GETOPT_COPYRIGHT('C', "copyright"), + ECORE_GETOPT_VERSION('V', "version"), + ECORE_GETOPT_HELP('h', "help"), + ECORE_GETOPT_SENTINEL + } +}; + +int +main(int argc, char *argv[]) +{ + int ret = 0; + List_Entry *test_list; + int args = 0; + const char *list_file; + Eina_List *itr; + const char *base_dir; + char tmp[EXACTNESS_PATH_MAX]; + Eina_Bool mode_play = EINA_FALSE, mode_init = EINA_FALSE, mode_simulation = EINA_FALSE; + Eina_Bool want_quit = EINA_FALSE, scan_objs = EINA_FALSE; + Ecore_Getopt_Value values[] = { + ECORE_GETOPT_VALUE_LIST(_base_dirs), + ECORE_GETOPT_VALUE_STR(_dest_dir), + ECORE_GETOPT_VALUE_STR(_wrap_command), + ECORE_GETOPT_VALUE_USHORT(_max_jobs), + ECORE_GETOPT_VALUE_BOOL(mode_play), + ECORE_GETOPT_VALUE_BOOL(mode_init), + ECORE_GETOPT_VALUE_BOOL(mode_simulation), + ECORE_GETOPT_VALUE_BOOL(scan_objs), + ECORE_GETOPT_VALUE_BOOL(_disable_screenshots), + ECORE_GETOPT_VALUE_STR(_fonts_dir), + ECORE_GETOPT_VALUE_BOOL(_stabilize_shots), + ECORE_GETOPT_VALUE_INT(_verbose), + + ECORE_GETOPT_VALUE_BOOL(want_quit), + ECORE_GETOPT_VALUE_BOOL(want_quit), + ECORE_GETOPT_VALUE_BOOL(want_quit), + ECORE_GETOPT_VALUE_BOOL(want_quit), + ECORE_GETOPT_VALUE_NONE + }; + + ecore_init(); + ecore_evas_init(); + evas_init(); + mode_play = mode_init = mode_simulation = EINA_FALSE; + want_quit = EINA_FALSE; + _dest_dir = "./"; + _scan_objs = scan_objs; + + args = ecore_getopt_parse(&optdesc, values, argc, argv); + if (args < 0) + { + fprintf(stderr, "Failed parsing arguments.\n"); + ret = 1; + goto end; + } + else if (want_quit) + { + ret = 1; + goto end; + } + else if (args == argc) + { + fprintf(stderr, "Expected test list as the last argument..\n"); + ecore_getopt_help(stderr, &optdesc); + ret = 1; + goto end; + } + else if (mode_play + mode_init + mode_simulation != 1) + { + fprintf(stderr, "At least and only one of the running modes can be set.\n"); + ecore_getopt_help(stderr, &optdesc); + ret = 1; + goto end; + } + + if (!_base_dirs) _base_dirs = eina_list_append(NULL, "./recordings"); + + list_file = argv[args]; + + /* Load the list file and start iterating over the records. */ + test_list = _list_file_load(list_file); + _next_test_to_run = test_list; + + if (!test_list) + { + fprintf(stderr, "No matching tests found in '%s'\n", list_file); + ret = 1; + goto end; + } + + /* Pre-run summary */ + fprintf(stderr, "Running with settings:\n"); + fprintf(stderr, "\tConcurrent jobs: %d\n", _max_jobs); + fprintf(stderr, "\tTest list: %s\n", list_file); + fprintf(stderr, "\tBase dirs:\n"); + EINA_LIST_FOREACH(_base_dirs, itr, base_dir) + fprintf(stderr, "\t\t%s\n", base_dir); + fprintf(stderr, "\tDest dir: %s\n", _dest_dir); + + if (mode_play) + { + _mode = RUN_PLAY; + if (snprintf(tmp, EXACTNESS_PATH_MAX, "%s/%s", _dest_dir, CURRENT_SUBDIR) + >= EXACTNESS_PATH_MAX) + { + fprintf(stderr, "Path too long: %s", tmp); + ret = 1; + goto end; + } + mkdir(tmp, 0744); + } + else if (mode_init) + { + _mode = RUN_INIT; + if (snprintf(tmp, EXACTNESS_PATH_MAX, "%s/%s", _dest_dir, ORIG_SUBDIR) + >= EXACTNESS_PATH_MAX) + { + fprintf(stderr, "Path too long: %s", tmp); + ret = 1; + goto end; + } + mkdir(tmp, 0744); + } + else if (mode_simulation) + { + _mode = RUN_SIMULATION; + } + _scheduler_run(); + + + ecore_main_loop_begin(); + + /* Results */ + printf("*******************************************************\n"); + if (mode_play && EINA_FALSE) + { + List_Entry *list_itr; + + EINA_INLIST_FOREACH(test_list, list_itr) + { + _run_test_compare(list_itr); + } + } + + printf("Finished executing %u out of %u tests.\n", + _tests_executed, + eina_inlist_count(EINA_INLIST_GET(test_list))); + + /* Sort the errors and the compare_errors. */ + _errors = eina_list_sort(_errors, 0, (Eina_Compare_Cb) _errors_sort_cb); + _compare_errors = eina_list_sort(_compare_errors, 0, (Eina_Compare_Cb) strcmp); + + if (_errors || _compare_errors) + { + FILE *report_file; + char report_filename[EXACTNESS_PATH_MAX] = ""; + /* Generate the filename. */ + snprintf(report_filename, EXACTNESS_PATH_MAX, + "%s/%s/errors.html", + _dest_dir, mode_init ? ORIG_SUBDIR : CURRENT_SUBDIR); + report_file = fopen(report_filename, "w+"); + if (report_file) + { + printf("%s %p\n", report_filename, report_file); + fprintf(report_file, + "" + "Exactness report"); + + if (_errors) + { + fprintf(report_file, + "

Tests that failed execution:

    "); + List_Entry *ent; + printf("List of tests that failed execution:\n"); + EINA_LIST_FOREACH(_errors, itr, ent) + { + printf("\t* %s\n", ent->name); + + fprintf(report_file, "
  • %s
  • ", ent->name); + } + fprintf(report_file, "
"); + } + + if (_compare_errors) + { + fprintf(report_file, + "

Images that failed comparison: (Original, Current, Diff)

    "); + char *test_name; + printf("List of images that failed comparison:\n"); + EINA_LIST_FREE(_compare_errors, test_name) + { + Eina_Bool is_from_exu; + char origpath[EXACTNESS_PATH_MAX]; + snprintf(origpath, EXACTNESS_PATH_MAX, "%s/%s/orig/%s", + _dest_dir, CURRENT_SUBDIR, test_name); + is_from_exu = ecore_file_exists(origpath); + printf("\t* %s\n", test_name); + + fprintf(report_file, "
  • %s

    Original Current Diff
  • ", + test_name, is_from_exu ? "" : "../", + test_name, test_name, test_name); + free(test_name); + } + fprintf(report_file, "
"); + } + fprintf(report_file, + ""); + + printf("Report html: %s\n", report_filename); + ret = 1; + } + else + { + perror("Failed opening report file"); + } + } + + _list_file_free(test_list); +end: + evas_shutdown(); + ecore_evas_shutdown(); + ecore_shutdown(); + + return ret; +} diff --git a/src/bin/exactness/injector.c b/src/bin/exactness/injector.c new file mode 100644 index 0000000000..95e71a6927 --- /dev/null +++ b/src/bin/exactness/injector.c @@ -0,0 +1,483 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include + +#ifndef EFL_EO_API_SUPPORT +#define EFL_EO_API_SUPPORT +#endif +#include +#include +#include +#include +#include + +#include + +#include "exactness_private.h" + +typedef struct +{ + Eina_Debug_Session *session; + int srcid; + void *buffer; + unsigned int size; +} _Main_Loop_Info; + +#define WRAPPER_TO_XFER_MAIN_LOOP(foo) \ +static void \ +_intern_main_loop ## foo(void *data) \ +{ \ + _Main_Loop_Info *info = data; \ + _main_loop ## foo(info->session, info->srcid, info->buffer, info->size); \ + free(info->buffer); \ + free(info); \ +} \ +static Eina_Bool \ +foo(Eina_Debug_Session *session, int srcid, void *buffer, int size) \ +{ \ + _Main_Loop_Info *info = calloc(1, sizeof(*info)); \ + info->session = session; \ + info->srcid = srcid; \ + info->size = size; \ + if (info->size) \ + { \ + info->buffer = malloc(info->size); \ + memcpy(info->buffer, buffer, info->size); \ + } \ + ecore_main_loop_thread_safe_call_async(_intern_main_loop ## foo, info); \ + return EINA_TRUE; \ +} + +#ifndef WORDS_BIGENDIAN +#define SWAP_64(x) x +#define SWAP_32(x) x +#define SWAP_16(x) x +#define SWAP_DBL(x) x +#else +#define SWAP_64(x) eina_swap64(x) +#define SWAP_32(x) eina_swap32(x) +#define SWAP_16(x) eina_swap16(x) +#define SWAP_DBL(x) SWAP_64(x) +#endif + +#define EXTRACT_INT(_buf) \ +({ \ + int __i; \ + memcpy(&__i, _buf, sizeof(int)); \ + _buf += sizeof(int); \ + SWAP_32(__i); \ +}) + +#define STORE_INT(_buf, __i) \ +{ \ + int __i2 = SWAP_32(__i); \ + memcpy(_buf, &__i2, sizeof(int)); \ + _buf += sizeof(int); \ +} + +#define STORE_DOUBLE(_buf, __d) \ +{ \ + double __d2 = SWAP_DBL(__d); \ + memcpy(_buf, &__d2, sizeof(double)); \ + _buf += sizeof(double); \ +} + +#define STORE_STRING(_buf, __s) \ +{ \ + int __len = (__s ? strlen(__s) : 0) + 1; \ + if (__s) memcpy(_buf, __s, __len); \ + else *_buf = '\0'; \ + _buf += __len; \ +} + +static Eina_Stringshare *_src_filename = NULL; +static Exactness_Unit *_src_unit = NULL; +static int _verbose = 0; + +static Eina_Debug_Session *_session = NULL; +static int _cid = -1, _pid = -1; +static Eina_List *_cur_event_list = NULL; + +static int _all_apps_get_op = EINA_DEBUG_OPCODE_INVALID; +static int _mouse_in_op = EINA_DEBUG_OPCODE_INVALID; +static int _mouse_out_op = EINA_DEBUG_OPCODE_INVALID; +static int _mouse_wheel_op = EINA_DEBUG_OPCODE_INVALID; +static int _multi_down_op = EINA_DEBUG_OPCODE_INVALID; +static int _multi_up_op = EINA_DEBUG_OPCODE_INVALID; +static int _multi_move_op = EINA_DEBUG_OPCODE_INVALID; +static int _key_down_op = EINA_DEBUG_OPCODE_INVALID; +static int _key_up_op = EINA_DEBUG_OPCODE_INVALID; +static int _take_shot_op = EINA_DEBUG_OPCODE_INVALID; +static int _efl_event_op = EINA_DEBUG_OPCODE_INVALID; +static int _click_on_op = EINA_DEBUG_OPCODE_INVALID; +static int _stabilize_op = EINA_DEBUG_OPCODE_INVALID; +static int _finish_op = EINA_DEBUG_OPCODE_INVALID; + +static Eina_Bool _all_apps_get_cb(Eina_Debug_Session *, int , void *, int); + +EINA_DEBUG_OPCODES_ARRAY_DEFINE(_debug_ops, + {"Daemon/Client/register_observer", &_all_apps_get_op, NULL}, + {"Daemon/Client/added", NULL, &_all_apps_get_cb}, + {"Exactness/Actions/Mouse In", &_mouse_in_op, NULL}, + {"Exactness/Actions/Mouse Out", &_mouse_out_op, NULL}, + {"Exactness/Actions/Mouse Wheel", &_mouse_wheel_op, NULL}, + {"Exactness/Actions/Multi Down", &_multi_down_op, NULL}, + {"Exactness/Actions/Multi Up", &_multi_up_op, NULL}, + {"Exactness/Actions/Multi Move", &_multi_move_op, NULL}, + {"Exactness/Actions/Key Down", &_key_down_op, NULL}, + {"Exactness/Actions/Key Up", &_key_up_op, NULL}, + {"Exactness/Actions/Take Shot", &_take_shot_op, NULL}, + {"Exactness/Actions/EFL Event", &_efl_event_op, NULL}, + {"Exactness/Actions/Click On", &_click_on_op, NULL}, + {"Exactness/Actions/Stabilize", &_stabilize_op, NULL}, + {"Exactness/Actions/Finish", &_finish_op, NULL}, + {NULL, NULL, 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 void +_feed_event(Exactness_Action_Type type, unsigned int n_evas, void *data) +{ + switch (type) + { + case EXACTNESS_ACTION_MOUSE_IN: + { + _printf(1, "Mouse in\n"); + _printf(2, "%s evas_event_feed_mouse_in n_evas=<%d>\n", __func__, n_evas); + eina_debug_session_send(_session, _cid, _mouse_in_op, &n_evas, sizeof(int)); + break; + } + case EXACTNESS_ACTION_MOUSE_OUT: + { + _printf(1, "Mouse out\n"); + _printf(2, "%s evas_event_feed_mouse_out n_evas=<%d>\n", __func__, n_evas); + eina_debug_session_send(_session, _cid, _mouse_out_op, &n_evas, sizeof(int)); + break; + } + case EXACTNESS_ACTION_MOUSE_WHEEL: + { + Exactness_Action_Mouse_Wheel *t = data; + int len = 3*sizeof(int); + char *buf = malloc(len), *tmp = buf; + _printf(1, "Mouse wheel\n"); + _printf(2, "%s evas_event_feed_mouse_wheel n_evas=<%d>\n", __func__, n_evas); + STORE_INT(tmp, n_evas); + STORE_INT(tmp, t->direction); + STORE_INT(tmp, t->z); + eina_debug_session_send(_session, _cid, _mouse_wheel_op, buf, len); + break; + } + case EXACTNESS_ACTION_MULTI_DOWN: + case EXACTNESS_ACTION_MULTI_UP: + { + Exactness_Action_Multi_Event *t = data; + int len = 5*sizeof(int)+7*sizeof(double)+sizeof(int); + char *buf = malloc(len), *tmp = buf; + _printf(2, "%s %s n_evas=<%d>\n", __func__, + type == EXACTNESS_ACTION_MULTI_DOWN ? "evas_event_feed_multi_down" : + "evas_event_feed_multi_up", n_evas); + STORE_INT(tmp, n_evas); + STORE_INT(tmp, t->d); + STORE_INT(tmp, t->b); + STORE_INT(tmp, t->x); + STORE_INT(tmp, t->y); + STORE_DOUBLE(tmp, t->rad); + STORE_DOUBLE(tmp, t->radx); + STORE_DOUBLE(tmp, t->rady); + STORE_DOUBLE(tmp, t->pres); + STORE_DOUBLE(tmp, t->ang); + STORE_DOUBLE(tmp, t->fx); + STORE_DOUBLE(tmp, t->fy); + STORE_INT(tmp, t->flags); + eina_debug_session_send(_session, _cid, + type == EXACTNESS_ACTION_MULTI_DOWN ? _multi_down_op : _multi_up_op, + buf, len); + break; + } + case EXACTNESS_ACTION_MULTI_MOVE: + { + Exactness_Action_Multi_Move *t = data; + int len = 4*sizeof(int)+7*sizeof(double); + char *buf = malloc(len), *tmp = buf; + _printf(2, "%s evas_event_feed_multi_move n_evas=<%d>\n", __func__, n_evas); + STORE_INT(tmp, n_evas); + STORE_INT(tmp, t->d); + STORE_INT(tmp, t->x); + STORE_INT(tmp, t->y); + STORE_DOUBLE(tmp, t->rad); + STORE_DOUBLE(tmp, t->radx); + STORE_DOUBLE(tmp, t->rady); + STORE_DOUBLE(tmp, t->pres); + STORE_DOUBLE(tmp, t->ang); + STORE_DOUBLE(tmp, t->fx); + STORE_DOUBLE(tmp, t->fy); + eina_debug_session_send(_session, _cid, _multi_move_op, buf, len); + break; + } + case EXACTNESS_ACTION_KEY_DOWN: + case EXACTNESS_ACTION_KEY_UP: + { + Exactness_Action_Key_Down_Up *t = data; + int len = 2*sizeof(int) + 4; + len += t->keyname ? strlen(t->keyname) : 0; + len += t->key ? strlen(t->key) : 0; + len += t->string ? strlen(t->string) : 0; + len += t->compose ? strlen(t->compose) : 0; + char *buf = malloc(len), *tmp = buf; + _printf(2, "%s %s n_evas=<%d>\n", __func__, + type == EXACTNESS_ACTION_KEY_DOWN ? "evas_event_feed_key_down " : + "evas_event_feed_key_up", n_evas); + STORE_INT(tmp, n_evas); + STORE_STRING(tmp, t->keyname); + STORE_STRING(tmp, t->key); + STORE_STRING(tmp, t->string); + STORE_STRING(tmp, t->compose); + STORE_INT(tmp, t->keycode); + eina_debug_session_send(_session, _cid, + type == EXACTNESS_ACTION_KEY_DOWN ? _key_down_op : _key_up_op, + buf, len); + break; + } + case EXACTNESS_ACTION_TAKE_SHOT: + { + _printf(2, "%s take shot n_evas=<%d>\n", __func__, n_evas); + eina_debug_session_send(_session, _cid, _take_shot_op, &n_evas, sizeof(int)); + break; + } + case EXACTNESS_ACTION_EFL_EVENT: + { + Exactness_Action_Efl_Event *t = data; + int len = 0; + len += t->wdg_name ? strlen(t->wdg_name) : 0; + len += t->event_name ? strlen(t->event_name) : 0; + char *buf = malloc(len), *tmp = buf; + _printf(2, "%s %s\n", __func__, "EFL event"); + STORE_STRING(tmp, t->wdg_name); + STORE_STRING(tmp, t->event_name); + eina_debug_session_send(_session, _cid, _efl_event_op, buf, len); + break; + } + case EXACTNESS_ACTION_CLICK_ON: + { + Exactness_Action_Click_On *t = data; + int len = 0; + len += t->wdg_name ? strlen(t->wdg_name) : 0; + char *buf = malloc(len), *tmp = buf; + _printf(2, "%s %s\n", __func__, "Click On"); + STORE_STRING(tmp, t->wdg_name); + eina_debug_session_send(_session, _cid, _click_on_op, buf, len); + break; + } + case EXACTNESS_ACTION_STABILIZE: + { + _printf(2, "%s stabilize\n", __func__); + eina_debug_session_send(_session, _cid, _stabilize_op, NULL, 0); + break; + } + default: /* All non-input events are not handeled */ + break; + } +} + +static Eina_Bool +_feed_event_timer_cb(void *data EINA_UNUSED) +{ + Exactness_Action *act = eina_list_data_get(_cur_event_list); + _feed_event(act->type, act->n_evas, act->data); + + _cur_event_list = eina_list_next(_cur_event_list); + + if (!_cur_event_list) + { /* Finished reading all events */ + eina_debug_session_send(_session, _cid, _finish_op, NULL, 0); + ecore_main_loop_quit(); + } + else + { + Exactness_Action *cur_act = eina_list_data_get(_cur_event_list); + ecore_timer_add(cur_act->delay_ms / 1000.0, _feed_event_timer_cb, NULL); + } + return ECORE_CALLBACK_CANCEL; +} + +static Eina_Bool +_src_open() +{ + double diff_time = 0; /* Time to wait before feeding the first event */ + + _printf(2, "<%s> Source file is <%s>\n", __func__, _src_filename); + if (!strcmp(_src_filename + strlen(_src_filename) - 4,".exu")) + { + _src_unit = exactness_unit_file_read(_src_filename); + } + else if (!strcmp(_src_filename + strlen(_src_filename) - 4,".rec")) + { + _src_unit = legacy_rec_file_read(_src_filename); + } + if (!_src_unit) return EINA_FALSE; + _cur_event_list = _src_unit->actions; + Exactness_Action *act = eina_list_data_get(_cur_event_list); + + if (act->delay_ms) + { + _printf(2, " Waiting <%f>\n", diff_time); + ecore_timer_add(act->delay_ms / 1000.0, _feed_event_timer_cb, NULL); + } + else + { + _feed_event_timer_cb(NULL); + } + return EINA_TRUE; +} + +static void +_main_loop_all_apps_get_cb(Eina_Debug_Session *session EINA_UNUSED, int srcid EINA_UNUSED, void *buffer, int size EINA_UNUSED) +{ + char *buf = buffer; + int chosen_cid = -1; + if (_cid != -1) return; + while (size > 0) + { + int cid, pid, len; + cid = EXTRACT_INT(buf); + pid = EXTRACT_INT(buf); + if (_pid != -1) + { + if (_pid == pid) + { + _cid = cid; + _src_open(); + return; + } + } + else + { + if (!strcmp(buf, "exactness_play")) + { + if (chosen_cid != -1) + { + fprintf(stderr, "Need to specify a PID - too much choice\n"); + return; + } + chosen_cid = cid; + } + } + len = strlen(buf) + 1; + buf += len; + size -= (2 * sizeof(int) + len); + } + if (chosen_cid != -1) + { + _cid = chosen_cid; + _src_open(); + } +} + +WRAPPER_TO_XFER_MAIN_LOOP(_all_apps_get_cb) + +static void +_ops_ready_cb(void *data EINA_UNUSED, Eina_Bool status) +{ + static Eina_Bool on = EINA_FALSE; + if (status) + { + if (!on) + { + eina_debug_session_send(_session, 0, _all_apps_get_op, NULL, 0); + } + on = EINA_TRUE; + } +} + +static const Ecore_Getopt optdesc = { + "exactness_inject", + "%prog [options] <-v|-p|-t|-h> command", + PACKAGE_VERSION, + "(C) 2018 Enlightenment", + "BSD", + "A scenario events injector for EFL based applications.", + 1, + { + ECORE_GETOPT_STORE_STR('t', "test", "Test to run on the given application"), + ECORE_GETOPT_STORE_INT('p', "pid", "PID of the application to connect to"), + ECORE_GETOPT_STORE_INT('r', "remote-port", "Port to connect remotely to the daemon. Local connection if not specified"), + ECORE_GETOPT_COUNT('v', "verbose", "Turn verbose messages on."), + + ECORE_GETOPT_LICENSE('L', "license"), + ECORE_GETOPT_COPYRIGHT('C', "copyright"), + ECORE_GETOPT_VERSION('V', "version"), + ECORE_GETOPT_HELP('h', "help"), + ECORE_GETOPT_SENTINEL + } +}; + +int main(int argc, char **argv) +{ + int opt_args = 0, real__ = 1, port = -1; + char *src = NULL; + Eina_Value *ret__; + Eina_Bool want_quit = EINA_FALSE; + + Ecore_Getopt_Value values[] = { + ECORE_GETOPT_VALUE_STR(src), + ECORE_GETOPT_VALUE_INT(_pid), + ECORE_GETOPT_VALUE_INT(port), + ECORE_GETOPT_VALUE_INT(_verbose), + + ECORE_GETOPT_VALUE_BOOL(want_quit), + ECORE_GETOPT_VALUE_BOOL(want_quit), + ECORE_GETOPT_VALUE_BOOL(want_quit), + ECORE_GETOPT_VALUE_BOOL(want_quit), + ECORE_GETOPT_VALUE_NONE + }; + + eina_init(); + eet_init(); + ecore_init(); + + opt_args = ecore_getopt_parse(&optdesc, values, argc, argv); + if (opt_args < 0) + { + fprintf(stderr, "Failed parsing arguments.\n"); + goto end; + } + if (want_quit) goto end; + + if (!src) + { + fprintf(stderr, "no test file specified\n"); + goto end; + } + _src_filename = eina_stringshare_add(src); + + if (port == -1) + _session = eina_debug_local_connect(EINA_TRUE); + else + _session = eina_debug_remote_connect(port); + eina_debug_opcodes_register(_session, _debug_ops(), _ops_ready_cb, NULL); + + elm_init(argc, argv); + ret__ = efl_loop_begin(efl_main_loop_get()); + real__ = efl_loop_exit_code_process(ret__); + elm_shutdown(); +end: + eet_shutdown(); + eina_shutdown(); + return real__; +} + diff --git a/src/bin/exactness/inspect.c b/src/bin/exactness/inspect.c new file mode 100644 index 0000000000..54f6627f22 --- /dev/null +++ b/src/bin/exactness/inspect.c @@ -0,0 +1,1653 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#ifndef EFL_BETA_API_SUPPORT +#define EFL_BETA_API_SUPPORT +#endif +#ifndef EFL_EO_API_SUPPORT +#define EFL_EO_API_SUPPORT +#endif +#include +#include +#include +#include +#include +#include + +#include "exactness_private.h" + +#define LDIFF(x) ""#x"" +#define RDIFF(x) ""#x"" + +typedef enum +{ + EX_FONTS_DIR, + EX_SCENARIO, + EX_IMAGE, + EX_OBJ_INFO +} _Data_Type; + +typedef struct +{ + void *p1; + void *p2; + _Data_Type dt; +} _Compare_Item_Data; + +typedef struct +{ + void *ex_parent; + Eo *gl_item; +} _Item_Info; + +static Eo *_main_box = NULL; +static Eina_List *_gls = NULL; +static Eina_List *_units = NULL; +static Eo *_comp_selected_item = NULL; + +static Elm_Genlist_Item_Class *_grp_itc = NULL, *_scn_itc = NULL, *_img_itc = NULL; +static Elm_Genlist_Item_Class *_objs_itc = NULL, *_obj_itc = NULL; + +static Eina_Hash *_item_infos_hash = NULL; + +static Eina_Bool _show_only_diffs = EINA_FALSE; +static Eina_List *_comp_vvs = NULL; + +static Eina_List *_modified_units = NULL; + +static const char * +_action_name_get(Exactness_Action *act) +{ + if (!act) return NULL; + switch(act->type) + { + case EXACTNESS_ACTION_MOUSE_IN: return "Mouse In"; + case EXACTNESS_ACTION_MOUSE_OUT: return "Mouse Out"; + case EXACTNESS_ACTION_MOUSE_WHEEL: return "Mouse Wheel"; + case EXACTNESS_ACTION_MULTI_DOWN: return "Multi Down"; + case EXACTNESS_ACTION_MULTI_UP: return "Multi Up"; + case EXACTNESS_ACTION_MULTI_MOVE: return "Multi Move"; + case EXACTNESS_ACTION_KEY_DOWN: return "Key Down"; + case EXACTNESS_ACTION_KEY_UP: return "Key Up"; + case EXACTNESS_ACTION_TAKE_SHOT: return "Take shot"; + case EXACTNESS_ACTION_EFL_EVENT: return "EFL Event"; + case EXACTNESS_ACTION_CLICK_ON: return "Click On"; + case EXACTNESS_ACTION_STABILIZE: return "Stabilize"; + default: return NULL; + } +} + +static int +_event_struct_len_get(Exactness_Action_Type type) +{ + switch(type) + { + case EXACTNESS_ACTION_MOUSE_WHEEL: + return sizeof(Exactness_Action_Mouse_Wheel); + case EXACTNESS_ACTION_MULTI_DOWN: + case EXACTNESS_ACTION_MULTI_UP: + return sizeof(Exactness_Action_Multi_Event); + case EXACTNESS_ACTION_MULTI_MOVE: + return sizeof(Exactness_Action_Multi_Move); + case EXACTNESS_ACTION_KEY_DOWN: + case EXACTNESS_ACTION_KEY_UP: + return sizeof(Exactness_Action_Key_Down_Up); + case EXACTNESS_ACTION_EFL_EVENT: + return sizeof(Exactness_Action_Efl_Event); + case EXACTNESS_ACTION_CLICK_ON: + return sizeof(Exactness_Action_Click_On); + default: return 0; + } +} + +static void +_action_specific_info_get(const Exactness_Action *act, char output[1024]) +{ + switch(act->type) + { + case EXACTNESS_ACTION_MOUSE_WHEEL: + { + Exactness_Action_Mouse_Wheel *t = act->data; + sprintf(output, "Direction %d Z %d", t->direction, t->z); + break; + } + case EXACTNESS_ACTION_MULTI_UP: case EXACTNESS_ACTION_MULTI_DOWN: + { + Exactness_Action_Multi_Event *t = act->data; + if (!t->d) + sprintf(output, "Button %d Flags %d", t->b, t->flags); + else + sprintf(output, "D %d X %d Y %d Rad %f RadX %f RadY %f Pres %f Ang %f FX %f FY %f Flags %d", + t->d, t->x, t->y, t->rad, t->radx, t->rady, t->pres, t->ang, t->fx, t->fy, t->flags); + break; + } + case EXACTNESS_ACTION_MULTI_MOVE: + { + Exactness_Action_Multi_Move *t = act->data; + if (!t->d) + sprintf(output, "X %d Y %d", t->x, t->y); + else + sprintf(output, "D %d X %d Y %d Rad %f RadX %f RadY %f Pres %f Ang %f FX %f FY %f", + t->d, t->x, t->y, t->rad, t->radx, t->rady, t->pres, t->ang, t->fx, t->fy); + break; + } + case EXACTNESS_ACTION_KEY_UP: case EXACTNESS_ACTION_KEY_DOWN: + { + Exactness_Action_Key_Down_Up *t = act->data; + sprintf(output, "Keyname %s Key %s String %s Compose %s Keycode %d", + t->keyname, t->key, t->string, t->compose, t->keycode); + break; + } + case EXACTNESS_ACTION_EFL_EVENT: + { + Exactness_Action_Efl_Event *t = act->data; + sprintf(output, "Widget %s Event %s", t->wdg_name, t->event_name); + break; + } + case EXACTNESS_ACTION_CLICK_ON: + { + Exactness_Action_Click_On *t = act->data; + sprintf(output, "Widget %s", t->wdg_name); + break; + } + default: + { + output[0] = '\0'; + break; + } + } +} + +static Eina_Bool +_is_hook_duplicate(const Exactness_Action *cur_act, const Exactness_Action *prev_act) +{ + if (!prev_act) return EINA_FALSE; + if (cur_act->type == prev_act->type) + { + int len = _event_struct_len_get(cur_act->type); + return (!len || !memcmp(cur_act->data, prev_act->data, len)); + } + return EINA_FALSE; +} + +static Eina_Bool +_are_scenario_entries_different(Exactness_Action *act1, Exactness_Action *act2) +{ + if (!act1 ^ !act2) return EINA_TRUE; + if (act1->type != act2->type) return EINA_TRUE; + switch(act1->type) + { + case EXACTNESS_ACTION_MOUSE_WHEEL: + return !!memcmp(act1->data, act2->data, sizeof(Exactness_Action_Mouse_Wheel)); + case EXACTNESS_ACTION_MULTI_DOWN: case EXACTNESS_ACTION_MULTI_UP: + return !!memcmp(act1->data, act2->data, sizeof(Exactness_Action_Multi_Event)); + case EXACTNESS_ACTION_MULTI_MOVE: + return !!memcmp(act1->data, act2->data, sizeof(Exactness_Action_Multi_Move)); + case EXACTNESS_ACTION_KEY_UP: case EXACTNESS_ACTION_KEY_DOWN: + return !!memcmp(act1->data, act2->data, sizeof(Exactness_Action_Key_Down_Up)); + case EXACTNESS_ACTION_EFL_EVENT: + { + Exactness_Action_Efl_Event *e1 = act1->data; + Exactness_Action_Efl_Event *e2 = act2->data; + return (!!strcmp(e1->wdg_name, e2->wdg_name) || + !!strcmp(e1->event_name, e2->event_name)); + } + case EXACTNESS_ACTION_CLICK_ON: + { + Exactness_Action_Click_On *e1 = act1->data; + Exactness_Action_Click_On *e2 = act2->data; + return (!!strcmp(e1->wdg_name, e2->wdg_name)); + } + default: + return EINA_FALSE; + } + return EINA_FALSE; +} + +static Eina_Bool +_are_images_different(Exactness_Image *e_img1, Exactness_Image *e_img2) +{ + unsigned int w, h; + int *pxs1 = NULL; + int *pxs2 = NULL; + if (!e_img1 ^ !e_img2) return EINA_TRUE; + if (e_img1->w != e_img2->w) return EINA_TRUE; + if (e_img1->h != e_img2->h) return EINA_TRUE; + pxs1 = e_img1->pixels; + pxs2 = e_img2->pixels; + for (w = 0; w < e_img1->w; w++) + { + for (h = 0; h < e_img1->h; h++) + { + if (pxs1[h * e_img1->w + w] != pxs2[h * e_img1->w + w]) + return EINA_TRUE; + } + } + return EINA_FALSE; +} + +static Eina_Bool +_are_objs_different(Exactness_Object *e_obj1, Exactness_Object *e_obj2, Eina_Bool check_objs) +{ + if (!e_obj1 ^ !e_obj2) return EINA_TRUE; + Eina_List *itr1 = e_obj1->children; + Eina_List *itr2 = e_obj2->children; + if (check_objs && + (strcmp(e_obj1->kl_name, e_obj2->kl_name) || + e_obj1->x != e_obj2->x || e_obj1->y != e_obj2->y || + e_obj1->w != e_obj2->w || e_obj1->h != e_obj2->h)) return EINA_TRUE; + while (itr1 || itr2) + { + if ((!itr1) ^ (!itr2)) return EINA_TRUE; + e_obj1 = eina_list_data_get(itr1); + e_obj2 = eina_list_data_get(itr2); + + if (_are_objs_different(e_obj1, e_obj2, EINA_TRUE)) return EINA_TRUE; + + itr1 = eina_list_next(itr1); + itr2 = eina_list_next(itr2); + } + return EINA_FALSE; +} + +static Eina_Bool +_are_objs_trees_different(Exactness_Objects *e_objs1, Exactness_Objects *e_objs2) +{ + if (!e_objs1 ^ !e_objs2) return EINA_TRUE; + Eina_List *itr1 = e_objs1->objs; + Eina_List *itr2 = e_objs2->objs; + Exactness_Object *e_obj1, *e_obj2; + while (itr1 || itr2) + { + if ((!itr1) ^ (!itr2)) return EINA_TRUE; + e_obj1 = eina_list_data_get(itr1); + e_obj2 = eina_list_data_get(itr2); + + if (_are_objs_different(e_obj1, e_obj2, EINA_TRUE)) return EINA_TRUE; + + itr1 = eina_list_next(itr1); + itr2 = eina_list_next(itr2); + } + return EINA_FALSE; +} + +static void +_win_del(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + efl_exit(0); /* exit the program's main loop that runs in elm_run() */ +} + +static void +_gui_win_create() +{ + Eo *win, *bg; + + elm_policy_set(ELM_POLICY_QUIT, ELM_POLICY_QUIT_LAST_WINDOW_CLOSED); + win = elm_win_add(NULL, "Window", ELM_WIN_BASIC); + evas_object_smart_callback_add(win, "delete,request", _win_del, NULL); + elm_win_maximized_set(win, EINA_TRUE); + elm_win_autodel_set(win, EINA_TRUE); + elm_win_title_set(win, "Exactness Inspector"); + efl_gfx_entity_size_set(win, EINA_SIZE2D(1000, 800)); + + bg = elm_bg_add(win); + evas_object_size_hint_weight_set(bg, 1.000000, 1.000000); + efl_gfx_entity_visible_set(bg, EINA_TRUE); + elm_win_resize_object_add(win, bg); + + _main_box = elm_box_add(win); + elm_box_horizontal_set(_main_box, EINA_TRUE); + elm_box_homogeneous_set(_main_box, EINA_TRUE); + evas_object_size_hint_weight_set(_main_box, 1.000000, 1.000000); + efl_gfx_entity_visible_set(_main_box, EINA_TRUE); + elm_win_resize_object_add(win, _main_box); + + efl_gfx_entity_visible_set(win, EINA_TRUE); +} + +static char * +_grp_text_get(void *data, Evas_Object *gl, const char *part EINA_UNUSED) +{ + char buf[256]; + const char *str = NULL; + Eina_Bool compare = !!efl_key_data_get(gl, "_exactness_gl_compare"); + _Data_Type dt = (_Data_Type) data; + switch (dt) + { + case EX_FONTS_DIR: + { + char buf2[256]; + if (!compare) + { + Exactness_Unit *unit = efl_key_data_get(gl, "unit"); + sprintf(buf2, "Fonts directory: %s", unit->fonts_path?unit->fonts_path:"None"); + } + else + { + Eo *gl1 = eina_list_nth(_gls, 0); + Eo *gl2 = eina_list_nth(_gls, 1); + Exactness_Unit *unit1 = efl_key_data_get(gl1, "unit"); + Exactness_Unit *unit2 = efl_key_data_get(gl2, "unit"); + if (!!unit1->fonts_path ^ !!unit2->fonts_path) + sprintf(buf2, "Fonts directory comparison: XXXXX"); + else if (!strcmp(unit1->fonts_path, unit2->fonts_path)) + sprintf(buf2, "Fonts directory comparison: %s", unit1->fonts_path); + else + sprintf(buf2, "Fonts directory comparison: "LDIFF(%s)"/"RDIFF(%s), + unit1->fonts_path, unit2->fonts_path); + } + return strdup(buf2); + } + case EX_SCENARIO: { str = "Scenario"; break; } + case EX_IMAGE: { str = "Images"; break; } + case EX_OBJ_INFO: { str = "Objects"; break; } + default: { str = "Unknown"; break; } + } + sprintf(buf, "%s%s", str, compare ? " comparison" : ""); + if (dt == EX_FONTS_DIR) eina_stringshare_del(str); + return strdup(buf); +} + +static char * +_scn_text_get(void *data, Evas_Object *gl, const char *part EINA_UNUSED) +{ + Eina_Strbuf *buf = eina_strbuf_new(); + char *ret = NULL; + Eina_Bool compare = !!efl_key_data_get(gl, "_exactness_gl_compare"); + if (compare) + { + _Compare_Item_Data *vv = data; + Exactness_Action *a1 = vv->p1; + Exactness_Action *a2 = vv->p2; + + if (!a1 ^ !a2) return strdup("XXXXX"); + + if (a1->delay_ms != a2->delay_ms) eina_strbuf_append_printf(buf, "[+"LDIFF(%.3f)"/+"RDIFF(%.3f)"]: ", a1->delay_ms/1000.0, a2->delay_ms/1000.0); + else eina_strbuf_append_printf(buf, "+%.3f: ", a1->delay_ms / 1000.0); + + if (a1->type != a2->type) + eina_strbuf_append_printf(buf, "["LDIFF(%s)"/"RDIFF(%s)"] - XXXXXX", _action_name_get(a1), _action_name_get(a2)); + else + { + char params1[1024]; + char params2[2024]; + _action_specific_info_get(a1, params1); + _action_specific_info_get(a2, params2); + + eina_strbuf_append_printf(buf, "%s", _action_name_get(a1)); + if (*params1 || *params2) + { + if (strcmp(params1, params2)) + eina_strbuf_append_printf(buf, " - ["LDIFF(%s)"/"RDIFF(%s)"]", params1, params2); + else + eina_strbuf_append_printf(buf, " - %s", params1); + } + } + } + else + { + Exactness_Action *act = data; + char specific_output[1024]; + if (act) + { + eina_strbuf_append_printf(buf, "+%.3f: ", act->delay_ms / 1000.0); + eina_strbuf_append_printf(buf, "%s", _action_name_get(act)); + _action_specific_info_get(act, specific_output); + if (*specific_output) eina_strbuf_append_printf(buf, " - %s", specific_output); + } + else + eina_strbuf_append(buf, "XXXXX"); + } + + ret = eina_strbuf_string_steal(buf); + eina_strbuf_free(buf); + return ret; +} + +static int +_unit_shot_no_get(Exactness_Unit *unit, Exactness_Action *act_ref) +{ + Eina_List *itr; + Exactness_Action *act; + int ret = 0; + if (!unit) return -1; + EINA_LIST_FOREACH(unit->actions, itr, act) + { + if (act->type == EXACTNESS_ACTION_TAKE_SHOT) + { + if (act == act_ref) return ret; + ret++; + } + } + return -1; +} + +static void +_goto_shot(void *data EINA_UNUSED, Evas_Object *bt, void *event_info EINA_UNUSED) +{ + Eo *gl = efl_key_data_get(bt, "gl"); + Eina_Bool compare = !!efl_key_data_get(gl, "_exactness_gl_compare"); + if (compare) + { + _Compare_Item_Data *vv; + Eina_List *itr; + Eo *gl1 = eina_list_nth(_gls, 0); + Eo *gl2 = eina_list_nth(_gls, 1); + Exactness_Unit *unit1 = efl_key_data_get(gl1, "unit"); + Exactness_Unit *unit2 = efl_key_data_get(gl2, "unit"); + int shot1_no = (intptr_t)efl_key_data_get(bt, "shot1_no"); + int shot2_no = (intptr_t)efl_key_data_get(bt, "shot2_no"); + Exactness_Image *ex_img1 = shot1_no != -1 ? eina_list_nth(unit1->imgs, shot1_no) : NULL; + Exactness_Image *ex_img2 = shot2_no != -1 ? eina_list_nth(unit2->imgs, shot2_no) : NULL; + EINA_LIST_FOREACH(_comp_vvs, itr, vv) + { + if (vv->p1 == ex_img1 && vv->p2 == ex_img2) + { + _Item_Info *ii = eina_hash_find(_item_infos_hash, &vv); + if (ii && ii->gl_item) + elm_genlist_item_show(ii->gl_item, ELM_GENLIST_ITEM_SCROLLTO_MIDDLE); + } + } + } + else + { + Exactness_Unit *unit = efl_key_data_get(gl, "unit"); + int shot_no = (intptr_t)efl_key_data_get(bt, "shot_no"); + Exactness_Image *ex_img = shot_no != -1 ? eina_list_nth(unit->imgs, shot_no) : NULL; + _Item_Info *ii = eina_hash_find(_item_infos_hash, &ex_img); + if (ii && ii->gl_item) + elm_genlist_item_show(ii->gl_item, ELM_GENLIST_ITEM_SCROLLTO_MIDDLE); + } +} + +static Evas_Object * +_scn_content_get(void *data, Evas_Object *gl, const char *part) +{ + Eina_Bool compare = !!efl_key_data_get(gl, "_exactness_gl_compare"); + if (compare) + { + if (!strcmp(part, "elm.swallow.end")) + { + _Compare_Item_Data *vv = data; + Exactness_Action *v1 = vv->p1; + Exactness_Action *v2 = vv->p2; + if (v1 && v2 && v1->type == EXACTNESS_ACTION_TAKE_SHOT && + v2->type == EXACTNESS_ACTION_TAKE_SHOT) + { + Eo *gl1 = eina_list_nth(_gls, 0); + Eo *gl2 = eina_list_nth(_gls, 1); + Exactness_Unit *unit1 = efl_key_data_get(gl1, "unit"); + Exactness_Unit *unit2 = efl_key_data_get(gl2, "unit"); + int shot1_no = _unit_shot_no_get(unit1, v1); + int shot2_no = _unit_shot_no_get(unit2, v2); + Exactness_Image *ex_img1 = shot1_no != -1 ? eina_list_nth(unit1->imgs, shot1_no) : NULL; + Exactness_Image *ex_img2 = shot2_no != -1 ? eina_list_nth(unit2->imgs, shot2_no) : NULL; + Exactness_Image *ex_imgO = NULL; + exactness_image_compare(ex_img1, ex_img2, &ex_imgO); + + if (ex_imgO) + { + Eo *bt, *ic, *evas_img; + + bt = elm_button_add(gl); + evas_object_size_hint_weight_set(bt, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(bt, EVAS_HINT_FILL, EVAS_HINT_FILL); + evas_object_show(bt); + efl_key_data_set(bt, "gl", gl); + efl_key_data_set(bt, "shot1_no", (void *)(intptr_t)shot1_no); + efl_key_data_set(bt, "shot2_no", (void *)(intptr_t)shot2_no); + evas_object_smart_callback_add(bt, "clicked", _goto_shot, NULL); + + ic = elm_icon_add(bt); + evas_img = elm_image_object_get(ic); + evas_object_image_size_set(evas_img, ex_imgO->w, ex_imgO->h); + evas_object_image_data_set(evas_img, ex_imgO->pixels); + evas_object_show(ic); + elm_object_part_content_set(bt, "icon", ic); + return bt; + } + } + } + } + else + { + if (!strcmp(part, "elm.swallow.end")) + { + Exactness_Action *v = data; + Exactness_Unit *unit = efl_key_data_get(gl, "unit"); + int shot_no = _unit_shot_no_get(unit, v); + Exactness_Image *ex_img = shot_no != -1 ? eina_list_nth(unit->imgs, shot_no) : NULL; + + if (ex_img) + { + Eo *bt, *ic, *evas_img; + + bt = elm_button_add(gl); + evas_object_size_hint_weight_set(bt, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(bt, EVAS_HINT_FILL, EVAS_HINT_FILL); + evas_object_show(bt); + efl_key_data_set(bt, "gl", gl); + efl_key_data_set(bt, "shot_no", (void *)(intptr_t)shot_no); + evas_object_smart_callback_add(bt, "clicked", _goto_shot, NULL); + + ic = elm_icon_add(bt); + evas_img = elm_image_object_get(ic); + evas_object_image_size_set(evas_img, ex_img->w, ex_img->h); + evas_object_image_data_set(evas_img, ex_img->pixels); + evas_object_show(ic); + elm_object_part_content_set(bt, "icon", ic); + + return bt; + } + } + } + return NULL; +} + +static Evas_Object * +_img_content_get(void *data, Evas_Object *gl, const char *part) +{ + if (strcmp(part, "elm.swallow.content")) return NULL; + Eo *img = elm_image_add(gl); + Eo *evas_img = elm_image_object_get(img); + Eina_Bool compare = !!efl_key_data_get(gl, "_exactness_gl_compare"); + if (compare) + { + _Compare_Item_Data *vv = data; + Exactness_Image *ex_img1 = vv->p1; + Exactness_Image *ex_img2 = vv->p2; + Exactness_Image *ex_imgO = NULL; + exactness_image_compare(ex_img1, ex_img2, &ex_imgO); + + evas_object_image_size_set(evas_img, ex_imgO->w, ex_imgO->h); + evas_object_image_data_set(evas_img, ex_imgO->pixels); + evas_object_size_hint_min_set(img, ELM_SCALE_SIZE(300), ELM_SCALE_SIZE(300)); + } + else + { + if (!data) + { + efl_del(img); + return NULL; + } + Exactness_Image *ex_img = data; + evas_object_image_size_set(evas_img, ex_img->w, ex_img->h); + evas_object_image_data_set(evas_img, ex_img->pixels); + evas_object_size_hint_min_set(img, ELM_SCALE_SIZE(300), ELM_SCALE_SIZE(300)); + } + return img; +} + +static char * +_objs_text_get(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, const char *part EINA_UNUSED) +{ + return strdup("Shot"); +} + +static char * +_obj_text_get(void *data, Evas_Object *gl, const char *part EINA_UNUSED) +{ + Eina_Strbuf *buf = eina_strbuf_new(); + char *ret = NULL; + Eina_Bool compare = !!efl_key_data_get(gl, "_exactness_gl_compare"); + if (compare) + { + _Compare_Item_Data *vv = data; + Exactness_Object *e_obj1 = vv->p1; + Exactness_Object *e_obj2 = vv->p2; + if ((!e_obj1 ^ !e_obj2) || strcmp(e_obj1->kl_name, e_obj2->kl_name)) + eina_strbuf_append_printf(buf, "("LDIFF(%s)"/"RDIFF(%s)")", + e_obj1 ? e_obj1->kl_name : "XXXXX", + e_obj2 ? e_obj2->kl_name : "XXXXX"); + else + eina_strbuf_append_printf(buf, "%s", e_obj1->kl_name); + + eina_strbuf_append(buf, " x = "); + if ((!e_obj1 ^ !e_obj2) || e_obj1->x != e_obj2->x) + eina_strbuf_append_printf(buf, LDIFF(%d)"/"RDIFF(%d), + e_obj1 ? e_obj1->x : -1, + e_obj2 ? e_obj2->x : -1); + else + eina_strbuf_append_printf(buf, "%d", e_obj1->x); + + eina_strbuf_append(buf, " y = "); + if ((!e_obj1 ^ !e_obj2) || e_obj1->y != e_obj2->y) + eina_strbuf_append_printf(buf, LDIFF(%d)"/"RDIFF(%d), + e_obj1 ? e_obj1->y : -1, + e_obj2 ? e_obj2->y : -1); + else + eina_strbuf_append_printf(buf, "%d", e_obj1->y); + + eina_strbuf_append(buf, " w = "); + if ((!e_obj1 ^ !e_obj2) || e_obj1->w != e_obj2->w) + eina_strbuf_append_printf(buf, LDIFF(%d)"/"RDIFF(%d), + e_obj1 ? e_obj1->w : -1, + e_obj2 ? e_obj2->w : -1); + else + eina_strbuf_append_printf(buf, "%d", e_obj1->w); + + eina_strbuf_append(buf, " h = "); + if ((!e_obj1 ^ !e_obj2) || e_obj1->h != e_obj2->h) + eina_strbuf_append_printf(buf, LDIFF(%d)"/"RDIFF(%d), + e_obj1 ? e_obj1->h : -1, + e_obj2 ? e_obj2->h : -1); + else + eina_strbuf_append_printf(buf, "%d", e_obj1->h); + + if (e_obj1 && e_obj2 && _are_objs_different(e_obj1, e_obj2, EINA_FALSE)) + eina_strbuf_append(buf, " - DIFF INSIDE"); + } + else + { + Exactness_Object *e_obj = data; + eina_strbuf_append_printf(buf, + "%s: x = %d y = %d w = %d h = %d", + e_obj->kl_name, + e_obj->x, e_obj->y, e_obj->w, e_obj->h); + } + + ret = eina_strbuf_string_steal(buf); + eina_strbuf_free(buf); + return ret; +} + +static void +_itc_init() +{ + if (!_grp_itc) + { + _grp_itc = elm_genlist_item_class_new(); + _grp_itc->item_style = "group_index"; + _grp_itc->func.text_get = _grp_text_get; + } + + if (!_scn_itc) + { + _scn_itc = elm_genlist_item_class_new(); + _scn_itc->item_style = "default_style"; + _scn_itc->func.text_get = _scn_text_get; + _scn_itc->func.content_get = _scn_content_get; + } + + if (!_img_itc) + { + _img_itc = elm_genlist_item_class_new(); + _img_itc->item_style = "full"; + _img_itc->func.content_get = _img_content_get; + } + + if (!_objs_itc) + { + _objs_itc = elm_genlist_item_class_new(); + _objs_itc->item_style = "default_style"; + _objs_itc->func.text_get = _objs_text_get; + } + + if (!_obj_itc) + { + _obj_itc = elm_genlist_item_class_new(); + _obj_itc->item_style = "default_style"; + _obj_itc->func.text_get = _obj_text_get; + } +} + +static void +_comp_gl_dragged_cb(Evas_Object *obj, void *data EINA_UNUSED) +{ + int x = 0, y = 0; + Eo *gl; + Eina_List *itr; + elm_interface_scrollable_content_pos_get(obj, &x, &y); + EINA_LIST_FOREACH(_gls, itr, gl) + { + if (gl != obj) + elm_interface_scrollable_content_pos_set(gl, x, y, EINA_FALSE); + } +} + +static void +_obj_item_realize(Exactness_Object *ex_obj) +{ + _Item_Info *ii = eina_hash_find(_item_infos_hash, &ex_obj); + if (!ii) return; + if (ii->gl_item) return; + _obj_item_realize(ii->ex_parent); + _Item_Info *iip = eina_hash_find(_item_infos_hash, &(ii->ex_parent)); + if (iip->gl_item) elm_genlist_item_expanded_set(iip->gl_item, EINA_TRUE); +} + +static void +_gl_expand_request_cb(void *data EINA_UNUSED, Evas_Object *gl, void *event_info) +{ + Elm_Object_Item *glit = event_info; + Eina_Bool compare = !!efl_key_data_get(gl, "_exactness_gl_compare"); + if (compare) + { + const Elm_Genlist_Item_Class *itc = elm_genlist_item_item_class_get(glit); + if (itc == _objs_itc) + { + _Compare_Item_Data *vv = elm_object_item_data_get(glit); + _Item_Info *ii = eina_hash_find(_item_infos_hash, &(vv->p1)); + if (ii) elm_genlist_item_expanded_set(ii->gl_item, EINA_TRUE); + ii = eina_hash_find(_item_infos_hash, &(vv->p2)); + if (ii) elm_genlist_item_expanded_set(ii->gl_item, EINA_TRUE); + } + else if (itc == _obj_itc) + { + _Compare_Item_Data *vv = elm_object_item_data_get(glit); + _Item_Info *ii = eina_hash_find(_item_infos_hash, &(vv->p1)); + if (!ii || !ii->gl_item) _obj_item_realize(vv->p1); + if (!ii) ii = eina_hash_find(_item_infos_hash, &(vv->p1)); + if (ii && ii->gl_item) elm_genlist_item_expanded_set(ii->gl_item, EINA_TRUE); + + ii = eina_hash_find(_item_infos_hash, &(vv->p2)); + if (!ii || !ii->gl_item) _obj_item_realize(vv->p2); + if (!ii) ii = eina_hash_find(_item_infos_hash, &(vv->p2)); + if (ii && ii->gl_item) elm_genlist_item_expanded_set(ii->gl_item, EINA_TRUE); + } + } + elm_genlist_item_expanded_set(glit, EINA_TRUE); +} + +static void +_gl_contract_request_cb(void *data EINA_UNUSED, Evas_Object *gl EINA_UNUSED, void *event_info) +{ + Elm_Object_Item *glit = event_info; + Eina_Bool compare = !!efl_key_data_get(gl, "_exactness_gl_compare"); + if (compare) + { + const Elm_Genlist_Item_Class *itc = elm_genlist_item_item_class_get(glit); + if (itc == _objs_itc) + { + _Compare_Item_Data *vv = elm_object_item_data_get(glit); + _Item_Info *ii = eina_hash_find(_item_infos_hash, &(vv->p1)); + if (ii) elm_genlist_item_expanded_set(ii->gl_item, EINA_FALSE); + ii = eina_hash_find(_item_infos_hash, &(vv->p2)); + if (ii) elm_genlist_item_expanded_set(ii->gl_item, EINA_FALSE); + } + else if (itc == _obj_itc) + { + _Compare_Item_Data *vv = elm_object_item_data_get(glit); + _Item_Info *ii = eina_hash_find(_item_infos_hash, &(vv->p1)); + if (ii && ii->gl_item) elm_genlist_item_expanded_set(ii->gl_item, EINA_FALSE); + + ii = eina_hash_find(_item_infos_hash, &(vv->p2)); + if (ii && ii->gl_item) elm_genlist_item_expanded_set(ii->gl_item, EINA_FALSE); + } + } + elm_genlist_item_expanded_set(glit, EINA_FALSE); +} + +static void +_gl_expanded_cb(void *_data EINA_UNUSED, Evas_Object *gl EINA_UNUSED, void *event_info) +{ + Elm_Object_Item *glit = event_info; + const Elm_Genlist_Item_Class *itc = elm_genlist_item_item_class_get(glit); + Eina_Bool compare = !!efl_key_data_get(gl, "_exactness_gl_compare"); + if (itc == _objs_itc) + { + if (compare) + { + _Compare_Item_Data *vv = elm_object_item_data_get(glit); + Exactness_Objects *e_objs1 = vv->p1; + Exactness_Objects *e_objs2 = vv->p2; + Eina_List *itr1 = e_objs1->main_objs, *itr2 = e_objs2->main_objs; + + while (itr1 || itr2) + { + Exactness_Object *e_obj1 = eina_list_data_get(itr1); + Exactness_Object *e_obj2 = eina_list_data_get(itr2); + vv = calloc(1, sizeof(*vv)); + vv->p1 = e_obj1; + vv->p2 = e_obj2; + vv->dt = EX_OBJ_INFO; + elm_genlist_item_append(gl, _obj_itc, vv, glit, + e_obj1->children || e_obj2->children ? ELM_GENLIST_ITEM_TREE : ELM_GENLIST_ITEM_NONE, + NULL, NULL); + itr1 = eina_list_next(itr1); + itr2 = eina_list_next(itr2); + } + } + else + { + Exactness_Objects *e_objs = elm_object_item_data_get(glit); + Eina_List *itr; + Exactness_Object *e_obj; + EINA_LIST_FOREACH(e_objs->main_objs, itr, e_obj) + { + _Item_Info *ii = eina_hash_find(_item_infos_hash, &e_obj); + if (!ii) + { + ii = calloc(1, sizeof(*ii)); + eina_hash_set(_item_infos_hash, &e_obj, ii); + } + ii->ex_parent = e_objs; + ii->gl_item = elm_genlist_item_append(gl, _obj_itc, e_obj, glit, + e_obj->children ? ELM_GENLIST_ITEM_TREE : ELM_GENLIST_ITEM_NONE, + NULL, NULL); + efl_wref_add(ii->gl_item, &(ii->gl_item)); + } + } + } + else if (itc == _obj_itc) + { + if (compare) + { + _Compare_Item_Data *vv = elm_object_item_data_get(glit); + Exactness_Object *e_obj1 = vv->p1; + Exactness_Object *e_obj2 = vv->p2; + Eina_List *itr1 = e_obj1->children, *itr2 = e_obj2->children; + + while (itr1 || itr2) + { + e_obj1 = eina_list_data_get(itr1); + e_obj2 = eina_list_data_get(itr2); + vv = calloc(1, sizeof(*vv)); + vv->p1 = e_obj1; + vv->p2 = e_obj2; + vv->dt = EX_OBJ_INFO; + elm_genlist_item_append(gl, _obj_itc, vv, glit, + (e_obj1 && e_obj1->children) || (e_obj2 && e_obj2->children) ? + ELM_GENLIST_ITEM_TREE : ELM_GENLIST_ITEM_NONE, + NULL, NULL); + itr1 = eina_list_next(itr1); + itr2 = eina_list_next(itr2); + } + } + else + { + Exactness_Object *e_obj = elm_object_item_data_get(glit), *e_obj2; + Eina_List *itr; + + EINA_LIST_FOREACH(e_obj->children, itr, e_obj2) + { + _Item_Info *ii = eina_hash_find(_item_infos_hash, &e_obj2); + if (!ii) + { + ii = calloc(1, sizeof(*ii)); + eina_hash_set(_item_infos_hash, &e_obj2, ii); + } + ii->ex_parent = e_obj; + ii->gl_item = elm_genlist_item_append(gl, _obj_itc, e_obj2, glit, + e_obj2->children ? ELM_GENLIST_ITEM_TREE : ELM_GENLIST_ITEM_NONE, + NULL, NULL); + efl_wref_add(ii->gl_item, &(ii->gl_item)); + } + } + } +} + +static void +_gl_contracted_cb(void *data EINA_UNUSED, Evas_Object *gl EINA_UNUSED, void *event_info) +{ + Elm_Object_Item *glit = event_info; + elm_genlist_item_subitems_clear(glit); +} + +static void +_comp_gl_selected_cb(void *data EINA_UNUSED, Evas_Object *gl EINA_UNUSED, void *event_info) +{ + _comp_selected_item = event_info; + _Compare_Item_Data *vv = elm_object_item_data_get(_comp_selected_item); + if (vv->p1) + { + _Item_Info *ii = eina_hash_find(_item_infos_hash, &(vv->p1)); + if (!ii || !ii->gl_item) _obj_item_realize(vv->p1); + elm_genlist_item_selected_set(ii->gl_item, EINA_TRUE); + } + + if (vv->p2) + { + _Item_Info *ii = eina_hash_find(_item_infos_hash, &(vv->p2)); + if (!ii || !ii->gl_item) _obj_item_realize(vv->p2); + elm_genlist_item_selected_set(ii->gl_item, EINA_TRUE); + } +} + +static void +_scn_item_remove(void *data, Evas_Object *menu EINA_UNUSED, void *item EINA_UNUSED) +{ + Eo *glit = data; + Exactness_Unit *unit = efl_key_data_get(efl_parent_get(glit), "unit"); + Exactness_Action *act = elm_object_item_data_get(glit); + unit->actions = eina_list_remove(unit->actions, act); + if (!eina_list_data_find(_modified_units, unit)) + _modified_units = eina_list_append(_modified_units, unit); + efl_del(glit); +} + +static void +_gl_clicked_right_cb(void *data, Evas_Object *gl, void *event_info) +{ + int x = 0, y = 0; + Eo *win = data, *menu; + Elm_Object_Item *glit = event_info; + + if (elm_genlist_item_item_class_get(glit) == _scn_itc) + { + elm_genlist_item_selected_set(glit, EINA_TRUE); + evas_pointer_canvas_xy_get(evas_object_evas_get(gl), &x, &y); + + menu = elm_menu_add(win); + elm_menu_move(menu, x, y); + elm_menu_item_add(menu, NULL, NULL, "Remove", _scn_item_remove, glit); + efl_gfx_entity_visible_set(menu, EINA_TRUE); + } +} + +static void +_gl_img_show(void *data, Evas_Object *obj, void *event_info EINA_UNUSED) +{ + static Eo *_img_win = NULL; + Exactness_Image *ex_img = data; + if (_img_win) efl_del(_img_win); + _img_win = efl_add(EFL_UI_WIN_CLASS, elm_win_get(obj), + efl_ui_win_type_set(efl_added, EFL_UI_WIN_TYPE_DIALOG_BASIC), + efl_ui_win_autodel_set(efl_added, EINA_TRUE)); + efl_wref_add(_img_win, &_img_win); + + Evas_Object *image = elm_image_add(_img_win); + Eo *evas_img = elm_image_object_get(image); + evas_object_image_size_set(evas_img, ex_img->w, ex_img->h); + evas_object_image_data_set(evas_img, ex_img->pixels); + efl_content_set(_img_win, image); + + efl_gfx_entity_size_set(_img_win, EINA_SIZE2D(550, 500)); +} + +static void +_gui_unit_display(Exactness_Unit *unit1, Exactness_Unit *unit2) +{ + Eina_List *itr1, *itr2; + Eo *gl1, *gl2 = NULL, *glc = NULL; + + gl1 = elm_genlist_add(_main_box); + elm_genlist_homogeneous_set(gl1, EINA_TRUE); + evas_object_size_hint_weight_set(gl1, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(gl1, EVAS_HINT_FILL, EVAS_HINT_FILL); + efl_gfx_entity_visible_set(gl1, EINA_TRUE); + _gls = eina_list_append(_gls, gl1); + elm_box_pack_end(_main_box, gl1); + + efl_key_data_set(gl1, "unit", unit1); + evas_object_smart_callback_add(gl1, "expand,request", _gl_expand_request_cb, NULL); + evas_object_smart_callback_add(gl1, "contract,request", _gl_contract_request_cb, NULL); + evas_object_smart_callback_add(gl1, "expanded", _gl_expanded_cb, NULL); + evas_object_smart_callback_add(gl1, "contracted", _gl_contracted_cb, NULL); + if (!unit2) + evas_object_smart_callback_add(gl1, "clicked,right", _gl_clicked_right_cb, elm_win_get(_main_box)); + + if (unit2) + { + glc = elm_genlist_add(_main_box); + elm_genlist_homogeneous_set(glc, EINA_TRUE); + evas_object_size_hint_weight_set(glc, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(glc, EVAS_HINT_FILL, EVAS_HINT_FILL); + efl_gfx_entity_visible_set(glc, EINA_TRUE); + elm_box_pack_end(_main_box, glc); + + evas_object_smart_callback_add(glc, "expand,request", _gl_expand_request_cb, NULL); + evas_object_smart_callback_add(glc, "contract,request", _gl_contract_request_cb, NULL); + evas_object_smart_callback_add(glc, "expanded", _gl_expanded_cb, NULL); + evas_object_smart_callback_add(glc, "contracted", _gl_contracted_cb, NULL); + + efl_key_data_set(glc, "_exactness_gl_compare", glc); + elm_interface_scrollable_scroll_down_cb_set(glc, _comp_gl_dragged_cb); + elm_interface_scrollable_scroll_up_cb_set(glc, _comp_gl_dragged_cb); + evas_object_smart_callback_add(glc, "selected", _comp_gl_selected_cb, NULL); + + gl2 = elm_genlist_add(_main_box); + elm_genlist_homogeneous_set(gl2, EINA_TRUE); + evas_object_size_hint_weight_set(gl2, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(gl2, EVAS_HINT_FILL, EVAS_HINT_FILL); + efl_gfx_entity_visible_set(gl2, EINA_TRUE); + _gls = eina_list_append(_gls, gl2); + elm_box_pack_end(_main_box, gl2); + + efl_key_data_set(gl2, "unit", unit2); + evas_object_smart_callback_add(gl2, "expand,request", _gl_expand_request_cb, NULL); + evas_object_smart_callback_add(gl2, "contract,request", _gl_contract_request_cb, NULL); + evas_object_smart_callback_add(gl2, "expanded", _gl_expanded_cb, NULL); + evas_object_smart_callback_add(gl2, "contracted", _gl_contracted_cb, NULL); + } + _itc_init(); + + if (unit1->fonts_path || (unit2 && unit2->fonts_path)) + { + if (!_show_only_diffs || !unit1 || !unit2 || + !unit1->fonts_path || !unit2->fonts_path || + strcmp(unit1->fonts_path, unit2->fonts_path)) + { + elm_genlist_item_append(gl1, _grp_itc, (void *)EX_FONTS_DIR, NULL, ELM_GENLIST_ITEM_GROUP, NULL, NULL); + elm_genlist_item_append(gl2, _grp_itc, (void *)EX_FONTS_DIR, NULL, ELM_GENLIST_ITEM_GROUP, NULL, NULL); + elm_genlist_item_append(glc, _grp_itc, (void *)EX_FONTS_DIR, NULL, ELM_GENLIST_ITEM_GROUP, NULL, NULL); + } + } + itr1 = unit1 ? unit1->actions : NULL; + itr2 = unit2 ? unit2->actions : NULL; + + if (itr1 || itr2) + { + elm_genlist_item_append(gl1, _grp_itc, (void *)EX_SCENARIO, NULL, ELM_GENLIST_ITEM_GROUP, NULL, NULL); + elm_genlist_item_append(gl2, _grp_itc, (void *)EX_SCENARIO, NULL, ELM_GENLIST_ITEM_GROUP, NULL, NULL); + elm_genlist_item_append(glc, _grp_itc, (void *)EX_SCENARIO, NULL, ELM_GENLIST_ITEM_GROUP, NULL, NULL); + } + while (itr1 || itr2) + { + Exactness_Action *v1 = itr1 ? eina_list_data_get(itr1) : NULL; + Exactness_Action *v2 = itr2 ? eina_list_data_get(itr2) : NULL; + if (!_show_only_diffs || _are_scenario_entries_different(v1, v2)) + { + _Item_Info *ii = calloc(1, sizeof(*ii)); + eina_hash_set(_item_infos_hash, &v1, ii); + ii->gl_item = elm_genlist_item_append(gl1, _scn_itc, v1, NULL, ELM_GENLIST_ITEM_NONE, NULL, NULL); + if (unit2) + { + _Compare_Item_Data *vv = calloc(1, sizeof(*vv)); + vv->p1 = v1; + vv->p2 = v2; + vv->dt = EX_SCENARIO; + ii = calloc(1, sizeof(*ii)); + eina_hash_set(_item_infos_hash, &v2, ii); + ii->gl_item = elm_genlist_item_append(gl2, _scn_itc, v2, NULL, ELM_GENLIST_ITEM_NONE, NULL, NULL); + elm_genlist_item_append(glc, _scn_itc, vv, NULL, ELM_GENLIST_ITEM_NONE, NULL, NULL); + } + } + if (itr1) itr1 = eina_list_next(itr1); + if (itr2) itr2 = eina_list_next(itr2); + } + + itr1 = unit1 ? unit1->imgs : NULL; + itr2 = unit2 ? unit2->imgs : NULL; + + if (itr1 || itr2) + { + elm_genlist_item_append(gl1, _grp_itc, (void *)EX_IMAGE, NULL, ELM_GENLIST_ITEM_GROUP, NULL, NULL); + elm_genlist_item_append(gl2, _grp_itc, (void *)EX_IMAGE, NULL, ELM_GENLIST_ITEM_GROUP, NULL, NULL); + elm_genlist_item_append(glc, _grp_itc, (void *)EX_IMAGE, NULL, ELM_GENLIST_ITEM_GROUP, NULL, NULL); + } + while (itr1 || itr2) + { + Exactness_Image *img1 = itr1 ? eina_list_data_get(itr1) : NULL; + Exactness_Image *img2 = itr2 ? eina_list_data_get(itr2) : NULL; + if (!_show_only_diffs || _are_images_different(img1, img2)) + { + _Item_Info *ii = calloc(1, sizeof(*ii)); + eina_hash_set(_item_infos_hash, &img1, ii); + ii->gl_item = elm_genlist_item_append(gl1, _img_itc, img1, NULL, ELM_GENLIST_ITEM_NONE, _gl_img_show, img1); + if (unit2) + { + _Compare_Item_Data *vv = calloc(1, sizeof(*vv)); + vv->p1 = img1; + vv->p2 = img2; + vv->dt = EX_IMAGE; + ii = calloc(1, sizeof(*ii)); + eina_hash_set(_item_infos_hash, &img2, ii); + ii->gl_item = elm_genlist_item_append(gl2, _img_itc, img2, NULL, ELM_GENLIST_ITEM_NONE, NULL, NULL); + /* This item info is needed to go to images from scenario shot entry */ + ii = calloc(1, sizeof(*ii)); + eina_hash_set(_item_infos_hash, &vv, ii); + ii->gl_item = elm_genlist_item_append(glc, _img_itc, vv, NULL, ELM_GENLIST_ITEM_NONE, NULL, NULL); + _comp_vvs = eina_list_append(_comp_vvs, vv); + } + } + if (itr1) itr1 = eina_list_next(itr1); + if (itr2) itr2 = eina_list_next(itr2); + } + + itr1 = unit1 ? unit1->objs : NULL; + itr2 = unit2 ? unit2->objs : NULL; + + if (itr1 || itr2) + { + elm_genlist_item_append(gl1, _grp_itc, (void *)EX_OBJ_INFO, NULL, ELM_GENLIST_ITEM_GROUP, NULL, NULL); + elm_genlist_item_append(gl2, _grp_itc, (void *)EX_OBJ_INFO, NULL, ELM_GENLIST_ITEM_GROUP, NULL, NULL); + elm_genlist_item_append(glc, _grp_itc, (void *)EX_OBJ_INFO, NULL, ELM_GENLIST_ITEM_GROUP, NULL, NULL); + } + while (itr1 || itr2) + { + Exactness_Objects *objs1 = itr1 ? eina_list_data_get(itr1) : NULL; + Exactness_Objects *objs2 = itr2 ? eina_list_data_get(itr2) : NULL; + if (!_show_only_diffs || _are_objs_trees_different(objs1, objs2)) + { + _Item_Info *ii = calloc(1, sizeof(*ii)); + eina_hash_set(_item_infos_hash, &objs1, ii); + ii->gl_item = elm_genlist_item_append(gl1, _objs_itc, objs1, NULL, + ELM_GENLIST_ITEM_TREE, NULL, NULL); + efl_wref_add(ii->gl_item, &(ii->gl_item)); + if (unit2) + { + _Compare_Item_Data *vv = calloc(1, sizeof(*vv)); + vv->p1 = objs1; + vv->p2 = objs2; + vv->dt = EX_OBJ_INFO; + ii = calloc(1, sizeof(*ii)); + eina_hash_set(_item_infos_hash, &objs2, ii); + ii->gl_item = elm_genlist_item_append(gl2, _objs_itc, objs2, NULL, + ELM_GENLIST_ITEM_TREE, NULL, NULL); + efl_wref_add(ii->gl_item, &(ii->gl_item)); + elm_genlist_item_append(glc, _objs_itc, vv, NULL, ELM_GENLIST_ITEM_TREE, NULL, NULL); + } + } + if (itr1) itr1 = eina_list_next(itr1); + if (itr2) itr2 = eina_list_next(itr2); + } +} + +static void +_diff_result_print(Exactness_Unit *unit1, Exactness_Unit *unit2) +{ + Eina_List *itr1, *itr2; + + int nb_scenario = 0, nb_diff_scenario = 0; + int nb_image = 0, nb_diff_image = 0; + int nb_objtree= 0, nb_diff_objtree = 0; + + itr1 = unit1 ? unit1->actions : NULL; + itr2 = unit2 ? unit2->actions : NULL; + + while (itr1 || itr2) + { + Exactness_Action *v1 = itr1 ? eina_list_data_get(itr1) : NULL; + Exactness_Action *v2 = itr2 ? eina_list_data_get(itr2) : NULL; + + nb_scenario++; + if (_are_scenario_entries_different(v1, v2)) + nb_diff_scenario++; + + if (itr1) itr1 = eina_list_next(itr1); + if (itr2) itr2 = eina_list_next(itr2); + } + + itr1 = unit1 ? unit1->imgs : NULL; + itr2 = unit2 ? unit2->imgs : NULL; + + while (itr1 || itr2) + { + Exactness_Image *img1 = itr1 ? eina_list_data_get(itr1) : NULL; + Exactness_Image *img2 = itr2 ? eina_list_data_get(itr2) : NULL; + + nb_image++; + if (_are_images_different(img1, img2)) + nb_diff_image++; + + if (itr1) itr1 = eina_list_next(itr1); + if (itr2) itr2 = eina_list_next(itr2); + } + + itr1 = unit1 ? unit1->objs : NULL; + itr2 = unit2 ? unit2->objs : NULL; + + while (itr1 || itr2) + { + Exactness_Objects *objs1 = itr1 ? eina_list_data_get(itr1) : NULL; + Exactness_Objects *objs2 = itr2 ? eina_list_data_get(itr2) : NULL; + + nb_objtree++; + if (_are_objs_trees_different(objs1, objs2)) + nb_diff_objtree++; + + if (itr1) itr1 = eina_list_next(itr1); + if (itr2) itr2 = eina_list_next(itr2); + } + + printf("%s\nscenario (%d/%d)\nimage (%d/%d)\nobjs_tree (%d/%d)\n", + nb_diff_scenario || nb_diff_image || nb_diff_objtree ? + "Failure" : "Success", + nb_scenario - nb_diff_scenario, nb_scenario, + nb_image - nb_diff_image, nb_image, + nb_objtree - nb_diff_objtree, nb_objtree); +} + +static Exactness_Image * +_image_read(const char *filename) +{ + int w, h; + Evas_Load_Error err; + Ecore_Evas *ee = ecore_evas_new(NULL, 0, 0, 100, 100, NULL); + + /* the canvas pointer, de facto */ + Eo *e = ecore_evas_get(ee); + + Eo *img = evas_object_image_add(e); + evas_object_image_file_set(img, filename, NULL); + err = evas_object_image_load_error_get(img); + if (err != EVAS_LOAD_ERROR_NONE) + { + fprintf(stderr, "could not load image '%s'. error string is \"%s\"\n", + filename, evas_load_error_str(err)); + return NULL; + } + + Exactness_Image *ex_img = malloc(sizeof(*ex_img)); + int len; + evas_object_image_size_get(img, &w, &h); + ex_img->w = w; + ex_img->h = h; + len = w * h * 4; + ex_img->pixels = malloc(len); + memcpy(ex_img->pixels, evas_object_image_data_get(img, EINA_FALSE), len); + + ecore_evas_free(ee); + return ex_img; +} + +static const Ecore_Getopt optdesc = { + "exactness_inspect", + "%prog [options] [ | ]", + NULL, + "(C) 2016 Enlightenment", + "BSD", + "Inspector for Exactness", + 0, + { + ECORE_GETOPT_STORE_USHORT('d', "delay", "Delay the given recording by a given time (in milliseconds)."), + ECORE_GETOPT_STORE_TRUE('c', "clean", "Clean the given recording from wrong actions."), + ECORE_GETOPT_STORE_TRUE('l', "list", "List the actions of the given recording."), + ECORE_GETOPT_STORE_TRUE('C', "compare", "Compare given files (images files or objects eet files)."), + ECORE_GETOPT_STORE_TRUE(0, "show-only-diffs", "Show only differences during comparison."), + ECORE_GETOPT_STORE_TRUE(0, "stabilize", "Stabilize after the given shot number in --shot."), + ECORE_GETOPT_STORE_TRUE(0, "pack", "Pack the given input files (scenario and images) into the given output."), + ECORE_GETOPT_STORE_STR('o', "output", "Output."), + ECORE_GETOPT_STORE_USHORT('s', "shot", "Select a specific shot (1 = 1st shot...)."), + + ECORE_GETOPT_LICENSE('L', "license"), + ECORE_GETOPT_COPYRIGHT('C', "copyright"), + ECORE_GETOPT_VERSION('V', "version"), + ECORE_GETOPT_HELP('h', "help"), + ECORE_GETOPT_SENTINEL + } +}; + +int +main(int argc, char *argv[]) +{ + Eina_List *units_filenames = NULL; + const char *ext = NULL; + char *output = NULL; + Exactness_Unit *unit = NULL; + int ret = 1, args = 0; + unsigned short delay = 0, shot = 0; + Eina_Bool write_file = EINA_FALSE; + Eina_Bool want_quit, clean = EINA_FALSE, list_get = EINA_FALSE, compare_files = EINA_FALSE; + Eina_Bool stabilize = EINA_FALSE, show_only_diffs = EINA_FALSE, gui_needed = EINA_TRUE; + Eina_Bool pack = EINA_FALSE; + + Ecore_Getopt_Value values[] = { + ECORE_GETOPT_VALUE_USHORT(delay), + ECORE_GETOPT_VALUE_BOOL(clean), + ECORE_GETOPT_VALUE_BOOL(list_get), + ECORE_GETOPT_VALUE_BOOL(compare_files), + ECORE_GETOPT_VALUE_BOOL(show_only_diffs), + ECORE_GETOPT_VALUE_BOOL(stabilize), + ECORE_GETOPT_VALUE_BOOL(pack), + ECORE_GETOPT_VALUE_STR(output), + ECORE_GETOPT_VALUE_USHORT(shot), + + ECORE_GETOPT_VALUE_BOOL(want_quit), + ECORE_GETOPT_VALUE_BOOL(want_quit), + ECORE_GETOPT_VALUE_BOOL(want_quit), + ECORE_GETOPT_VALUE_BOOL(want_quit), + ECORE_GETOPT_VALUE_NONE + }; + + ecore_evas_init(); + ecore_init(); + eet_init(); + elm_init(0, NULL); + want_quit = EINA_FALSE; + + args = ecore_getopt_parse(&optdesc, values, argc, argv); + if (args < 0) + { + fprintf(stderr, "Failed parsing arguments.\n"); + goto end; + } + if (want_quit) + { + goto end; + } + if ((clean || delay || shot || list_get || stabilize || pack) && args == argc) + { + fprintf(stderr, "Expected scenario (.rec/.exu) as the last argument.\n"); + ecore_getopt_help(stderr, &optdesc); + goto end; + } + if (shot && (!delay && !stabilize)) + { + fprintf(stderr, "shot option can only be used with delay or stabilize option.\n"); + goto end; + } + if (delay && !shot) + { + fprintf(stderr, "delay option can only be used with shot option.\n"); + goto end; + } + if (stabilize && !shot) + { + fprintf(stderr, "stabilize option can only be used with shot option.\n"); + goto end; + } + if (compare_files && argc - args < 2) + { + fprintf(stderr, "Expected at least two files to compare as last arguments.\n"); + ecore_getopt_help(stderr, &optdesc); + goto end; + } + if (show_only_diffs && !compare_files) + { + fprintf(stderr, "--show-only-diffs is available with --compare only\n"); + goto end; + } + if (show_only_diffs && output) + { + fprintf(stderr, "--show-only-diffs works in GUI only\n"); + goto end; + } + _show_only_diffs = show_only_diffs; + + if (clean || delay || list_get || stabilize || pack) + { + int arg; + Eina_List *images = NULL; + gui_needed = EINA_FALSE; + for (arg = args; arg < argc; arg++) + { + const char *src_file = argv[arg]; + ext = strrchr(src_file, '.'); + if (!ext) + { + fprintf(stderr, "Extension required\n"); + goto end; + } + if (!strcmp(ext, ".exu")) + { + if (!unit) unit = exactness_unit_file_read(src_file); + else + { + fprintf(stderr, "%s - scenario already provided\n", src_file); + goto end; + } + } + else if (!strcmp(ext, ".rec")) + { + if (!unit) unit = legacy_rec_file_read(src_file); + else + { + fprintf(stderr, "%s - scenario already provided\n", src_file); + goto end; + } + } + else if (!strcmp(ext, ".png")) + { + Exactness_Image *ex_img = _image_read(src_file); + if (!ex_img) + { + fprintf(stderr, "Issue while reading %s\n", src_file); + goto end; + } + images = eina_list_append(images, ex_img); + } + else + { + fprintf(stderr, "Correct extension (.exu/.rec/.png) required\n"); + goto end; + } + } + if (unit) + { + Exactness_Image *ex_img; + EINA_LIST_FREE(images, ex_img) + { + unit->imgs = eina_list_append(unit->imgs, ex_img); + unit->nb_shots++; + } + } + } + else + { + int arg; + if (output) gui_needed = EINA_FALSE; + for (arg = args; arg < argc; arg++) + { + ext = strrchr(argv[arg], '.'); + if (!ext) + { + fprintf(stderr, "Extension required\n"); + goto end; + } + if (!strcmp(ext, ".exu")) + { + Exactness_Unit *ex_unit = exactness_unit_file_read(argv[arg]); + units_filenames = eina_list_append(units_filenames, argv[arg]); + _units = eina_list_append(_units, ex_unit); + } + else if (!strcmp(ext, ".rec")) + { + Exactness_Unit *ex_unit = legacy_rec_file_read(argv[arg]); + if (!ex_unit) + { + fprintf(stderr, "Issue while reading %s\n", argv[arg]); + goto end; + } + _units = eina_list_append(_units, ex_unit); + } + else if (!strcmp(ext, ".png")) + { + Exactness_Unit *ex_unit = calloc(1, sizeof(*ex_unit)); + Exactness_Image *ex_img = _image_read(argv[arg]); + if (!ex_img) + { + fprintf(stderr, "Issue while reading %s\n", argv[arg]); + goto end; + } + ex_unit->imgs = eina_list_append(ex_unit->imgs, ex_img); + ex_unit->nb_shots++; + _units = eina_list_append(_units, ex_unit); + } + } + } + + if (clean) + { + Exactness_Action *act; + Eina_List *itr, *itr2; + EINA_LIST_FOREACH_SAFE(unit->actions, itr, itr2, act) + { + Exactness_Action *prev_act = eina_list_data_get(eina_list_prev(itr)); + if (_is_hook_duplicate(act, prev_act)) + { + prev_act->delay_ms += act->delay_ms; + unit->actions = eina_list_remove_list(unit->actions, itr); + } + } + EINA_LIST_REVERSE_FOREACH_SAFE(unit->actions, itr, itr2, act) + { + if (act->type == EXACTNESS_ACTION_TAKE_SHOT) break; + unit->actions = eina_list_remove(unit->actions, act); + } + write_file = EINA_TRUE; + } + + if (delay || stabilize) + { + Exactness_Action *act; + Eina_List *itr; + unsigned int cur_shot = 0; + EINA_LIST_FOREACH(unit->actions, itr, act) + { + if (act->type == EXACTNESS_ACTION_TAKE_SHOT) + { + cur_shot++; + if (cur_shot == shot) + { + if (delay) act->delay_ms = delay; + if (stabilize) + { + Exactness_Action *s_act = malloc(sizeof(*s_act)); + s_act->type = EXACTNESS_ACTION_STABILIZE; + s_act->delay_ms = act->delay_ms; + s_act->n_evas = act->n_evas; + s_act->data = NULL; + act->delay_ms = 0; /* Shot right after stabilization */ + unit->actions = eina_list_prepend_relative(unit->actions, s_act, act); + } + write_file = EINA_TRUE; + break; + } + } + } + } + + if (pack) write_file = EINA_TRUE; + + if (list_get) + { + Exactness_Action *act; + Eina_List *itr; + if (unit->fonts_path) printf("Fonts dir: %s\n", unit->fonts_path); + EINA_LIST_FOREACH(unit->actions, itr, act) + { + char specific_output[1024]; + printf("+%.3f: %s", act->delay_ms / 1000.0, _action_name_get(act)); + _action_specific_info_get(act, specific_output); + if (*specific_output) printf(" - %s", specific_output); + printf("\n"); + } + } + + if (compare_files && output) + { + const char *out_ext = strrchr(output, '.'); + Exactness_Unit *unit1 = NULL, *unit2 = NULL, *unitO = NULL; + int nb_diffs = 0; + Eina_List *itr1, *itr2; + EINA_LIST_FOREACH(_units, itr1, unit) + { + if (!unit1) unit1 = unit; + else if (!unit2) unit2 = unit; + else + { + fprintf(stderr, "Too much files to compare (only 2).\n"); + goto end; + } + } + + if (!strcmp(out_ext, ".png")) + { + if (unit1->nb_shots != 1 || unit2->nb_shots != 1) + { + fprintf(stderr, "Comparison output can be png only if the number of shots to compare is 1.\n"); + goto end; + } + } + + itr1 = unit1 ? unit1->imgs : NULL; + itr2 = unit2 ? unit2->imgs : NULL; + + while (itr1 || itr2) + { + Exactness_Image *ex_img1 = itr1 ? eina_list_data_get(itr1) : NULL; + Exactness_Image *ex_img2 = itr2 ? eina_list_data_get(itr2) : NULL; + Exactness_Image *ex_imgO = NULL; + Eina_Bool has_diff = exactness_image_compare(ex_img1, ex_img2, &ex_imgO); + if (has_diff || !strcmp(out_ext, ".exu")) + { + if (has_diff) nb_diffs++; + if (!unitO) unitO = calloc(1, sizeof(*unitO)); + unitO->imgs = eina_list_append(unitO->imgs, ex_imgO); + unitO->nb_shots++; + } + itr1 = eina_list_next(itr1); + itr2 = eina_list_next(itr2); + } + if (!strcmp(out_ext, ".png")) + { + Ecore_Evas *ee; + Eo *e, *img; + if (unitO->nb_shots == 1) + { + Exactness_Image *ex_imgO = eina_list_data_get(unitO->imgs); + ee = ecore_evas_new(NULL, 0, 0, 100, 100, NULL); + e = ecore_evas_get(ee); + img = evas_object_image_add(e); + evas_object_image_size_set(img, ex_imgO->w, ex_imgO->h); + evas_object_image_data_set(img, ex_imgO->pixels); + evas_object_image_save(img, output, NULL, NULL); + ecore_evas_free(ee); + goto end; + } + ret = 0; + } + else if (!strcmp(out_ext, ".exu")) + { + _diff_result_print(unit1, unit2); + if (nb_diffs) exactness_unit_file_write(unitO, output); + else ret = 0; + } + else + { + fprintf(stderr, "Correct output extension (.exu/.png) required\n"); + } + goto end; + } + + ret = 0; + if (write_file) + { + if (!output) + { + fprintf(stderr, "An output file is required to write the modifications.\n"); + } + else + { + const char *out_ext = strrchr(output, '.'); + if (!out_ext || strcmp(out_ext, ".exu")) + { + fprintf(stderr, "Only exu extension is supported as output.\n"); + goto end; + } + exactness_unit_file_write(unit, output); + } + goto end; + } + + if (gui_needed) + { + Eina_List *itr; + Exactness_Unit *unit1 = NULL, *unit2 = NULL; + Eina_Bool need_compare = compare_files && eina_list_count(_units) == 2; + _item_infos_hash = eina_hash_pointer_new(NULL); + _gui_win_create(); + EINA_LIST_FOREACH(_units, itr, unit) + { + if (need_compare) + { + if (!unit1) unit1 = unit; + else unit2 = unit; + } + else _gui_unit_display(unit, NULL); + } + if (need_compare) _gui_unit_display(unit1, unit2); + elm_run(); + EINA_LIST_FREE(_modified_units, unit) + { + int i = 0; + EINA_LIST_FOREACH(_units, itr, unit2) + { + if (unit2 == unit) break; + i++; + } + exactness_unit_file_write(unit, eina_list_nth(units_filenames, i)); + } + } + +end: + elm_shutdown(); + eet_shutdown(); + ecore_shutdown(); + ecore_evas_shutdown(); + + return ret; +} diff --git a/src/bin/exactness/meson.build b/src/bin/exactness/meson.build new file mode 100644 index 0000000000..6303e3fffe --- /dev/null +++ b/src/bin/exactness/meson.build @@ -0,0 +1,42 @@ +exactness_bin = executable('exactness', + [ 'exactness.c' ], + dependencies: [ ecore, ecore_evas, ecore_file, exactness ], + install: true, + ) + +exactness_inject_bin = executable('exactness_inject', + [ 'injector.c' ], + dependencies: [ elementary, exactness ], + install: true, + ) + +exactness_inspect_bin = executable('exactness_inspect', + [ 'inspect.c' ], + dependencies: [ elementary, exactness ], + install: true, + ) + +edjs = custom_target('player_entry', + input : 'player_entry.edc', + output : 'player_entry.edj', + install : true, + install_dir : 'share/exactness', + command : edje_cc_exe + [ + '-id', join_paths(meson.source_root(), 'data', 'elementary', 'themes', 'img'), + '-sd', join_paths(meson.source_root(), 'data', 'elementary', 'themes', 'snd'), + '@INPUT@', '@OUTPUT@'], + depends : edje_cc) + +exactness_play_bin = executable('exactness_play', + [ 'player.c', edjs ], + dependencies: [ elementary, exactness ], + c_args: '-DDATA_DIR="'+join_paths(dir_data, 'exactness')+'"', + install: true, + ) + +exactness_record_bin = executable('exactness_record', + [ 'recorder.c' ], + dependencies: [ elementary, exactness ], + install: true, + ) + diff --git a/src/bin/exactness/player.c b/src/bin/exactness/player.c new file mode 100644 index 0000000000..a2700f4653 --- /dev/null +++ b/src/bin/exactness/player.c @@ -0,0 +1,1393 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#define _POSIX_ +#include +#include +#include +#include +#include + +#ifdef HAVE_FORK +# ifdef HAVE_SYS_WAIT_H +# include +# endif +# ifdef HAVE_SYS_TYPES_H +# include +# endif +# ifdef HAVE_SYS_SYSINFO_H +# include +# endif +#endif + +#ifndef EFL_EO_API_SUPPORT +#define EFL_EO_API_SUPPORT +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "exactness_private.h" + +#define PATH_ 1024 +#define IMAGE_FILENAME_EXT ".png" +#define PAUSE_KEY_STR "F2" + +typedef struct +{ + Eina_Debug_Session *session; + int srcid; + void *buffer; + unsigned int size; +} _Main_Loop_Info; + +#define WRAPPER_TO_XFER_MAIN_LOOP(foo) \ +static void \ +_intern_main_loop ## foo(void *data) \ +{ \ + _Main_Loop_Info *info = data; \ + _main_loop ## foo(info->session, info->srcid, info->buffer, info->size); \ + free(info->buffer); \ + free(info); \ +} \ +static Eina_Bool \ +foo(Eina_Debug_Session *session, int srcid, void *buffer, int size) \ +{ \ + _Main_Loop_Info *info = calloc(1, sizeof(*info)); \ + info->session = session; \ + info->srcid = srcid; \ + info->size = size; \ + if (info->size) \ + { \ + info->buffer = malloc(info->size); \ + memcpy(info->buffer, buffer, info->size); \ + } \ + ecore_main_loop_thread_safe_call_async(_intern_main_loop ## foo, info); \ + return EINA_TRUE; \ +} + +#ifndef WORDS_BIGENDIAN +#define SWAP_64(x) x +#define SWAP_32(x) x +#define SWAP_16(x) x +#define SWAP_DBL(x) x +#else +#define SWAP_64(x) eina_swap64(x) +#define SWAP_32(x) eina_swap32(x) +#define SWAP_16(x) eina_swap16(x) +#define SWAP_DBL(x) SWAP_64(x) +#endif + +#define EXTRACT_INT(_buf) \ +({ \ + int __i; \ + memcpy(&__i, _buf, sizeof(int)); \ + _buf += sizeof(int); \ + SWAP_32(__i); \ +}) + +#define EXTRACT_DOUBLE(_buf) \ +({ \ + double __d; \ + memcpy(&__d, _buf, sizeof(double)); \ + _buf += sizeof(double); \ + SWAP_DBL(__d); \ +}) + +#define EXTRACT_STRING(_buf) \ +({ \ + char *__s = _buf ? strdup(_buf) : NULL; \ + int __len = (__s ? strlen(__s) : 0) + 1; \ + _buf += __len; \ + __s; \ +}) + +#define STORE_INT(_buf, __i) \ +({ \ + int __si = SWAP_32(__i); \ + memcpy(_buf, &__si, sizeof(int)); \ + _buf += sizeof(int); \ +}) + +typedef enum +{ + FTYPE_UNKNOWN, + FTYPE_DIR, + FTYPE_REC = FTYPE_DIR, + FTYPE_EXU, + FTYPE_REMOTE +} File_Type; + +static File_Type _dest_type = FTYPE_UNKNOWN; +static Eina_Stringshare *_dest = NULL; +static Exactness_Unit *_dest_unit = NULL; + +static File_Type _src_type = FTYPE_UNKNOWN; +static Eina_Stringshare *_src_filename = NULL; +static Exactness_Unit *_src_unit = NULL; + +static const char *_test_name = NULL; +static int _verbose = 0; + +static Evas *(*_evas_new)(void) = NULL; +static Eina_List *_evas_list = NULL; + +static Eina_List *_cur_event_list = NULL; + +static int _cur_shot_id = 0; +static Eina_Bool _shot_needed = EINA_FALSE; +static Eina_Bool _scan_objects = EINA_FALSE, _disable_shots = EINA_FALSE, _stabilize_shots = EINA_FALSE; + +static Eina_Debug_Session *_last_debug_session = NULL; +static int _last_debug_src_cid = 0; +static int _take_shot_op = EINA_DEBUG_OPCODE_INVALID; + +static Eina_Bool _stabilization_timer_cb(void *); +static double _speed = 1.0; + +static Eina_Bool _exit_required = EINA_FALSE; +static Eina_Bool _pause_request = EINA_FALSE; +static Eina_Bool _playing_status = EINA_FALSE; + +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 Exactness_Image * +_snapshot_shot_get(Evas *e) +{ + Exactness_Image *ex_img; + Evas_Object *snapshot; + void *pixels; + int w, h, nb_bytes; + + if (!e) return NULL; + + evas_output_size_get(e, &w, &h); + if ((w < 1) || (h < 1)) return NULL; + + snapshot = efl_key_data_get(e, "_snapshot"); + if (!snapshot) + { + snapshot = evas_object_image_filled_add(e); + if (snapshot) + { + evas_object_image_snapshot_set(snapshot, EINA_TRUE); + evas_object_geometry_set(snapshot, 0, 0, w, h); + efl_gfx_entity_visible_set(snapshot, EINA_TRUE); + efl_key_data_set(e, "_snapshot", snapshot); + } + return NULL; + } + + pixels = evas_object_image_data_get(snapshot, EINA_FALSE); + if (!pixels) return NULL; + + ex_img = malloc(sizeof(*ex_img)); + nb_bytes = w * h * 4; + ex_img->w = w; + ex_img->h = h; + ex_img->pixels = malloc(nb_bytes); + memcpy(ex_img->pixels, pixels, nb_bytes); + return ex_img; +} + +static void +_evas_render_post_cb(void *data EINA_UNUSED, const Efl_Event *event) +{ + if (_shot_needed) + { + Evas_Event_Render_Post *post = event->info; + void *e_data = efl_key_data_get(event->object, "_shot"); + + // Nothing was updated, so let's not bother sending nothingness + if (post && !post->updated_area) return; + Exactness_Image *ex_shot = efl_key_data_get(event->object, "_last_shot"); + if (!ex_shot) ex_shot = _snapshot_shot_get(event->object); + if (!ex_shot) return; + + efl_key_data_set(event->object, "_last_shot", NULL); + + if (e_data) + { + if (_dest_type == FTYPE_DIR) + { + char *filename = e_data; + Eo *o = evas_object_image_add(event->object); + evas_object_image_size_set(o, ex_shot->w, ex_shot->h); + evas_object_image_data_set(o, ex_shot->pixels); + _printf(1, "Shot taken (%s).\n", filename); + if (!evas_object_image_save(o, filename, NULL, NULL)) + { + printf("Cannot save widget to <%s>\n", filename); + } + free(filename); + } + else if (_dest_type == FTYPE_EXU) + { + Exactness_Image *ex_img = e_data; + memcpy(ex_img, ex_shot, sizeof(Exactness_Image)); + ex_shot->pixels = NULL; + _printf(1, "Shot taken (in %s).\n", _dest); + } + else if (_dest_type == FTYPE_REMOTE) + { + int len = sizeof(int) + sizeof(int) + ex_shot->w * ex_shot->h * 4; + char *buf = alloca(len); + char *tmp = buf; + STORE_INT(tmp, ex_shot->w); + STORE_INT(tmp, ex_shot->h); + memcpy(tmp, ex_shot->pixels, ex_shot->w * ex_shot->h * 4); + eina_debug_session_send(_last_debug_session, _last_debug_src_cid, _take_shot_op, buf, len); + } + } + exactness_image_free(ex_shot); + efl_key_data_set(event->object, "_shot", NULL); + evas_object_del(efl_key_data_get(event->object, "_snapshot")); + efl_key_data_set(event->object, "_snapshot", NULL); + /* This part is needed when the shot is needed at the end of the scenario. + * As it is async, we need to wait for the shot termination. */ + _shot_needed = EINA_FALSE; + if (_exit_required) ecore_main_loop_quit(); + } +} + +static void +_shot_do(Evas *e) +{ + void *e_data = NULL; + if (!e) return; + + if (!_disable_shots) + { + if (_dest_type == FTYPE_DIR) + { + int dir_name_len; + char *filename; + + dir_name_len = strlen(_dest) + 1; /* includes space of a '/' */ + filename = malloc(strlen(_test_name) + strlen(IMAGE_FILENAME_EXT) + + dir_name_len + 8); /* also space for serial */ + + sprintf(filename, "%s/%s%c%03d%s", _dest, _test_name, + SHOT_DELIMITER, _cur_shot_id, IMAGE_FILENAME_EXT); + e_data = filename; + } + else if (_dest_type == FTYPE_EXU) + { + Exactness_Image *ex_img = malloc(sizeof(*ex_img)); + _dest_unit->imgs = eina_list_append(_dest_unit->imgs, ex_img); + _dest_unit->nb_shots++; + e_data = ex_img; + } + else if (_dest_type == FTYPE_REMOTE) + { + e_data = e; + } + } + efl_key_data_set(e, "_shot", e_data); + _shot_needed = EINA_TRUE; + Efl_Event ev; + ev.info = NULL; + ev.object = e; + _evas_render_post_cb(NULL, &ev); + + if (_scan_objects && _dest_type == FTYPE_EXU) + { + Eina_Iterator *iter; + Eo *obj; + Exactness_Objects *e_objs = calloc(1, sizeof(*e_objs)); + iter = eo_objects_iterator_new(); + EINA_ITERATOR_FOREACH(iter, obj) + { + if (!efl_isa(obj, EFL_CANVAS_OBJECT_CLASS) && + !efl_isa(obj, EFL_CANVAS_SCENE_INTERFACE)) continue; + Exactness_Object *e_obj = calloc(1, sizeof(*e_obj)); + Eo *parent = efl_parent_get(obj); + e_obj->id = (long long) obj; + if (efl_isa(parent, EFL_CANVAS_OBJECT_CLASS) || + efl_isa(parent, EFL_CANVAS_SCENE_INTERFACE)) + { + e_obj->parent_id = (long long) efl_parent_get(obj); + } + else + { + e_obj->parent_id = 0; + } + e_obj->kl_name = eina_stringshare_add(efl_class_name_get(obj)); + if (efl_isa(obj, EFL_CANVAS_OBJECT_CLASS)) + { + Eina_Size2D sz = efl_gfx_entity_size_get(obj); + e_obj->w = sz.w; + e_obj->h = sz.h; + Eina_Position2D pos = efl_gfx_entity_position_get(obj); + e_obj->x = pos.x; + e_obj->y = pos.y; + } + e_objs->objs = eina_list_append(e_objs->objs, e_obj); + } + eina_iterator_free(iter); + _dest_unit->objs = eina_list_append(_dest_unit->objs, e_objs); + } +} + +static void +_feed_event(Exactness_Action_Type type, unsigned int n_evas, void *data) +{ + static Evas_Object *rect = NULL; + static unsigned int rect_evas; + + Eo *e = eina_list_nth(_evas_list, n_evas); + + if (rect && rect_evas != n_evas) + { + efl_del(rect); + rect = NULL; + } + if (_verbose && !rect) + { + rect = evas_object_rectangle_add(e); + evas_object_repeat_events_set(rect, EINA_TRUE); + evas_object_color_set(rect, 255, 0, 0, 255); + evas_object_resize(rect, 15, 15); + evas_object_layer_set(rect, 100); + evas_object_show(rect); + rect_evas = n_evas; + } + + switch (type) + { + case EXACTNESS_ACTION_MOUSE_IN: + { + _printf(1, "Mouse in\n"); + _printf(2, "%s evas_event_feed_mouse_in n_evas=<%d>\n", __func__, n_evas); + if (e) evas_event_feed_mouse_in(e, time(NULL), NULL); + break; + } + case EXACTNESS_ACTION_MOUSE_OUT: + { + _printf(1, "Mouse out\n"); + _printf(2, "%s evas_event_feed_mouse_out n_evas=<%d>\n", __func__, n_evas); + if (e) evas_event_feed_mouse_out(e, time(NULL), NULL); + break; + } + case EXACTNESS_ACTION_MOUSE_WHEEL: + { + Exactness_Action_Mouse_Wheel *t = data; + _printf(1, "Mouse wheel\n"); + _printf(2, "%s evas_event_feed_mouse_wheel n_evas=<%d>\n", __func__, n_evas); + if (e) evas_event_feed_mouse_wheel(e, t->direction, t->z, time(NULL), NULL); + + break; + } + case EXACTNESS_ACTION_MULTI_DOWN: + { + Exactness_Action_Multi_Event *t = data; + _printf(2, "%s evas_event_feed_multi_down n_evas=<%d>\n", __func__, n_evas); + if (!t->d) + { + if (e) evas_event_feed_mouse_down(e, t->b, t->flags, time(NULL), NULL); + if (rect) evas_object_color_set(rect, 255, 255, 0, 255); + } + else + { + if (e) evas_event_feed_multi_down(e, + t->d, t->x, t->y, t->rad, + t->radx, t->rady, t->pres, t->ang, t->fx, t->fy, + t->flags, time(NULL), NULL); + } + + break; + } + case EXACTNESS_ACTION_MULTI_UP: + { + Exactness_Action_Multi_Event *t = data; + _printf(2, "%s evas_event_feed_multi_up n_evas=<%d>\n", __func__, n_evas); + if (!t->d) + { + if (e) evas_event_feed_mouse_up(e, t->b, t->flags, time(NULL), NULL); + if (rect) evas_object_color_set(rect, 255, 0, 0, 255); + } + else + { + if (e) evas_event_feed_multi_up(e, + t->d, t->x, t->y, t->rad, + t->radx, t->rady, t->pres, t->ang, t->fx, t->fy, + t->flags, time(NULL), NULL); + } + + break; + } + case EXACTNESS_ACTION_MULTI_MOVE: + { + Exactness_Action_Multi_Move *t = data; + _printf(2, "%s evas_event_feed_multi_move n_evas=<%d>\n", __func__, n_evas); + if (!t->d) + { + if (e) evas_event_feed_mouse_move(e, t->x, t->y, time(NULL), NULL); + if (rect) + { + evas_object_move(rect, t->x, t->y); + evas_object_color_set(rect, 255, 0, 0, 255); + } + } + else + { + if (e) evas_event_feed_multi_move(e, + t->d, t->x, t->y, t->rad, + t->radx, t->rady, t->pres, t->ang, t->fx, t->fy, + time(NULL), NULL); + } + + break; + } + case EXACTNESS_ACTION_KEY_DOWN: + { + Exactness_Action_Key_Down_Up *t = data; + _printf(2, "%s evas_event_feed_key_down n_evas=<%d>\n", __func__, n_evas); + if (e) + evas_event_feed_key_down_with_keycode(e, + t->keyname, t->key, t->string, + t->compose, time(NULL), NULL, t->keycode); + break; + } + case EXACTNESS_ACTION_KEY_UP: + { + Exactness_Action_Key_Down_Up *t = data; + _printf(2, "%s evas_event_feed_key_up n_evas=<%d>\n", __func__, n_evas); + if (e) evas_event_feed_key_up_with_keycode(e, + t->keyname, t->key, t->string, + t->compose, time(NULL), NULL, t->keycode); + + break; + } + case EXACTNESS_ACTION_TAKE_SHOT: + { + _printf(2, "%s take shot n_evas=<%d>\n", __func__, n_evas); + if (rect) evas_object_color_set(rect, 0, 0, 255, 255); + _cur_shot_id++; + if (_dest_type != FTYPE_UNKNOWN && e) _shot_do(e); + break; + } + case EXACTNESS_ACTION_EFL_EVENT: + { + Exactness_Action_Efl_Event *t = data; + Eina_Bool found = EINA_FALSE; + Eina_List *itr; + EINA_LIST_FOREACH(_evas_list, itr, e) + { + Eo *o = efl_name_find(e, t->wdg_name); + if (o) + { + _printf(2, "%s EFL event invoke %s on %s\n", + __func__, t->event_name, t->wdg_name); + Efl_Event_Description d; + found = EINA_TRUE; + memset(&d, 0, sizeof(d)); + d.name = t->event_name; + d.legacy_is = EINA_TRUE; + efl_event_callback_legacy_call(o, &d, NULL); +#if 0 + /* Remove when events stuff work well */ + Eina_Size2D sz = efl_gfx_size_get(o); + Eina_Position2D pos = efl_gfx_position_get(o); + if (!strcmp(t->event_name, "clicked") || + !strcmp(t->event_name, "clicked,double")) + { + int x = pos.x + (sz.w / 2); + int y = pos.y + (sz.h / 2); + evas_event_feed_mouse_move(e, x, y, time(NULL), NULL); + evas_event_feed_mouse_down(e, 0, EVAS_BUTTON_NONE, time(NULL), NULL); + evas_event_feed_mouse_up(e, 0, EVAS_BUTTON_NONE, time(NULL), NULL); + if (rect) + { + evas_object_move(rect, x, y); + evas_object_color_set(rect, 255, 0, 0, 255); + } + if (!strcmp(t->event_name, "clicked,double")) + { + evas_event_feed_mouse_down(e, 0, EVAS_BUTTON_DOUBLE_CLICK, + time(NULL), NULL); + evas_event_feed_mouse_up(e, 0, EVAS_BUTTON_DOUBLE_CLICK, + time(NULL), NULL); + } + } +#endif + } + } + if (!found) fprintf(stderr, "Failed finding %s.\n", t->wdg_name); + break; + } + case EXACTNESS_ACTION_CLICK_ON: + { + Exactness_Action_Click_On *t = data; + Eina_List *itr; + Eo *o; + n_evas = 0; + EINA_LIST_FOREACH(_evas_list, itr, e) + { + o = efl_name_find(e, t->wdg_name); + if (o) goto wdg_found; + n_evas++; + } + o = NULL; +wdg_found: + if (o) + { + Eina_Size2D sz = efl_gfx_entity_size_get(o); + Eina_Position2D pos = efl_gfx_entity_position_get(o); + int x = pos.x + (sz.w / 2); + int y = pos.y + (sz.h / 2); + Exactness_Action_Multi_Move *d_move = calloc(1, sizeof(*d_move)); + Exactness_Action_Multi_Event *d_event = calloc(1, sizeof(*d_event)); + Exactness_Action *act, *prev_act = eina_list_data_get(_cur_event_list); + + _printf(2, "%s click on %s\n", __func__, t->wdg_name); + act = calloc(1, sizeof(*act)); + act->type = EXACTNESS_ACTION_MULTI_MOVE; + act->delay_ms = 100; + act->n_evas = n_evas; + act->data = d_move; + d_move->x = x; + d_move->y = y; + _cur_event_list = eina_list_append_relative(_cur_event_list, + act, prev_act); + prev_act = act; + + act = calloc(1, sizeof(*act)); + act->type = EXACTNESS_ACTION_MULTI_DOWN; + act->delay_ms = 100; + act->n_evas = n_evas; + act->data = d_event; + d_event->b = 1; + _cur_event_list = eina_list_append_relative(_cur_event_list, + act, prev_act); + prev_act = act; + + act = calloc(1, sizeof(*act)); + act->type = EXACTNESS_ACTION_MULTI_UP; + act->delay_ms = 100; + act->n_evas = n_evas; + d_event = calloc(1, sizeof(*d_event)); + act->data = d_event; + d_event->b = 1; + _cur_event_list = eina_list_append_relative(_cur_event_list, + act, prev_act); + } + else fprintf(stderr, "Failed finding %s.\n", t->wdg_name); + break; + } + case EXACTNESS_ACTION_STABILIZE: + { + _printf(2, "%s stabilize\n", __func__); + if (rect) evas_object_color_set(rect, 255, 165, 0, 255); + ecore_timer_add(0.1, _stabilization_timer_cb, NULL); + break; + } + default: /* All non-input events are not handeled */ + break; + } +} + +static Eina_Bool +_feed_event_timer_cb(void *data EINA_UNUSED) +{ + if (_pause_request) return ECORE_CALLBACK_CANCEL; + Exactness_Action *act = eina_list_data_get(_cur_event_list); + if (act) _feed_event(act->type, act->n_evas, act->data); + + _cur_event_list = eina_list_next(_cur_event_list); + if (!_cur_event_list) + { /* Finished reading all events */ + _exit_required = EINA_TRUE; + if (!_shot_needed) ecore_main_loop_quit(); + } + else + { + if (act->type != EXACTNESS_ACTION_STABILIZE) + { + act = eina_list_data_get(_cur_event_list); + _printf(2, " %s timer_time=<%f>\n", __func__, act->delay_ms / 1000.0); + ecore_timer_add(act->delay_ms / 1000.0, _feed_event_timer_cb, NULL); + } + } + return ECORE_CALLBACK_CANCEL; +} + +static Eina_Bool +_stabilization_timer_cb(void *data EINA_UNUSED) +{ + Eina_List *itr; + Evas *e; +#define STAB_MAX 5 + static int need_more = STAB_MAX; + _printf(2, "Not stable yet!\n"); + EINA_LIST_FOREACH(_evas_list, itr, e) + { + if (!e) continue; + Exactness_Image *last_img = efl_key_data_get(e, "_last_stab_shot"); + Exactness_Image *cur_img = _snapshot_shot_get(e); + if (!last_img || exactness_image_compare(last_img, cur_img, NULL)) need_more = STAB_MAX; + exactness_image_free(last_img); + efl_key_data_set(e, "_last_stab_shot", cur_img); + } + EINA_LIST_FOREACH(_evas_list, itr, e) + { + if (!need_more) + { + evas_object_del(efl_key_data_get(e, "_snapshot")); + efl_key_data_set(e, "_snapshot", NULL); + } + } + if (!need_more) + { + _playing_status = EINA_FALSE; + if (_src_type != FTYPE_REMOTE && !_pause_request) + { + Exactness_Action *act = eina_list_data_get(_cur_event_list); + _printf(2, " %s timer_time=<%f>\n", __func__, act->delay_ms / 1000.0); + ecore_timer_add(act->delay_ms / 1000.0, _feed_event_timer_cb, NULL); + } + need_more = STAB_MAX; + return ECORE_CALLBACK_CANCEL; + } + need_more--; + return ECORE_CALLBACK_RENEW; +} + +static void +_main_loop_mouse_in_cb(Eina_Debug_Session *session EINA_UNUSED, int srcid EINA_UNUSED, void *buffer, int size EINA_UNUSED) +{ + char *buf = buffer; + int n_evas = EXTRACT_INT(buf); + _feed_event(EXACTNESS_ACTION_MOUSE_IN, n_evas, NULL); +} + +static void +_main_loop_mouse_out_cb(Eina_Debug_Session *session EINA_UNUSED, int srcid EINA_UNUSED, void *buffer, int size EINA_UNUSED) +{ + char *buf = buffer; + int n_evas = EXTRACT_INT(buf); + _feed_event(EXACTNESS_ACTION_MOUSE_OUT, n_evas, NULL); +} + +static void +_main_loop_mouse_wheel_cb(Eina_Debug_Session *session EINA_UNUSED, int srcid EINA_UNUSED, void *buffer, int size EINA_UNUSED) +{ + char *buf = buffer; + Exactness_Action_Mouse_Wheel t; + int n_evas = EXTRACT_INT(buf); + t.direction = EXTRACT_INT(buf); + t.z = EXTRACT_INT(buf); + _feed_event(EXACTNESS_ACTION_MOUSE_WHEEL, n_evas, &t); +} + +static void +_main_loop_multi_down_cb(Eina_Debug_Session *session EINA_UNUSED, int srcid EINA_UNUSED, void *buffer, int size EINA_UNUSED) +{ + char *buf = buffer; + Exactness_Action_Multi_Event t; + int n_evas = EXTRACT_INT(buf); + t.d = EXTRACT_INT(buf); + t.b = EXTRACT_INT(buf); + t.x = EXTRACT_INT(buf); + t.y = EXTRACT_INT(buf); + t.rad = EXTRACT_DOUBLE(buf); + t.radx = EXTRACT_DOUBLE(buf); + t.rady = EXTRACT_DOUBLE(buf); + t.pres = EXTRACT_DOUBLE(buf); + t.ang = EXTRACT_DOUBLE(buf); + t.fx = EXTRACT_DOUBLE(buf); + t.fy = EXTRACT_DOUBLE(buf); + t.flags = EXTRACT_INT(buf); + _feed_event(EXACTNESS_ACTION_MULTI_DOWN, n_evas, &t); +} + +static void +_main_loop_multi_up_cb(Eina_Debug_Session *session EINA_UNUSED, int srcid EINA_UNUSED, void *buffer, int size EINA_UNUSED) +{ + char *buf = buffer; + Exactness_Action_Multi_Event t; + int n_evas = EXTRACT_INT(buf); + t.d = EXTRACT_INT(buf); + t.b = EXTRACT_INT(buf); + t.x = EXTRACT_INT(buf); + t.y = EXTRACT_INT(buf); + t.rad = EXTRACT_DOUBLE(buf); + t.radx = EXTRACT_DOUBLE(buf); + t.rady = EXTRACT_DOUBLE(buf); + t.pres = EXTRACT_DOUBLE(buf); + t.ang = EXTRACT_DOUBLE(buf); + t.fx = EXTRACT_DOUBLE(buf); + t.fy = EXTRACT_DOUBLE(buf); + t.flags = EXTRACT_INT(buf); + _feed_event(EXACTNESS_ACTION_MULTI_UP, n_evas, &t); +} + +static void +_main_loop_multi_move_cb(Eina_Debug_Session *session EINA_UNUSED, int srcid EINA_UNUSED, void *buffer, int size EINA_UNUSED) +{ + char *buf = buffer; + Exactness_Action_Multi_Move t; + int n_evas = EXTRACT_INT(buf); + t.d = EXTRACT_INT(buf); + t.x = EXTRACT_INT(buf); + t.y = EXTRACT_INT(buf); + t.rad = EXTRACT_DOUBLE(buf); + t.radx = EXTRACT_DOUBLE(buf); + t.rady = EXTRACT_DOUBLE(buf); + t.pres = EXTRACT_DOUBLE(buf); + t.ang = EXTRACT_DOUBLE(buf); + t.fx = EXTRACT_DOUBLE(buf); + t.fy = EXTRACT_DOUBLE(buf); + _feed_event(EXACTNESS_ACTION_MULTI_MOVE, n_evas, &t); +} + +static void +_main_loop_key_down_cb(Eina_Debug_Session *session EINA_UNUSED, int srcid EINA_UNUSED, void *buffer, int size EINA_UNUSED) +{ + char *buf = buffer; + Exactness_Action_Key_Down_Up t; + int n_evas = EXTRACT_INT(buf); + t.keyname = EXTRACT_STRING(buf); + t.key = EXTRACT_STRING(buf); + t.string = EXTRACT_STRING(buf); + t.compose = EXTRACT_STRING(buf); + t.keycode = EXTRACT_INT(buf); + _feed_event(EXACTNESS_ACTION_KEY_DOWN, n_evas, &t); +} + +static void +_main_loop_key_up_cb(Eina_Debug_Session *session EINA_UNUSED, int srcid EINA_UNUSED, void *buffer, int size EINA_UNUSED) +{ + char *buf = buffer; + Exactness_Action_Key_Down_Up t; + int n_evas = EXTRACT_INT(buf); + t.keyname = EXTRACT_STRING(buf); + t.key = EXTRACT_STRING(buf); + t.string = EXTRACT_STRING(buf); + t.compose = EXTRACT_STRING(buf); + t.keycode = EXTRACT_INT(buf); + _feed_event(EXACTNESS_ACTION_KEY_UP, n_evas, &t); +} + +static void +_main_loop_take_shot_cb(Eina_Debug_Session *session, int srcid, void *buffer, int size EINA_UNUSED) +{ + char *buf = buffer; + int n_evas = EXTRACT_INT(buf); + _feed_event(EXACTNESS_ACTION_TAKE_SHOT, n_evas, NULL); + _last_debug_session = session; + _last_debug_src_cid = srcid; +} + +static void +_main_loop_efl_event_cb(Eina_Debug_Session *session EINA_UNUSED, int srcid EINA_UNUSED, void *buffer, int size EINA_UNUSED) +{ + char *buf = buffer; + Exactness_Action_Efl_Event t; + t.wdg_name = EXTRACT_STRING(buf); + t.event_name = EXTRACT_STRING(buf); + _feed_event(EXACTNESS_ACTION_EFL_EVENT, 0, &t); +} + +static void +_main_loop_click_on_cb(Eina_Debug_Session *session EINA_UNUSED, int srcid EINA_UNUSED, void *buffer, int size EINA_UNUSED) +{ + char *buf = buffer; + Exactness_Action_Click_On t; + t.wdg_name = EXTRACT_STRING(buf); + _feed_event(EXACTNESS_ACTION_CLICK_ON, 0, &t); +} + +static void +_main_loop_stabilize_cb(Eina_Debug_Session *session EINA_UNUSED, int srcid EINA_UNUSED, void *buffer EINA_UNUSED, int size EINA_UNUSED) +{ + _feed_event(EXACTNESS_ACTION_STABILIZE, 0, NULL); +} + +static void +_main_loop_finish_cb(Eina_Debug_Session *session EINA_UNUSED, int srcid EINA_UNUSED, void *buffer EINA_UNUSED, int size EINA_UNUSED) +{ + ecore_main_loop_quit(); +} + +WRAPPER_TO_XFER_MAIN_LOOP(_mouse_in_cb) +WRAPPER_TO_XFER_MAIN_LOOP(_mouse_out_cb) +WRAPPER_TO_XFER_MAIN_LOOP(_mouse_wheel_cb) +WRAPPER_TO_XFER_MAIN_LOOP(_multi_down_cb) +WRAPPER_TO_XFER_MAIN_LOOP(_multi_up_cb) +WRAPPER_TO_XFER_MAIN_LOOP(_multi_move_cb) +WRAPPER_TO_XFER_MAIN_LOOP(_key_down_cb) +WRAPPER_TO_XFER_MAIN_LOOP(_key_up_cb) +WRAPPER_TO_XFER_MAIN_LOOP(_take_shot_cb) +WRAPPER_TO_XFER_MAIN_LOOP(_efl_event_cb) +WRAPPER_TO_XFER_MAIN_LOOP(_click_on_cb) +WRAPPER_TO_XFER_MAIN_LOOP(_stabilize_cb) +WRAPPER_TO_XFER_MAIN_LOOP(_finish_cb) + +EINA_DEBUG_OPCODES_ARRAY_DEFINE(_debug_ops, + {"Exactness/Actions/Mouse In", NULL, &_mouse_in_cb}, + {"Exactness/Actions/Mouse Out", NULL, &_mouse_out_cb}, + {"Exactness/Actions/Mouse Wheel", NULL, &_mouse_wheel_cb}, + {"Exactness/Actions/Multi Down", NULL, &_multi_down_cb}, + {"Exactness/Actions/Multi Up", NULL, &_multi_up_cb}, + {"Exactness/Actions/Multi Move", NULL, &_multi_move_cb}, + {"Exactness/Actions/Key Down", NULL, &_key_down_cb}, + {"Exactness/Actions/Key Up", NULL, &_key_up_cb}, + {"Exactness/Actions/Take Shot", &_take_shot_op, &_take_shot_cb}, + {"Exactness/Actions/EFL Event", NULL, &_efl_event_cb}, + {"Exactness/Actions/Click On", NULL, &_click_on_cb}, + {"Exactness/Actions/Stabilize", NULL, &_stabilize_cb}, + {"Exactness/Actions/Finish", NULL, &_finish_cb}, + {NULL, NULL, NULL} +); + +static Eina_Bool +_src_feed(void *data EINA_UNUSED) +{ + if (!_evas_list) return EINA_TRUE; + _cur_event_list = _src_unit->actions; + Exactness_Action *act = eina_list_data_get(_cur_event_list); + + if (act && act->delay_ms) + { + _printf(2, " Waiting <%f>\n", act->delay_ms / 1000.0); + ecore_timer_add(act->delay_ms / 1000.0, _feed_event_timer_cb, NULL); + } + else + { + _feed_event_timer_cb(NULL); + } + return EINA_FALSE; +} + +static Eina_Bool +_src_open() +{ + if (_src_type != FTYPE_REMOTE) + { + Eina_List *itr, *itr2; + Exactness_Action *act; + _printf(2, "<%s> Source file is <%s>\n", __func__, _src_filename); + if (_src_type == FTYPE_EXU) + { + _src_unit = exactness_unit_file_read(_src_filename); + } + else if (_src_type == FTYPE_REC) + { + _src_unit = legacy_rec_file_read(_src_filename); + } + if (!_src_unit) return EINA_FALSE; + if (_stabilize_shots) + { + Exactness_Action_Type last_action_type = EXACTNESS_ACTION_UNKNOWN; + EINA_LIST_FOREACH_SAFE(_src_unit->actions, itr, itr2, act) + { + if (act->type == EXACTNESS_ACTION_TAKE_SHOT && + last_action_type != EXACTNESS_ACTION_STABILIZE) + { + Exactness_Action *act2 = calloc(1, sizeof(*act2)); + act2->type = EXACTNESS_ACTION_STABILIZE; + _src_unit->actions = eina_list_prepend_relative(_src_unit->actions, act2, act); + } + last_action_type = act->type; + } + } + if (_speed && _speed != 1) + { + EINA_LIST_FOREACH(_src_unit->actions, itr, act) + act->delay_ms /= _speed; + } + } + else + { + eina_debug_opcodes_register(NULL, _debug_ops(), NULL, NULL); + } + return EINA_TRUE; +} + +static int +_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 1; + } + 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); + elm_theme_overlay_add(NULL, DATA_DIR"/exactness_play.edj"); + 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); + elm_theme_overlay_add(NULL, DATA_DIR"/exactness_play.edj"); + 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[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 Eina_Bool +_mkdir(const char *path, Eina_Bool skip_last) +{ + if (!ecore_file_exists(path)) + { + const char *cur = path + 1; + do + { + char *slash = strchr(cur, '/'); + if (slash) *slash = '\0'; + else if (skip_last) return EINA_TRUE; + if (!ecore_file_exists(path) && !ecore_file_mkdir(path)) return EINA_FALSE; + if (slash) *slash = '/'; + if (slash) cur = slash + 1; + else cur = NULL; + } + while (cur); + } + return EINA_TRUE; +} + +static void +_old_shots_rm_cb(const char *name, const char *path, void *data) +{ + const char *prefix = data; + unsigned int len = strlen(prefix); + if (!strncmp(name, prefix, len) && (strlen(name) > len) && (name[len] == SHOT_DELIMITER)) + { + char *buf = alloca(strlen(path) + strlen(name)); + sprintf(buf, "%s/%s", path, name); + if (unlink(buf)) + { + printf("Failed deleting '%s/%s': ", path, name); + perror(""); + } + } +} + +static void +_evas_del_cb(void *data EINA_UNUSED, const Efl_Event *event) +{ + Eina_List *p = eina_list_data_find_list(_evas_list, event->object); + eina_list_data_set(p, NULL); +} + +static void +_event_key_cb(void *data EINA_UNUSED, const Efl_Event *event) +{ + Efl_Input_Key *evk = event->info; + if (!evk) return; + const char *key = efl_input_key_name_get(evk); + + if (!strcmp(key, PAUSE_KEY_STR) && efl_input_key_pressed_get(evk)) + { + _pause_request = !_pause_request; + if (_pause_request) _printf(1, "Pausing scenario\n"); + else + { + _printf(1, "Playing scenario\n"); + if (!_playing_status) + _feed_event_timer_cb(NULL); + } + } +} + +EFL_CALLBACKS_ARRAY_DEFINE(_evas_callbacks, + { EFL_EVENT_DEL, _evas_del_cb }, + { EFL_CANVAS_SCENE_EVENT_RENDER_POST, _evas_render_post_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_event_callback_array_add(e, _evas_callbacks(), NULL); + } + return e; +} + +static const Ecore_Getopt optdesc = { + "exactness_play", + "%prog [options] <-s|-o|-v|-t|-h> command", + PACKAGE_VERSION, + "(C) 2017 Enlightenment", + "BSD", + "A scenario player for EFL based applications.", + 1, + { + ECORE_GETOPT_STORE_STR('o', "output", + " Set the destination for the shots.\n" + " If a .exu is given, the shots are stored in the file.\n" + " Otherwise the given path is considered as a directory\n" + " where shots will be stored.\n" + " If omitted, the output type (exu or dir) is determined\n" + " by the given test extension (resp. exu or rec)."), + ECORE_GETOPT_STORE_STR('t', "test", "Test to run on the given application"), + ECORE_GETOPT_STORE_TRUE('s', "show-on-screen", "Show on screen."), + ECORE_GETOPT_STORE_TRUE(0, "scan-objects", "Extract information of all the objects at every shot."), + ECORE_GETOPT_STORE_TRUE(0, "external-injection", "Expect events injection via Eina debug channel."), + ECORE_GETOPT_STORE_TRUE(0, "disable-screenshots", "Disable screenshots."), + ECORE_GETOPT_STORE_STR('f', "fonts-dir", "Specify a directory of the fonts that should be used."), + ECORE_GETOPT_STORE_TRUE(0, "stabilize-shots", "Wait for the frames to be stable before taking the shots."), + ECORE_GETOPT_STORE_DOUBLE(0, "speed", "Set the speed used to play the given test (default 1.0)."), + ECORE_GETOPT_COUNT('v', "verbose", "Turn verbose messages on."), + + ECORE_GETOPT_LICENSE('L', "license"), + ECORE_GETOPT_COPYRIGHT('C', "copyright"), + ECORE_GETOPT_VERSION('V', "version"), + ECORE_GETOPT_HELP('h', "help"), + ECORE_GETOPT_SENTINEL + } +}; + +int main(int argc, char **argv) +{ + int pret = 1, opt_args = 0; + char *src = NULL, *dest = NULL, *eq; + char *fonts_dir = NULL; + const char *chosen_fonts = NULL; + Eina_Bool show_on_screen = EINA_FALSE; + Eina_Bool want_quit = EINA_FALSE, external_injection = EINA_FALSE; + + Ecore_Getopt_Value values[] = { + ECORE_GETOPT_VALUE_STR(dest), + ECORE_GETOPT_VALUE_STR(src), + ECORE_GETOPT_VALUE_BOOL(show_on_screen), + ECORE_GETOPT_VALUE_BOOL(_scan_objects), + ECORE_GETOPT_VALUE_BOOL(external_injection), + ECORE_GETOPT_VALUE_BOOL(_disable_shots), + ECORE_GETOPT_VALUE_STR(fonts_dir), + ECORE_GETOPT_VALUE_BOOL(_stabilize_shots), + ECORE_GETOPT_VALUE_DOUBLE(_speed), + ECORE_GETOPT_VALUE_INT(_verbose), + + ECORE_GETOPT_VALUE_BOOL(want_quit), + ECORE_GETOPT_VALUE_BOOL(want_quit), + ECORE_GETOPT_VALUE_BOOL(want_quit), + ECORE_GETOPT_VALUE_BOOL(want_quit), + ECORE_GETOPT_VALUE_NONE + }; + + eina_init(); + eet_init(); + ecore_init(); + + opt_args = ecore_getopt_parse(&optdesc, values, argc, argv); + if (opt_args < 0) + { + fprintf(stderr, "Failed parsing arguments.\n"); + goto end; + } + if (want_quit) goto end; + + /* Check for a sentinel */ + if (argv[opt_args] && !strcmp(argv[opt_args], "--")) opt_args++; + + /* Check for env variables */ + do + { + eq = argv[opt_args] ? strchr(argv[opt_args], '=') : NULL; + if (eq) + { + char *var = malloc(eq - argv[opt_args] + 1); + memcpy(var, argv[opt_args], eq - argv[opt_args]); + var[eq - argv[opt_args]] = '\0'; + setenv(var, eq + 1, 1); + opt_args++; + } + } while (eq); + + if (dest) + { + _dest = eina_stringshare_add(dest); + if (!strcmp(_dest + strlen(_dest) - 4,".exu")) + { + _dest_type = FTYPE_EXU; + if (!_mkdir(_dest, EINA_TRUE)) + { + fprintf(stderr, "Path for %s cannot be created\n", _dest); + goto end; + } + } + else + { + _dest_type = FTYPE_DIR; + if (!_mkdir(_dest, EINA_FALSE)) + { + fprintf(stderr, "Directory %s cannot be created\n", _dest); + goto end; + } + } + } + if (!src && !external_injection) + { + fprintf(stderr, "no test file specified\n"); + goto end; + } + if (src && external_injection) + { + fprintf(stderr, "Cannot inject events from a source file and from outside simultaneously\n"); + goto end; + } + if (external_injection) + { + _src_type = FTYPE_REMOTE; + if (_dest_type == FTYPE_UNKNOWN) _dest_type = FTYPE_REMOTE; + } + if (src) + { + _src_filename = eina_stringshare_add(src); + if (!strcmp(_src_filename + strlen(_src_filename) - 4,".exu")) + { + _src_type = FTYPE_EXU; + if (_dest_type == FTYPE_UNKNOWN) + { + _dest_type = FTYPE_EXU; + _dest = "./output.exu"; + } + } + else if (!strcmp(_src_filename + strlen(_src_filename) - 4,".rec")) + { + _src_type = FTYPE_REC; + if (_dest_type == FTYPE_UNKNOWN) + { + _dest_type = FTYPE_DIR; + _dest = "."; + } + } + char *slash = strrchr(_src_filename, '/'); + if (slash) _test_name = strdup(slash + 1); + else _test_name = strdup(_src_filename); + char *dot = strrchr(_test_name, '.'); + if (dot) *dot = '\0'; + } + + if (_scan_objects && _dest_type != FTYPE_EXU) + { + fprintf(stderr, "Scan objects options is available only if the destination is a EXU file\n"); + goto end; + } + + if (_dest_type == FTYPE_EXU) _dest_unit = calloc(1, sizeof(*_dest_unit)); + + if (_dest_type == FTYPE_DIR && _test_name) + eina_file_dir_list(_dest, 0, _old_shots_rm_cb, (void *)_test_name); + + if (!_src_open()) + { + fprintf(stderr, "Unable to read source file\n"); + goto end; + } + + if (!show_on_screen) setenv("ELM_ENGINE", "buffer", 1); + if (_src_unit && _src_unit->fonts_path) + { + char buf[PATH_]; + if (!fonts_dir) fonts_dir = "./fonts"; + sprintf(buf, "%s/%s", fonts_dir, _src_unit->fonts_path); + if (!ecore_file_exists(buf)) + { + fprintf(stderr, "Unable to use the fonts path '%s' provided in %s\n", + _src_unit->fonts_path, _src_filename); + goto end; + } + chosen_fonts = _src_unit->fonts_path; + } + if (fonts_dir) + { + Eina_Tmpstr *fonts_conf_name = NULL; + if (!ecore_file_exists(fonts_dir)) + { + fprintf(stderr, "Unable to find fonts directory %s\n", fonts_dir); + goto end; + } + if (!chosen_fonts) + { + Eina_List *dated_fonts = ecore_file_ls(fonts_dir); + char *date_dir; + chosen_fonts = eina_stringshare_add(eina_list_last_data_get(dated_fonts)); + EINA_LIST_FREE(dated_fonts, date_dir) free(date_dir); + } + if (chosen_fonts) + { + int tmp_fd = eina_file_mkstemp("/tmp/fonts_XXXXXX.conf", &fonts_conf_name); + FILE *tmp_f = fdopen(tmp_fd, "wb"); + fprintf(tmp_f, + "\n\n\n" + "%s/%s\n\n", + fonts_dir, chosen_fonts); + fclose(tmp_f); + close(tmp_fd); + + setenv("FONTCONFIG_FILE", fonts_conf_name, 1); + } + } + efl_object_init(); + evas_init(); + + if (argv[opt_args]) + { + /* Replace the current command line to hide the Exactness part */ + int len = argv[argc - 1] + strlen(argv[argc - 1]) - argv[opt_args]; + memcpy(argv[0], argv[opt_args], len); + memset(argv[0] + len, 0, _POSIX_PATH_MAX - len); + + int i; + for (i = opt_args; i < argc; i++) + { + if (i != opt_args) + { + argv[i - opt_args] = argv[0] + (argv[i] - argv[opt_args]); + } + _printf(1, "%s ", argv[i - opt_args]); + } + _printf(1, "\n"); + } + else + { + Eina_List *itr; + Exactness_Source_Code *code; + Eina_Tmpstr *f_output = NULL; + EINA_LIST_FOREACH(_src_unit->codes, itr, code) + { + if (!strcmp(code->language, "C") && code->command) + { + int status; + Ecore_Exe *exe; + Eina_Tmpstr *f_code; + Eina_Strbuf *sbuf; + int fd_code = eina_file_mkstemp("exactness_XXXXXX.c", &f_code); + int fd_output = eina_file_mkstemp("exactness_XXXXXX.output", &f_output); + close(fd_output); + write(fd_code, code->content, strlen(code->content)); + close(fd_code); + + sbuf = eina_strbuf_new(); + eina_strbuf_append(sbuf, code->command); + eina_strbuf_replace_all(sbuf, "$SRC", f_code); + eina_strbuf_replace_all(sbuf, "$DEST", f_output); + exe = ecore_exe_pipe_run(eina_strbuf_string_get(sbuf), ECORE_EXE_NONE, NULL); +#ifdef HAVE_FORK + waitpid(ecore_exe_pid_get(exe), &status, 0); +#endif + } + } + if (!f_output) + { + fprintf(stderr, "no program specified\nUse -h for more information\n"); + goto end; + } + argv[0] = strdup(f_output); + } + + + ecore_evas_callback_new_set(_my_evas_new); + if (_src_type != FTYPE_REMOTE) + ecore_idler_add(_src_feed, NULL); + pret = _prg_invoke(_prg_full_path_guess(argv[0]), argc - opt_args, argv); + + if (_dest && _dest_unit) + { + if (_src_unit) + { + Exactness_Unit *tmp = NULL; + if (_src_type == FTYPE_EXU) tmp = exactness_unit_file_read(_src_filename); + if (_src_type == FTYPE_REC) tmp = legacy_rec_file_read(_src_filename); + _dest_unit->actions = tmp->actions; + _dest_unit->codes = tmp->codes; + } + exactness_unit_file_write(_dest_unit, _dest); + } + +end: + eet_shutdown(); + eina_shutdown(); + return pret; +} diff --git a/src/bin/exactness/player_entry.edc b/src/bin/exactness/player_entry.edc new file mode 100644 index 0000000000..b0d1966d6e --- /dev/null +++ b/src/bin/exactness/player_entry.edc @@ -0,0 +1,932 @@ +collections { + #include "../../../data/elementary/themes/fonts.edc" + group { name: "elm/entry/cursor/default"; + min: 1 0; + images.image: "white_bar_vert_glow.png" COMP; + parts { + part { name: "cursor"; mouse_events: 0; + clip_to: "clipper"; + description { state: "default" 0.0; + rel1.offset: -4 -4; + rel2.offset: 3 3; + image.normal: "white_bar_vert_glow.png"; + image.border: 4 4 4 4; + fill.smooth: 0; + color: 255 255 255 0; + color_class: "entry_cursor"; + min: 9 10; + } + description { state: "visible" 0.0; + inherit: "default" 0.0; + color: 255 255 255 255; + } + } + part { name: "clipper"; type: RECT; + description { state: "default" 0.0; + rel1.to: "cursor"; + rel2.to: "cursor"; + fixed: 1 1; + } + description { state: "hidden" 0.0; + inherit: "default" 0.0; + visible: 0; + } + } + } + programs { + program { + signal: "selection,changed"; source: "elm.text"; + action: STATE_SET "hidden" 0.0; + target: "clipper"; + } + program { + signal: "selection,cleared"; source: "elm.text"; + action: STATE_SET "default" 0.0; + target: "clipper"; + } + program { + signal: "selection,reset"; source: "elm.text"; + action: STATE_SET "default" 0.0; + target: "clipper"; + } + program { + signal: "elm,action,focus"; source: "elm"; + action: ACTION_STOP; + target: "cursor_show"; + target: "cursor_hide"; + target: "cursor_show_timer"; + target: "cursor_hide_timer"; + after: "cursor_show"; + } + program { + signal: "elm,action,unfocus"; source: "elm"; + action: ACTION_STOP; + target: "cursor_show"; + target: "cursor_hide"; + target: "cursor_show_timer"; + target: "cursor_hide_timer"; + after: "cursor_hide_stop"; + } + program { + signal: "elm,action,show,cursor"; source: "elm"; + action: ACTION_STOP; + target: "cursor_show"; + target: "cursor_hide"; + target: "cursor_show_timer"; + target: "cursor_hide_timer"; + after: "cursor_show"; + } + program { name: "cursor_hide_stop"; + action: STATE_SET "default" 0.0; + target: "cursor"; + } + program { name: "cursor_show"; + action: STATE_SET "default" 0.0; + target: "cursor"; + after: "cursor_show_timer"; + } + program { name: "cursor_hide"; + action: STATE_SET "default" 0.0; + target: "cursor"; + transition: SINUSOIDAL 0.2; + after: "cursor_hide_timer"; + } + program { name: "cursor_show_timer"; + in: 0.5 0.0; + after: "cursor_hide"; + } + program { name: "cursor_hide_timer"; + in: 0.2 0.0; + after: "cursor_show"; + } + } + } + + group { name: "elm/entry/selection/default"; + parts { + part { name: "base"; type: RECT; + description { state: "default" 0.0; + color: 51 153 255 255; + } + } + } + } + + group { name: "elm/entry/anchor/default"; + images.image: "horizontal_separated_bar_small_glow.png" COMP; + parts { + part { name: "bar"; + description { state: "default" 0.0; + image.normal: "horizontal_separated_bar_small_glow.png"; + image.border: 4 4 4 4; + fill.smooth: 0; + fixed: 0 1; + rel1.relative: 0.0 1.0; + rel1.offset: -3 -5; + rel2.offset: 2 4; + } + } + } + } + + group { name: "elm/entry/base/default"; + sounds { + sample { name: "key-tap1" LOSSY 64; + source: "kbd-tap.wav"; + } + sample { name: "key-tap2" LOSSY 64; + source: "kbd-tap2.wav"; + } + sample { name: "key-tap3" LOSSY 64; + source: "kbd-tap3.wav"; + } + sample { name: "key-tap4" LOSSY 64; + source: "kbd-tap4.wav"; + } + sample { name: "key-tap5" LOSSY 64; + source: "kbd-tap5.wav"; + } + } + + styles { + style { name: "entry_style"; + base: "font="FN" font_size=10 color=#ffffff style=shadow,bottom shadow_color=#00000080 wrap=word text_class=entry_text color_class=entry_text left_margin=2 right_margin=2"; + ENABLED_TEXTBLOCK_TAGS + } + style { name: "entry_nowrap_style"; + base: "font="FN" font_size=10 color=#ffffff style=shadow,bottom shadow_color=#00000080 text_class=entry_text color_class=entry_text left_margin=2 right_margin=2"; + ENABLED_TEXTBLOCK_TAGS + } + style { name: "entry_disabled_style"; + base: "font="FN" font_size=10 color=#151515 style=shadow,bottom shadow_color=#ffffff19 wrap=word text_class=entry_text_disabled color_class=entry_text_disabled left_margin=2 right_margin=2"; + DISABLED_TEXTBLOCK_TAGS + } + style { name: "entry_nowrap_disabled_style"; + base: "font="FN" font_size=10 color=#151515 style=shadow,bottom shadow_color=#ffffff19 text_class=entry_text_disabled color_class=entry_text_disabled left_margin=2 right_margin=2"; + DISABLED_TEXTBLOCK_TAGS + } + style { name: "entry_guide_style"; + base: "font="FN" font_size=10 color=#000000 style=shadow,bottom shadow_color=#ffffff19 wrap=word text_class=entry_guide_text color_class=entry_guide_text left_margin=2 right_margin=2 ellipsis=0.0"; + DISABLED_TEXTBLOCK_TAGS + } + } + // data.item: "context_menu_orientation" "horizontal"; + parts { + part { name: "elm.swallow.background"; type: SWALLOW; + description { state: "default" 0.0; + rel1.offset: 1 1; + rel2.offset: -2 -2; + } + } + part { name: "elm.guide"; type: TEXTBLOCK; mouse_events: 0; + scale: 1; + description { state: "default" 0.0; + rel1.to: "elm.text"; + rel2.to: "elm.text"; + text { style: "entry_guide_style"; + min: 0 1; + align: 0.0 0.0; + } + } + description { state: "hidden" 0.0; + inherit: "default" 0.0; + visible: 0; + } + } + part { name: "elm.text"; type: TEXTBLOCK; + scale: 1; + entry_mode: EDITABLE; + select_mode: DEFAULT; + // select_mode: EXPLICIT; + cursor_mode: BEFORE; + multiline: 1; + source: "elm/entry/selection/default"; // selection under + // source2: "X"; // selection over + // source3: "X"; // cursor under + source4: "elm/entry/cursor/default"; // cursorover + // source5: "elm/entry/anchor/default"; // anchor under + source6: "elm/entry/anchor/default"; // anchor over + description { state: "default" 0.0; + /* we gotta use 0 0 here, because of scrolled entries */ + fixed: 0 0; + rel1.offset: 2 2; + rel2.offset: -3 -3; + text { style: "entry_style"; + min: 0 1; + align: 0.0 0.0; + } + } + description { state: "disabled" 0.0; + inherit: "default" 0.0; + text { style: "entry_disabled_style"; + min: 0 1; + } + } + } + } + programs { + program { + signal: "load"; source: ""; + action: FOCUS_SET; + target: "elm.text"; + } + program { + signal: "elm,state,disabled"; source: "elm"; + action: STATE_SET "disabled" 0.0; + target: "elm.text"; + } + program { + signal: "elm,state,enabled"; source: "elm"; + action: STATE_SET "default" 0.0; + target: "elm.text"; + } + program { + signal: "elm,guide,disabled"; source: "elm"; + action: STATE_SET "hidden" 0.0; + target: "elm.guide"; + } + program { + signal: "elm,guide,enabled"; source: "elm"; + action: STATE_SET "default" 0.0; + target: "elm.guide"; + } + program { name: "key-down"; + signal: "entry,keydown"; source: "elm.text"; + script { + new buf[32]; + snprintf(buf, 31, "key-down%i", (rand() % 5) + 1); + run_program(get_program_id(buf)); + } + } + program { name: "key-down1"; + action: PLAY_SAMPLE "key-tap1" 1.0 INPUT; + } + program { name: "key-down2"; + action: PLAY_SAMPLE "key-tap2" 1.0 INPUT; + } + program { name: "key-down3"; + action: PLAY_SAMPLE "key-tap3" 1.0 INPUT; + } + program { name: "key-down4"; + action: PLAY_SAMPLE "key-tap4" 1.0 INPUT; + } + program { name: "key-down5"; + action: PLAY_SAMPLE "key-tap5" 1.0 INPUT; + } + } + } + + group { name: "elm/entry/base-mixedwrap/default"; + inherit: "elm/entry/base/default"; + styles { + style { name: "entry_style_mixedwrap"; + base: "font="FN" font_size=10 color=#ffffff style=shadow,bottom shadow_color=#00000080 wrap=mixed text_class=entry_text color_class=entry_text left_margin=2 right_margin=2"; + ENABLED_TEXTBLOCK_TAGS + } + style { name: "entry_disabled_style_mixedwrap"; + base: "font="FN" font_size=10 color=#151515 style=shadow,bottom shadow_color=#ffffff19 wrap=mixed text_class=entry_text_disabled color_class=entry_text_disabled left_margin=2 right_margin=2"; + DISABLED_TEXTBLOCK_TAGS + } + style { name: "entry_guide_style_mixedwrap"; + base: "font="FN" font_size=10 color=#000000 style=shadow,bottom shadow_color=#ffffff19 wrap=mixed text_class=entry_guide_text color_class=entry_guide_text left_margin=2 right_margin=2 ellipsis=0.0"; + DISABLED_TEXTBLOCK_TAGS + } + } + parts { + part { name: "elm.guide"; type: TEXTBLOCK; mouse_events: 0; + scale: 1; + description { state: "default" 0.0; + rel1.to: "elm.text"; + rel2.to: "elm.text"; + text { style: "entry_guide_style_mixedwrap"; + min: 0 1; + align: 0.0 0.0; + } + } + description { state: "hidden" 0.0; + inherit: "default" 0.0; + visible: 0; + } + } + part { name: "elm.text"; + description { state: "default" 0.0; + fixed: 1 0; + text { style: "entry_style_mixedwrap"; + min: 0 1; + align: 0.0 0.0; + } + } + description { state: "disabled" 0.0; + inherit: "default" 0.0; + text { style: "entry_disabled_style_mixedwrap"; + min: 0 1; + } + } + } + } + } + + group { name: "elm/entry/base-charwrap/default"; + inherit: "elm/entry/base/default"; + styles { + style { name: "entry_style_charwrap"; + base: "font="FN" font_size=10 color=#ffffff style=shadow,bottom shadow_color=#00000080 wrap=char text_class=entry_text color_class=entry_text left_margin=2 right_margin=2"; + ENABLED_TEXTBLOCK_TAGS + } + style { name: "entry_disabled_style_charwrap"; + base: "font="FN" font_size=10 color=#151515 style=shadow,bottom shadow_color=#ffffff19 wrap=char text_class=entry_text_disabled color_class=entry_text_disabled left_margin=2 right_margin=2"; + DISABLED_TEXTBLOCK_TAGS + } + style { name: "entry_guide_style_charwrap"; + base: "font="FN" font_size=10 color=#000000 style=shadow,bottom shadow_color=#ffffff19 wrap=char text_class=entry_guide_text color_class=entry_guide_text left_margin=2 right_margin=2 ellipsis=0.0"; + DISABLED_TEXTBLOCK_TAGS + } + } + parts { + part { name: "elm.guide"; type: TEXTBLOCK; mouse_events: 0; + scale: 1; + description { state: "default" 0.0; + rel1.to: "elm.text"; + rel2.to: "elm.text"; + text { style: "entry_guide_style_charwrap"; + min: 0 1; + align: 0.0 0.0; + } + } + description { state: "hidden" 0.0; + inherit: "default" 0.0; + visible: 0; + } + } + part { name: "elm.text"; + description { state: "default" 0.0; + fixed: 1 1; + text { style: "entry_style_charwrap"; + min: 0 1; + align: 0.0 0.0; + } + } + description { state: "disabled" 0.0; + inherit: "default" 0.0; + text { style: "entry_disabled_style_charwrap"; + min: 0 1; + } + } + } + } + } + + group { name: "elm/entry/base-nowrap/default"; + inherit: "elm/entry/base/default"; + parts { + part { name: "elm.guide"; type: TEXTBLOCK; mouse_events: 0; + scale: 1; + description { state: "default" 0.0; + rel1.to: "elm.text"; + rel2.to: "elm.text"; + text { style: "entry_guide_style"; + min: 0 1; + align: 0.0 0.0; + } + } + description { state: "hidden" 0.0; + inherit: "default" 0.0; + visible: 0; + } + } + part { name: "elm.text"; + description { state: "default" 0.0; + text { style: "entry_nowrap_style"; + min: 1 1; + ellipsis: -1; + align: 0.0 0.0; + } + } + description { state: "disabled" 0.0; + inherit: "default" 0.0; + text { style: "entry_nowrap_disabled_style"; + min: 0 1; + } + } + } + } + } + + group { name: "elm/entry/base-single/default"; + inherit: "elm/entry/base/default"; + styles { + style { name: "entry_single_style"; + base: "font="FN" font_size=10 color=#ffffff style=shadow,bottom shadow_color=#00000080 wrap=none text_class=entry_text color_class=entry_text left_margin=2 right_margin=2"; + ENABLED_TEXTBLOCK_TAGS + } + style { name: "entry_single_disabled_style"; + base: "font="FN" font_size=10 color=#151515 style=shadow,bottom shadow_color=#ffffff19 wrap=none text_class=entry_text_disabled color_class=entry_text_disabled left_margin=2 right_margin=2"; + DISABLED_TEXTBLOCK_TAGS + } + style { name: "entry_single_guide_style"; + base: "font="FN" font_size=10 color=#000000 style=shadow,bottom shadow_color=#ffffff19 wrap=none text_class=entry_guide_text color_class=entry_guide_text left_margin=2 right_margin=2 ellipsis=0.0"; + DISABLED_TEXTBLOCK_TAGS + } + } + parts { + part { name: "elm.guide"; type: TEXTBLOCK; mouse_events: 0; + scale: 1; + description { state: "default" 0.0; + rel1.to: "elm.text"; + rel2.to: "elm.text"; + text { style: "entry_single_guide_style"; + min: 0 1; + align: 0.0 0.5; + } + } + description { state: "hidden" 0.0; + inherit: "default" 0.0; + visible: 0; + } + } + part { name: "elm.text"; + multiline: 0; + description { state: "default" 0.0; + text { style: "entry_single_style"; + min: 1 1; + ellipsis: -1; + max: 0 0; + align: 0.0 0.5; + } + } + description { state: "disabled" 0.0; + inherit: "default" 0.0; + text { style: "entry_single_disabled_style"; + } + } + } + } + } + + group { name: "elm/entry/base-single/spinner/default"; + alias: "elm/entry/base-single/spinner/vertical"; + inherit: "elm/entry/base-single/default"; + styles { + style { name: "entry_single_spinner_style"; + base: "font="FN" font_size=10 color=#ffffff style=shadow,bottom shadow_color=#00000080 align=center wrap=none text_class=entry_text color_class=entry_text left_margin=2 right_margin=2"; + ENABLED_TEXTBLOCK_TAGS + } + } + parts { + part { name: "elm.text"; + description { state: "default" 0.0; + text.style: "entry_single_spinner_style"; + } + } + } + } + + group { name: "elm/entry/base-single-noedit/default"; + inherit: "elm/entry/base/default"; + parts { + part { name: "elm.text"; + entry_mode: PLAIN; + multiline: 0; + source: "elm/entry/selection/default"; // selection under + source4: ""; // cursorover + source6: "elm/entry/anchor/default"; // anchor over + description { state: "default" 0.0; + text { style: "entry_single_style"; + min: 1 1; + ellipsis: -1; + max: 0 0; + align: 0.0 0.5; + } + } + description { state: "disabled" 0.0; + inherit: "default" 0.0; + text { style: "entry_single_disabled_style"; + } + } + } + } + } + + group { name: "elm/entry/base-noedit/default"; + inherit: "elm/entry/base/default"; + parts { + part { name: "elm.text"; + entry_mode: PLAIN; + source: "elm/entry/selection/default"; // selection under + source4: ""; // cursorover + source6: "elm/entry/anchor/default"; // anchor over + description { state: "default" 0.0; + fixed: 1 0; + text { style: "entry_style"; + min: 0 1; + align: 0.0 0.0; + } + } + description { state: "disabled" 0.0; + inherit: "default" 0.0; + text { style: "entry_disabled_style"; + } + } + } + } + } + + group { name: "elm/entry/base-noedit-mixedwrap/default"; + inherit: "elm/entry/base/default"; + parts { + part { name: "elm.text"; + entry_mode: PLAIN; + source: "elm/entry/selection/default"; // selection under + source4: ""; // cursorover + source6: "elm/entry/anchor/default"; // anchor over + description { state: "default" 0.0; + fixed: 1 0; + text { style: "entry_style_mixedwrap"; + min: 0 1; + align: 0.0 0.0; + } + } + description { state: "disabled" 0.0; + inherit: "default" 0.0; + text { style: "entry_disabled_style_mixedwrap"; + } + } + } + } + } + + group { name: "elm/entry/base-noedit-charwrap/default"; + inherit: "elm/entry/base/default"; + parts { + part { name: "elm.text"; + entry_mode: PLAIN; + source: "elm/entry/selection/default"; // selection under + source4: ""; // cursorover + source6: "elm/entry/anchor/default"; // anchor under + description { state: "default" 0.0; + fixed: 1 0; + text { style: "entry_style_charwrap"; + min: 0 1; + align: 0.0 0.0; + } + } + description { state: "disabled" 0.0; + inherit: "default" 0.0; + text { style: "entry_disabled_style_charwrap"; + } + } + } + } + } + + group { name: "elm/entry/base-nowrap-noedit/default"; + inherit: "elm/entry/base/default"; + parts { + part { name: "elm.text"; + entry_mode: PLAIN; + source: "elm/entry/selection/default"; // selection under + source4: ""; // cursorover + source6: "elm/entry/anchor/default"; // anchor under + description { state: "default" 0.0; + text { style: "entry_style"; + min: 1 1; + ellipsis: -1; + align: 0.0 0.0; + } + } + description { state: "disabled" 0.0; + inherit: "default" 0.0; + text { style: "entry_disabled_style"; + } + } + } + } + } + + group { name: "elm/entry/base-password/default"; + inherit: "elm/entry/base/default"; + parts { + part { name: "elm.guide"; type: TEXTBLOCK; mouse_events: 0; + scale: 1; + description { state: "default" 0.0; + rel1.to: "elm.text"; + rel2.to: "elm.text"; + text { style: "entry_single_guide_style"; + min: 0 1; + align: 0.0 0.5; + } + } + description { state: "hidden" 0.0; + inherit: "default" 0.0; + visible: 0; + } + } + part { name: "elm.text"; + entry_mode: PASSWORD; + multiline: 0; + source: "elm/entry/selection/default"; // selection under + source4: "elm/entry/cursor/default"; // cursorover + source6: "elm/entry/anchor/default"; // anchor under + description { state: "default" 0.0; + text { style: "entry_single_style"; + repch: "*"; + min: 1 1; + ellipsis: -1; + max: 0 0; + align: 0.0 0.5; + } + } + description { state: "disabled" 0.0; + inherit: "default" 0.0; + text { style: "entry_single_disabled_style"; + } + } + } + } + } + + group { name: "elm/entry/magnifier/default"; + images.image: "frame_rounded.png" COMP; + parts { + part { name: "bg"; type: RECT; mouse_events: 0; + description { state: "default" 0.0; + rel1.offset: 10 10; + rel1.to: "over"; + rel2.offset: -11 -11; + rel2.to: "over"; + color: 48 48 48 255; + } + description { state: "hidden" 0.0; + inherit: "default" 0.0; + visible: 0; + } + } + part { name: "elm.swallow.content"; type: SWALLOW; mouse_events: 0; + description { state: "default" 0.0; + rel1.offset: 10 10; + rel1.to: "over"; + rel2.offset: -11 -11; + rel2.to: "over"; + } + description { state: "hidden" 0.0; + inherit: "default" 0.0; + visible: 0; + } + } + part { name: "over"; mouse_events: 0; + scale: 1; + description { state: "default" 0.0; + min: 128 64; + align: 0 0; + image.normal: "frame_rounded.png"; + image.border: 14 14 14 14; + image.middle: 0; + fill.smooth: 0; + } + description { state: "hidden" 0.0; + inherit: "default" 0.0; + visible: 0; + } + } + } + programs { + program { name: "magnifier_show"; + signal: "elm,action,show,magnifier"; source: "elm"; + action: STATE_SET "default" 0.0; + target: "elm.swallow.content"; + target: "bg"; + target: "over"; + } + program { name: "magnifier_hide"; + signal: "elm,action,hide,magnifier"; source: "elm"; + action: STATE_SET "hidden" 0.0; + target: "elm.swallow.content"; + target: "bg"; + target: "over"; + } + } + } + + group { name: "elm/entry/handler/start/default"; + images.image: "handle_pick_up_left.png" COMP; + parts { + part { name: "base"; mouse_events: 0; + scale: 1; + description { state: "default" 0.0; + min: 21 27; // 42 54 + image.normal: "handle_pick_up_left.png"; + align: (29/42) (11/54); + color_class: "entry_selection_handler"; + visible: 0; + } + description { state: "visible" 0.0; + inherit: "default" 0.0; + visible: 1; + } + } + part { name: "event"; type: RECT; + scale: 1; + description { state: "default" 0.0; + color: 0 0 0 0; + rel1.to: "base"; + rel2.to: "base"; + min: 32 32; + visible: 0; + } + description { state: "visible" 0.0; + inherit: "default" 0.0; + visible: 1; + } + } + } + programs { + program { + signal: "elm,handler,show"; source: "elm"; + action: STATE_SET "visible" 0.0; + target: "base"; + target: "event"; + } + program { + signal: "elm,handler,hide"; source: "elm"; + action: STATE_SET "default" 0.0; + target: "base"; + target: "event"; + } + } + } + + group { name: "elm/entry/handler/end/default"; + images.image: "handle_pick_up_right.png" COMP; + parts { + part { name: "base"; mouse_events: 0; + scale: 1; + description { state: "default" 0.0; + min: 21 27; // 42 54 + image.normal: "handle_pick_up_right.png"; + align: (12/42) (11/54); + color_class: "entry_selection_handler"; + visible: 0; + } + description { state: "visible" 0.0; + inherit: "default" 0.0; + visible: 1; + } + } + part { name: "event"; type: RECT; + scale: 1; + description { state: "default" 0.0; + color: 0 0 0 0; + rel1.to: "base"; + rel2.to: "base"; + min: 32 32; + visible: 0; + } + description { state: "visible" 0.0; + inherit: "default" 0.0; + visible: 1; + } + } + } + programs { + program { + signal: "elm,handler,show"; source: "elm"; + action: STATE_SET "visible" 0.0; + target: "base"; + target: "event"; + } + program { + signal: "elm,handler,hide"; source: "elm"; + action: STATE_SET "default" 0.0; + target: "base"; + target: "event"; + } + } + } + + /////////////////////////////////////////////////////////////////////////////// + // emoticon images from: + // Tanya - Latvia + // http://lazycrazy.deviantart.com/ + // http://lazycrazy.deviantart.com/art/Very-Emotional-Emoticons-144461621 + group { name: "elm/entry/emoticon/angry/default"; images.image: + "emo-angry.png" COMP; parts { part { name: "icon"; mouse_events: 0; description { state: "default" 0.0; max: 64 64; image.normal: + "emo-angry.png"; } } } } + group { name: "elm/entry/emoticon/angry-shout/default"; images.image: + "emo-angry-shout.png" COMP; parts { part { name: "icon"; mouse_events: 0; description { state: "default" 0.0; max: 64 64; image.normal: + "emo-angry-shout.png"; } } } } + group { name: "elm/entry/emoticon/crazy-laugh/default"; images.image: + "emo-crazy-laugh.png" COMP; parts { part { name: "icon"; mouse_events: 0; description { state: "default" 0.0; max: 64 64; image.normal: + "emo-crazy-laugh.png"; } } } } + group { name: "elm/entry/emoticon/evil-laugh/default"; images.image: + "emo-evil-laugh.png" COMP; parts { part { name: "icon"; mouse_events: 0; description { state: "default" 0.0; max: 64 64; image.normal: + "emo-evil-laugh.png"; } } } } + group { name: "elm/entry/emoticon/evil/default"; images.image: + "emo-evil.png" COMP; parts { part { name: "icon"; mouse_events: 0; description { state: "default" 0.0; max: 64 64; image.normal: + "emo-evil.png"; } } } } + group { name: "elm/entry/emoticon/goggle-smile/default"; images.image: + "emo-goggle-smile.png" COMP; parts { part { name: "icon"; mouse_events: 0; description { state: "default" 0.0; max: 64 64; image.normal: + "emo-goggle-smile.png"; } } } } + group { name: "elm/entry/emoticon/grumpy/default"; images.image: + "emo-grumpy.png" COMP; parts { part { name: "icon"; mouse_events: 0; description { state: "default" 0.0; max: 64 64; image.normal: + "emo-grumpy.png"; } } } } + group { name: "elm/entry/emoticon/grumpy-smile/default"; images.image: + "emo-grumpy-smile.png" COMP; parts { part { name: "icon"; mouse_events: 0; description { state: "default" 0.0; max: 64 64; image.normal: + "emo-grumpy-smile.png"; } } } } + group { name: "elm/entry/emoticon/guilty/default"; images.image: + "emo-guilty.png" COMP; parts { part { name: "icon"; mouse_events: 0; description { state: "default" 0.0; max: 64 64; image.normal: + "emo-guilty.png"; } } } } + group { name: "elm/entry/emoticon/guilty-smile/default"; images.image: + "emo-guilty-smile.png" COMP; parts { part { name: "icon"; mouse_events: 0; description { state: "default" 0.0; max: 64 64; image.normal: + "emo-guilty-smile.png"; } } } } + group { name: "elm/entry/emoticon/haha/default"; images.image: + "emo-haha.png" COMP; parts { part { name: "icon"; mouse_events: 0; description { state: "default" 0.0; max: 64 64; image.normal: + "emo-haha.png"; } } } } + group { name: "elm/entry/emoticon/half-smile/default"; images.image: + "emo-half-smile.png" COMP; parts { part { name: "icon"; mouse_events: 0; description { state: "default" 0.0; max: 64 64; image.normal: + "emo-half-smile.png"; } } } } + group { name: "elm/entry/emoticon/happy-panting/default"; images.image: + "emo-happy-panting.png" COMP; parts { part { name: "icon"; mouse_events: 0; description { state: "default" 0.0; max: 64 64; image.normal: + "emo-happy-panting.png"; } } } } + group { name: "elm/entry/emoticon/happy/default"; images.image: + "emo-happy.png" COMP; parts { part { name: "icon"; mouse_events: 0; description { state: "default" 0.0; max: 64 64; image.normal: + "emo-happy.png"; } } } } + group { name: "elm/entry/emoticon/indifferent/default"; images.image: + "emo-indifferent.png" COMP; parts { part { name: "icon"; mouse_events: 0; description { state: "default" 0.0; max: 64 64; image.normal: + "emo-indifferent.png"; } } } } + group { name: "elm/entry/emoticon/kiss/default"; images.image: + "emo-kiss.png" COMP; parts { part { name: "icon"; mouse_events: 0; description { state: "default" 0.0; max: 64 64; image.normal: + "emo-kiss.png"; } } } } + group { name: "elm/entry/emoticon/knowing-grin/default"; images.image: + "emo-knowing-grin.png" COMP; parts { part { name: "icon"; mouse_events: 0; description { state: "default" 0.0; max: 64 64; image.normal: + "emo-knowing-grin.png"; } } } } + group { name: "elm/entry/emoticon/laugh/default"; images.image: + "emo-laugh.png" COMP; parts { part { name: "icon"; mouse_events: 0; description { state: "default" 0.0; max: 64 64; image.normal: + "emo-laugh.png"; } } } } + group { name: "elm/entry/emoticon/little-bit-sorry/default"; images.image: + "emo-little-bit-sorry.png" COMP; parts { part { name: "icon"; mouse_events: 0; description { state: "default" 0.0; max: 64 64; image.normal: + "emo-little-bit-sorry.png"; } } } } + group { name: "elm/entry/emoticon/love-lots/default"; images.image: + "emo-love-lots.png" COMP; parts { part { name: "icon"; mouse_events: 0; description { state: "default" 0.0; max: 64 64; image.normal: + "emo-love-lots.png"; } } } } + group { name: "elm/entry/emoticon/love/default"; images.image: + "emo-love.png" COMP; parts { part { name: "icon"; mouse_events: 0; description { state: "default" 0.0; max: 64 64; image.normal: + "emo-love.png"; } } } } + group { name: "elm/entry/emoticon/minimal-smile/default"; images.image: + "emo-minimal-smile.png" COMP; parts { part { name: "icon"; mouse_events: 0; description { state: "default" 0.0; max: 64 64; image.normal: + "emo-minimal-smile.png"; } } } } + group { name: "elm/entry/emoticon/not-happy/default"; images.image: + "emo-not-happy.png" COMP; parts { part { name: "icon"; mouse_events: 0; description { state: "default" 0.0; max: 64 64; image.normal: + "emo-not-happy.png"; } } } } + group { name: "elm/entry/emoticon/not-impressed/default"; images.image: + "emo-not-impressed.png" COMP; parts { part { name: "icon"; mouse_events: 0; description { state: "default" 0.0; max: 64 64; image.normal: + "emo-not-impressed.png"; } } } } + group { name: "elm/entry/emoticon/omg/default"; images.image: + "emo-omg.png" COMP; parts { part { name: "icon"; mouse_events: 0; description { state: "default" 0.0; max: 64 64; image.normal: + "emo-omg.png"; } } } } + group { name: "elm/entry/emoticon/opensmile/default"; images.image: + "emo-opensmile.png" COMP; parts { part { name: "icon"; mouse_events: 0; description { state: "default" 0.0; max: 64 64; image.normal: + "emo-opensmile.png"; } } } } + group { name: "elm/entry/emoticon/smile/default"; images.image: + "emo-smile.png" COMP; parts { part { name: "icon"; mouse_events: 0; description { state: "default" 0.0; max: 64 64; image.normal: + "emo-smile.png"; } } } } + group { name: "elm/entry/emoticon/sorry/default"; images.image: + "emo-sorry.png" COMP; parts { part { name: "icon"; mouse_events: 0; description { state: "default" 0.0; max: 64 64; image.normal: + "emo-sorry.png"; } } } } + group { name: "elm/entry/emoticon/squint-laugh/default"; images.image: + "emo-squint-laugh.png" COMP; parts { part { name: "icon"; mouse_events: 0; description { state: "default" 0.0; max: 64 64; image.normal: + "emo-squint-laugh.png"; } } } } + group { name: "elm/entry/emoticon/surprised/default"; images.image: + "emo-surprised.png" COMP; parts { part { name: "icon"; mouse_events: 0; description { state: "default" 0.0; max: 64 64; image.normal: + "emo-surprised.png"; } } } } + group { name: "elm/entry/emoticon/suspicious/default"; images.image: + "emo-suspicious.png" COMP; parts { part { name: "icon"; mouse_events: 0; description { state: "default" 0.0; max: 64 64; image.normal: + "emo-suspicious.png"; } } } } + group { name: "elm/entry/emoticon/tongue-dangling/default"; images.image: + "emo-tongue-dangling.png" COMP; parts { part { name: "icon"; mouse_events: 0; description { state: "default" 0.0; max: 64 64; image.normal: + "emo-tongue-dangling.png"; } } } } + group { name: "elm/entry/emoticon/tongue-poke/default"; images.image: + "emo-tongue-poke.png" COMP; parts { part { name: "icon"; mouse_events: 0; description { state: "default" 0.0; max: 64 64; image.normal: + "emo-tongue-poke.png"; } } } } + group { name: "elm/entry/emoticon/uh/default"; images.image: + "emo-uh.png" COMP; parts { part { name: "icon"; mouse_events: 0; description { state: "default" 0.0; max: 64 64; image.normal: + "emo-uh.png"; } } } } + group { name: "elm/entry/emoticon/unhappy/default"; images.image: + "emo-unhappy.png" COMP; parts { part { name: "icon"; mouse_events: 0; description { state: "default" 0.0; max: 64 64; image.normal: + "emo-unhappy.png"; } } } } + group { name: "elm/entry/emoticon/very-sorry/default"; images.image: + "emo-very-sorry.png" COMP; parts { part { name: "icon"; mouse_events: 0; description { state: "default" 0.0; max: 64 64; image.normal: + "emo-very-sorry.png"; } } } } + group { name: "elm/entry/emoticon/what/default"; images.image: + "emo-what.png" COMP; parts { part { name: "icon"; mouse_events: 0; description { state: "default" 0.0; max: 64 64; image.normal: + "emo-what.png"; } } } } + group { name: "elm/entry/emoticon/wink/default"; images.image: + "emo-wink.png" COMP; parts { part { name: "icon"; mouse_events: 0; description { state: "default" 0.0; max: 64 64; image.normal: + "emo-wink.png"; } } } } + group { name: "elm/entry/emoticon/worried/default"; images.image: + "emo-worried.png" COMP; parts { part { name: "icon"; mouse_events: 0; description { state: "default" 0.0; max: 64 64; image.normal: + "emo-worried.png"; } } } } + group { name: "elm/entry/emoticon/wtf/default"; images.image: + "emo-wtf.png" COMP; parts { part { name: "icon"; mouse_events: 0; description { state: "default" 0.0; max: 64 64; image.normal: + "emo-wtf.png"; } } } } + //------------------------------------------------------------ +} diff --git a/src/bin/exactness/recorder.c b/src/bin/exactness/recorder.c new file mode 100644 index 0000000000..11a2087d08 --- /dev/null +++ b/src/bin/exactness/recorder.c @@ -0,0 +1,531 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include + +#include +#ifdef HAVE_SYS_SYSINFO_H +# include +#endif + +#ifndef EFL_EO_API_SUPPORT +#define EFL_EO_API_SUPPORT +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define MAX_PATH 1024 +#define STABILIZE_KEY_STR "F1" +#define SHOT_KEY_STR "F2" +#define SAVE_KEY_STR "F3" + +static Evas *(*_evas_new)(void) = NULL; +static const char *_out_filename = NULL; +static const char *_test_name = NULL; +static int _verbose = 0; + +static Eina_List *_evas_list = NULL; +static unsigned int _last_evas_id = 0; + +static Exactness_Unit *_unit = NULL; + +static char *_shot_key = NULL; +static unsigned int _last_timestamp = 0.0; + +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 Exactness_Action_Type +_event_pointer_type_get(Efl_Pointer_Action t) +{ + switch(t) + { + case EFL_POINTER_ACTION_IN: return EXACTNESS_ACTION_MOUSE_IN; + case EFL_POINTER_ACTION_OUT: return EXACTNESS_ACTION_MOUSE_OUT; + case EFL_POINTER_ACTION_DOWN: return EXACTNESS_ACTION_MULTI_DOWN; + case EFL_POINTER_ACTION_UP: return EXACTNESS_ACTION_MULTI_UP; + case EFL_POINTER_ACTION_MOVE: return EXACTNESS_ACTION_MULTI_MOVE; + case EFL_POINTER_ACTION_WHEEL: return EXACTNESS_ACTION_MOUSE_WHEEL; + default: return EXACTNESS_ACTION_UNKNOWN; + } +} + +static void +_output_write() +{ + if (_unit) exactness_unit_file_write(_unit, _out_filename); +} + +static void +_add_to_list(Exactness_Action_Type type, unsigned int n_evas, unsigned int timestamp, void *data, int len) +{ + if (_unit) + { + const Exactness_Action *prev_v = eina_list_last_data_get(_unit->actions); + if (prev_v) + { + if (prev_v->type == type && + timestamp == _last_timestamp && + prev_v->n_evas == n_evas && + (!len || !memcmp(prev_v->data, data, len))) return; + } + _printf(1, "Recording %s\n", _exactness_action_type_to_string_get(type)); + Exactness_Action *act = malloc(sizeof(*act)); + act->type = type; + act->n_evas = n_evas; + act->delay_ms = timestamp - _last_timestamp; + _last_timestamp = timestamp; + if (len) + { + act->data = malloc(len); + memcpy(act->data, data, len); + } + _unit->actions = eina_list_append(_unit->actions, act); + } +} + +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); + Exactness_Action_Type evt = _event_pointer_type_get(action); + + if (!timestamp) return; + + _printf(2, "Calling \"%s\" timestamp=<%u>\n", _exactness_action_type_to_string_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_touch_id_get(evp); + Eina_Position2D pos = efl_input_pointer_position_get(evp); + Exactness_Action_Multi_Move t = { tool, pos.x, pos.y, rad, radx, rady, pres, ang, fx, fy }; + if (n_evas >= 0) _add_to_list(evt, n_evas, timestamp, &t, sizeof(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_touch_id_get(evp); + Eina_Position2D pos = efl_input_pointer_position_get(evp); + Efl_Pointer_Flags flags = efl_input_pointer_button_flags_get(evp); + Exactness_Action_Multi_Event t = { tool, b, pos.x, pos.y, rad, radx, rady, pres, ang, + fx, fy, flags }; + if (n_evas >= 0) _add_to_list(evt, n_evas, timestamp, &t, sizeof(t)); + break; + } + case EFL_POINTER_ACTION_IN: case EFL_POINTER_ACTION_OUT: + { + if (n_evas >= 0) _add_to_list(evt, n_evas, timestamp, NULL, 0); + 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); + Exactness_Action_Mouse_Wheel t = { horiz, z }; + if (n_evas >= 0) _add_to_list(evt, n_evas, timestamp, &t, sizeof(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); + Exactness_Action_Type evt = EXACTNESS_ACTION_KEY_UP; + + if (efl_input_key_pressed_get(evk)) + { + if (!strcmp(key, _shot_key)) + { + _printf(2, "Take Screenshot: %s timestamp=<%u>\n", __func__, timestamp); + _add_to_list(EXACTNESS_ACTION_TAKE_SHOT, n_evas, timestamp, NULL, 0); + return; + } + if (!strcmp(key, STABILIZE_KEY_STR)) + { + _printf(2, "Stabilize: %s timestamp=<%u>\n", __func__, timestamp); + _add_to_list(EXACTNESS_ACTION_STABILIZE, n_evas, timestamp, NULL, 0); + return; + } + if (!strcmp(key, SAVE_KEY_STR)) + { + _output_write(); + _printf(2, "Save events: %s timestamp=<%u>\n", __func__, timestamp); + return; + } + evt = EXACTNESS_ACTION_KEY_DOWN; + } + else + { + if (!strcmp(key, _shot_key) || !strcmp(key, SAVE_KEY_STR) || !strcmp(key, STABILIZE_KEY_STR)) return; + } + if (_unit) + { /* Construct duplicate strings, free them when list if freed */ + Exactness_Action_Key_Down_Up t; + t.keyname = eina_stringshare_add(key); + t.key = eina_stringshare_add(efl_input_key_sym_get(evk)); + t.string = eina_stringshare_add(efl_input_key_string_get(evk)); + t.compose = eina_stringshare_add(efl_input_key_compose_string_get(evk)); + t.keycode = efl_input_key_code_get(evk); + _add_to_list(evt, n_evas, timestamp, &t, sizeof(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 int +_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 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; +} + +static const Ecore_Getopt optdesc = { + "exactness_record", + "%prog [options] <-v|-t|-h> command", + PACKAGE_VERSION, + "(C) 2017 Enlightenment", + "BSD", + "A scenario recorder for EFL based applications.\n" + "\tF1 - Request stabilization\n" + "\tF2 - Request shot\n" + "\tF3 - Request to save the scenario\n", + 1, + { + ECORE_GETOPT_STORE_STR('t', "test", "Name of the filename where to store the test."), + ECORE_GETOPT_STORE_STR('f', "fonts-dir", "Specify a directory of the fonts that should be used."), + ECORE_GETOPT_COUNT('v', "verbose", "Turn verbose messages on."), + + ECORE_GETOPT_LICENSE('L', "license"), + ECORE_GETOPT_COPYRIGHT('C', "copyright"), + ECORE_GETOPT_VERSION('V', "version"), + ECORE_GETOPT_HELP('h', "help"), + ECORE_GETOPT_SENTINEL + } +}; + +int main(int argc, char **argv) +{ + char *dest = NULL, *eq; + char *fonts_dir = NULL; + int pret = 1, opt_args = 0; + Eina_Bool want_quit = EINA_FALSE; + + Ecore_Getopt_Value values[] = { + ECORE_GETOPT_VALUE_STR(dest), + ECORE_GETOPT_VALUE_STR(fonts_dir), + ECORE_GETOPT_VALUE_INT(_verbose), + + ECORE_GETOPT_VALUE_BOOL(want_quit), + ECORE_GETOPT_VALUE_BOOL(want_quit), + ECORE_GETOPT_VALUE_BOOL(want_quit), + ECORE_GETOPT_VALUE_BOOL(want_quit), + ECORE_GETOPT_VALUE_NONE + }; + + eina_init(); + ecore_init(); + + opt_args = ecore_getopt_parse(&optdesc, values, argc, argv); + if (opt_args < 0) + { + fprintf(stderr, "Failed parsing arguments.\n"); + goto end; + } + if (want_quit) goto end; + + /* Check for a sentinel */ + if (argv[opt_args] && !strcmp(argv[opt_args], "--")) opt_args++; + + /* Check for env variables */ + do + { + eq = argv[opt_args] ? strchr(argv[opt_args], '=') : NULL; + if (eq) + { + char *var = malloc(eq - argv[opt_args] + 1); + memcpy(var, argv[opt_args], eq - argv[opt_args]); + var[eq - argv[opt_args]] = '\0'; + setenv(var, eq + 1, 1); + opt_args++; + } + } while (eq); + _out_filename = eina_stringshare_add(dest); + + if (!_out_filename) + { + fprintf(stderr, "no test file specified\n"); + goto end; + } + else + { + char *slash = strrchr(_out_filename, '/'); + if (slash) _test_name = strdup(slash + 1); + else _test_name = strdup(_out_filename); + char *dot = strrchr(_test_name, '.'); + if (dot) *dot = '\0'; + if (slash) + { + *slash = '\0'; + if (!_mkdir(_out_filename)) + { + fprintf(stderr, "Can't create %s\n", _out_filename); + goto end; + } + *slash = '/'; + } + } + if (strcmp(_out_filename + strlen(_out_filename) - 4,".exu")) + { + fprintf(stderr, "A file with a exu extension is required - %s invalid\n", _out_filename); + goto end; + } + + if (strcmp(_out_filename + strlen(_out_filename) - 4,".exu")) + { + fprintf(stderr, "A file with a exu extension is required - %s invalid\n", _out_filename); + goto end; + } + + if (!argv[opt_args]) + { + fprintf(stderr, "no program specified\nUse -h for more information\n"); + goto end; + } + + efl_object_init(); + evas_init(); + + if (!_unit) + { + _unit = calloc(1, sizeof(*_unit)); + } + + if (fonts_dir) + { + Eina_Tmpstr *fonts_conf_name = NULL; + if (!ecore_file_exists(fonts_dir)) + { + fprintf(stderr, "Unable to find fonts directory %s\n", fonts_dir); + goto end; + } + Eina_List *dated_fonts = ecore_file_ls(fonts_dir); + char *date_dir; + _unit->fonts_path = strdup(eina_list_last_data_get(dated_fonts)); + EINA_LIST_FREE(dated_fonts, date_dir) free(date_dir); + if (_unit->fonts_path) + { + int tmp_fd = eina_file_mkstemp("/tmp/fonts_XXXXXX.conf", &fonts_conf_name); + FILE *tmp_f = fdopen(tmp_fd, "wb"); + fprintf(tmp_f, + "\n\n\n" + "%s/%s\n\n", + fonts_dir, _unit->fonts_path); + fclose(tmp_f); + close(tmp_fd); + + setenv("FONTCONFIG_FILE", fonts_conf_name, 1); + } + } + + /* Replace the current command line to hide the Exactness part */ + int len = argv[argc - 1] + strlen(argv[argc - 1]) - argv[opt_args]; + memcpy(argv[0], argv[opt_args], len); + memset(argv[0] + len, 0, MAX_PATH - len); + + int i; + for (i = opt_args; i < argc; i++) + { + if (i != opt_args) + { + argv[i - opt_args] = argv[0] + (argv[i] - argv[opt_args]); + } + _printf(1, "%s ", argv[i - opt_args]); + } + _printf(1, "\n"); + + if (!_shot_key) _shot_key = getenv("SHOT_KEY"); + if (!_shot_key) _shot_key = SHOT_KEY_STR; + + ecore_evas_callback_new_set(_my_evas_new); + _last_timestamp = ecore_time_get() * 1000; + pret = _prg_invoke(_prg_full_path_guess(argv[0]), argc - opt_args, argv); + + _output_write(); + //free_events(_events_list, EINA_TRUE); + //_events_list = NULL; + + pret = 0; +end: + eina_shutdown(); + return pret; +} diff --git a/src/lib/exactness/Exactness.h b/src/lib/exactness/Exactness.h new file mode 100644 index 0000000000..d5587c3fbf --- /dev/null +++ b/src/lib/exactness/Exactness.h @@ -0,0 +1,268 @@ +#ifndef _EXACTNESS_H +#define _EXACTNESS_H + +#include + +#ifdef EAPI +# undef EAPI +#endif + +#ifdef _WIN32 +# ifdef EXACTNESS_BUILD +# ifdef DLL_EXPORT +# define EAPI __declspec(dllexport) +# else +# define EAPI +# endif /* ! DLL_EXPORT */ +# else +# define EAPI __declspec(dllimport) +# endif /* ! EXACTNESS_BUILD */ +#else +# ifdef __GNUC__ +# if __GNUC__ >= 4 +# define EAPI __attribute__ ((visibility("default"))) +# else +# define EAPI +# endif +# else +# define EAPI +# endif +#endif /* ! _WIN32 */ + +/** + * @page exactness_main Exactness + * + * @date 2018 (created) + * + * This page describes the public structures and APIs available for Exactness. + * + * @addtogroup Exactness + * @{ + */ + +/** + * @typedef Exactness_Action_Type + * The type values for an Exactness action. + */ +typedef enum +{ + EXACTNESS_ACTION_UNKNOWN = 0, + EXACTNESS_ACTION_MOUSE_IN, + EXACTNESS_ACTION_MOUSE_OUT, + EXACTNESS_ACTION_MOUSE_WHEEL, + EXACTNESS_ACTION_MULTI_DOWN, + EXACTNESS_ACTION_MULTI_UP, + EXACTNESS_ACTION_MULTI_MOVE, + EXACTNESS_ACTION_KEY_DOWN, + EXACTNESS_ACTION_KEY_UP, + EXACTNESS_ACTION_TAKE_SHOT, + EXACTNESS_ACTION_EFL_EVENT, + EXACTNESS_ACTION_CLICK_ON, + EXACTNESS_ACTION_STABILIZE, + EXACTNESS_ACTION_LAST = EXACTNESS_ACTION_STABILIZE + /* Add any supported actions here and update _LAST */ +} Exactness_Action_Type; + +/** + * @typedef Exactness_Action_Mouse_Wheel + * The type for the Exactness Mouse Wheel action. + */ +typedef struct +{ + int direction; + int z; +} Exactness_Action_Mouse_Wheel; + +/** + * @typedef Exactness_Action_Key_Down_Up + * The type for the Exactness Key Down Up action. + */ +typedef struct +{ + const char *keyname; + const char *key; + const char *string; + const char *compose; + unsigned int keycode; +} Exactness_Action_Key_Down_Up; + +/** + * @typedef Exactness_Action_Multi_Event + * The type for the Exactness Multi Event action. + */ +typedef struct +{ + int d; + int b; /* In case of simple mouse down/up, corresponds to the button */ + int x; + int y; + double rad; + double radx; + double rady; + double pres; + double ang; + double fx; + double fy; + Evas_Button_Flags flags; +} Exactness_Action_Multi_Event; + +/** + * @typedef Exactness_Action_Multi_Move + * The type for the Exactness Multi Move action. + */ +typedef struct +{ + int d; + int x; + int y; + double rad; + double radx; + double rady; + double pres; + double ang; + double fx; + double fy; +} Exactness_Action_Multi_Move; + +/** + * @typedef Exactness_Action_Efl_Event + * The type for the Exactness EFL Event action. + */ +typedef struct +{ + char *wdg_name; + char *event_name; +} Exactness_Action_Efl_Event; + +/** + * @typedef Exactness_Action_Click_On + * The type for the Exactness Click on (widget) action. + */ +typedef struct +{ + char *wdg_name; +} Exactness_Action_Click_On; + +/** + * @typedef Exactness_Action + * The type for the Exactness action. + */ +typedef struct +{ + Exactness_Action_Type type; /**< The action type */ + unsigned int n_evas; /**< The evas number on which the action has to be applied */ + unsigned int delay_ms; /**< The delay (in ms) to wait for this action */ + void *data; /**< The specific action data */ +} Exactness_Action; + +/** + * @typedef Exactness_Object + * The type for the Exactness object. + */ +typedef struct +{ + long long id; /**< The Eo pointer */ + long long parent_id; /**< The Eo parent pointer */ + const char *kl_name; /**< The class name */ + + Eina_List *children; /* NOT EET */ + + /* Evas stuff */ + int x; /**< The X coordinate */ + int y; /**< The Y coordinate */ + int w; /**< The object width */ + int h; /**< The object height */ +} Exactness_Object; + +/** + * @typedef Exactness_Objects + * The type for the Exactness objects list. + */ +typedef struct +{ + Eina_List *objs; /**< List of all the objects */ + /* main_objs not in EET */ + Eina_List *main_objs; /**< List of the main objects */ +} Exactness_Objects; + +/** + * @typedef Exactness_Image + * The type for the Exactness Image. + */ +typedef struct +{ + unsigned int w; /**< Width of the image */ + unsigned int h; /**< Height of the image */ + void *pixels; /**< Pixels of the image */ +} Exactness_Image; + +typedef struct +{ + char *language; /**< String describing the language of the content e.g "C"...*/ + char *content; /**< Content used as source */ + char *command; /**< Command needed to generate the application from the content */ +} Exactness_Source_Code; + +typedef struct +{ + Eina_List *actions; /**< List of Exactness_Action */ + /* imgs not in EET */ + Eina_List *imgs; /**< List of Exactness_Image */ + Eina_List *objs; /**< List of Exactness_Objects */ + Eina_List *codes; /**< List of Exactness_Source_Code */ + const char *fonts_path; /**< Path to the fonts to use, relative to the fonts dir given in parameter to the player/recorder */ + int nb_shots; /**< The number of shots present in the unit */ +} Exactness_Unit; + +/** + * @brief Read an unit from a given file + * + * @param filename Name of the file containing the unit + * + * @return the unit + */ +EAPI Exactness_Unit *exactness_unit_file_read(const char *filename); + +/** + * @brief Write an unit into the given file + * + * @param unit Unit to store + * @param filename Name of the file containing the unit + * + * @return EINA_TRUE on success, EINA_FALSE otherwise + */ +EAPI Eina_Bool exactness_unit_file_write(Exactness_Unit *unit, const char *filename); + +/** + * @brief Compare two images + * + * @param img1 first image + * @param img2 second image + * @param imgO pointer for the diff image. Can be NULL + * + * @return EINA_TRUE if the images are different, EINA_FALSE otherwise + */ +EAPI Eina_Bool exactness_image_compare(Exactness_Image *img1, Exactness_Image *img2, Exactness_Image **imgO); + +/** + * @brief Free the given image + * + * @param img the image + * + */ +EAPI void exactness_image_free(Exactness_Image *img); + +/** + * @brief Read a legacy file and convert it to an unit + * + * @param filename Name of the legacy file + * + * @return the unit + */ +EAPI Exactness_Unit *legacy_rec_file_read(const char *filename); + +/** + * @} + */ + +#endif /* _EXACTNESS_H */ diff --git a/src/lib/exactness/exactness_private.h b/src/lib/exactness/exactness_private.h new file mode 100644 index 0000000000..68bf4d8ddb --- /dev/null +++ b/src/lib/exactness/exactness_private.h @@ -0,0 +1,10 @@ +#ifndef _EXACTNESS_PRIVATE_H +#define _EXACTNESS_PRIVATE_H + +#include + +/* private header */ +EAPI const char *_exactness_action_type_to_string_get(Exactness_Action_Type type); + +#define SHOT_DELIMITER '+' +#endif diff --git a/src/lib/exactness/legacy_file.c b/src/lib/exactness/legacy_file.c new file mode 100644 index 0000000000..78ef333551 --- /dev/null +++ b/src/lib/exactness/legacy_file.c @@ -0,0 +1,876 @@ +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "Exactness.h" + +#define CACHE_FILE_ENTRY "cache" + +enum _Tsuite_Event_Type +{ /* Add any supported events here */ + TSUITE_EVENT_NOT_SUPPORTED = 0, + TSUITE_EVENT_MOUSE_IN, + TSUITE_EVENT_MOUSE_OUT, + TSUITE_EVENT_MOUSE_DOWN, + TSUITE_EVENT_MOUSE_UP, + TSUITE_EVENT_MOUSE_MOVE, + TSUITE_EVENT_MOUSE_WHEEL, + TSUITE_EVENT_MULTI_DOWN, + TSUITE_EVENT_MULTI_UP, + TSUITE_EVENT_MULTI_MOVE, + TSUITE_EVENT_KEY_DOWN, + TSUITE_EVENT_KEY_UP, + TSUITE_EVENT_KEY_DOWN_WITH_KEYCODE, + TSUITE_EVENT_KEY_UP_WITH_KEYCODE, + TSUITE_EVENT_TAKE_SHOT +}; +typedef enum _Tsuite_Event_Type Tsuite_Event_Type; + +struct _eet_event_type_mapping +{ + Tsuite_Event_Type t; + const char *name; +}; +typedef struct _eet_event_type_mapping eet_event_type_mapping; + +struct _Variant_Type_st +{ + const char *type; + Eina_Bool unknow : 1; +}; +typedef struct _Variant_Type_st Variant_Type_st; + +struct _Variant_st +{ + Variant_Type_st t; + void *data; /* differently than the union type, we + * don't need to pre-allocate the memory + * for the field*/ +}; +typedef struct _Variant_st Variant_st; + +struct _Timer_Data +{ + unsigned int recent_event_time; + Eina_List *current_event; +}; +typedef struct _Timer_Data Timer_Data; + +struct _Tsuite_Data +{ + int serial; /**< Serial number of current-file */ + Timer_Data *td; +}; +typedef struct _Tsuite_Data Tsuite_Data; + +struct _mouse_in_mouse_out +{ + unsigned int timestamp; + int n_evas; +}; + +struct _mouse_down_mouse_up +{ + int b; + Evas_Button_Flags flags; + unsigned int timestamp; + int n_evas; +}; + +struct _mouse_move +{ + int x; + int y; + unsigned int timestamp; + int n_evas; +}; + +struct _mouse_wheel +{ + int direction; + int z; + unsigned int timestamp; + int n_evas; +}; + +struct _key_down_key_up +{ + unsigned int timestamp; + const char *keyname; + const char *key; + const char *string; + const char *compose; + int n_evas; +}; + +struct _key_down_key_up_with_keycode +{ + unsigned int timestamp; + const char *keyname; + const char *key; + const char *string; + const char *compose; + int n_evas; + unsigned int keycode; +}; + +struct _multi_event +{ + int d; + int b; /* In case of simple mouse down/up, corresponds to the button */ + int x; + int y; + double rad; + double radx; + double rady; + double pres; + double ang; + double fx; + double fy; + Evas_Button_Flags flags; + unsigned int timestamp; + int n_evas; +}; + +struct _multi_move +{ + int d; + int x; + int y; + double rad; + double radx; + double rady; + double pres; + double ang; + double fx; + double fy; + unsigned int timestamp; + int n_evas; +}; + +typedef struct _mouse_in_mouse_out mouse_in_mouse_out; +typedef struct _mouse_down_mouse_up mouse_down_mouse_up; +typedef struct _mouse_move mouse_move; +typedef struct _mouse_wheel mouse_wheel; +typedef struct _multi_event multi_event; +typedef struct _multi_move multi_move; +typedef struct _key_down_key_up key_down_key_up; +typedef struct _key_down_key_up_with_keycode key_down_key_up_with_keycode; +typedef struct _mouse_in_mouse_out take_screenshot; + +/* START - EET support typedefs */ +#define TSUITE_EVENT_MOUSE_IN_STR "tsuite_event_mouse_in" +#define TSUITE_EVENT_MOUSE_OUT_STR "tsuite_event_mouse_out" +#define TSUITE_EVENT_MOUSE_DOWN_STR "tsuite_event_mouse_down" +#define TSUITE_EVENT_MOUSE_UP_STR "tsuite_event_mouse_up" +#define TSUITE_EVENT_MOUSE_MOVE_STR "tsuite_event_mouse_move" +#define TSUITE_EVENT_MOUSE_WHEEL_STR "tsuite_event_mouse_wheel" +#define TSUITE_EVENT_MULTI_DOWN_STR "tsuite_event_multi_down" +#define TSUITE_EVENT_MULTI_UP_STR "tsuite_event_multi_up" +#define TSUITE_EVENT_MULTI_MOVE_STR "tsuite_event_multi_move" +#define TSUITE_EVENT_KEY_DOWN_STR "tsuite_event_key_down" +#define TSUITE_EVENT_KEY_UP_STR "tsuite_event_key_up" +#define TSUITE_EVENT_KEY_DOWN_WITH_KEYCODE_STR "tsuite_event_key_down_with_keycode" +#define TSUITE_EVENT_KEY_UP_WITH_KEYCODE_STR "tsuite_event_key_up_with_keycode" +#define TSUITE_EVENT_TAKE_SHOT_STR "tsuite_event_take_shot" + +struct _Lists_st +{ + Eina_List *variant_list; + unsigned int first_timestamp; +}; +typedef struct _Lists_st Lists_st; + +struct _data_desc +{ + Eet_Data_Descriptor *take_screenshot; + Eet_Data_Descriptor *mouse_in_mouse_out; + Eet_Data_Descriptor *mouse_down_mouse_up; + Eet_Data_Descriptor *mouse_move; + Eet_Data_Descriptor *mouse_wheel; + Eet_Data_Descriptor *multi_event; + Eet_Data_Descriptor *multi_move; + Eet_Data_Descriptor *key_down_key_up; + Eet_Data_Descriptor *key_down_key_up_with_keycode; + + /* list, variant EET desc support */ + Eet_Data_Descriptor *lists_descriptor; + Eet_Data_Descriptor *variant_descriptor; + Eet_Data_Descriptor *variant_unified_descriptor; +}; +typedef struct _data_desc data_desc; +/* END - EET support typedefs */ + +static data_desc *_desc = NULL; /* this struct holds descs (alloc on init) */ + +static eet_event_type_mapping eet_mapping[] = { + { TSUITE_EVENT_MOUSE_IN, TSUITE_EVENT_MOUSE_IN_STR }, + { TSUITE_EVENT_MOUSE_OUT, TSUITE_EVENT_MOUSE_OUT_STR }, + { TSUITE_EVENT_MOUSE_DOWN, TSUITE_EVENT_MOUSE_DOWN_STR }, + { TSUITE_EVENT_MOUSE_UP, TSUITE_EVENT_MOUSE_UP_STR }, + { TSUITE_EVENT_MOUSE_MOVE, TSUITE_EVENT_MOUSE_MOVE_STR }, + { TSUITE_EVENT_MOUSE_WHEEL, TSUITE_EVENT_MOUSE_WHEEL_STR }, + { TSUITE_EVENT_MULTI_DOWN, TSUITE_EVENT_MULTI_DOWN_STR }, + { TSUITE_EVENT_MULTI_UP, TSUITE_EVENT_MULTI_UP_STR }, + { TSUITE_EVENT_MULTI_MOVE, TSUITE_EVENT_MULTI_MOVE_STR }, + { TSUITE_EVENT_KEY_DOWN, TSUITE_EVENT_KEY_DOWN_STR }, + { TSUITE_EVENT_KEY_UP, TSUITE_EVENT_KEY_UP_STR }, + { TSUITE_EVENT_KEY_DOWN_WITH_KEYCODE, TSUITE_EVENT_KEY_DOWN_WITH_KEYCODE_STR }, + { TSUITE_EVENT_KEY_UP_WITH_KEYCODE, TSUITE_EVENT_KEY_UP_WITH_KEYCODE_STR }, + { TSUITE_EVENT_TAKE_SHOT, TSUITE_EVENT_TAKE_SHOT_STR }, + { TSUITE_EVENT_NOT_SUPPORTED, NULL } +}; + +static Tsuite_Event_Type +_event_mapping_type_get(const char *name) +{ + int i; + for (i = 0; eet_mapping[i].name != NULL; ++i) + if (strcmp(name, eet_mapping[i].name) == 0) + return eet_mapping[i].t; + + return TSUITE_EVENT_NOT_SUPPORTED; +} + +static unsigned int +_evt_time_get(unsigned int tm, Variant_st *v) +{ + if (!v) return tm; + switch(_event_mapping_type_get(v->t.type)) + { + case TSUITE_EVENT_MOUSE_IN: + { + mouse_in_mouse_out *t = v->data; + return t->timestamp; + } + case TSUITE_EVENT_MOUSE_OUT: + { + mouse_in_mouse_out *t = v->data; + return t->timestamp; + } + case TSUITE_EVENT_MOUSE_DOWN: + { + mouse_down_mouse_up *t = v->data; + return t->timestamp; + } + case TSUITE_EVENT_MOUSE_UP: + { + mouse_down_mouse_up *t = v->data; + return t->timestamp; + } + case TSUITE_EVENT_MOUSE_MOVE: + { + mouse_move *t = v->data; + return t->timestamp; + } + case TSUITE_EVENT_MOUSE_WHEEL: + { + mouse_wheel *t = v->data; + return t->timestamp; + } + case TSUITE_EVENT_MULTI_DOWN: + { + multi_event *t = v->data; + return t->timestamp; + } + case TSUITE_EVENT_MULTI_UP: + { + multi_event *t = v->data; + return t->timestamp; + } + case TSUITE_EVENT_MULTI_MOVE: + { + multi_move *t = v->data; + return t->timestamp; + } + case TSUITE_EVENT_KEY_DOWN: + { + key_down_key_up *t = v->data; + return t->timestamp; + } + case TSUITE_EVENT_KEY_UP: + { + key_down_key_up *t = v->data; + return t->timestamp; + } + case TSUITE_EVENT_KEY_DOWN_WITH_KEYCODE: + { + key_down_key_up_with_keycode *t = v->data; + return t->timestamp; + } + case TSUITE_EVENT_KEY_UP_WITH_KEYCODE: + { + key_down_key_up_with_keycode *t = v->data; + return t->timestamp; + } + case TSUITE_EVENT_TAKE_SHOT: + { + take_screenshot *t = v->data; + return t->timestamp; + } + default: /* All non-input events are not handeled */ + return tm; + break; + } +} + +static Lists_st * +_free_events(Lists_st *st) +{ + Variant_st *v; + if (!st) goto end; + EINA_LIST_FREE(st->variant_list, v) + { + free(v->data); + free(v); + } + + free(st); /* Allocated when reading data from EET file */ +end: + return NULL; +} + +static const char * +_variant_type_get(const void *data, Eina_Bool *unknow) +{ + const Variant_Type_st *type = data; + int i; + + if (unknow) + *unknow = type->unknow; + + for (i = 0; eet_mapping[i].name != NULL; ++i) + if (strcmp(type->type, eet_mapping[i].name) == 0) + return eet_mapping[i].name; + + if (unknow) + *unknow = EINA_FALSE; + + return type->type; +} /* _variant_type_get */ + +static Eina_Bool +_variant_type_set(const char *type, + void *data, + Eina_Bool unknow) +{ + Variant_Type_st *vt = data; + + vt->type = type; + vt->unknow = unknow; + return EINA_TRUE; +} /* _variant_type_set */ + +/* START Event struct descriptors */ +static Eet_Data_Descriptor * +_take_screenshot_desc_make(void) +{ + Eet_Data_Descriptor_Class eddc; + Eet_Data_Descriptor *_d; + EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eddc, take_screenshot); + _d = eet_data_descriptor_stream_new(&eddc); + + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, take_screenshot, "timestamp", + timestamp, EET_T_UINT); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, take_screenshot, "n_evas", + n_evas, EET_T_INT); + + return _d; +} + +static Eet_Data_Descriptor * +_mouse_in_mouse_out_desc_make(void) +{ + Eet_Data_Descriptor_Class eddc; + Eet_Data_Descriptor *_d; + EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eddc, mouse_in_mouse_out); + _d = eet_data_descriptor_stream_new(&eddc); + + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, mouse_in_mouse_out, "timestamp", + timestamp, EET_T_UINT); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, mouse_in_mouse_out, "n_evas", + n_evas, EET_T_INT); + + return _d; +} + +static Eet_Data_Descriptor * +_mouse_down_mouse_up_desc_make(void) +{ + Eet_Data_Descriptor_Class eddc; + Eet_Data_Descriptor *_d; + EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eddc, mouse_down_mouse_up); + _d = eet_data_descriptor_stream_new(&eddc); + + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, mouse_down_mouse_up, "b", b, EET_T_INT); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, mouse_down_mouse_up, "flags", + flags, EET_T_INT); + + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, mouse_down_mouse_up, "timestamp", + timestamp, EET_T_UINT); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, mouse_down_mouse_up, "n_evas", + n_evas, EET_T_INT); + + return _d; +} + +static Eet_Data_Descriptor * +_mouse_move_desc_make(void) +{ + Eet_Data_Descriptor_Class eddc; + Eet_Data_Descriptor *_d; + EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eddc, mouse_move); + _d = eet_data_descriptor_stream_new(&eddc); + + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, mouse_move, "x", x, EET_T_INT); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, mouse_move, "y", y, EET_T_INT); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, mouse_move, "timestamp", + timestamp, EET_T_UINT); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, mouse_move, "n_evas", + n_evas, EET_T_INT); + + return _d; +} + +static Eet_Data_Descriptor * +_mouse_wheel_desc_make(void) +{ + Eet_Data_Descriptor_Class eddc; + Eet_Data_Descriptor *_d; + EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eddc, mouse_wheel); + _d = eet_data_descriptor_stream_new(&eddc); + + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, mouse_wheel, "direction", + direction, EET_T_INT); + + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, mouse_wheel, "z", z, EET_T_INT); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, mouse_wheel, "timestamp", + timestamp, EET_T_UINT); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, mouse_wheel, "n_evas", + n_evas, EET_T_INT); + + return _d; +} + +static Eet_Data_Descriptor * +_key_down_key_up_desc_make(void) +{ + Eet_Data_Descriptor_Class eddc; + Eet_Data_Descriptor *_d; + EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eddc, key_down_key_up); + _d = eet_data_descriptor_stream_new(&eddc); + + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, key_down_key_up, "timestamp", + timestamp, EET_T_UINT); + + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, key_down_key_up, "keyname", + keyname, EET_T_STRING); + + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, key_down_key_up, "key", + key, EET_T_STRING); + + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, key_down_key_up, "string", + string, EET_T_STRING); + + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, key_down_key_up, "compose", + compose, EET_T_STRING); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, key_down_key_up, "n_evas", + n_evas, EET_T_INT); + + return _d; +} + +static Eet_Data_Descriptor * +_key_down_key_up_with_keycode_desc_make(void) +{ + Eet_Data_Descriptor_Class eddc; + Eet_Data_Descriptor *_d; + EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eddc, key_down_key_up_with_keycode); + _d = eet_data_descriptor_stream_new(&eddc); + + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, key_down_key_up_with_keycode, "timestamp", + timestamp, EET_T_UINT); + + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, key_down_key_up_with_keycode, "keyname", + keyname, EET_T_STRING); + + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, key_down_key_up_with_keycode, "key", + key, EET_T_STRING); + + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, key_down_key_up_with_keycode, "string", + string, EET_T_STRING); + + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, key_down_key_up_with_keycode, "compose", + compose, EET_T_STRING); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, key_down_key_up_with_keycode, "n_evas", + n_evas, EET_T_INT); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, key_down_key_up_with_keycode, "keycode", + keycode, EET_T_INT); + + return _d; +} + +static Eet_Data_Descriptor * +_multi_event_desc_make(void) +{ + Eet_Data_Descriptor_Class eddc; + Eet_Data_Descriptor *_d; + EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eddc, multi_event); + _d = eet_data_descriptor_stream_new(&eddc); + + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, multi_event, "d", d, EET_T_UINT); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, multi_event, "b", b, EET_T_UINT); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, multi_event, "x", x, EET_T_UINT); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, multi_event, "y", y, EET_T_UINT); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, multi_event, "rad", rad, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, multi_event, "radx", radx, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, multi_event, "rady", rady, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, multi_event, "pres", pres, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, multi_event, "ang", ang, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, multi_event, "fx", fx, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, multi_event, "fy", fy, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, multi_event, "flags", flags, EET_T_INT); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, multi_event, "timestamp", + timestamp, EET_T_UINT); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, multi_event, "n_evas", + n_evas, EET_T_INT); + + return _d; +} + +static Eet_Data_Descriptor * +_multi_move_desc_make(void) +{ + Eet_Data_Descriptor_Class eddc; + Eet_Data_Descriptor *_d; + EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eddc, multi_move); + _d = eet_data_descriptor_stream_new(&eddc); + + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, multi_move, "d", d, EET_T_UINT); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, multi_move, "x", x, EET_T_UINT); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, multi_move, "y", y, EET_T_UINT); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, multi_move, "rad", rad, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, multi_move, "radx", radx, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, multi_move, "rady", rady, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, multi_move, "pres", pres, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, multi_move, "ang", ang, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, multi_move, "fx", fx, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, multi_move, "fy", fy, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, multi_move, "timestamp", + timestamp, EET_T_UINT); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, multi_move, "n_evas", + n_evas, EET_T_INT); + + return _d; +} + +/* declaring types */ +static data_desc * +_data_descriptors_init(void) +{ + if (_desc) /* Was allocated */ + return _desc; + + _desc = calloc(1, sizeof(data_desc)); + + Eet_Data_Descriptor_Class eddc; + + EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Lists_st); + _desc->lists_descriptor = eet_data_descriptor_file_new(&eddc); + + _desc->take_screenshot = _take_screenshot_desc_make(); + _desc->mouse_in_mouse_out = _mouse_in_mouse_out_desc_make(); + _desc->mouse_down_mouse_up = _mouse_down_mouse_up_desc_make(); + _desc->mouse_move = _mouse_move_desc_make(); + _desc->mouse_wheel = _mouse_wheel_desc_make(); + _desc->multi_event = _multi_event_desc_make(); + _desc->multi_move = _multi_move_desc_make(); + _desc->key_down_key_up = _key_down_key_up_desc_make(); + _desc->key_down_key_up_with_keycode = _key_down_key_up_with_keycode_desc_make(); + + /* for variant */ + EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Variant_st); + _desc->variant_descriptor = eet_data_descriptor_file_new(&eddc); + + eddc.version = EET_DATA_DESCRIPTOR_CLASS_VERSION; + eddc.func.type_get = _variant_type_get; + eddc.func.type_set = _variant_type_set; + _desc->variant_unified_descriptor = eet_data_descriptor_stream_new(&eddc); + + EET_DATA_DESCRIPTOR_ADD_MAPPING(_desc->variant_unified_descriptor, + TSUITE_EVENT_MOUSE_IN_STR, _desc->mouse_in_mouse_out); + + EET_DATA_DESCRIPTOR_ADD_MAPPING(_desc->variant_unified_descriptor, + TSUITE_EVENT_MOUSE_OUT_STR, _desc->mouse_in_mouse_out); + + EET_DATA_DESCRIPTOR_ADD_MAPPING(_desc->variant_unified_descriptor, + TSUITE_EVENT_MOUSE_DOWN_STR, _desc->mouse_down_mouse_up); + + EET_DATA_DESCRIPTOR_ADD_MAPPING(_desc->variant_unified_descriptor, + TSUITE_EVENT_MOUSE_UP_STR, _desc->mouse_down_mouse_up); + + EET_DATA_DESCRIPTOR_ADD_MAPPING(_desc->variant_unified_descriptor, + TSUITE_EVENT_MOUSE_MOVE_STR, _desc->mouse_move); + + EET_DATA_DESCRIPTOR_ADD_MAPPING(_desc->variant_unified_descriptor, + TSUITE_EVENT_MOUSE_WHEEL_STR, _desc->mouse_wheel); + + EET_DATA_DESCRIPTOR_ADD_MAPPING(_desc->variant_unified_descriptor, + TSUITE_EVENT_MULTI_DOWN_STR, _desc->multi_event); + + EET_DATA_DESCRIPTOR_ADD_MAPPING(_desc->variant_unified_descriptor, + TSUITE_EVENT_MULTI_UP_STR, _desc->multi_event); + + EET_DATA_DESCRIPTOR_ADD_MAPPING(_desc->variant_unified_descriptor, + TSUITE_EVENT_MULTI_MOVE_STR, _desc->multi_move); + + EET_DATA_DESCRIPTOR_ADD_MAPPING(_desc->variant_unified_descriptor, + TSUITE_EVENT_KEY_DOWN_STR, _desc->key_down_key_up); + + EET_DATA_DESCRIPTOR_ADD_MAPPING(_desc->variant_unified_descriptor, + TSUITE_EVENT_KEY_UP_STR, _desc->key_down_key_up); + + EET_DATA_DESCRIPTOR_ADD_MAPPING(_desc->variant_unified_descriptor, + TSUITE_EVENT_KEY_DOWN_WITH_KEYCODE_STR, _desc->key_down_key_up_with_keycode); + + EET_DATA_DESCRIPTOR_ADD_MAPPING(_desc->variant_unified_descriptor, + TSUITE_EVENT_KEY_UP_WITH_KEYCODE_STR, _desc->key_down_key_up_with_keycode); + + EET_DATA_DESCRIPTOR_ADD_MAPPING(_desc->variant_unified_descriptor, + TSUITE_EVENT_TAKE_SHOT_STR, _desc->take_screenshot); + + + EET_DATA_DESCRIPTOR_ADD_VARIANT(_desc->variant_descriptor, + Variant_st, "data", data, t, _desc->variant_unified_descriptor); + + EET_DATA_DESCRIPTOR_ADD_BASIC(_desc->lists_descriptor, + Lists_st, "first_timestamp", first_timestamp, EET_T_UINT); + EET_DATA_DESCRIPTOR_ADD_LIST(_desc->lists_descriptor, + Lists_st, "variant_list", variant_list, _desc->variant_descriptor); + + return _desc; +} + +static void +_data_descriptors_shutdown(void) +{ + if (_desc) + { + eet_data_descriptor_free(_desc->mouse_in_mouse_out); + eet_data_descriptor_free(_desc->mouse_down_mouse_up); + eet_data_descriptor_free(_desc->mouse_move); + eet_data_descriptor_free(_desc->mouse_wheel); + eet_data_descriptor_free(_desc->multi_event); + eet_data_descriptor_free(_desc->multi_move); + eet_data_descriptor_free(_desc->key_down_key_up); + eet_data_descriptor_free(_desc->key_down_key_up_with_keycode); + eet_data_descriptor_free(_desc->take_screenshot); + eet_data_descriptor_free(_desc->lists_descriptor); + eet_data_descriptor_free(_desc->variant_descriptor); + eet_data_descriptor_free(_desc->variant_unified_descriptor); + + free(_desc); + _desc = NULL; + /* FIXME: Should probably only init and shutdown once */ + } +} + +EAPI Exactness_Unit * +legacy_rec_file_read(const char *filename) +{ + Lists_st *vr_list; + Eina_List *itr; + Variant_st *v, *prev_v = NULL; + Exactness_Unit *unit = NULL; + Eet_File *fp = eet_open(filename, EET_FILE_MODE_READ); + if (!fp) + { + printf("Failed to open input file <%s>.\n", filename); + return NULL; + } + + /* Read events list */ + _data_descriptors_init(); + vr_list = eet_data_read(fp, _desc->lists_descriptor, CACHE_FILE_ENTRY); + eet_close(fp); + _data_descriptors_shutdown(); + + unit = calloc(1, sizeof(*unit)); + + EINA_LIST_FOREACH(vr_list->variant_list, itr, v) + { + Exactness_Action *act = calloc(1, sizeof(*act)); + Tsuite_Event_Type old_type = _event_mapping_type_get(v->t.type); + unsigned int vtm = _evt_time_get(0, v); + if (!vtm) continue; + switch (old_type) + { + case TSUITE_EVENT_MOUSE_IN: + { + mouse_in_mouse_out *d_i = v->data; + act->type = EXACTNESS_ACTION_MOUSE_IN; + act->n_evas = d_i->n_evas; + break; + } + case TSUITE_EVENT_MOUSE_OUT: + { + mouse_in_mouse_out *d_i = v->data; + act->type = EXACTNESS_ACTION_MOUSE_OUT; + act->n_evas = d_i->n_evas; + break; + } + case TSUITE_EVENT_MOUSE_DOWN: + case TSUITE_EVENT_MOUSE_UP: + { + mouse_down_mouse_up *d_i = v->data; + Exactness_Action_Multi_Event *d_o = calloc(1, sizeof(*d_o)); + d_o->b = d_i->b; + d_o->flags = d_i->flags; + if (old_type == TSUITE_EVENT_MOUSE_DOWN) + act->type = EXACTNESS_ACTION_MULTI_DOWN; + else + act->type = EXACTNESS_ACTION_MULTI_UP; + act->n_evas = d_i->n_evas; + act->data = d_o; + break; + } + case TSUITE_EVENT_MOUSE_MOVE: + { + mouse_move *d_i = v->data; + Exactness_Action_Multi_Move *d_o = calloc(1, sizeof(*d_o)); + d_o->x = d_i->x; + d_o->y = d_i->y; + act->type = EXACTNESS_ACTION_MULTI_MOVE; + act->n_evas = d_i->n_evas; + act->data = d_o; + break; + } + case TSUITE_EVENT_MOUSE_WHEEL: + { + mouse_wheel *d_i = v->data; + Exactness_Action_Mouse_Wheel *d_o = calloc(1, sizeof(*d_o)); + d_o->direction = d_i->direction; + d_o->z = d_i->z; + act->type = EXACTNESS_ACTION_MOUSE_WHEEL; + act->n_evas = d_i->n_evas; + act->data = d_o; + break; + } + case TSUITE_EVENT_MULTI_DOWN: + case TSUITE_EVENT_MULTI_UP: + { + multi_event *d_i = v->data; + Exactness_Action_Multi_Event *d_o = calloc(1, sizeof(*d_o)); + d_o->d = d_i->d; + d_o->b = d_i->b; + d_o->x = d_i->x; + d_o->y = d_i->y; + d_o->rad = d_i->rad; + d_o->radx = d_i->radx; + d_o->rady = d_i->rady; + d_o->pres = d_i->pres; + d_o->ang = d_i->ang; + d_o->fx = d_i->fx; + d_o->fy = d_i->fy; + d_o->flags = d_i->flags; + if (old_type == TSUITE_EVENT_MULTI_DOWN) + act->type = EXACTNESS_ACTION_MULTI_DOWN; + else + act->type = EXACTNESS_ACTION_MULTI_UP; + act->n_evas = d_i->n_evas; + act->data = d_o; + break; + } + case TSUITE_EVENT_MULTI_MOVE: + { + multi_move *d_i = v->data; + Exactness_Action_Multi_Move *d_o = calloc(1, sizeof(*d_o)); + d_o->d = d_i->d; + d_o->x = d_i->x; + d_o->y = d_i->y; + d_o->rad = d_i->rad; + d_o->radx = d_i->radx; + d_o->rady = d_i->rady; + d_o->pres = d_i->pres; + d_o->ang = d_i->ang; + d_o->fx = d_i->fx; + d_o->fy = d_i->fy; + act->type = EXACTNESS_ACTION_MULTI_MOVE; + act->n_evas = d_i->n_evas; + act->data = d_o; + break; + } + case TSUITE_EVENT_KEY_DOWN: + case TSUITE_EVENT_KEY_UP: + { + key_down_key_up *d_i = v->data; + Exactness_Action_Key_Down_Up *d_o = calloc(1, sizeof(*d_o)); + d_o->keyname = d_i->keyname; + d_o->key = d_i->key; + d_o->string = d_i->string; + d_o->compose = d_i->compose; + if (old_type == TSUITE_EVENT_KEY_DOWN) + act->type = EXACTNESS_ACTION_KEY_DOWN; + else + act->type = EXACTNESS_ACTION_KEY_UP; + act->n_evas = d_i->n_evas; + act->data = d_o; + break; + } + case TSUITE_EVENT_KEY_DOWN_WITH_KEYCODE: + case TSUITE_EVENT_KEY_UP_WITH_KEYCODE: + { + key_down_key_up_with_keycode *d_i = v->data; + Exactness_Action_Key_Down_Up *d_o = calloc(1, sizeof(*d_o)); + d_o->keyname = d_i->keyname; + d_o->key = d_i->key; + d_o->string = d_i->string; + d_o->compose = d_i->compose; + d_o->keycode = d_i->keycode; + if (old_type == TSUITE_EVENT_KEY_DOWN_WITH_KEYCODE) + act->type = EXACTNESS_ACTION_KEY_DOWN; + else + act->type = EXACTNESS_ACTION_KEY_UP; + act->n_evas = d_i->n_evas; + act->data = d_o; + break; + } + case TSUITE_EVENT_TAKE_SHOT: + { + take_screenshot *d_i = v->data; + act->type = EXACTNESS_ACTION_TAKE_SHOT; + act->n_evas = d_i->n_evas; + break; + } + default: break; + } + if (!prev_v) + { + if (vr_list->first_timestamp) + act->delay_ms = _evt_time_get(0, v) - vr_list->first_timestamp; + else + act->delay_ms = 0; + } + else + { + if (vtm > _evt_time_get(0, prev_v)) + act->delay_ms = vtm - _evt_time_get(0, prev_v); + else act->delay_ms = 0; + } + unit->actions = eina_list_append(unit->actions, act); + prev_v = v; + } +#ifdef DEBUG_TSUITE + printf("%s number of actions in the scenario <%d>\n", __func__, eina_list_count(unit->actions)); +#endif + _free_events(vr_list); + + return unit; +} + diff --git a/src/lib/exactness/meson.build b/src/lib/exactness/meson.build new file mode 100644 index 0000000000..82929b301d --- /dev/null +++ b/src/lib/exactness/meson.build @@ -0,0 +1,31 @@ +exactness_pub_deps = [eina, evas] +exactness_deps = [] + +exactness_src = [ + 'Exactness.h', + 'exactness_private.h', + 'legacy_file.c', + 'unit.c' +] + +exactness_lib = library('exactness', exactness_src, + dependencies: [ eina, eet, evas ], + install: true, + version : meson.project_version() +) + +exactness = declare_dependency( + include_directories: include_directories('.'), + link_with : exactness_lib, + dependencies: [ eina, evas, eet ], +) + +exactness_include_dir = join_paths(dir_data, 'exactness', 'include') + +exactness_header_src = [ +'Exactness.h', +] + +install_headers(exactness_header_src, + install_dir : dir_package_include +) diff --git a/src/lib/exactness/unit.c b/src/lib/exactness/unit.c new file mode 100644 index 0000000000..51f3453332 --- /dev/null +++ b/src/lib/exactness/unit.c @@ -0,0 +1,424 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#include "Exactness.h" +#include "exactness_private.h" + +typedef struct _Dummy +{ +} _Dummy; +/* +static Eet_Data_Descriptor * +_mouse_in_out_desc_make(void) +{ + Eet_Data_Descriptor_Class eddc; + Eet_Data_Descriptor *_d; + EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eddc, Exactness_Action_Mouse_In_Out); + _d = eet_data_descriptor_stream_new(&eddc); + + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, Exactness_Action_Mouse_In_Out, "n_evas", n_evas, EET_T_INT); + + return _d; +} +*/ + +static Eet_Data_Descriptor * +_mouse_wheel_desc_make(void) +{ + Eet_Data_Descriptor_Class eddc; + Eet_Data_Descriptor *_d; + EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eddc, Exactness_Action_Mouse_Wheel); + _d = eet_data_descriptor_stream_new(&eddc); + + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, Exactness_Action_Mouse_Wheel, "direction", direction, EET_T_INT); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, Exactness_Action_Mouse_Wheel, "z", z, EET_T_INT); + + return _d; +} + +static Eet_Data_Descriptor * +_key_down_up_desc_make(void) +{ + Eet_Data_Descriptor_Class eddc; + Eet_Data_Descriptor *_d; + EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eddc, Exactness_Action_Key_Down_Up); + _d = eet_data_descriptor_stream_new(&eddc); + + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, Exactness_Action_Key_Down_Up, "keyname", keyname, EET_T_STRING); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, Exactness_Action_Key_Down_Up, "key", key, EET_T_STRING); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, Exactness_Action_Key_Down_Up, "string", string, EET_T_STRING); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, Exactness_Action_Key_Down_Up, "compose", compose, EET_T_STRING); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, Exactness_Action_Key_Down_Up, "keycode", keycode, EET_T_INT); + + return _d; +} + +static Eet_Data_Descriptor * +_multi_event_desc_make(void) +{ + Eet_Data_Descriptor_Class eddc; + Eet_Data_Descriptor *_d; + EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eddc, Exactness_Action_Multi_Event); + _d = eet_data_descriptor_stream_new(&eddc); + + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, Exactness_Action_Multi_Event, "d", d, EET_T_UINT); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, Exactness_Action_Multi_Event, "b", b, EET_T_UINT); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, Exactness_Action_Multi_Event, "x", x, EET_T_UINT); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, Exactness_Action_Multi_Event, "y", y, EET_T_UINT); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, Exactness_Action_Multi_Event, "rad", rad, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, Exactness_Action_Multi_Event, "radx", radx, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, Exactness_Action_Multi_Event, "rady", rady, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, Exactness_Action_Multi_Event, "pres", pres, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, Exactness_Action_Multi_Event, "ang", ang, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, Exactness_Action_Multi_Event, "fx", fx, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, Exactness_Action_Multi_Event, "fy", fy, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, Exactness_Action_Multi_Event, "flags", flags, EET_T_INT); + + return _d; +} + +static Eet_Data_Descriptor * +_multi_move_desc_make(void) +{ + Eet_Data_Descriptor_Class eddc; + Eet_Data_Descriptor *_d; + EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eddc, Exactness_Action_Multi_Move); + _d = eet_data_descriptor_stream_new(&eddc); + + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, Exactness_Action_Multi_Move, "d", d, EET_T_UINT); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, Exactness_Action_Multi_Move, "x", x, EET_T_UINT); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, Exactness_Action_Multi_Move, "y", y, EET_T_UINT); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, Exactness_Action_Multi_Move, "rad", rad, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, Exactness_Action_Multi_Move, "radx", radx, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, Exactness_Action_Multi_Move, "rady", rady, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, Exactness_Action_Multi_Move, "pres", pres, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, Exactness_Action_Multi_Move, "ang", ang, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, Exactness_Action_Multi_Move, "fx", fx, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, Exactness_Action_Multi_Move, "fy", fy, EET_T_DOUBLE); + + return _d; +} + +static Eet_Data_Descriptor * +_efl_event_desc_make(void) +{ + Eet_Data_Descriptor_Class eddc; + Eet_Data_Descriptor *_d; + EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eddc, Exactness_Action_Efl_Event); + _d = eet_data_descriptor_stream_new(&eddc); + + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, Exactness_Action_Efl_Event, "wdg_name", wdg_name, EET_T_STRING); + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, Exactness_Action_Efl_Event, "event_name", event_name, EET_T_STRING); + + return _d; +} + +static Eet_Data_Descriptor * +_click_on_desc_make(void) +{ + Eet_Data_Descriptor_Class eddc; + Eet_Data_Descriptor *_d; + EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eddc, Exactness_Action_Click_On); + _d = eet_data_descriptor_stream_new(&eddc); + + EET_DATA_DESCRIPTOR_ADD_BASIC(_d, Exactness_Action_Click_On, "wdg_name", wdg_name, EET_T_STRING); + + return _d; +} + +static Eet_Data_Descriptor * +_dummy_desc_make(void) +{ + Eet_Data_Descriptor_Class eddc; + Eet_Data_Descriptor *_d; + EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eddc, _Dummy); + _d = eet_data_descriptor_stream_new(&eddc); + + return _d; +} + +/* !!! SAME ORDER AS Exactness_Action_Type */ +static const char *_mapping[] = +{ + "", + "exactness_action_mouse_in", + "exactness_action_mouse_out", + "exactness_action_mouse_wheel", + "exactness_action_multi_down", + "exactness_action_multi_up", + "exactness_action_multi_move", + "exactness_action_key_down", + "exactness_action_key_up", + "exactness_action_take_shot", + "exactness_action_efl_event", + "exactness_action_click_on", + "exactness_action_stabilize" +}; + +const char * +_exactness_action_type_to_string_get(Exactness_Action_Type type) +{ + if (type <= EXACTNESS_ACTION_LAST) return _mapping[type]; + return NULL; +} + +static const char * +_variant_type_get(const void *data, Eina_Bool *unknow) +{ + const Exactness_Action *act = data; + + if (unknow) *unknow = EINA_FALSE; + if (act->type <= EXACTNESS_ACTION_LAST) return _mapping[act->type]; + + return NULL; +} + +static Eina_Bool +_variant_type_set(const char *type, + void *data, + Eina_Bool unknow EINA_UNUSED) +{ + int i; + Exactness_Action *act = data; + for (i = 0; i <= EXACTNESS_ACTION_LAST; i++) + { + if (!strcmp(_mapping[i], type)) act->type = i; + } + return EINA_TRUE; +} + +static Eet_Data_Descriptor * +_unit_desc_make(void) +{ + Eet_Data_Descriptor_Class eddc; + static Eet_Data_Descriptor *unit_d = NULL; + static Eet_Data_Descriptor *action_d = NULL, *action_variant_d = NULL; + static Eet_Data_Descriptor *objs_d = NULL; + static Eet_Data_Descriptor *obj_d = NULL; + if (!obj_d) + { + EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eddc, Exactness_Object); + obj_d = eet_data_descriptor_stream_new(&eddc); + EET_DATA_DESCRIPTOR_ADD_BASIC(obj_d, Exactness_Object, "kl_name", kl_name, EET_T_STRING); + EET_DATA_DESCRIPTOR_ADD_BASIC(obj_d, Exactness_Object, "id", id, EET_T_ULONG_LONG); + EET_DATA_DESCRIPTOR_ADD_BASIC(obj_d, Exactness_Object, "parent_id", parent_id, EET_T_ULONG_LONG); + /* Evas stuff */ + EET_DATA_DESCRIPTOR_ADD_BASIC(obj_d, Exactness_Object, "x", x, EET_T_INT); + EET_DATA_DESCRIPTOR_ADD_BASIC(obj_d, Exactness_Object, "y", y, EET_T_INT); + EET_DATA_DESCRIPTOR_ADD_BASIC(obj_d, Exactness_Object, "w", w, EET_T_INT); + EET_DATA_DESCRIPTOR_ADD_BASIC(obj_d, Exactness_Object, "h", h, EET_T_INT); + + EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eddc, Exactness_Objects); + objs_d = eet_data_descriptor_stream_new(&eddc); + EET_DATA_DESCRIPTOR_ADD_LIST(objs_d, Exactness_Objects, "objs", objs, obj_d); + } + if (!unit_d) + { + Eet_Data_Descriptor *code_d = NULL; + EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eddc, Exactness_Source_Code); + code_d = eet_data_descriptor_stream_new(&eddc); + EET_DATA_DESCRIPTOR_ADD_BASIC(code_d, Exactness_Source_Code, "language", language, EET_T_STRING); + EET_DATA_DESCRIPTOR_ADD_BASIC(code_d, Exactness_Source_Code, "content", content, EET_T_STRING); + EET_DATA_DESCRIPTOR_ADD_BASIC(code_d, Exactness_Source_Code, "command", command, EET_T_STRING); + + EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eddc, Exactness_Action); + action_d = eet_data_descriptor_stream_new(&eddc); + + eddc.version = EET_DATA_DESCRIPTOR_CLASS_VERSION; + eddc.func.type_get = _variant_type_get; + eddc.func.type_set = _variant_type_set; + action_variant_d = eet_data_descriptor_stream_new(&eddc); + + EET_DATA_DESCRIPTOR_ADD_MAPPING(action_variant_d, + _mapping[EXACTNESS_ACTION_MOUSE_IN], _dummy_desc_make()); + EET_DATA_DESCRIPTOR_ADD_MAPPING(action_variant_d, + _mapping[EXACTNESS_ACTION_MOUSE_OUT], _dummy_desc_make()); + EET_DATA_DESCRIPTOR_ADD_MAPPING(action_variant_d, + _mapping[EXACTNESS_ACTION_MOUSE_WHEEL], _mouse_wheel_desc_make()); + EET_DATA_DESCRIPTOR_ADD_MAPPING(action_variant_d, + _mapping[EXACTNESS_ACTION_MULTI_DOWN], _multi_event_desc_make()); + EET_DATA_DESCRIPTOR_ADD_MAPPING(action_variant_d, + _mapping[EXACTNESS_ACTION_MULTI_UP], _multi_event_desc_make()); + EET_DATA_DESCRIPTOR_ADD_MAPPING(action_variant_d, + _mapping[EXACTNESS_ACTION_MULTI_MOVE], _multi_move_desc_make()); + EET_DATA_DESCRIPTOR_ADD_MAPPING(action_variant_d, + _mapping[EXACTNESS_ACTION_KEY_DOWN], _key_down_up_desc_make()); + EET_DATA_DESCRIPTOR_ADD_MAPPING(action_variant_d, + _mapping[EXACTNESS_ACTION_KEY_UP], _key_down_up_desc_make()); + EET_DATA_DESCRIPTOR_ADD_MAPPING(action_variant_d, + _mapping[EXACTNESS_ACTION_TAKE_SHOT], _dummy_desc_make()); + EET_DATA_DESCRIPTOR_ADD_MAPPING(action_variant_d, + _mapping[EXACTNESS_ACTION_EFL_EVENT], _efl_event_desc_make()); + EET_DATA_DESCRIPTOR_ADD_MAPPING(action_variant_d, + _mapping[EXACTNESS_ACTION_CLICK_ON], _click_on_desc_make()); + EET_DATA_DESCRIPTOR_ADD_MAPPING(action_variant_d, + _mapping[EXACTNESS_ACTION_STABILIZE], _dummy_desc_make()); + + EET_DATA_DESCRIPTOR_ADD_BASIC(action_d, Exactness_Action, "n_evas", n_evas, EET_T_UINT); + EET_DATA_DESCRIPTOR_ADD_BASIC(action_d, Exactness_Action, "delay_ms", delay_ms, EET_T_UINT); + EET_DATA_DESCRIPTOR_ADD_VARIANT(action_d, Exactness_Action, "data", data, type, action_variant_d); + + /* Exactness_Unit */ + EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eddc, Exactness_Unit); + unit_d = eet_data_descriptor_stream_new(&eddc); + EET_DATA_DESCRIPTOR_ADD_LIST(unit_d, Exactness_Unit, "actions", actions, action_d); + EET_DATA_DESCRIPTOR_ADD_LIST(unit_d, Exactness_Unit, "objs", objs, objs_d); + EET_DATA_DESCRIPTOR_ADD_LIST(unit_d, Exactness_Unit, "codes", codes, code_d); + EET_DATA_DESCRIPTOR_ADD_BASIC(unit_d, Exactness_Unit, "fonts_path", fonts_path, EET_T_STRING); + EET_DATA_DESCRIPTOR_ADD_BASIC(unit_d, Exactness_Unit, "nb_shots", nb_shots, EET_T_UINT); + } + + return unit_d; +} +/* END Event struct descriptors */ + +EAPI Exactness_Unit * +exactness_unit_file_read(const char *filename) +{ + int i; + Eina_List *itr, *itr2; + Exactness_Objects *e_objs; + Exactness_Object *e_obj, *e_parent; + Exactness_Unit *unit = NULL; + Eet_File *file; + eet_init(); + file = eet_open(filename, EET_FILE_MODE_READ); + if (!file) + { + fprintf(stderr, "Impossible to extract EET from %s\n", filename); + return NULL; + } + unit = eet_data_read(file, _unit_desc_make(), "cache"); + for (i = 0; i < unit->nb_shots; i++) + { + char entry[32]; + Exactness_Image *ex_img = malloc(sizeof(*ex_img)); + sprintf(entry, "images/%d", i + 1); + ex_img->pixels = eet_data_image_read(file, entry, + &ex_img->w, &ex_img->h, NULL, + NULL, NULL, NULL); + unit->imgs = eina_list_append(unit->imgs, ex_img); + } + EINA_LIST_FOREACH(unit->objs, itr, e_objs) + { + Eina_Hash *hash = eina_hash_pointer_new(NULL); + EINA_LIST_FOREACH(e_objs->objs, itr2, e_obj) + { + eina_hash_set(hash, &(e_obj->id), e_obj); + } + EINA_LIST_FOREACH(e_objs->objs, itr2, e_obj) + { + if (!e_obj->parent_id) + e_objs->main_objs = eina_list_append(e_objs->main_objs, e_obj); + else + { + e_parent = eina_hash_find(hash, &(e_obj->parent_id)); + if (e_parent) e_parent->children = eina_list_append(e_parent->children, e_obj); + } + } + eina_hash_free(hash); + } + eet_close(file); + eet_shutdown(); + return unit; +} + +EAPI Eina_Bool +exactness_unit_file_write(Exactness_Unit *unit, const char *filename) +{ + Eina_List *itr; + Exactness_Image *ex_img; + Eet_File *file; + int i = 1; + eet_init(); + file = eet_open(filename, EET_FILE_MODE_WRITE); + eet_data_write(file, _unit_desc_make(), "cache", unit, EINA_TRUE); + EINA_LIST_FOREACH(unit->imgs, itr, ex_img) + { + char entry[32]; + sprintf(entry, "images/%d", i++); + eet_data_image_write(file, entry, + ex_img->pixels, ex_img->w, ex_img->h, 0xFF, + 0, 100, EET_IMAGE_LOSSLESS); + } + eet_close(file); + eet_shutdown(); + return EINA_TRUE; +} + +EAPI Eina_Bool +exactness_image_compare(Exactness_Image *img1, Exactness_Image *img2, Exactness_Image **imgO) +{ + unsigned int w, h; + int *pxs1, *pxs2, *pxsO = NULL; + unsigned int w1 = img1 ? img1->w : 0, h1 = img1 ? img1->h : 0; + unsigned int w2 = img2 ? img2->w : 0, h2 = img2 ? img2->h : 0; + unsigned int wO = MAX(w1, w2); + unsigned int hO = MAX(h1, h2); + Eina_Bool ret = EINA_FALSE; + if (imgO) *imgO = NULL; + if (!wO || !hO) return EINA_FALSE; + + pxs1 = img1 ? img1->pixels : NULL; + pxs2 = img2 ? img2->pixels : NULL; + if (imgO) pxsO = malloc(wO * hO * 4); + + for (w = 0; w < wO; w++) + { + for (h = 0; h < hO; h++) + { + Eina_Bool valid1 = img1 ? w < w1 && h < h1 : EINA_FALSE; + Eina_Bool valid2 = img2 ? w < w2 && h < h2 : EINA_FALSE; + int px1 = valid1 ? pxs1[h * w1 + w] : 0; + int px2 = valid2 ? pxs2[h * w2 + w] : 0; + int r1 = (px1 & 0x00FF0000) >> 16; + int r2 = (px2 & 0x00FF0000) >> 16; + int g1 = (px1 & 0x0000FF00) >> 8; + int g2 = (px2 & 0x0000FF00) >> 8; + int b1 = (px1 & 0x000000FF); + int b2 = (px2 & 0x000000FF); + int new_r, new_g, new_b; + if (valid1 || valid2) + { + if (px1 != px2) + { + new_r = 0xFF; + new_g = ((g1 + g2) >> 1) >> 2; + new_b = ((b1 + b2) >> 1) >> 2; + ret = EINA_TRUE; + } + else + { + new_r = (((r1 + r2) >> 1) >> 2) + 0xC0; + new_g = (((g1 + g2) >> 1) >> 2) + 0xC0; + new_b = (((b1 + b2) >> 1) >> 2) + 0xC0; + } + } + else + { + new_r = new_g = new_b = 0x0; + } + if (pxsO) pxsO[h * wO + w] = 0xFF000000 | new_r << 16 | new_g << 8 | new_b; + } + } + if (imgO) + { + Exactness_Image *imgR = calloc(1, sizeof(Exactness_Image)); + *imgO = imgR; + imgR->w = wO; + imgR->h = hO; + imgR->pixels = pxsO; + } + return ret; +} + +EAPI void exactness_image_free(Exactness_Image *img) +{ + if (!img) return; + free(img->pixels); + free(img); +} +