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