1 #include "e.h"
2 
3 E_API int E_EVENT_FM_OP_REGISTRY_ADD = 0;
4 E_API int E_EVENT_FM_OP_REGISTRY_DEL = 0;
5 E_API int E_EVENT_FM_OP_REGISTRY_CHANGED = 0;
6 
7 static Eina_Hash *_e_fm2_op_registry = NULL;
8 static unsigned int _e_fm2_init_count = 0;
9 
10 typedef struct _E_Fm2_Op_Registry_Entry_Listener E_Fm2_Op_Registry_Entry_Listener;
11 typedef struct _E_Fm2_Op_Registry_Entry_Internal E_Fm2_Op_Registry_Entry_Internal;
12 
13 struct _E_Fm2_Op_Registry_Entry_Listener
14 {
15    EINA_INLIST;
16    void  (*cb)(void *data, const E_Fm2_Op_Registry_Entry *entry);
17    void *data;
18    void  (*free_data)(void *data);
19 };
20 
21 struct _E_Fm2_Op_Registry_Entry_Internal
22 {
23    E_Fm2_Op_Registry_Entry entry;
24    Eina_Inlist            *listeners;
25    int                     references;
26    Ecore_Event            *changed_event;
27 };
28 
29 static void
_e_fm2_op_registry_entry_e_fm_deleted(void * data,Evas * evas EINA_UNUSED,Evas_Object * e_fm EINA_UNUSED,void * event EINA_UNUSED)30 _e_fm2_op_registry_entry_e_fm_deleted(void *data, Evas *evas EINA_UNUSED, Evas_Object *e_fm EINA_UNUSED, void *event EINA_UNUSED)
31 {
32    E_Fm2_Op_Registry_Entry *entry = data;
33 
34    entry->e_fm = NULL;
35    e_fm2_op_registry_entry_changed(entry);
36 }
37 
38 static void
_e_fm2_op_registry_entry_e_fm_monitor_start(const E_Fm2_Op_Registry_Entry * entry)39 _e_fm2_op_registry_entry_e_fm_monitor_start(const E_Fm2_Op_Registry_Entry *entry)
40 {
41    if (!entry->e_fm) return;
42    evas_object_event_callback_add
43      (entry->e_fm, EVAS_CALLBACK_DEL,
44      _e_fm2_op_registry_entry_e_fm_deleted, entry);
45 }
46 
47 static void
_e_fm2_op_registry_entry_e_fm_monitor_stop(const E_Fm2_Op_Registry_Entry * entry)48 _e_fm2_op_registry_entry_e_fm_monitor_stop(const E_Fm2_Op_Registry_Entry *entry)
49 {
50    if (!entry->e_fm) return;
51    evas_object_event_callback_del_full
52      (entry->e_fm, EVAS_CALLBACK_DEL,
53      _e_fm2_op_registry_entry_e_fm_deleted, entry);
54 }
55 
56 static inline E_Fm2_Op_Registry_Entry_Internal *
_e_fm2_op_registry_entry_internal_get(const E_Fm2_Op_Registry_Entry * entry)57 _e_fm2_op_registry_entry_internal_get(const E_Fm2_Op_Registry_Entry *entry)
58 {
59    return (E_Fm2_Op_Registry_Entry_Internal *)entry;
60 }
61 
62 static void
_e_fm2_op_registry_entry_internal_free(E_Fm2_Op_Registry_Entry_Internal * e)63 _e_fm2_op_registry_entry_internal_free(E_Fm2_Op_Registry_Entry_Internal *e)
64 {
65    _e_fm2_op_registry_entry_e_fm_monitor_stop(&(e->entry));
66 
67    while (e->listeners)
68      {
69         E_Fm2_Op_Registry_Entry_Listener *listener = (void *)e->listeners;
70         e->listeners = eina_inlist_remove(e->listeners, e->listeners);
71 
72         if (listener->free_data) listener->free_data(listener->data);
73         free(listener);
74      }
75 
76    eina_stringshare_del(e->entry.src);
77    eina_stringshare_del(e->entry.dst);
78    free(e);
79 }
80 
81 static inline int
_e_fm2_op_registry_entry_internal_unref(E_Fm2_Op_Registry_Entry_Internal * e)82 _e_fm2_op_registry_entry_internal_unref(E_Fm2_Op_Registry_Entry_Internal *e)
83 {
84    if (e->references < 1)
85      return 0;
86 
87    e->references--;
88    if (e->references > 0)
89      return e->references;
90 
91    _e_fm2_op_registry_entry_internal_free(e);
92    return 0;
93 }
94 
95 static inline int
_e_fm2_op_registry_entry_internal_ref(E_Fm2_Op_Registry_Entry_Internal * e)96 _e_fm2_op_registry_entry_internal_ref(E_Fm2_Op_Registry_Entry_Internal *e)
97 {
98    e->references++;
99    return e->references;
100 }
101 
102 static void
_e_fm2_op_registry_entry_listeners_call(const E_Fm2_Op_Registry_Entry_Internal * e)103 _e_fm2_op_registry_entry_listeners_call(const E_Fm2_Op_Registry_Entry_Internal *e)
104 {
105    E_Fm2_Op_Registry_Entry_Listener *listener;
106    Eina_Inlist *l;
107 
108    if (!e->listeners) return;
109 
110    EINA_INLIST_FOREACH_SAFE(e->listeners, l, listener)
111      listener->cb(listener->data, &e->entry);
112 }
113 
114 static void
_e_fm2_op_registry_entry_internal_unref_on_event(void * data,void * event EINA_UNUSED)115 _e_fm2_op_registry_entry_internal_unref_on_event(void *data, void *event EINA_UNUSED)
116 {
117    E_Fm2_Op_Registry_Entry_Internal *e = data;
118    _e_fm2_op_registry_entry_internal_unref(e);
119 }
120 
121 static void
_e_fm2_op_registry_entry_internal_event(E_Fm2_Op_Registry_Entry_Internal * e,int event_type)122 _e_fm2_op_registry_entry_internal_event(E_Fm2_Op_Registry_Entry_Internal *e, int event_type)
123 {
124    _e_fm2_op_registry_entry_internal_ref(e);
125    ecore_event_add(event_type, &(e->entry),
126                    _e_fm2_op_registry_entry_internal_unref_on_event, e);
127 }
128 
129 Eina_Bool
e_fm2_op_registry_entry_add(int id,Evas_Object * e_fm,E_Fm_Op_Type op,E_Fm2_Op_Registry_Abort_Func abrt)130 e_fm2_op_registry_entry_add(int id, Evas_Object *e_fm, E_Fm_Op_Type op, E_Fm2_Op_Registry_Abort_Func abrt)
131 {
132    E_Fm2_Op_Registry_Entry_Internal *e;
133 
134    e = E_NEW(E_Fm2_Op_Registry_Entry_Internal, 1);
135    if (!e) return 0;
136 
137    e->entry.id = id;
138    e->entry.e_fm = e_fm;
139    e->entry.start_time = ecore_loop_time_get();
140    e->entry.op = op;
141    e->entry.status = E_FM2_OP_STATUS_IN_PROGRESS;
142    e->entry.func.abort = abrt;
143    e->references = 1;
144 
145    if (!eina_hash_add(_e_fm2_op_registry, &id, e))
146      {
147         free(e);
148         return 0;
149      }
150 
151    _e_fm2_op_registry_entry_e_fm_monitor_start(&(e->entry));
152    _e_fm2_op_registry_entry_internal_event(e, E_EVENT_FM_OP_REGISTRY_ADD);
153 
154    return 1;
155 }
156 
157 Eina_Bool
e_fm2_op_registry_entry_del(int id)158 e_fm2_op_registry_entry_del(int id)
159 {
160    E_Fm2_Op_Registry_Entry_Internal *e;
161 
162    e = eina_hash_find(_e_fm2_op_registry, &id);
163    if (!e) return 0;
164    eina_hash_del_by_key(_e_fm2_op_registry, &id);
165 
166    _e_fm2_op_registry_entry_internal_event(e, E_EVENT_FM_OP_REGISTRY_DEL);
167    _e_fm2_op_registry_entry_internal_unref(e);
168 
169    return 1;
170 }
171 
172 static void
_e_fm2_op_registry_entry_internal_unref_on_changed_event(void * data,void * event EINA_UNUSED)173 _e_fm2_op_registry_entry_internal_unref_on_changed_event(void *data, void *event EINA_UNUSED)
174 {
175    E_Fm2_Op_Registry_Entry_Internal *e = data;
176    e->changed_event = NULL;
177    _e_fm2_op_registry_entry_internal_unref(e);
178 }
179 
180 void
e_fm2_op_registry_entry_changed(const E_Fm2_Op_Registry_Entry * entry)181 e_fm2_op_registry_entry_changed(const E_Fm2_Op_Registry_Entry *entry)
182 {
183    E_Fm2_Op_Registry_Entry_Internal *e;
184 
185    if (!entry) return;
186    e = _e_fm2_op_registry_entry_internal_get(entry);
187 
188    _e_fm2_op_registry_entry_listeners_call(e);
189 
190    if (e->changed_event) return;
191    _e_fm2_op_registry_entry_internal_ref(e);
192    e->changed_event = ecore_event_add
193        (E_EVENT_FM_OP_REGISTRY_CHANGED, &(e->entry),
194        _e_fm2_op_registry_entry_internal_unref_on_changed_event, e);
195 }
196 
197 /**
198  * Set the new e_fm for this operation.
199  *
200  * Use this call instead of directly setting in order to have the
201  * object to be monitored, when it is gone, the pointer will be made
202  * NULL.
203  *
204  * @note: it will not call any listener or add any event, please use
205  * e_fm2_op_registry_entry_changed().
206  */
207 void
e_fm2_op_registry_entry_e_fm_set(E_Fm2_Op_Registry_Entry * entry,Evas_Object * e_fm)208 e_fm2_op_registry_entry_e_fm_set(E_Fm2_Op_Registry_Entry *entry, Evas_Object *e_fm)
209 {
210    if (!entry) return;
211    _e_fm2_op_registry_entry_e_fm_monitor_stop(entry);
212    entry->e_fm = e_fm;
213    _e_fm2_op_registry_entry_e_fm_monitor_start(entry);
214 }
215 
216 /**
217  * Set the new files for this operation.
218  *
219  * Use this call instead of directly setting in order to have
220  * stringshare references right.
221  *
222  * @note: it will not call any listener or add any event, please use
223  * e_fm2_op_registry_entry_changed().
224  */
225 void
e_fm2_op_registry_entry_files_set(E_Fm2_Op_Registry_Entry * entry,const char * src,const char * dst)226 e_fm2_op_registry_entry_files_set(E_Fm2_Op_Registry_Entry *entry, const char *src, const char *dst)
227 {
228    if (!entry) return;
229 
230    eina_stringshare_replace(&entry->src, src);
231    eina_stringshare_replace(&entry->dst, dst);
232 }
233 
234 /**
235  * Adds a reference to given entry.
236  *
237  * @return: new number of references after operation or -1 on error.
238  */
239 E_API int
e_fm2_op_registry_entry_ref(E_Fm2_Op_Registry_Entry * entry)240 e_fm2_op_registry_entry_ref(E_Fm2_Op_Registry_Entry *entry)
241 {
242    E_Fm2_Op_Registry_Entry_Internal *e;
243 
244    if (!entry) return -1;
245 
246    e = _e_fm2_op_registry_entry_internal_get(entry);
247    return _e_fm2_op_registry_entry_internal_ref(e);
248 }
249 
250 /**
251  * Releases a reference to given entry.
252  *
253  * @return: new number of references after operation or -1 on error,
254  *    if 0 the entry was freed and pointer is then invalid.
255  */
256 E_API int
e_fm2_op_registry_entry_unref(E_Fm2_Op_Registry_Entry * entry)257 e_fm2_op_registry_entry_unref(E_Fm2_Op_Registry_Entry *entry)
258 {
259    E_Fm2_Op_Registry_Entry_Internal *e;
260 
261    if (!entry) return -1;
262 
263    e = _e_fm2_op_registry_entry_internal_get(entry);
264    return _e_fm2_op_registry_entry_internal_unref(e);
265 }
266 
267 /**
268  * Returns the X window associated to this operation.
269  *
270  * This will handle all bureaucracy to get X window based on e_fm evas
271  * object.
272  *
273  * @return: 0 if no window, window identifier otherwise.
274  */
275 E_API Ecore_X_Window
e_fm2_op_registry_entry_xwin_get(const E_Fm2_Op_Registry_Entry * entry)276 e_fm2_op_registry_entry_xwin_get(const E_Fm2_Op_Registry_Entry *entry)
277 {
278    Evas *e;
279    Ecore_Evas *ee;
280 
281    if (!entry) return 0;
282    if (!entry->e_fm) return 0;
283 
284    e = evas_object_evas_get(entry->e_fm);
285    if (!e) return 0;
286 
287    ee = evas_data_attach_get(e);
288    if (!ee) return 0;
289 
290    return (Ecore_X_Window)(long)ecore_evas_window_get(ee);
291 }
292 
293 /**
294  * Discover entry based on its identifier.
295  *
296  * @note: does not increment reference.
297  */
298 E_API E_Fm2_Op_Registry_Entry *
e_fm2_op_registry_entry_get(int id)299 e_fm2_op_registry_entry_get(int id)
300 {
301    return eina_hash_find(_e_fm2_op_registry, &id);
302 }
303 
304 /**
305  * Adds a function to be called when entry changes.
306  *
307  * When entry changes any attribute this function will be called.
308  *
309  * @param: entry entry to operate on.
310  * @param: cb function to callback on changes.
311  * @param: data extra data to give to @p cb
312  * @param: free_data function to call when listener is removed, entry
313  *    is deleted or any error occur in this function and listener
314  *    cannot be added.
315  *
316  * @note: does not increment reference.
317  * @note: on errors, @p free_data will be called.
318  */
319 E_API void
e_fm2_op_registry_entry_listener_add(E_Fm2_Op_Registry_Entry * entry,void (* cb)(void * data,const E_Fm2_Op_Registry_Entry * entry),const void * data,void (* free_data)(void * data))320 e_fm2_op_registry_entry_listener_add(E_Fm2_Op_Registry_Entry *entry, void (*cb)(void *data, const E_Fm2_Op_Registry_Entry *entry), const void *data, void (*free_data)(void *data))
321 {
322    E_Fm2_Op_Registry_Entry_Internal *e;
323    E_Fm2_Op_Registry_Entry_Listener *listener;
324 
325    if ((!entry) || (!cb))
326      {
327         if (free_data) free_data((void *)data);
328         return;
329      }
330 
331    listener = malloc(sizeof(*listener));
332    if (!listener)
333      {
334         if (free_data) free_data((void *)data);
335         return;
336      }
337    listener->cb = cb;
338    listener->data = (void *)data;
339    listener->free_data = free_data;
340 
341    e = _e_fm2_op_registry_entry_internal_get(entry);
342    e->listeners = eina_inlist_append(e->listeners, EINA_INLIST_GET(listener));
343 }
344 
345 /**
346  * Removes the function to be called when entry changes.
347  *
348  * @param: entry entry to operate on.
349  * @param: cb function to callback on changes.
350  * @param: data extra data to give to @p cb
351  *
352  * @note: does not decrement reference.
353  * @see: e_fm2_op_registry_entry_listener_add()
354  */
355 E_API void
e_fm2_op_registry_entry_listener_del(E_Fm2_Op_Registry_Entry * entry,void (* cb)(void * data,const E_Fm2_Op_Registry_Entry * entry),const void * data)356 e_fm2_op_registry_entry_listener_del(E_Fm2_Op_Registry_Entry *entry, void (*cb)(void *data, const E_Fm2_Op_Registry_Entry *entry), const void *data)
357 {
358    E_Fm2_Op_Registry_Entry_Internal *e;
359    E_Fm2_Op_Registry_Entry_Listener *l;
360 
361    if ((!entry) || (!cb)) return;
362    e = _e_fm2_op_registry_entry_internal_get(entry);
363 
364    EINA_INLIST_FOREACH(e->listeners, l)
365      if ((l->cb == cb) && (l->data == data))
366        {
367           e->listeners = eina_inlist_remove(e->listeners, EINA_INLIST_GET(l));
368           if (l->free_data) l->free_data(l->data);
369           free(l);
370           return;
371        }
372 }
373 
374 /**
375  * Returns an iterator over all the entries in the fm operations registry.
376  *
377  * @warning: this iterator is just valid until new entries are added
378  *    or removed (usually happens from main loop). This is because
379  *    when system is back to main loop it can report new events and
380  *    operations can be added or removed from this registry. In other
381  *    words, it is fine to call this function, immediately walk the
382  *    iterator and do something, then free the iterator. You can use
383  *    it to create a shadow list if you wish.
384  *
385  * @see e_fm2_op_registry_get_all()
386  */
387 E_API Eina_Iterator *
e_fm2_op_registry_iterator_new(void)388 e_fm2_op_registry_iterator_new(void)
389 {
390    return eina_hash_iterator_data_new(_e_fm2_op_registry);
391 }
392 
393 /**
394  * Returns a shadow list with all entries in the registry.
395  *
396  * All entries will have references incremented, so you must free the
397  * list with e_fm2_op_registry_get_all_free() to free the list and
398  * release these references.
399  *
400  * @note: List is unsorted!
401  * @note: if you need a simple, immediate walk, use
402  *    e_fm2_op_registry_iterator_new()
403  */
404 E_API Eina_List *
e_fm2_op_registry_get_all(void)405 e_fm2_op_registry_get_all(void)
406 {
407    Eina_List *list;
408    Eina_Iterator *it;
409    E_Fm2_Op_Registry_Entry_Internal *e;
410 
411    list = NULL;
412    it = eina_hash_iterator_data_new(_e_fm2_op_registry);
413    EINA_ITERATOR_FOREACH(it, e)
414      {
415         _e_fm2_op_registry_entry_internal_ref(e);
416         list = eina_list_append(list, &(e->entry));
417      }
418    eina_iterator_free(it);
419 
420    return list;
421 }
422 
423 E_API void
e_fm2_op_registry_get_all_free(Eina_List * list)424 e_fm2_op_registry_get_all_free(Eina_List *list)
425 {
426    E_Fm2_Op_Registry_Entry *entry;
427    EINA_LIST_FREE(list, entry)
428      e_fm2_op_registry_entry_unref(entry);
429 }
430 
431 E_API Eina_Bool
e_fm2_op_registry_is_empty(void)432 e_fm2_op_registry_is_empty(void)
433 {
434    return eina_hash_population(_e_fm2_op_registry) == 0;
435 }
436 
437 E_API int
e_fm2_op_registry_count(void)438 e_fm2_op_registry_count(void)
439 {
440    return eina_hash_population(_e_fm2_op_registry);
441 }
442 
443 EINTERN unsigned int
e_fm2_op_registry_init(void)444 e_fm2_op_registry_init(void)
445 {
446    _e_fm2_init_count++;
447    if (_e_fm2_init_count > 1) return _e_fm2_init_count;
448 
449    _e_fm2_op_registry = eina_hash_int32_new(NULL);
450    if (!_e_fm2_op_registry)
451      {
452         _e_fm2_init_count = 0;
453         return 0;
454      }
455 
456    if (E_EVENT_FM_OP_REGISTRY_ADD == 0)
457      E_EVENT_FM_OP_REGISTRY_ADD = ecore_event_type_new();
458    if (E_EVENT_FM_OP_REGISTRY_DEL == 0)
459      E_EVENT_FM_OP_REGISTRY_DEL = ecore_event_type_new();
460    if (E_EVENT_FM_OP_REGISTRY_CHANGED == 0)
461      E_EVENT_FM_OP_REGISTRY_CHANGED = ecore_event_type_new();
462 
463    return 1;
464 }
465 
466 EINTERN unsigned int
e_fm2_op_registry_shutdown(void)467 e_fm2_op_registry_shutdown(void)
468 {
469    if (_e_fm2_init_count == 0) return 0;
470    _e_fm2_init_count--;
471    if (_e_fm2_init_count > 0) return _e_fm2_init_count;
472 
473    eina_hash_free(_e_fm2_op_registry);
474    _e_fm2_op_registry = NULL;
475 
476    return 0;
477 }
478 
479 E_API void
e_fm2_op_registry_entry_abort(E_Fm2_Op_Registry_Entry * entry)480 e_fm2_op_registry_entry_abort(E_Fm2_Op_Registry_Entry *entry)
481 {
482    if (!entry) return;
483 
484    if (entry->func.abort)
485      entry->func.abort(entry);
486 }
487 
488