jpeg pushed a commit to branch master. http://git.enlightenment.org/core/elementary.git/commit/?id=71d532695f10d9a8e24c0b06da0961e4b85729d2
commit 71d532695f10d9a8e24c0b06da0961e4b85729d2 Author: Jean-Philippe Andre <jp.an...@samsung.com> Date: Tue Mar 31 11:58:10 2015 +0900 elm_image: Implement async file open Summary: This implements the interface Efl.File.async.{set,get,wait} Reviewers: cedric, raster, tasn Differential Revision: https://phab.enlightenment.org/D2262 --- src/lib/elm_image.c | 262 +++++++++++++++++++++++++++++++++++++++++---- src/lib/elm_image.eo | 3 + src/lib/elm_widget_image.h | 10 ++ 3 files changed, 257 insertions(+), 18 deletions(-) diff --git a/src/lib/elm_image.c b/src/lib/elm_image.c index 257ff89..3b43405 100644 --- a/src/lib/elm_image.c +++ b/src/lib/elm_image.c @@ -34,6 +34,7 @@ static const Evas_Smart_Cb_Description _smart_callbacks[] = { }; static Eina_Bool _key_action_activate(Evas_Object *obj, const char *params); +static void _elm_image_smart_internal_file_set(Eo *obj, Elm_Image_Data *sd, const char *file, const Eina_File *f, const char *key, Eina_Bool *ret); static const Elm_Action key_actions[] = { {"activate", _key_action_activate}, @@ -204,9 +205,161 @@ _elm_image_internal_sizing_eval(Evas_Object *obj, Elm_Image_Data *sd) evas_object_resize(sd->hit_rect, w, h); } -/* WARNING: whenever you patch this function, remember to do the same - * on elm_icon.c:_elm_icon_smart_file_set()'s 2nd half. - */ +static void +_elm_image_async_open_do(void *data, Ecore_Thread *thread EINA_UNUSED) +{ + Evas_Object *obj = data; + Eina_Bool ok = EINA_FALSE; + Eina_File *f = NULL; + void *map = NULL; + + ELM_IMAGE_DATA_GET(obj, sd); + + sd->async_opening = EINA_TRUE; + if (sd->async.f_set) + { + f = sd->async.f_set; + sd->async.f_set = NULL; + } + else if (sd->async.file) + { + f = eina_file_open(sd->async.file, EINA_FALSE); + } + else ERR("Async open has no input file!"); // CRI? + + if (f) + { + // Read just enough data for map to actually do something. + unsigned int buf; + map = eina_file_map_all(f, EINA_FILE_SEQUENTIAL); + if (map && (eina_file_size_get(f) >= sizeof(buf))) + { + memcpy(&buf, map, sizeof(buf)); + ok = EINA_TRUE; + } + } + + sd->async.f = f; + sd->async.map = map; + sd->async_failed = !ok; + sd->async_opening = EINA_FALSE; +} + +static void +_elm_image_async_open_done(void *data, Ecore_Thread *thread EINA_UNUSED) +{ + Evas_Object *obj = data; + Eina_Stringshare *file, *key; + Eina_Bool ok; + Eina_File *f; + void *map; + + ELM_IMAGE_DATA_GET(obj, sd); + + key = sd->async.key; + map = sd->async.map; + f = sd->async.f; + ok = (!sd->async_failed) && f && map; + if (sd->async.file) + file = sd->async.file; + else + file = f ? eina_file_filename_get(f) : NULL; + + // sd->async.f_set is already NULL + sd->async.f = NULL; + sd->async.th = NULL; + sd->async.map = NULL; + sd->async_opening = EINA_FALSE; + + if (sd->edje) + { + if (ok) ok = edje_object_mmap_set(sd->img, f, key); + if (!ok) + { + ERR("failed to open edje file '%s', group '%s': %s", file, key, + edje_load_error_str(edje_object_load_error_get(sd->img))); + } + } + else + { + if (ok) _elm_image_smart_internal_file_set(obj, sd, file, f, key, &ok); + if (!ok) + { + ERR("failed to open image file '%s', key '%s': %s", file, key, + evas_load_error_str(evas_object_image_load_error_get(sd->img))); + } + } + + if (ok) + { + // TODO: Implement Efl.File async,opened event_info type + sd->async_failed = EINA_FALSE; + ELM_SAFE_FREE(sd->async.file, eina_stringshare_del); + ELM_SAFE_FREE(sd->async.key, eina_stringshare_del); + eo_do(obj, eo_event_callback_call(EFL_FILE_EVENT_ASYNC_OPENED, NULL)); + _elm_image_internal_sizing_eval(obj, sd); + } + else + { + // TODO: Implement Efl.File async,error event_info type + sd->async_failed = EINA_TRUE; + // keep file,key around for file_get + eo_do(obj, eo_event_callback_call(EFL_FILE_EVENT_ASYNC_ERROR, NULL)); + } + + if (f) + { + if (map) eina_file_map_free(f, map); + eina_file_close(f); + } +} + +static void +_elm_image_async_open_cancel(void *data, Ecore_Thread *thread EINA_UNUSED) +{ + Elm_Image_Data *sd = data; + + if (sd->async.map) + { + eina_file_map_free(sd->async.f, sd->async.map); + sd->async.map = NULL; + } + if (sd->async.f_set) + ELM_SAFE_FREE(sd->async.f_set, eina_file_close); + if (sd->async.f) + ELM_SAFE_FREE(sd->async.f, eina_file_close); + sd->async_failed = EINA_TRUE; +} + +static Eina_Bool +_elm_image_async_file_set(Eo *obj, Elm_Image_Data *sd, + const char *file, const Eina_File *f, const char *key) +{ + Eina_Bool ret; + + ecore_thread_cancel(sd->async.th); + //ecore_thread_wait(sd->async.th); + + eina_stringshare_replace(&sd->async.file, file); + eina_stringshare_replace(&sd->async.key, key); + if (sd->async.map) + { + eina_file_map_free(sd->async.f, sd->async.map); + sd->async.map = NULL; + } + if (sd->async.f) + ELM_SAFE_FREE(sd->async.f, eina_file_close); + sd->async.f_set = eina_file_dup(f); + sd->async_failed = EINA_FALSE; + sd->async.th = ecore_thread_run(_elm_image_async_open_do, + _elm_image_async_open_done, + _elm_image_async_open_cancel, obj); + + ret = (sd->async.th != NULL); + if (!ret) sd->async_failed = EINA_TRUE; + return ret; +} + static Eina_Bool _elm_image_edje_file_set(Evas_Object *obj, const char *file, @@ -232,21 +385,29 @@ _elm_image_edje_file_set(Evas_Object *obj, } sd->edje = EINA_TRUE; - if (f) + if (!sd->async_enable) { - if (!edje_object_mmap_set(sd->img, f, group)) + if (f) { - ERR("failed to set edje file '%s', group '%s': %s", file, group, - edje_load_error_str(edje_object_load_error_get(sd->img))); - return EINA_FALSE; + if (!edje_object_mmap_set(sd->img, f, group)) + { + ERR("failed to set edje file '%s', group '%s': %s", file, group, + edje_load_error_str(edje_object_load_error_get(sd->img))); + return EINA_FALSE; + } + } + else + { + if (!edje_object_file_set(sd->img, file, group)) + { + ERR("failed to set edje file '%s', group '%s': %s", file, group, + edje_load_error_str(edje_object_load_error_get(sd->img))); + return EINA_FALSE; + } } } - else if (!edje_object_file_set(sd->img, file, group)) - { - ERR("failed to set edje file '%s', group '%s': %s", file, group, - edje_load_error_str(edje_object_load_error_get(sd->img))); - return EINA_FALSE; - } + else + return _elm_image_async_file_set(obj, sd, file, f, group); /* FIXME: do i want to update icon on file change ? */ _elm_image_internal_sizing_eval(obj, sd); @@ -387,6 +548,23 @@ _elm_image_evas_object_smart_del(Eo *obj, Elm_Image_Data *sd) free(sd->remote_data); eina_stringshare_del(sd->key); + if (sd->async.th) + { + ecore_thread_cancel(sd->async.th); + if (!ecore_thread_wait(sd->async.th, 1.0)) + { + ERR("Async open thread timed out during cancellation."); + // skipping all other data free to avoid crashes (this leaks) + eo_do_super(obj, MY_CLASS, evas_obj_smart_del()); + return; + } + } + + if (sd->async.map) eina_file_map_free(sd->async.f, sd->async.map); + if (sd->async.f) eina_file_close(sd->async.f); + eina_stringshare_del(sd->async.file); + eina_stringshare_del(sd->async.key); + eo_do_super(obj, MY_CLASS, evas_obj_smart_del()); } @@ -826,7 +1004,10 @@ _elm_image_efl_file_file_set(Eo *obj, Elm_Image_Data *sd, const char *file, cons break; } - _elm_image_smart_internal_file_set(obj, sd, file, NULL, key, &ret); + if (!sd->async_enable) + _elm_image_smart_internal_file_set(obj, sd, file, NULL, key, &ret); + else + ret = _elm_image_async_file_set(obj, sd, file, NULL, key); return ret; } @@ -880,9 +1061,14 @@ _elm_image_mmap_set(Eo *obj, Elm_Image_Data *sd, const Eina_File *f, const char if (sd->remote) _elm_url_cancel(sd->remote); sd->remote = NULL; - _elm_image_smart_internal_file_set(obj, sd, - eina_file_filename_get(f), f, - key, &ret); + if (!sd->async_enable) + { + _elm_image_smart_internal_file_set(obj, sd, + eina_file_filename_get(f), f, + key, &ret); + } + else + ret = _elm_image_async_file_set(obj, sd, eina_file_filename_get(f), f, key); return ret; } @@ -890,6 +1076,13 @@ _elm_image_mmap_set(Eo *obj, Elm_Image_Data *sd, const Eina_File *f, const char EOLIAN static void _elm_image_efl_file_file_get(Eo *obj EINA_UNUSED, Elm_Image_Data *sd, const char **file, const char **key) { + if (sd->async_enable && (sd->async_opening || sd->async_failed)) + { + if (file) *file = sd->async.file; + if (key) *key = sd->async.key; + return; + } + if (sd->edje) edje_object_file_get(sd->img, file, key); else @@ -910,6 +1103,39 @@ _elm_image_smooth_get(Eo *obj EINA_UNUSED, Elm_Image_Data *sd) return sd->smooth; } +static Eina_Bool +_elm_image_efl_file_async_wait(Eo *obj EINA_UNUSED, Elm_Image_Data *pd) +{ + Eina_Bool ok = EINA_TRUE; + if (!pd->async_enable) return ok; + if (!pd->async.th) return ok; + if (!ecore_thread_wait(pd->async.th, 1.0)) + { + ERR("Failed to wait on async file open!"); + ok = EINA_FALSE; + if (!pd->async.map) + pd->async_failed = EINA_TRUE; + } + return ok; +} + +EOLIAN static void +_elm_image_efl_file_async_set(Eo *obj, Elm_Image_Data *pd, Eina_Bool async) +{ + if (pd->async_enable == async) + return; + + pd->async_enable = async; + if (!async) + _elm_image_efl_file_async_wait(obj, pd); +} + +EOLIAN static Eina_Bool +_elm_image_efl_file_async_get(Eo *obj EINA_UNUSED, Elm_Image_Data *pd) +{ + return pd->async_enable; +} + EOLIAN static void _elm_image_object_size_get(Eo *obj EINA_UNUSED, Elm_Image_Data *sd, int *w, int *h) { diff --git a/src/lib/elm_image.eo b/src/lib/elm_image.eo index d5592cb..096d647 100644 --- a/src/lib/elm_image.eo +++ b/src/lib/elm_image.eo @@ -538,6 +538,9 @@ class Elm_Image (Elm_Widget, Efl.File, Efl.Image, Evas.Clickable_Interface, Eo.Base.constructor; Efl.File.file.set; Efl.File.file.get; + Efl.File.async.set; + Efl.File.async.get; + Efl.File.async_wait; Efl.Image.load_size.set; Efl.Image.load_size.get; Efl.Image.smooth_scale.set; diff --git a/src/lib/elm_widget_image.h b/src/lib/elm_widget_image.h index b43d656..c5ad2cc 100644 --- a/src/lib/elm_widget_image.h +++ b/src/lib/elm_widget_image.h @@ -54,6 +54,13 @@ struct _Elm_Image_Data Elm_Image_Orient orient; + struct { + Eina_Stringshare *file, *key; + Eina_File *f, *f_set; + void *map; + Ecore_Thread *th; + } async; + Eina_Bool aspect_fixed : 1; Eina_Bool fill_inside : 1; Eina_Bool resize_down : 1; @@ -66,6 +73,9 @@ struct _Elm_Image_Data Eina_Bool edje : 1; Eina_Bool anim : 1; Eina_Bool play : 1; + Eina_Bool async_enable : 1; + Eina_Bool async_opening : 1; + Eina_Bool async_failed : 1; }; /** --