Imported Upstream version 1.7.7
[platform/upstream/edje.git] / src / lib / edje_cache.c
1 #include "edje_private.h"
2
3
4 static Eina_Hash   *_edje_file_hash = NULL;
5 static int          _edje_file_cache_size = 16;
6 static Eina_List   *_edje_file_cache = NULL;
7
8 static int          _edje_collection_cache_size = 16;
9
10 static Edje_Part_Collection *
11 _edje_file_coll_open(Edje_File *edf, const char *coll)
12 {
13    Edje_Part_Collection *edc = NULL;
14    Edje_Part_Collection_Directory_Entry *ce;
15    int id = -1, size = 0;
16    Eina_List *l;
17    char buf[256];
18    char *buffer;
19    void *data;
20
21    ce = eina_hash_find(edf->collection, coll);
22    if (!ce) return NULL;
23
24    if (ce->ref)
25      {
26         ce->ref->references++;
27         return ce->ref;
28      }
29
30    EINA_LIST_FOREACH(edf->collection_cache, l, edc)
31      {
32         if (!strcmp(edc->part, coll))
33           {
34              edc->references = 1;
35              ce->ref = edc;
36
37              edf->collection_cache = eina_list_remove_list(edf->collection_cache, l);
38              return ce->ref;
39           }
40      }
41
42    id = ce->id;
43    if (id < 0) return NULL;
44
45 #define INIT_EMP(Tp, Sz, Ce)                                           \
46    buffer = alloca(strlen(ce->entry) + strlen(#Tp) + 2);               \
47    sprintf(buffer, "%s/%s", ce->entry, #Tp);                           \
48    Ce->mp.Tp = eina_mempool_add("one_big", buffer, NULL, sizeof (Sz), Ce->count.Tp); \
49    _emp_##Tp = Ce->mp.Tp;
50
51 #define INIT_EMP_BOTH(Tp, Sz, Ce)                                       \
52    INIT_EMP(Tp, Sz, Ce)                                                 \
53    Ce->mp_rtl.Tp = eina_mempool_add("one_big", buffer, NULL,            \
54          sizeof (Sz), Ce->count.Tp);
55
56    INIT_EMP_BOTH(RECTANGLE, Edje_Part_Description_Common, ce);
57    INIT_EMP_BOTH(TEXT, Edje_Part_Description_Text, ce);
58    INIT_EMP_BOTH(IMAGE, Edje_Part_Description_Image, ce);
59    INIT_EMP_BOTH(PROXY, Edje_Part_Description_Proxy, ce);
60    INIT_EMP_BOTH(SWALLOW, Edje_Part_Description_Common, ce);
61    INIT_EMP_BOTH(TEXTBLOCK, Edje_Part_Description_Text, ce);
62    INIT_EMP_BOTH(GROUP, Edje_Part_Description_Common, ce);
63    INIT_EMP_BOTH(BOX, Edje_Part_Description_Box, ce);
64    INIT_EMP_BOTH(TABLE, Edje_Part_Description_Table, ce);
65    INIT_EMP_BOTH(EXTERNAL, Edje_Part_Description_External, ce);
66    INIT_EMP_BOTH(SPACER, Edje_Part_Description_Common, ce);
67    INIT_EMP(part, Edje_Part, ce);
68
69    snprintf(buf, sizeof(buf), "edje/collections/%i", id);
70    edc = eet_data_read(edf->ef, _edje_edd_edje_part_collection, buf);
71    if (!edc) return NULL;
72
73    edc->references = 1;
74    edc->part = ce->entry;
75
76    /* For Edje file build with Edje 1.0 */
77    if (edf->version <= 3 && edf->minor <= 1)
78      {
79         /* This will preserve previous rendering */
80         unsigned int i;
81
82         /* people expect signal to not be broadcasted */
83         edc->broadcast_signal = EINA_FALSE;
84
85         /* people expect text.align to be 0.0 0.0 */
86         for (i = 0; i < edc->parts_count; ++i)
87           {
88              if (edc->parts[i]->type == EDJE_PART_TYPE_TEXTBLOCK)
89                {
90                   Edje_Part_Description_Text *text;
91                   unsigned int j;
92
93                   text = (Edje_Part_Description_Text*) edc->parts[i]->default_desc;
94                   text->text.align.x = TO_DOUBLE(0.0);
95                   text->text.align.y = TO_DOUBLE(0.0);
96
97                   for (j = 0; j < edc->parts[i]->other.desc_count; ++j)
98                     {
99                        text =  (Edje_Part_Description_Text*) edc->parts[i]->other.desc[j];
100                        text->text.align.x = TO_DOUBLE(0.0);
101                        text->text.align.y = TO_DOUBLE(0.0);
102                     }
103                }
104           }
105      }
106
107    snprintf(buf, sizeof(buf), "edje/scripts/embryo/compiled/%i", id);
108    data = eet_read(edf->ef, buf, &size);
109
110    if (data)
111      {
112         edc->script = embryo_program_new(data, size);
113         _edje_embryo_script_init(edc);
114         free(data);
115      }
116
117    snprintf(buf, sizeof(buf), "edje/scripts/lua/%i", id);
118    data = eet_read(edf->ef, buf, &size);
119
120    if (data)
121      {
122         _edje_lua2_script_load(edc, data, size);
123         free(data);
124      }
125
126    ce->ref = edc;
127
128    return edc;
129 }
130
131 #ifdef HAVE_EIO
132 static Eina_Bool
133 _edje_file_warn(void *data)
134 {
135    Edje_File *edf = data;
136    Eina_List *l, *ll;
137    Edje *ed;
138
139    edf->references++;
140
141    EINA_LIST_FOREACH(edf->edjes, l, ed)
142      _edje_ref(ed);
143
144    EINA_LIST_FOREACH(edf->edjes, l, ed)
145      {
146         _edje_emit(ed, "edje,change,file", "edje");
147      }
148
149    EINA_LIST_FOREACH_SAFE(edf->edjes, l, ll, ed)
150      _edje_unref(ed);
151
152    edf->references--;
153
154    edf->timeout = NULL;
155    return EINA_FALSE;
156 }
157
158 static Eina_Bool
159 _edje_file_change(void *data, int ev_type __UNUSED__, void *event)
160 {
161    Edje_File *edf = data;
162    Eio_Monitor_Event *ev = event;
163
164    if (ev->monitor == edf->monitor)
165      {
166         if (edf->timeout) ecore_timer_del(edf->timeout);
167         edf->timeout = ecore_timer_add(0.5, _edje_file_warn, edf);
168      }
169    return ECORE_CALLBACK_PASS_ON;
170 }
171 #endif
172
173 static Edje_File *
174 _edje_file_open(const char *file, const char *coll, int *error_ret, Edje_Part_Collection **edc_ret, time_t mtime)
175 {
176    Edje_Color_Class *cc;
177    Edje_File *edf;
178    Eina_List *l;
179    Edje_Part_Collection *edc;
180    Eet_File *ef;
181 #ifdef HAVE_EIO
182    Ecore_Event_Handler *ev;
183 #endif
184
185    ef = eet_open(file, EET_FILE_MODE_READ);
186    if (!ef)
187      {
188         *error_ret = EDJE_LOAD_ERROR_UNKNOWN_FORMAT;
189         return NULL;
190      }
191    edf = eet_data_read(ef, _edje_edd_edje_file, "edje/file");
192    if (!edf)
193      {
194         *error_ret = EDJE_LOAD_ERROR_CORRUPT_FILE;
195         eet_close(ef);
196         return NULL;
197      }
198
199    edf->ef = ef;
200    edf->mtime = mtime;
201 #ifdef HAVE_EIO
202    edf->monitor = eio_monitor_add(file);
203    ev = ecore_event_handler_add(EIO_MONITOR_FILE_DELETED, _edje_file_change, edf);
204    edf->handlers = eina_list_append(edf->handlers, ev);
205    ev = ecore_event_handler_add(EIO_MONITOR_FILE_MODIFIED, _edje_file_change, edf);
206    edf->handlers = eina_list_append(edf->handlers, ev);
207    ev = ecore_event_handler_add(EIO_MONITOR_FILE_CREATED, _edje_file_change, edf);
208    edf->handlers = eina_list_append(edf->handlers, ev);
209    ev = ecore_event_handler_add(EIO_MONITOR_SELF_DELETED, _edje_file_change, edf);
210    edf->handlers = eina_list_append(edf->handlers, ev);
211 #endif
212
213    if (edf->version != EDJE_FILE_VERSION)
214      {
215         *error_ret = EDJE_LOAD_ERROR_INCOMPATIBLE_FILE;
216         _edje_file_free(edf);
217         return NULL;
218      }
219    if (!edf->collection)
220      {
221         *error_ret = EDJE_LOAD_ERROR_CORRUPT_FILE;
222         _edje_file_free(edf);
223         return NULL;
224      }
225
226    if (edf->minor > EDJE_FILE_MINOR)
227      {
228         WRN("`%s` may use feature from a newer edje and could not show up as expected.", file);
229      }
230
231    edf->path = eina_stringshare_add(file);
232    edf->references = 1;
233
234    /* This should be done at edje generation time */
235    _edje_textblock_style_parse_and_fix(edf);
236    edf->color_hash = eina_hash_string_small_new(NULL);
237    EINA_LIST_FOREACH(edf->color_classes, l, cc)
238      if (cc->name)
239        eina_hash_direct_add(edf->color_hash, cc->name, cc);
240
241    if (coll)
242      {
243         edc = _edje_file_coll_open(edf, coll);
244         if (!edc)
245           {
246              *error_ret = EDJE_LOAD_ERROR_UNKNOWN_COLLECTION;
247           }
248         if (edc_ret) *edc_ret = edc;
249      }
250
251    return edf;
252 }
253
254 static void
255 _edje_file_dangling(Edje_File *edf)
256 {
257    if (edf->dangling) return;
258    edf->dangling = EINA_TRUE;
259
260    eina_hash_del(_edje_file_hash, edf->path, edf);
261    if (!eina_hash_population(_edje_file_hash))
262      {
263        eina_hash_free(_edje_file_hash);
264        _edje_file_hash = NULL;
265      }
266 }
267
268 Edje_File *
269 _edje_cache_file_coll_open(const char *file, const char *coll, int *error_ret, Edje_Part_Collection **edc_ret, Edje *ed)
270 {
271    Edje_File *edf;
272    Eina_List *l, *hist;
273    Edje_Part_Collection *edc;
274    Edje_Part *ep;
275    struct stat st;
276
277    if (stat(file, &st) != 0)
278      {
279         *error_ret = EDJE_LOAD_ERROR_DOES_NOT_EXIST;
280         return NULL;
281      }
282
283    if (!_edje_file_hash)
284      {
285         _edje_file_hash = eina_hash_string_small_new(NULL);
286         goto find_list;
287      }
288
289    edf = eina_hash_find(_edje_file_hash, file);
290    if (edf)
291      {
292         if (edf->mtime != st.st_mtime)
293           {
294              _edje_file_dangling(edf);
295              goto open_new;
296           }
297
298         edf->references++;
299         goto open;
300      }
301    
302 find_list:
303    EINA_LIST_FOREACH(_edje_file_cache, l, edf)
304      {
305         if (!strcmp(edf->path, file))
306           {
307              if (edf->mtime != st.st_mtime)
308                {
309                   _edje_file_cache = eina_list_remove_list(_edje_file_cache, l);
310                   _edje_file_free(edf);
311                   goto open_new;
312                }
313
314              edf->references = 1;
315              _edje_file_cache = eina_list_remove_list(_edje_file_cache, l);
316              eina_hash_add(_edje_file_hash, file, edf);
317              goto open;
318           }
319      }
320
321 open_new:
322    if (!_edje_file_hash)
323       _edje_file_hash = eina_hash_string_small_new(NULL);
324
325    edf = _edje_file_open(file, coll, error_ret, edc_ret, st.st_mtime);
326    if (!edf)
327       return NULL;
328
329 #ifdef HAVE_EIO
330    if (ed) edf->edjes = eina_list_append(edf->edjes, ed);
331 #else
332    (void) ed;
333 #endif
334
335    eina_hash_add(_edje_file_hash, file, edf);
336    return edf;
337
338 open:
339    if (!coll)
340       return edf;
341
342    edc = _edje_file_coll_open(edf, coll);
343    if (!edc)
344      {
345         *error_ret = EDJE_LOAD_ERROR_UNKNOWN_COLLECTION;
346      }
347    else
348      {
349         if (!edc->checked)
350           {
351              unsigned int j;
352
353              for (j = 0; j < edc->parts_count; ++j)
354                {
355                   Edje_Part *ep2;
356
357                   ep = edc->parts[j];
358
359                   /* Register any color classes in this parts descriptions. */
360                   hist = NULL;
361                   hist = eina_list_append(hist, ep);
362                   ep2 = ep;
363                   while (ep2->dragable.confine_id >= 0)
364                     {
365                        if (ep2->dragable.confine_id >= (int) edc->parts_count)
366                          {
367                             ERR("confine_to above limit. invalidating it.");
368                             ep2->dragable.confine_id = -1;
369                             break;
370                          }
371
372                        ep2 = edc->parts[ep2->dragable.confine_id];
373                        if (eina_list_data_find(hist, ep2))
374                          {
375                             ERR("confine_to loops. invalidating loop.");
376                             ep2->dragable.confine_id = -1;
377                             break;
378                          }
379                        hist = eina_list_append(hist, ep2);
380                     }
381                   eina_list_free(hist);
382                   hist = NULL;
383                   hist = eina_list_append(hist, ep);
384                   ep2 = ep;
385                   while (ep2->dragable.event_id >= 0)
386                     {
387                        Edje_Part* prev;
388
389                        if (ep2->dragable.event_id >= (int) edc->parts_count)
390                          {
391                             ERR("event_id above limit. invalidating it.");
392                             ep2->dragable.event_id = -1;
393                             break;
394                          }
395                        prev = ep2;
396
397                        ep2 = edc->parts[ep2->dragable.event_id];
398                        if (!ep2->dragable.x && !ep2->dragable.y)
399                          {
400                             prev->dragable.event_id = -1;
401                             break;
402                          }
403
404                        if (eina_list_data_find(hist, ep2))
405                          {
406                             ERR("events_to loops. invalidating loop.");
407                             ep2->dragable.event_id = -1;
408                             break;
409                          }
410                        hist = eina_list_append(hist, ep2);
411                     }
412                   eina_list_free(hist);
413                   hist = NULL;
414                   hist = eina_list_append(hist, ep);
415                   ep2 = ep;
416                   while (ep2->clip_to_id >= 0)
417                     {
418                        if (ep2->clip_to_id >= (int) edc->parts_count)
419                          {
420                             ERR("clip_to_id above limit. invalidating it.");
421                             ep2->clip_to_id = -1;
422                             break;
423                          }
424
425                        ep2 = edc->parts[ep2->clip_to_id];
426                        if (eina_list_data_find(hist, ep2))
427                          {
428                             ERR("clip_to loops. invalidating loop.");
429                             ep2->clip_to_id = -1;
430                             break;
431                          }
432                        hist = eina_list_append(hist, ep2);
433                     }
434                   eina_list_free(hist);
435                   hist = NULL;
436                }
437             edc->checked = 1;
438           }
439      }
440 #ifdef HAVE_EIO
441    if (edc && ed) edf->edjes = eina_list_append(edf->edjes, ed);
442 #else
443    (void) ed;
444 #endif
445
446    if (edc_ret) *edc_ret = edc;
447
448    return edf;
449 }
450
451 void
452 _edje_cache_coll_clean(Edje_File *edf)
453 {
454    while ((edf->collection_cache) &&
455           (eina_list_count(edf->collection_cache) > (unsigned int) _edje_collection_cache_size))
456      {
457         Edje_Part_Collection_Directory_Entry *ce;
458         Edje_Part_Collection *edc;
459
460         edc = eina_list_data_get(eina_list_last(edf->collection_cache));
461         edf->collection_cache = eina_list_remove_list(edf->collection_cache, eina_list_last(edf->collection_cache));
462
463         ce = eina_hash_find(edf->collection, edc->part);
464         _edje_collection_free(edf, edc, ce);
465      }
466 }
467
468 void
469 _edje_cache_coll_flush(Edje_File *edf)
470 {
471    while (edf->collection_cache)
472      {
473         Edje_Part_Collection_Directory_Entry *ce;
474         Edje_Part_Collection *edc;
475         Eina_List *last;
476
477         last = eina_list_last(edf->collection_cache);
478         edc = eina_list_data_get(last);
479         edf->collection_cache = eina_list_remove_list(edf->collection_cache,
480                                                       last);
481
482         ce = eina_hash_find(edf->collection, edc->part);
483         _edje_collection_free(edf, edc, ce);
484      }
485 }
486
487 void
488 _edje_cache_coll_unref(Edje_File *edf, Edje_Part_Collection *edc)
489 {
490    Edje_Part_Collection_Directory_Entry *ce;
491
492    edc->references--;
493    if (edc->references != 0) return;
494
495    ce = eina_hash_find(edf->collection, edc->part);
496    if (!ce)
497      {
498         ERR("Something is wrong with reference count of '%s'.", edc->part);
499      }
500    else if (ce->ref)
501      {
502         ce->ref = NULL;
503
504         if (edf->dangling)
505           {
506              /* No need to keep the collection around if the file is dangling */
507              _edje_collection_free(edf, edc, ce);
508              _edje_cache_coll_flush(edf);
509           }
510         else
511           {
512              edf->collection_cache = eina_list_prepend(edf->collection_cache, edc);
513              _edje_cache_coll_clean(edf);
514           }
515      }
516 }
517
518 static void
519 _edje_cache_file_clean(void)
520 {
521    int count;
522
523    count = eina_list_count(_edje_file_cache);
524    while ((_edje_file_cache) && (count > _edje_file_cache_size))
525      {
526         Eina_List *last;
527         Edje_File *edf;
528
529         last = eina_list_last(_edje_file_cache);
530         edf = eina_list_data_get(last);
531         _edje_file_cache = eina_list_remove_list(_edje_file_cache, last);
532         _edje_file_free(edf);
533         count = eina_list_count(_edje_file_cache);
534      }
535 }
536
537 void
538 _edje_cache_file_unref(Edje_File *edf)
539 {
540    edf->references--;
541    if (edf->references != 0) return;
542
543    if (edf->dangling)
544      {
545         _edje_file_free(edf);
546         return;
547      }
548
549    eina_hash_del(_edje_file_hash, edf->path, edf);
550    if (!eina_hash_population(_edje_file_hash))
551      {
552        eina_hash_free(_edje_file_hash);
553        _edje_file_hash = NULL;
554      }
555    _edje_file_cache = eina_list_prepend(_edje_file_cache, edf);
556    _edje_cache_file_clean();
557 }
558
559 void
560 _edje_file_cache_shutdown(void)
561 {
562    edje_file_cache_flush();
563 }
564
565
566 /*============================================================================*
567  *                                 Global                                     *
568  *============================================================================*/
569
570 /*============================================================================*
571  *                                   API                                      *
572  *============================================================================*/
573
574
575 EAPI void
576 edje_file_cache_set(int count)
577 {
578    if (count < 0) count = 0;
579    _edje_file_cache_size = count;
580    _edje_cache_file_clean();
581 }
582
583
584 EAPI int
585 edje_file_cache_get(void)
586 {
587    return _edje_file_cache_size;
588 }
589
590
591 EAPI void
592 edje_file_cache_flush(void)
593 {
594    int ps;
595
596    ps = _edje_file_cache_size;
597    _edje_file_cache_size = 0;
598    _edje_cache_file_clean();
599    _edje_file_cache_size = ps;
600 }
601
602
603 EAPI void
604 edje_collection_cache_set(int count)
605 {
606    Eina_List *l;
607    Edje_File *edf;
608
609    if (count < 0) count = 0;
610    _edje_collection_cache_size = count;
611    EINA_LIST_FOREACH(_edje_file_cache, l, edf)
612      _edje_cache_coll_clean(edf);
613    /* FIXME: freach in file hash too! */
614 }
615
616
617 EAPI int
618 edje_collection_cache_get(void)
619 {
620    return _edje_collection_cache_size;
621 }
622
623
624 EAPI void
625 edje_collection_cache_flush(void)
626 {
627    int ps;
628    Eina_List *l;
629    Edje_File *edf;
630
631    ps = _edje_collection_cache_size;
632    _edje_collection_cache_size = 0;
633    EINA_LIST_FOREACH(_edje_file_cache, l, edf)
634      _edje_cache_coll_flush(edf);
635    /* FIXME: freach in file hash too! */
636    _edje_collection_cache_size = ps;
637 }