#define _GNU_SOURCE 1 #include #include #include #include #include #include #include #include "config.h" #ifndef EFL_EO_API_SUPPORT #define EFL_EO_API_SUPPORT #endif #include #include #include #include #include #include #include #include #include "tsuite_file_data.h" #include "exactness_private.h" #define MAX_PATH 1024 #define IMAGE_FILENAME_EXT ".png" typedef enum { FTYPE_UNKNOWN, FTYPE_DIR, FTYPE_REC = FTYPE_DIR, FTYPE_EXU } 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 int _ignore_evas_creation = 0; static Eina_List *_evas_list = NULL; static Eina_List *_cur_event_list = NULL; static unsigned int _last_event_time = 0; static int _cur_shot_id = 0; static Eina_Bool _scan_objects = 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 void _shot_do(Evas *e) { Ecore_Evas *ee_orig; unsigned int *pixels; int w, h; Eina_Bool alpha; if (!e) return; ee_orig = ecore_evas_ecore_evas_get(e); ecore_evas_manual_render(ee_orig); pixels = (void *)ecore_evas_buffer_pixels_get(ee_orig); if (!pixels) return; alpha = ecore_evas_alpha_get(ee_orig); ecore_evas_geometry_get(ee_orig, NULL, NULL, &w, &h); if ((w < 1) || (h < 1)) return; if (_dest_type == FTYPE_DIR) { int dir_name_len; char *filename; Evas_Object *o; Ecore_Evas *ee; _ignore_evas_creation++; ee = ecore_evas_buffer_new(1, 1); _ignore_evas_creation--; o = evas_object_image_add(ecore_evas_get(ee)); evas_object_image_alpha_set(o, alpha); evas_object_image_size_set(o, w, h); evas_object_image_data_set(o, pixels); 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); _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); ecore_evas_free(ee); } else if (_dest_type == FTYPE_EXU) { Exactness_Image *ex_img = malloc(sizeof(*ex_img)); int nb_bytes = w * h * 4; ex_img->w = w; ex_img->h = h; ex_img->alpha = alpha; ex_img->pixels = malloc(nb_bytes); memcpy(ex_img->pixels, pixels, nb_bytes); _dest_unit->imgs = eina_list_append(_dest_unit->imgs, ex_img); _dest_unit->nb_shots++; _printf(1, "Shot taken (in %s).\n", _dest); if (_scan_objects) { 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 Eina_Bool _feed_event(void *data EINA_UNUSED) { time_t evt_time; static Evas_Object *rect = NULL; if (_verbose && !rect) { rect = evas_object_rectangle_add(eina_list_data_get(_evas_list)); 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); } Variant_st *v = eina_list_data_get(_cur_event_list); switch(tsuite_event_mapping_type_get(v->t.type)) { case TSUITE_EVENT_MOUSE_IN: { mouse_in_mouse_out *t = v->data; Eo *e = eina_list_nth(_evas_list, t->n_evas); evt_time = t->timestamp; _printf(1, "Mouse in\n"); _printf(2, "%s evas_event_feed_mouse_in timestamp=<%u> t->n_evas=<%d>\n", __func__, t->timestamp, t->n_evas); if (e) evas_event_feed_mouse_in(e, time(NULL), NULL); break; } case TSUITE_EVENT_MOUSE_OUT: { mouse_in_mouse_out *t = v->data; Eo *e = eina_list_nth(_evas_list, t->n_evas); evt_time = t->timestamp; _printf(1, "Mouse out\n"); _printf(2, "%s evas_event_feed_mouse_out timestamp=<%u> t->n_evas=<%d>\n", __func__, t->timestamp,t->n_evas); if (e) evas_event_feed_mouse_out(e, time(NULL), NULL); break; } case TSUITE_EVENT_MOUSE_DOWN: { mouse_down_mouse_up *t = v->data; Eo *e = eina_list_nth(_evas_list, t->n_evas); evt_time = t->timestamp; _printf(1, "Mouse down\n"); _printf(2, "%s evas_event_feed_mouse_down timestamp=<%u> t->n_evas=<%d>\n", __func__, t->timestamp, t->n_evas); if (rect) evas_object_color_set(rect, 255, 255, 0, 255); if (e) evas_event_feed_mouse_down(e, t->b, t->flags, time(NULL), NULL); break; } case TSUITE_EVENT_MOUSE_UP: { mouse_down_mouse_up *t = v->data; Eo *e = eina_list_nth(_evas_list, t->n_evas); evt_time = t->timestamp; _printf(1, "Mouse up\n"); _printf(2, "%s evas_event_feed_mouse_up timestamp=<%u> t->n_evas=<%d>\n", __func__, t->timestamp,t->n_evas); 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); break; } case TSUITE_EVENT_MOUSE_MOVE: { mouse_move *t = v->data; Eo *e = eina_list_nth(_evas_list, t->n_evas); evt_time = t->timestamp; _printf(2, "%s evas_event_feed_mouse_move (x,y)=(%d,%d) timestamp=<%u> t->n_evas=<%d>\n", __func__, t->x, t->y, t->timestamp,t->n_evas); 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); } break; } case TSUITE_EVENT_MOUSE_WHEEL: { mouse_wheel *t = v->data; Eo *e = eina_list_nth(_evas_list, t->n_evas); evt_time = t->timestamp; _printf(1, "Mouse wheel\n"); _printf(2, "%s evas_event_feed_mouse_wheel timestamp=<%u> t->n_evas=<%d>\n", __func__, t->timestamp, t->n_evas); if (e) evas_event_feed_mouse_wheel(e, t->direction, t->z, time(NULL), NULL); break; } case TSUITE_EVENT_MULTI_DOWN: { multi_event *t = v->data; Eo *e = eina_list_nth(_evas_list, t->n_evas); evt_time = t->timestamp; _printf(2, "%s evas_event_feed_multi_down timestamp=<%u>, t->n_evas=<%d>\n", __func__, t->timestamp,t->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 TSUITE_EVENT_MULTI_UP: { multi_event *t = v->data; Eo *e = eina_list_nth(_evas_list, t->n_evas); evt_time = t->timestamp; _printf(2, "%s evas_event_feed_multi_up timestamp=<%u> t->n_evas=<%d>\n", __func__, t->timestamp,t->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 TSUITE_EVENT_MULTI_MOVE: { multi_move *t = v->data; Eo *e = eina_list_nth(_evas_list, t->n_evas); evt_time = t->timestamp; _printf(2, "%s evas_event_feed_multi_move timestamp=<%u> t->n_evas=<%d>\n", __func__, t->timestamp, t->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 TSUITE_EVENT_KEY_DOWN: { key_down_key_up *t = v->data; Eo *e = eina_list_nth(_evas_list, t->n_evas); evt_time = t->timestamp; _printf(2, "%s evas_event_feed_key_down timestamp=<%u> t->n_evas=<%d>\n", __func__, t->timestamp, t->n_evas); if (e) evas_event_feed_key_down(e, t->keyname, t->key, t->string, t->compose, time(NULL), NULL); break; } case TSUITE_EVENT_KEY_UP: { key_down_key_up *t = v->data; Eo *e = eina_list_nth(_evas_list, t->n_evas); evt_time = t->timestamp; _printf(2, "%s evas_event_feed_key_up timestamp=<%u> t->n_evas=<%d>\n", __func__, t->timestamp, t->n_evas); if (e) evas_event_feed_key_up(e, t->keyname, t->key, t->string, t->compose, time(NULL), NULL); break; } case TSUITE_EVENT_KEY_DOWN_WITH_KEYCODE: { key_down_key_up_with_keycode *t = v->data; Eo *e = eina_list_nth(_evas_list, t->n_evas); evt_time = t->timestamp; _printf(2, "%s evas_event_feed_key_down_with_keycode timestamp=<%u> t->n_evas=<%d> t->keycode=<%u>\n", __func__, t->timestamp, t->n_evas, t->keycode); 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 TSUITE_EVENT_KEY_UP_WITH_KEYCODE: { key_down_key_up_with_keycode *t = v->data; Eo *e = eina_list_nth(_evas_list, t->n_evas); evt_time = t->timestamp; _printf(2, "%s evas_event_feed_key_up_with_keycode timestamp=<%u> t->n_evas=<%d> t->keycode=<%u>\n", __func__, t->timestamp, t->n_evas, t->keycode); if (e) evas_event_feed_key_up(e, t->keyname, t->key, t->string, t->compose, time(NULL), NULL); break; } case TSUITE_EVENT_TAKE_SHOT: { take_screenshot *t = v->data; Eo *e = eina_list_nth(_evas_list, t->n_evas); evt_time = t->timestamp; _printf(2, "%s take shot timestamp=<%u> t->n_evas=<%d>\n", __func__, t->timestamp, t->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; } default: /* All non-input events are not handeled */ evt_time = _last_event_time; break; } double timer_time; _cur_event_list = eina_list_next(_cur_event_list); if (!_cur_event_list) { /* Finished reading all events */ ecore_main_loop_quit(); return ECORE_CALLBACK_CANCEL; } _last_event_time = evt_time; unsigned int current_event_time = evt_time_get(evt_time, eina_list_data_get(_cur_event_list)); if (current_event_time < _last_event_time) /* Could happen with refeed event */ current_event_time = _last_event_time; _printf(2, " %s _last_event_time=<%u> current_event_time=<%u>\n", __func__, _last_event_time, current_event_time); timer_time = (current_event_time - _last_event_time) / 1000.0; if (!_last_event_time) timer_time = 0.0; _printf(2, " %s timer_time=<%f>\n", __func__, timer_time); ecore_timer_add(timer_time, _feed_event, NULL); return ECORE_CALLBACK_CANCEL; } static Eina_Bool _src_open() { unsigned int first_tm = 0; double diff_time = 0; /* Time to wait before feeding the first event */ _printf(2, "<%s> Source file is <%s>\n", __func__, _src_filename); if (_src_type == FTYPE_EXU) { _src_unit = unit_eet_read(_src_filename); } else if (_src_type == FTYPE_REC) { _src_unit = calloc(1, sizeof(*_src_unit)); Lists_st *events_list = read_events(_src_filename, NULL); if (!events_list) { free(_src_unit); _src_unit = NULL; } else { _src_unit->events = events_list->variant_list; first_tm = events_list->first_timestamp; } } if (!_src_unit) return EINA_FALSE; _cur_event_list = _src_unit->events; Variant_st *v = eina_list_data_get(_cur_event_list); /* Calculate the time to wait before feeding the first event */ unsigned int current_event_time = evt_time_get(0, v); if (current_event_time > first_tm) diff_time = (current_event_time - first_tm) / 1000.0; _printf(2, "%s first_time_stamp=<%u> current_event_time=<%u>\n", __func__, first_tm, current_event_time); if (diff_time) { _printf(2, " Waiting <%f>\n", diff_time); ecore_timer_add(diff_time, _feed_event, NULL); } else { _feed_event(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 (_src_open()) { 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; } } else 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 *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 Evas * _my_evas_new(int w EINA_UNUSED, int h EINA_UNUSED) { Evas *e; if (!_evas_new) return NULL; e = _evas_new(); if (e && !_ignore_evas_creation) { _printf(1, "New Evas\n"); _evas_list = eina_list_append(_evas_list, e); } 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_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; Eina_Bool show_on_screen = EINA_FALSE; Eina_Bool want_quit = 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_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) { fprintf(stderr, "no test file specified\n"); goto end; } else { _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 (!show_on_screen) setenv("ELM_ENGINE", "buffer", 1); if (!argv[opt_args]) { fprintf(stderr, "no program specified\nUse -h for more information\n"); goto end; } if (_dest_type == FTYPE_EXU) _dest_unit = calloc(1, sizeof(*_dest_unit)); efl_object_init(); evas_init(); /* Replace the current command line to hide the Exactness part */ int len = argv[argc - 1] + strlen(argv[argc - 1]) - argv[opt_args]; memcpy(argv[0], argv[opt_args], len); memset(argv[0] + len, 0, _POSIX_PATH_MAX - len); for (int 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"); ecore_evas_callback_new_set(_my_evas_new); pret = _prg_invoke(_prg_full_path_guess(argv[0]), argc - opt_args, argv); if (_dest && _dest_unit) { _dest_unit->events = _src_unit->events; unit_eet_write(_dest_unit, _dest); } end: eet_shutdown(); eina_shutdown(); return pret; }