#ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include #include #include "Eio.h" #include "eio_private.h" #include "efl_io_model_private.h" #define MY_CLASS EFL_IO_MODEL_CLASS #define MY_CLASS_NAME "Efl_Io_Model" static void _efl_io_model_info_free(Efl_Io_Model_Info *info, Eina_Bool model); static void _efl_io_model_efl_model_monitor_add(Efl_Io_Model_Data *priv); EINA_VALUE_STRUCT_DESC_DEFINE(_eina_file_direct_info_desc, NULL, sizeof (Efl_Io_Model_Info), EINA_VALUE_STRUCT_MEMBER(EINA_VALUE_TYPE_ULONG, Efl_Io_Model_Info, path_length), EINA_VALUE_STRUCT_MEMBER(EINA_VALUE_TYPE_ULONG, Efl_Io_Model_Info, name_length), EINA_VALUE_STRUCT_MEMBER(EINA_VALUE_TYPE_ULONG, Efl_Io_Model_Info, name_start), EINA_VALUE_STRUCT_MEMBER(EINA_VALUE_TYPE_UINT, Efl_Io_Model_Info, type), EINA_VALUE_STRUCT_MEMBER(EINA_VALUE_TYPE_STRINGSHARE, Efl_Io_Model_Info, path)); /** * Callbacks * Property */ static void _eio_move_done_cb(void *data, Eio_File *handler) { Efl_Io_Model_Data *pd = ecore_thread_local_data_find(handler->thread, ".pd"); Eina_Promise *p = data; // FIXME: generate events eina_promise_resolve(p, eina_value_string_init(pd->path)); pd->request.move = NULL; } static void _eio_file_error_cb(void *data, Eio_File *handler, int error) { Efl_Io_Model_Data *pd = ecore_thread_local_data_find(handler->thread, ".pd"); Eina_Promise *p = data; eina_promise_reject(p, error); pd->request.move = NULL; } /** * Callbacks * Ecore Events */ static Eina_Bool _efl_model_evt_added_ecore_cb(void *data, int type, void *event) { Eio_Monitor_Event *ev = event; Efl_Io_Model *obj; Efl_Io_Model_Data *pd = data; Efl_Model_Children_Event cevt; Efl_Io_Model_Info *mi; Eina_List *l; Eina_Stringshare *spath = NULL; char *path = NULL; if (type != EIO_MONITOR_DIRECTORY_CREATED && type != EIO_MONITOR_FILE_CREATED) return EINA_TRUE; if (ev->monitor != pd->monitor) return EINA_TRUE; obj = pd->self; path = ecore_file_dir_get(ev->filename); if (strcmp(pd->path, path) != 0) goto end; spath = eina_stringshare_add(ev->filename); EINA_LIST_FOREACH(pd->files, l, mi) { if (mi->path == spath) goto end; } mi = calloc(1, sizeof (Efl_Io_Model_Info)); if (!mi) goto end; mi->path_length = eina_stringshare_strlen(spath); mi->path = eina_stringshare_ref(spath); mi->name_start = eina_stringshare_strlen(pd->path) + 1; mi->name_length = mi->path_length - mi->name_start; mi->type = EINA_FILE_UNKNOWN; mi->parent_ref = EINA_FALSE; mi->child_ref = EINA_TRUE; // Honor filter on new added file too if (pd->filter.cb) { Eina_File_Direct_Info info = { 0 }; info.path_length = mi->path_length; info.name_start = mi->name_start; info.name_length = mi->name_length; info.type = EINA_FILE_UNKNOWN; strncpy(info.path, mi->path, (EINA_PATH_MAX - 1)); if (!pd->filter.cb(pd->filter.data, obj, &info)) { eina_stringshare_replace(&mi->path, NULL); free(mi); goto end; } } cevt.index = eina_list_count(pd->files); pd->files = eina_list_append(pd->files, mi); // Notify of the new child being added efl_event_callback_call(obj, EFL_MODEL_EVENT_CHILD_ADDED, &cevt); efl_event_callback_call(obj, EFL_MODEL_EVENT_CHILDREN_COUNT_CHANGED, NULL); end: eina_stringshare_del(spath); free(path); return EINA_TRUE; } static Eina_Bool _efl_model_evt_deleted_ecore_cb(void *data, int type, void *event) { Efl_Io_Model_Info *mi; Eina_List *l; Eio_Monitor_Event *ev = event; Efl_Io_Model *obj; Efl_Io_Model_Data *pd = data; Eina_Stringshare *spath = NULL; Efl_Model_Children_Event cevt = { 0 }; unsigned int i = 0; if (type != EIO_MONITOR_DIRECTORY_DELETED && type != EIO_MONITOR_FILE_DELETED) return EINA_TRUE; if (ev->monitor != pd->monitor) return EINA_TRUE; obj = pd->self; spath = eina_stringshare_add(ev->filename); // FIXME: Linear search is pretty slow EINA_LIST_FOREACH(pd->files, l, mi) { if (mi->path == spath) break ; ++i; } if (i >= eina_list_count(pd->files)) goto end; cevt.index = i; cevt.child = mi->object; efl_event_callback_call(obj, EFL_MODEL_EVENT_CHILD_REMOVED, &cevt); efl_event_callback_call(obj, EFL_MODEL_EVENT_CHILDREN_COUNT_CHANGED, NULL); // Remove the entry from the files list pd->files = eina_list_remove_list(pd->files, l); // This will only trigger the data destruction if no object is referencing them. _efl_io_model_info_free(mi, EINA_FALSE); end: eina_stringshare_del(spath); return EINA_TRUE; } /** * Callbacks * Child Del */ static void _eio_del_cleanup(Efl_Io_Model *obj) { Efl_Io_Model_Data *pd = efl_data_scope_get(obj, EFL_IO_MODEL_CLASS); pd->request.del = NULL; efl_unref(obj); } static void _eio_done_unlink_cb(void *data, Eio_File *handler EINA_UNUSED) { Efl_Io_Model *child = data; _eio_del_cleanup(child); } static void _eio_error_unlink_cb(void *data, Eio_File *handler EINA_UNUSED, int error) { Efl_Io_Model *child = data; ERR("%d: %s.", error, strerror(error)); _eio_del_cleanup(child); } static void _efl_io_model_info_free(Efl_Io_Model_Info *info, Eina_Bool model) { if (!info) return ; if (!model) { info->child_ref = EINA_FALSE; } else { info->parent_ref = EINA_FALSE; } if (info->child_ref || info->parent_ref) return ; eina_stringshare_replace(&info->path, NULL); free(info); } static Eina_File_Type _efl_io_model_info_type_get(const Eina_File_Direct_Info *info, const Eina_Stat *st) { if (info && info->type != EINA_FILE_UNKNOWN) return info->type; if (st) { if (S_ISREG(st->mode)) return EINA_FILE_REG; else if (S_ISDIR(st->mode)) return EINA_FILE_DIR; else if (S_ISCHR(st->mode)) return EINA_FILE_CHR; else if (S_ISBLK(st->mode)) return EINA_FILE_BLK; else if (S_ISFIFO(st->mode)) return EINA_FILE_FIFO; else if (S_ISLNK(st->mode)) return EINA_FILE_LNK; #ifdef S_ISSOCK else if (S_ISSOCK(st->mode)) return EINA_FILE_SOCK; #endif } return EINA_FILE_UNKNOWN; } static void _efl_io_model_info_build(const Efl_Io_Model *model, Efl_Io_Model_Data *pd) { char *path; if (pd->info) goto end; pd->info = calloc(1, sizeof (Efl_Io_Model_Info)); if (!pd->info) return ; pd->info->path_length = eina_stringshare_strlen(pd->path); pd->info->path = eina_stringshare_ref(pd->path); path = strdup(pd->path); pd->info->name_start = basename(path) - path; pd->info->name_length = pd->info->path_length - pd->info->name_start; free(path); pd->info->type = _efl_io_model_info_type_get(NULL, pd->st); efl_model_properties_changed(model, "direct_info"); end: pd->info->parent_ref = EINA_TRUE; } static void _eio_build_st_done(void *data, Eio_File *handler EINA_UNUSED, const Eina_Stat *stat) { Efl_Io_Model *model = data; Efl_Io_Model_Data *pd = efl_data_scope_get(model, EFL_IO_MODEL_CLASS); if (!pd) return ; pd->request.stat = NULL; pd->st = malloc(sizeof (Eina_Stat)); if (!pd->st) return ; memcpy(pd->st, stat, sizeof (Eina_Stat)); if (!pd->info) _efl_io_model_info_build(model, pd); if (pd->info->type == EINA_FILE_UNKNOWN) pd->info->type = _efl_io_model_info_type_get(NULL, stat); efl_model_properties_changed(model, "mtime", "atime", "ctime", "is_dir", "is_lnk", "size", "stat"); if (eio_file_is_dir(pd->st)) { // Now that we know we are a directory, we should whatch it _efl_io_model_efl_model_monitor_add(pd); // And start listing its child efl_model_children_count_get(model); } efl_unref(model); } static void _eio_build_st_done_clobber(void *data, Eio_File *handler, const Eina_Stat *stat) { Efl_Io_Model *model = data; Efl_Io_Model *parent; efl_ref(model); // For st_done _eio_build_st_done(data, handler, stat); parent = efl_parent_get(model); efl_model_child_del(parent, model); efl_unref(model); // From the async thread early ref } static void _eio_build_st_error(void *data, Eio_File *handler EINA_UNUSED, int error) { Efl_Io_Model *model = data; Efl_Io_Model_Data *pd = efl_data_scope_get(model, EFL_IO_MODEL_CLASS); pd->request.stat = NULL; pd->error = error; efl_model_properties_changed(model, "direct_info", "mtime", "atime", "ctime", "is_dir", "is_lnk", "size", "stat"); efl_unref(model); } static void _eio_build_st_error_clobber(void *data, Eio_File *handler, int error) { Efl_Io_Model *model = data; efl_ref(model); // For st_error unref _eio_build_st_error(data, handler, error); efl_unref(model); // From the async thread early ref } static void _eio_build_st(const Efl_Io_Model *model, Efl_Io_Model_Data *pd) { if (pd->st) return ; if (pd->request.stat) return ; if (pd->error) return ; pd->request.stat = eio_file_direct_stat(pd->path, _eio_build_st_done, _eio_build_st_error, efl_ref(model)); } static void _eio_build_st_then_clobber(const Efl_Io_Model *model, Efl_Io_Model_Data *pd) { if (pd->st) return ; if (pd->request.stat) return ; if (pd->error) return ; pd->request.stat = eio_file_direct_stat(pd->path, _eio_build_st_done_clobber, _eio_build_st_error_clobber, efl_ref(model)); } static Eina_List *delayed_queue = NULL; static Eina_Bool delayed_one = EINA_FALSE; static void _delayed_flush(void *data EINA_UNUSED, const Efl_Event *ev) { Eina_Promise *p; Eina_List *tmp = delayed_queue; efl_event_callback_del(ev->object, EFL_LOOP_EVENT_IDLE, _delayed_flush, NULL); delayed_one = EINA_FALSE; delayed_queue = NULL; EINA_LIST_FREE(tmp, p) eina_promise_resolve(p, EINA_VALUE_EMPTY); } static Eina_Value _cancel_request(Eo *model EINA_UNUSED, void *data, Eina_Error error) { delayed_queue = eina_list_remove_list(delayed_queue, data); return eina_value_error_init(error); } static Eina_Future * _build_delay(Efl_Io_Model *model) { Eina_Promise *p; p = efl_loop_promise_new(model); if (!delayed_queue) { // Remove callback, just in case it is still there. efl_event_callback_del(efl_loop_get(model), EFL_LOOP_EVENT_IDLE, _delayed_flush, NULL); efl_event_callback_add(efl_loop_get(model), EFL_LOOP_EVENT_IDLE, _delayed_flush, NULL); // FIXME: It would be nice to be able to build a future directly to be triggered on one event } delayed_queue = eina_list_append(delayed_queue, p); return efl_future_then(model, eina_future_new(p), .error = _cancel_request, .data = eina_list_last(delayed_queue)); } static Eina_Value _eio_build_mime_now(Eo *model, void *data, const Eina_Value v) { Efl_Io_Model_Data *pd = data; // Make sure that we are not over consuming time in the main loop if (!delayed_one && (delayed_queue || ecore_time_get() - ecore_loop_time_get() > 0.004)) { Eina_Future *f = efl_future_then(model, _build_delay(model), .success = _eio_build_mime_now, .data = pd); return eina_future_as_value(efl_future_then(model, f)); } pd->mime_type = efreet_mime_type_get(pd->path); efl_model_properties_changed(model, "mime_type"); delayed_one = EINA_TRUE; return v; } static void _eio_build_mime(const Efl_Object *model, Efl_Io_Model_Data *pd) { if (pd->mime_type) return ; if (pd->request.mime) return ; pd->request.mime = efl_future_then(model, efl_loop_job(efl_loop_get(model)), .success = _eio_build_mime_now, .data = pd); } static Eina_Value * _property_filename_cb(const Eo *obj, Efl_Io_Model_Data *pd) { _efl_io_model_info_build(obj, pd); if (pd->info) return eina_value_string_new(pd->info->path + pd->info->name_start); return eina_value_error_new(EAGAIN); } static Eina_Value * _property_path_cb(const Eo *obj EINA_UNUSED, Efl_Io_Model_Data *pd) { return eina_value_stringshare_new(pd->path); } static Eina_Value * _property_direct_info_cb(const Eo *obj, Efl_Io_Model_Data *pd) { _efl_io_model_info_build(obj, pd); if (pd->info) { Eina_Value *r; r = eina_value_struct_new(_eina_file_direct_info_desc()); if (!r) return NULL; if (!eina_value_pset(r, pd->info)) { eina_value_free(r); return NULL; } return r; } if (pd->error) return eina_value_error_new(pd->error); return eina_value_error_new(EAGAIN); } #define TIMECB(Prop) \ static Eina_Value * \ _property_##Prop##_cb(const Eo *obj, Efl_Io_Model_Data *pd) \ { \ if (pd->st) \ return eina_value_time_new(pd->st->Prop); \ if (pd->error) \ return eina_value_error_new(pd->error); \ \ _eio_build_st(obj, pd); \ return eina_value_error_new(EAGAIN); \ } TIMECB(mtime); TIMECB(atime); TIMECB(ctime); static Eina_Value * _property_is_dir_cb(const Eo *obj, Efl_Io_Model_Data *pd) { if (pd->st) return eina_value_bool_new(eio_file_is_dir(pd->st)); if (pd->error) return eina_value_error_new(pd->error); _eio_build_st(obj, pd); return eina_value_error_new(EAGAIN); } static Eina_Value * _property_is_lnk_cb(const Eo *obj, Efl_Io_Model_Data *pd) { if (pd->st) return eina_value_bool_new(eio_file_is_lnk(pd->st)); if (pd->error) return eina_value_error_new(pd->error); _eio_build_st(obj, pd); return eina_value_error_new(EAGAIN); } static Eina_Value * _property_size_cb(const Eo *obj, Efl_Io_Model_Data *pd) { if (pd->st) return eina_value_ulong_new(pd->st->size); if (pd->error) return eina_value_error_new(pd->error); _eio_build_st(obj, pd); return eina_value_error_new(EAGAIN); } static Eina_Value * _property_stat_cb(const Eo *obj, Efl_Io_Model_Data *pd) { if (pd->st) { Eina_Value *r; r = eina_value_struct_new(_eina_stat_desc()); if (!r) return NULL; if (!eina_value_pset(r, pd->info)) { eina_value_free(r); return NULL; } return r; } if (pd->error) return eina_value_error_new(pd->error); _eio_build_st(obj, pd); return eina_value_error_new(EAGAIN); } static Eina_Value * _property_mime_type_cb(const Eo *obj, Efl_Io_Model_Data *pd) { if (pd->mime_type) return eina_value_string_new(pd->mime_type); if (pd->error) return eina_value_error_new(pd->error); _eio_build_mime(obj, pd); return eina_value_error_new(EAGAIN); } #define PP(Name) \ { #Name, _property_##Name##_cb } static struct { const char *name; Eina_Value *(*cb)(const Eo *obj, Efl_Io_Model_Data *pd); } properties[] = { PP(filename), PP(path), PP(direct_info), PP(mtime), PP(atime), PP(ctime), PP(is_dir), PP(is_lnk), PP(size), PP(stat), PP(mime_type) }; /** * Interfaces impl. */ static Eina_Iterator * _efl_io_model_efl_model_properties_get(const Eo *obj EINA_UNUSED, Efl_Io_Model_Data *pd EINA_UNUSED) { return EINA_C_ARRAY_ITERATOR_NEW(properties); } static Eina_Value * _efl_io_model_efl_model_property_get(const Eo *obj, Efl_Io_Model_Data *pd, const char *property) { unsigned int i; if (!property) return NULL; for (i = 0; i < EINA_C_ARRAY_LENGTH(properties); ++i) if (property == properties[i].name || !strcmp(property, properties[i].name)) return properties[i].cb(obj, pd); ERR("Could not find property '%s'.", property); // Unknow value request return eina_value_error_new(EFL_MODEL_ERROR_NOT_SUPPORTED); } static Eina_Future * _efl_io_model_efl_model_property_set(Eo *obj, Efl_Io_Model_Data *pd, const char *property, Eina_Value *value) { const char *path; Eina_Future *f; Eina_Value s = EINA_VALUE_EMPTY; Eina_Error err = EFL_MODEL_ERROR_NOT_FOUND; Eina_Bool finalized = !!pd->path; if (!property) goto on_error; err = EFL_MODEL_ERROR_NOT_SUPPORTED; if (strcmp(property, "path") != 0) goto on_error; if (finalized && pd->request.move) goto on_error; err = EFL_MODEL_ERROR_UNKNOWN; if (!eina_value_setup(&s, EINA_VALUE_TYPE_STRING)) goto on_error; if (!eina_value_convert(value, &s)) goto on_error; if (!eina_value_string_get(&s, &path)) goto on_error; eina_stringshare_replace(&pd->path, path); if (finalized) { Eina_Promise *p = efl_loop_promise_new(obj); f = eina_future_new(p); pd->request.move = eio_file_move(pd->path, path, NULL, /* FIXME: have a move progress property */ _eio_move_done_cb, _eio_file_error_cb, p); ecore_thread_local_data_add(pd->request.move->thread, ".pd", pd, NULL, EINA_TRUE); f = _efl_io_manager_future(obj, f, pd->request.move); // FIXME: turn on monitor in the finalize stage or after move } else { f = efl_loop_future_resolved(obj, eina_value_stringshare_init(pd->path)); } return f; on_error: return efl_loop_future_rejected(obj, err); } static void _efl_io_model_children_list(void *data, Eina_Array *entries) { Eina_File_Direct_Info *info; Efl_Model *obj = data; Efl_Io_Model_Data *pd; Efl_Model_Children_Event cevt = { 0 }; Eina_Array_Iterator iterator; unsigned int i; pd = efl_data_scope_get(obj, EFL_IO_MODEL_CLASS); if (!pd) return ; EINA_ARRAY_ITER_NEXT(entries, i, info, iterator) { Efl_Io_Model_Info *mi; if (pd->filter.cb) { if (!pd->filter.cb(pd->filter.data, obj, info)) continue ; } mi = calloc(1, sizeof (Efl_Io_Model_Info)); if (!mi) continue ; mi->path_length = info->path_length; mi->path = eina_stringshare_add(info->path); mi->name_start = info->name_start; mi->name_length = info->name_length; mi->type = _efl_io_model_info_type_get(info, NULL); mi->parent_ref = EINA_FALSE; mi->child_ref = EINA_TRUE; cevt.index = eina_list_count(pd->files); cevt.child = NULL; pd->files = eina_list_append(pd->files, mi); efl_event_callback_call(obj, EFL_MODEL_EVENT_CHILD_ADDED, &cevt); } efl_event_callback_call(obj, EFL_MODEL_EVENT_CHILDREN_COUNT_CHANGED, NULL); } static Eina_Value _efl_io_model_children_list_on(Eo *o EINA_UNUSED, void *data, const Eina_Value v) { Efl_Io_Model_Data *pd = data; // Now that we have listed the content of the directory, // we can whatch over it _efl_io_model_efl_model_monitor_add(pd); return v; } static void _efl_io_model_children_list_cleanup(Eo *o EINA_UNUSED, void *data, const Eina_Future *dead_future EINA_UNUSED) { Efl_Io_Model_Data *pd = data; pd->request.listing = NULL; pd->listed = EINA_TRUE; } /** * Children Count Get */ static unsigned int _efl_io_model_efl_model_children_count_get(const Eo *obj, Efl_Io_Model_Data *pd) { // If we have no information on the object, let's build it. if (efl_invalidated_get(obj)) { return 0; } else if (!pd->info) { _eio_build_st(obj, pd); } else if (!pd->listed && !pd->request.listing && pd->info->type == EINA_FILE_DIR) { Efl_Io_Manager *iom; Eina_Future *f; iom = efl_provider_find(obj, EFL_IO_MANAGER_CLASS); if (!iom) { ERR("Could not find an Efl.Io.Manager on %p.", obj); return 0; } f = efl_io_manager_direct_ls(iom, pd->path, EINA_FALSE, (void*) obj, _efl_io_model_children_list, NULL); pd->request.listing = efl_future_then(obj, f, .success = _efl_io_model_children_list_on, .free = _efl_io_model_children_list_cleanup, .data = pd); } return eina_list_count(pd->files); } static void _efl_io_model_efl_model_monitor_add(Efl_Io_Model_Data *priv) { if (!priv->monitor) { int i = 0; priv->monitor = eio_monitor_add(priv->path); for (i = 0; priv->mon.mon_event_child_add[i] != EIO_MONITOR_ERROR ; ++i) priv->mon.ecore_child_add_handler[i] = ecore_event_handler_add(priv->mon.mon_event_child_add[i], _efl_model_evt_added_ecore_cb, priv); for (i = 0; priv->mon.mon_event_child_del[i] != EIO_MONITOR_ERROR ; ++i) priv->mon.ecore_child_del_handler[i] = ecore_event_handler_add(priv->mon.mon_event_child_del[i], _efl_model_evt_deleted_ecore_cb, priv); } } static void _efl_io_model_efl_model_monitor_del(Efl_Io_Model_Data *priv) { if (priv->monitor) { int i = 0; for (i = 0; priv->mon.mon_event_child_add[i] != EIO_MONITOR_ERROR ; ++i) ecore_event_handler_del(priv->mon.ecore_child_add_handler[i]); for (i = 0; priv->mon.mon_event_child_del[i] != EIO_MONITOR_ERROR ; ++i) ecore_event_handler_del(priv->mon.ecore_child_del_handler[i]); eio_monitor_del(priv->monitor); priv->monitor = NULL; } } /* * Callbacks * Children Load */ static void _efl_io_model_children_filter_set(Eo *obj EINA_UNUSED, Efl_Io_Model_Data *pd, void *filter_data, EflIoFilter filter, Eina_Free_Cb filter_free_cb) { pd->filter.data = filter_data; pd->filter.cb = filter; pd->filter.free = filter_free_cb; } /** * Child Add */ static Eo * _efl_io_model_efl_model_child_add(Eo *obj EINA_UNUSED, Efl_Io_Model_Data *priv EINA_UNUSED) { return NULL; } /** * Child Remove */ static void _efl_io_model_efl_model_child_del(Eo *obj EINA_UNUSED, Efl_Io_Model_Data *priv EINA_UNUSED, Eo *child) { Efl_Io_Model_Data *child_pd; Eina_File_Type type; child_pd = efl_data_scope_get(child, MY_CLASS); if (!child_pd->info) return; if (child_pd->error) return; type = child_pd->info->type; if (type == EINA_FILE_UNKNOWN) { _eio_build_st_then_clobber(child, child_pd); return ; } // Already in progress if (child_pd->request.del) return; efl_ref(child); if (type == EINA_FILE_DIR) { child_pd->request.del = eio_dir_unlink(child_pd->path, NULL, NULL, _eio_done_unlink_cb, _eio_error_unlink_cb, child); } else { child_pd->request.del = eio_file_unlink(child_pd->path, _eio_done_unlink_cb, _eio_error_unlink_cb, child); } } /** * Children Slice Get */ static Eina_Future * _efl_io_model_efl_model_children_slice_get(Eo *obj, Efl_Io_Model_Data *pd, unsigned int start, unsigned int count) { Eina_Future_Scheduler *scheduler = NULL; Eina_Value array = EINA_VALUE_EMPTY; Eina_List *ls = NULL; // If called on an invalidated model, we won't have a scheduler scheduler = efl_loop_future_scheduler_get(obj); if (!scheduler) return NULL; if (count == 0) { count = eina_list_count(pd->files); start = 0; } // Children must have been listed first if (count == 0 || (start + count > eina_list_count(pd->files))) return eina_future_rejected(scheduler, EFL_MODEL_ERROR_INCORRECT_VALUE); eina_value_array_setup(&array, EINA_VALUE_TYPE_OBJECT, count % 8); ls = eina_list_nth_list(pd->files, start); while (count > 0) { Efl_Io_Model_Info *info = eina_list_data_get(ls); Efl_Io_Model_Data *child_data = NULL; info->parent_ref = EINA_TRUE; if (info->object == NULL) // Little trick here, setting internal data before finalize info->object = efl_add_ref(EFL_IO_MODEL_CLASS, obj, efl_loop_model_volatile_make(efl_added), child_data = efl_data_scope_get(efl_added, EFL_IO_MODEL_CLASS), child_data->info = info, child_data->path = eina_stringshare_ref(info->path), child_data->parent = ls, // NOTE: We are assuming here that the parent model will outlive all its children child_data->filter.cb = pd->filter.cb, child_data->filter.data = pd->filter.data); eina_value_array_append(&array, info->object); efl_wref_add(info->object, &info->object); efl_unref(info->object); count--; ls = eina_list_next(ls); } return eina_future_resolved(scheduler, array); } /** * Class definitions */ static Efl_Object * _efl_io_model_efl_object_finalize(Eo *obj, Efl_Io_Model_Data *pd) { if (!pd->path) return NULL; if (!efl_provider_find(obj, EFL_LOOP_CLASS)) { ERR("Eio.Model require a parent that provide access to the main loop."); return NULL; } // If we have no info at all, let's check this path first if (!pd->info) _eio_build_st(obj, pd); // Setup monitor pd->mon.mon_event_child_add[0] = EIO_MONITOR_DIRECTORY_CREATED; pd->mon.mon_event_child_add[1] = EIO_MONITOR_FILE_CREATED; pd->mon.mon_event_child_add[2] = EIO_MONITOR_ERROR; pd->mon.mon_event_child_del[0] = EIO_MONITOR_DIRECTORY_DELETED; pd->mon.mon_event_child_del[1] = EIO_MONITOR_FILE_DELETED; pd->mon.mon_event_child_del[2] = EIO_MONITOR_ERROR; pd->self = obj; return obj; } static void _efl_io_model_path_set(Eo *obj EINA_UNUSED, Efl_Io_Model_Data *priv, const char *path) { char *sanitized = eina_file_path_sanitize(path); priv->path = eina_stringshare_add(sanitized); free(sanitized); } static const char * _efl_io_model_path_get(const Eo *obj EINA_UNUSED, Efl_Io_Model_Data *priv) { return priv->path; } static void _efl_io_model_efl_object_destructor(Eo *obj , Efl_Io_Model_Data *priv) { Efl_Io_Model_Info *info; free(priv->st); priv->st = NULL; _efl_io_model_info_free(priv->info, EINA_TRUE); priv->info = NULL; EINA_LIST_FREE(priv->files, info) _efl_io_model_info_free(info, EINA_FALSE); eina_stringshare_replace(&priv->path, NULL); efl_destructor(efl_super(obj, MY_CLASS)); } static void _efl_io_model_efl_object_invalidate(Eo *obj , Efl_Io_Model_Data *priv) { // This has to be done first, to make sure that the automatic // del is not done anymore. Or it would lead to too much // unref when stopping async thread (During invalidate, we // are already in the process of doing an implicit del). efl_invalidate(efl_super(obj, EFL_IO_MODEL_CLASS)); _efl_io_model_efl_model_monitor_del(priv); // Unlink the object from the parent if (priv->info) { efl_wref_del(priv->info->object, &priv->info->object); priv->info->object = NULL; } if (priv->request.del) { if (!ecore_thread_wait(priv->request.del->thread, 0.1)) { ecore_thread_cancel(priv->request.del->thread); ecore_thread_wait(priv->request.del->thread, 0.1); } } if (priv->request.move) { if (!ecore_thread_wait(priv->request.move->thread, 0.1)) { ecore_thread_cancel(priv->request.move->thread); ecore_thread_wait(priv->request.move->thread, 0.1); } } if (priv->request.stat) { if (!ecore_thread_wait(priv->request.stat->thread, 0.1)) { ecore_thread_cancel(priv->request.stat->thread); ecore_thread_wait(priv->request.stat->thread, 0.1); } } } #include "efl_io_model.eo.c"