#define _GNU_SOURCE 1 #define EFL_EO_API_SUPPORT #define EFL_BETA_API_SUPPORT #include #include #include #include #include #include #include #include #include #include #include #include "tsuite_file_data.h" #include "exactness_private.h" #define TSUITE_MAX_PATH 1024 #define IMAGE_FILENAME_EXT ".png" #define OBJECTS_FILENAME_EXT ".eet" struct _evas_hook_setting { char *dest_dir; char *test_name; char *file_name; Eina_Bool verbose; Eina_Bool store_objects; }; typedef struct _evas_hook_setting evas_hook_setting; static Lists_st *vr_list = NULL; static evas_hook_setting *_hook_setting = NULL; static Tsuite_Data ts; static Eina_List *evas_list = NULL; /* List of Evas pointers */ static int ignore_evas_new = 0; /* Counter to know if we should ignore evas new or not. */ typedef struct { const char *kl_name; int last; } Main_Widget_Id; static Eina_List *_main_widget_ids = NULL; static Object_Info _widgets_list = {0}; static void _objects_snapshot_do(); static void _tsuite_verbosef(const char *fmt, ...) { va_list ap; if (!_hook_setting->verbose) return; va_start(ap, fmt); vprintf(fmt, ap); va_end(ap); } /** * @internal * * This function takes actual shot and saves it in PNG * @param data Tsuite_Data pointer initiated by user * @param obj Window pointer * @param obj name file name. Will use name_+serial if NULL * * @ingroup Tsuite */ static void _shot_do(char *name, Evas *e) { if (!e) return; Ecore_Evas *ee = NULL, *ee_orig; Evas_Object *o; unsigned int *pixels; int w, h,dir_name_len = 0; char *filename; if (_hook_setting->dest_dir) dir_name_len = strlen(_hook_setting->dest_dir) + 1; /* includes space of a '/' */ if (name) { filename = malloc(strlen(name) + strlen(IMAGE_FILENAME_EXT) + dir_name_len + 4); if (_hook_setting->dest_dir) sprintf(filename, "%s/", _hook_setting->dest_dir); sprintf(filename + dir_name_len, "%s%s", name, IMAGE_FILENAME_EXT); } else { filename = malloc(strlen(_hook_setting->test_name) + strlen(IMAGE_FILENAME_EXT) + dir_name_len + 8); /* also space for serial */ if (_hook_setting->dest_dir) sprintf(filename, "%s/", _hook_setting->dest_dir); sprintf(filename + dir_name_len, "%s%c%03d%s", _hook_setting->test_name, SHOT_DELIMITER, ts.serial, IMAGE_FILENAME_EXT); } _tsuite_verbosef("Shot taken (%s).\n", filename); 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) goto end; ecore_evas_geometry_get(ee_orig, NULL, NULL, &w, &h); if ((w < 1) || (h < 1)) goto end; ignore_evas_new++; ee = ecore_evas_buffer_new(1, 1); ignore_evas_new--; o = evas_object_image_add(ecore_evas_get(ee)); evas_object_image_alpha_set(o, ecore_evas_alpha_get(ee_orig)); evas_object_image_size_set(o, w, h); evas_object_image_data_set(o, pixels); if (!evas_object_image_save(o, filename, NULL, NULL)) { printf("Cannot save widget to <%s>\n", filename); } end: if (ee) { ecore_evas_free(ee); } free(filename); } static int _ecore_init_count = 0; EAPI int ecore_init(void) { int ret; int (*_ecore_init)(void) = dlsym(RTLD_NEXT, "ecore_init"); _ecore_init_count++; if ((_ecore_init_count == 1) && (!_hook_setting)) { const char *tmp; _hook_setting = calloc(1, sizeof(evas_hook_setting)); _hook_setting->dest_dir = getenv("TSUITE_DEST_DIR"); _hook_setting->test_name = getenv("TSUITE_TEST_NAME"); _hook_setting->file_name = getenv("TSUITE_FILE_NAME"); _hook_setting->store_objects = !!getenv("TSUITE_STORE_OBJECTS"); tmp = getenv("TSUITE_VERBOSE"); if (tmp) _hook_setting->verbose = atoi(tmp); #ifdef DEBUG_TSUITE printf("<%s> test_name=<%s>\n", __func__, _hook_setting->test_name); printf("<%s> dest_dir=<%s>\n", __func__, _hook_setting->dest_dir); printf("<%s> rec file is <%s>\n", __func__, _hook_setting->file_name); #endif } ret = _ecore_init(); eet_init(); return ret; } EAPI int ecore_shutdown(void) { int (*_ecore_shutdown)(void) = dlsym(RTLD_NEXT, "ecore_shutdown"); _ecore_init_count--; if (_ecore_init_count == 0) { if (_hook_setting) { vr_list = free_events(vr_list, EINA_FALSE); free(_hook_setting); _hook_setting = NULL; } if (ts.td) free(ts.td); evas_list = eina_list_free(evas_list); memset(&ts, 0, sizeof(Tsuite_Data)); } eet_shutdown(); return _ecore_shutdown(); } EAPI Evas * evas_new(void) { Evas * (*_evas_new)(void) = dlsym(RTLD_NEXT, __FUNCTION__); Evas *evas = _evas_new(); if (ignore_evas_new == 0) { evas_list = eina_list_append(evas_list, evas); #ifdef DEBUG_TSUITE printf("Appended EVAS=<%p> list size=<%d>\n", evas, eina_list_count(evas_list)); #endif } return evas; } EAPI Ecore_Evas * ecore_evas_new(const char *engine_name EINA_UNUSED, int x, int y, int w, int h, const char *extra_options) { Ecore_Evas * (*_ecore_evas_new)(const char *engine_name, int x, int y, int w, int h, const char *extra_options) = dlsym(RTLD_NEXT, __FUNCTION__); return _ecore_evas_new("buffer", x, y, w, h, extra_options); } static Eina_Bool tsuite_feed_event(void *data) { Timer_Data *td = data; time_t evt_time; if (!td) return ECORE_CALLBACK_CANCEL; static Evas_Object *rect = NULL; if (_hook_setting->verbose) { if (!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(td->current_event); 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; _tsuite_verbosef("Mouse in\n"); #ifdef DEBUG_TSUITE printf("%s evas_event_feed_mouse_in timestamp=<%u> t->n_evas=<%d>\n", __func__, t->timestamp, t->n_evas); #endif 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; _tsuite_verbosef("Mouse out\n"); #ifdef DEBUG_TSUITE printf("%s evas_event_feed_mouse_out timestamp=<%u> t->n_evas=<%d>\n", __func__, t->timestamp,t->n_evas); #endif 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; _tsuite_verbosef("Mouse down\n"); #ifdef DEBUG_TSUITE printf("%s evas_event_feed_mouse_down timestamp=<%u> t->n_evas=<%d>\n", __func__, t->timestamp, t->n_evas); #endif 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; _tsuite_verbosef("Mouse up\n"); #ifdef DEBUG_TSUITE printf("%s evas_event_feed_mouse_up timestamp=<%u> t->n_evas=<%d>\n", __func__, t->timestamp,t->n_evas); #endif 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; #ifdef DEBUG_TSUITE printf("%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); #endif 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; _tsuite_verbosef("Mouse wheel\n"); #ifdef DEBUG_TSUITE printf("%s evas_event_feed_mouse_wheel timestamp=<%u> t->n_evas=<%d>\n", __func__, t->timestamp, t->n_evas); #endif 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; #ifdef DEBUG_TSUITE printf("%s evas_event_feed_multi_down timestamp=<%u>, t->n_evas=<%d>\n", __func__, t->timestamp,t->n_evas); #endif 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; #ifdef DEBUG_TSUITE printf("%s evas_event_feed_multi_up timestamp=<%u> t->n_evas=<%d>\n", __func__, t->timestamp,t->n_evas); #endif 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; #ifdef DEBUG_TSUITE printf("%s evas_event_feed_multi_move timestamp=<%u> t->n_evas=<%d>\n", __func__, t->timestamp, t->n_evas); #endif 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; #ifdef DEBUG_TSUITE printf("%s evas_event_feed_key_down timestamp=<%u> t->n_evas=<%d>\n", __func__, t->timestamp, t->n_evas); #endif 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; #ifdef DEBUG_TSUITE printf("%s evas_event_feed_key_up timestamp=<%u> t->n_evas=<%d>\n", __func__, t->timestamp, t->n_evas); #endif 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; #ifdef DEBUG_TSUITE printf("%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); #endif 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; #ifdef DEBUG_TSUITE printf("%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); #endif 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; #ifdef DEBUG_TSUITE printf("%s take shot timestamp=<%u> t->n_evas=<%d>\n", __func__, t->timestamp, t->n_evas); #endif if (rect) evas_object_color_set(rect, 0, 0, 255, 255); ts.serial++; if (_hook_setting->dest_dir) { if (e) _shot_do(NULL, e); /* Serial name based on test-name */ if (_hook_setting->store_objects) _objects_snapshot_do(); } break; } default: /* All non-input events are not handeled */ evt_time = td->recent_event_time; break; } double timer_time; td->current_event = eina_list_next(td->current_event); if (!td->current_event) { /* Finished reading all events */ ecore_main_loop_quit(); return ECORE_CALLBACK_CANCEL; } td->recent_event_time = evt_time; unsigned int current_event_time = evt_time_get(evt_time, eina_list_data_get(td->current_event)); if (current_event_time < td->recent_event_time) /* Could happen with refeed event */ current_event_time = td->recent_event_time; #ifdef DEBUG_TSUITE printf(" %s td->recent_event_time=<%u> current_event_time=<%u>\n", __func__, td->recent_event_time, current_event_time); #endif timer_time = (current_event_time - td->recent_event_time) / 1000.0; if (!td->recent_event_time) timer_time = 0.0; #ifdef DEBUG_TSUITE printf(" %s timer_time=<%f>\n", __func__, timer_time); #endif ecore_timer_add(timer_time, tsuite_feed_event, td); return ECORE_CALLBACK_CANCEL; } EOAPI Eina_Value * efl_loop_begin(Eo *obj) { static Eina_Value * (*_foo)(Eo *) = NULL; if (!_foo) _foo = dlsym(RTLD_NEXT, __func__); if (_hook_setting->file_name) { double diff_time = 0; /* Time to wait before feeding the first event */ ts.td = calloc(1, sizeof(Timer_Data)); #ifdef DEBUG_TSUITE printf("<%s> rec file is <%s>\n", __func__, _hook_setting->file_name); #endif vr_list = read_events(_hook_setting->file_name, ts.td); if (ts.td->current_event) { /* Got first event in list, run test */ /* Calculate the time to wait before feeding the first event */ unsigned int current_event_time = evt_time_get(0, eina_list_data_get(ts.td->current_event)); if (current_event_time > vr_list->first_timestamp) diff_time = (current_event_time - vr_list->first_timestamp) / 1000.0; #ifdef DEBUG_TSUITE printf("%s first_time_stamp=<%u> current_event_time=<%u>\n", __func__, vr_list->first_timestamp, current_event_time); #endif if (diff_time) { #ifdef DEBUG_TSUITE printf(" Waiting <%f>\n", diff_time); #endif ecore_timer_add(diff_time, tsuite_feed_event, ts.td); } else { tsuite_feed_event(ts.td); } } } return _foo(obj); } static void _obj_del(void *data EINA_UNUSED, const Efl_Event *event) { Eo *parent = efl_parent_get(event->object); Object_Info *info = efl_key_data_get(event->object, "exactness_info"); if (parent) { Object_Info *parent_info = parent ? efl_key_data_get(parent, "exactness_info") : NULL; if (parent_info) parent_info->children = eina_list_remove(parent_info->children, info); } else { _widgets_list.children = eina_list_remove(_widgets_list.children, info); } efl_key_data_set(event->object, "exactness_info", NULL); eina_stringshare_del(info->kl_name); free(info); } EAPI Eo * _efl_add_internal_start(const char *file, int line, const Efl_Class *klass_id, Eo *parent_id, Eina_Bool ref, Eina_Bool is_fallback) { Eo *(*foo)(const char *, int, const Efl_Class *, Eo *, Eina_Bool, Eina_Bool) = dlsym(RTLD_NEXT, __FUNCTION__); Eo *ret = foo(file, line, klass_id, parent_id, ref, is_fallback); if (!efl_isa(ret, EFL_CANVAS_INTERFACE) && !efl_isa(ret, EFL_CANVAS_OBJECT_CLASS)) goto end; efl_event_callback_add(ret, EFL_EVENT_DEL, _obj_del, NULL); end: return ret; } EOAPI void efl_parent_set(Eo *obj, Efl_Object *new_parent) { void (*foo)(Eo *, Efl_Object *) = dlsym(RTLD_NEXT, __FUNCTION__); Object_Info *info = NULL; if (!efl_isa(obj, EFL_CANVAS_INTERFACE) && !efl_isa(obj, EFL_CANVAS_OBJECT_CLASS)) goto end; info = efl_key_data_get(obj, "exactness_info"); if (!info) { info = calloc(1, sizeof(*info)); info->object = obj; info->kl_name = eina_stringshare_add(efl_class_name_get(obj)); efl_key_data_set(obj, "exactness_info", info); } Eo *old_parent = efl_parent_get(obj); if (info->id && old_parent == new_parent) goto end; if (old_parent) { Object_Info *old_parent_info = efl_key_data_get(old_parent, "exactness_info"); if (old_parent_info) old_parent_info->children = eina_list_remove(old_parent_info->children, info); int last_parent_id = (intptr_t)efl_key_data_get(old_parent, info->kl_name); if (info->id && last_parent_id == info->id) efl_key_data_set(old_parent, info->kl_name, (void *)(intptr_t)(last_parent_id - 1)); } else { Eina_List *itr; Main_Widget_Id *wid; EINA_LIST_FOREACH(_main_widget_ids, itr, wid) { if (info->kl_name == wid->kl_name) goto found_old_parent; } wid = NULL; found_old_parent: if (wid && wid->last == info->id) wid->last--; _widgets_list.children = eina_list_remove(_widgets_list.children, info); } info->id = 0; info->parent = new_parent; if (new_parent) { int last_parent_id = (intptr_t)efl_key_data_get(new_parent, info->kl_name); info->id = ++last_parent_id; efl_key_data_set(new_parent, info->kl_name, (void *)(intptr_t)last_parent_id); Object_Info *new_parent_info = efl_key_data_get(new_parent, "exactness_info"); if (new_parent_info) new_parent_info->children = eina_list_append(new_parent_info->children, info); } else { Eina_List *itr; Main_Widget_Id *wid; EINA_LIST_FOREACH(_main_widget_ids, itr, wid) { if (info->kl_name == wid->kl_name) goto found_new_parent; } wid = calloc(1, sizeof(*wid)); wid->kl_name = info->kl_name; _main_widget_ids = eina_list_append(_main_widget_ids, wid); found_new_parent: info->id = ++wid->last; _widgets_list.children = eina_list_append(_widgets_list.children, info); } end: foo(obj, new_parent); } static void _info_fill(Object_Info *info) { Eina_List *itr; if (efl_isa(info->object, EFL_CANVAS_OBJECT_CLASS)) evas_object_geometry_get(info->object, &info->x, &info->y, &info->w, &info->h); EINA_LIST_FOREACH(info->children, itr, info) { _info_fill(info); } } static void _objects_snapshot_do() { Eina_List *itr; Object_Info *info; int dir_name_len = _hook_setting->dest_dir ? strlen(_hook_setting->dest_dir) + 1 : 0; /* includes space of a '/' */ char *filename = malloc(strlen(_hook_setting->test_name) + strlen(OBJECTS_FILENAME_EXT) + dir_name_len + 8); /* also space for serial */ if (_hook_setting->dest_dir) sprintf(filename, "%s/", _hook_setting->dest_dir); sprintf(filename + dir_name_len, "%s%c%03d%s", _hook_setting->test_name, SHOT_DELIMITER, ts.serial, OBJECTS_FILENAME_EXT); printf("%d objects saved into %s\n", eina_list_count(_widgets_list.children), filename); EINA_LIST_FOREACH(_widgets_list.children, itr, info) { _info_fill(info); } Eet_File *file = eet_open(filename, EET_FILE_MODE_WRITE); eet_data_write(file, object_info_desc_make(), "entry", &_widgets_list, EINA_TRUE); eet_close(file); free(filename); }