evas/cserve2: Only declare timeval function if debug is
[profile/ivi/evas.git] / src / bin / evas_cserve2_cache.c
1 #ifdef HAVE_CONFIG_H
2 # include "config.h"
3 #endif
4
5 #include <string.h>
6
7 #ifdef DEBUG_LOAD_TIME
8    #include <sys/time.h>
9 #endif
10
11 #include "evas_cserve2.h"
12 #include "evas_cs2_utils.h"
13
14 typedef struct _Request_Funcs Request_Funcs;
15 typedef struct _Request Request;
16
17 typedef struct _Entry Entry;
18 typedef struct _Reference Reference;
19 typedef struct _Waiter Waiter;
20 typedef struct _File_Data File_Data;
21 typedef struct _Image_Data Image_Data;
22 typedef struct _File_Watch File_Watch;
23
24 typedef struct _Font_Source Font_Source;
25 typedef struct _Font_Entry Font_Entry;
26 typedef struct _Font_Cache Font_Cache;
27
28 typedef void *(*Request_Msg_Create)(Entry *e, int *size);
29 typedef void (*Request_Response)(Entry *e, void *resp);
30 typedef void (*Request_Error)(Entry *e, Error_Type error);
31
32 struct _Request_Funcs {
33    Request_Msg_Create msg_create;
34    Request_Response response;
35    Request_Error error;
36 };
37
38 struct _Request {
39    Entry *entry;
40    Eina_List *waiters;
41    Eina_Bool processing;
42    Request_Funcs *funcs;
43 };
44
45 typedef enum {
46    CSERVE2_IMAGE_FILE,
47    CSERVE2_IMAGE_DATA,
48    CSERVE2_FONT_ENTRY
49 } Entry_Type;
50
51 struct _Entry {
52    unsigned int id;
53    Eina_List *references;
54    Request *request;
55    Entry_Type type;
56 #ifdef DEBUG_LOAD_TIME
57    struct timeval load_start;
58    struct timeval load_finish;
59 #endif
60 };
61
62 struct _File_Data {
63    Entry base;
64    char *path;
65    char *key;
66    int w, h;
67    int frame_count;
68    int loop_count;
69    int loop_hint;
70    const char *loader_data;
71    File_Watch *watcher;
72    Eina_List *images;
73    Eina_Bool alpha : 1;
74    Eina_Bool invalid : 1;
75 };
76
77 // Default values for load options commented below
78 struct _Image_Data {
79    Entry base;
80    unsigned int file_id;
81    File_Data *file;
82    struct {
83       double dpi; // dpi < -1
84       int w, h; // w and h < -1
85       int scale_down; // scale_down < -1
86       int rx, ry, rw, rh; // rx, ry, rw, rh < -1
87       Eina_Bool orientation; // orientation == 0
88    } opts;
89    Shm_Handle *shm;
90    Eina_Bool alpha_sparse : 1;
91    Eina_Bool unused : 1;
92    Eina_Bool doload : 1;
93 };
94
95 struct _Font_Source {
96    const char *key;
97    const char *name;
98    const char *file;
99    int references;
100    void *ft;
101 };
102
103 struct _Font_Entry {
104    Entry base;
105    Font_Request *request;
106    unsigned int rend_flags;
107    unsigned int size;
108    unsigned int dpi;
109    Font_Source *src;
110    void *ft;
111    Fash_Glyph2 *glyphs;
112    Eina_Inlist *caches;
113    Font_Cache *last_cache;
114    Eina_Bool unused : 1;
115 #ifdef DEBUG_LOAD_TIME
116    struct timeval load_start;
117    struct timeval load_finish;
118    int gl_load_time;
119 #endif
120 };
121
122 struct _Font_Cache {
123    EINA_INLIST;
124    Font_Entry *fe;
125    Shm_Handle *shm;
126    unsigned int usage;
127    int inuse;
128    Eina_Inlist *glyphs;
129    unsigned int nglyphs;
130 };
131
132 struct _Glyph_Entry {
133    EINA_INLIST;
134    Font_Entry *fe;
135    Font_Cache *fc;
136    unsigned int index;
137    unsigned int offset;
138    unsigned int size;
139    unsigned int rows;
140    unsigned int width;
141    unsigned int pitch;
142    unsigned int num_grays;
143    unsigned int pixel_mode;
144 };
145
146 struct _Glyphs_Request {
147    Client *client;
148    Font_Entry *fe;
149    unsigned int current;
150    unsigned int nglyphs;
151    unsigned int *glyphs;
152    unsigned int nrender;
153    unsigned int *render;
154    unsigned int nanswer;
155    Glyph_Entry **answer;
156    unsigned int hint;
157 };
158
159 typedef struct _Glyphs_Request Glyphs_Request;
160
161 struct _Glyphs_Group {
162    Font_Cache *fc;
163    Eina_List *glyphs;
164 };
165
166 typedef struct _Glyphs_Group Glyphs_Group;
167
168 struct _Reference {
169    Client *client;
170    Entry *entry;
171    unsigned int client_entry_id; // for reverse lookup
172    int count;
173 };
174
175 struct _Waiter {
176    Reference *ref;
177    unsigned int rid;
178    Message_Type type;
179 };
180
181 struct _File_Watch {
182    const char *path;
183    Eina_List *entries;
184 };
185
186 static Eina_List *open_requests = NULL;
187 static Eina_List *load_requests = NULL;
188 static Eina_List *spload_requests = NULL; // speculative preload requests
189
190 static unsigned int _file_id = 0; // id unique number
191 static unsigned int _image_id = 0; // id unique number
192 static Eina_Hash *file_ids = NULL; // maps path + key --> file_id
193 static Eina_Hash *file_entries = NULL; // maps file_id --> entry
194
195 static Eina_Hash *image_ids = NULL; // maps file id + load opts --> image id
196 static Eina_Hash *image_entries = NULL; // maps image_id --> entry
197
198 static Eina_Hash *font_sources = NULL; // font path --> font source
199 static Eina_Hash *font_entries = NULL; // maps font path + options --> entry
200
201 static Eina_Hash *file_watch = NULL;
202
203 static Eina_List *image_entries_lru = NULL;
204
205 static Eina_List *font_shm_lru = NULL;
206
207 static int max_unused_mem_usage = 5 * 1024; /* in kbytes */
208 static int unused_mem_usage = 0;
209 static int max_font_usage = 10 * 4 * 1024; /* in kbytes */
210 static int font_mem_usage = 0;
211
212 static inline void
213 _entry_load_start(Entry *e)
214 {
215 #ifdef DEBUG_LOAD_TIME
216    gettimeofday(&e->load_start, NULL);
217 #endif
218 }
219
220 static inline void
221 _entry_load_finish(Entry *e)
222 {
223 #ifdef DEBUG_LOAD_TIME
224    gettimeofday(&e->load_finish, NULL);
225 #endif
226 }
227
228 #ifdef DEBUG_LOAD_TIME
229 static int
230 _timeval_sub(const struct timeval *tv2, const struct timeval *tv1)
231 {
232     int t1, t2;
233
234     t1 = tv1->tv_usec + tv1->tv_sec * 1000000;
235     t2 = tv2->tv_usec + tv2->tv_sec * 1000000;
236
237     return t2 - t1;
238 }
239 #endif
240
241 static void
242 _image_opened_send(Client *client, File_Data *entry, unsigned int rid)
243 {
244     int size;
245     Msg_Opened msg;
246
247     DBG("Sending OPENED reply for entry: %d and RID: %d.", entry->base.id, rid);
248     // clear the struct with possible paddings, since it is not aligned.
249     memset(&msg, 0, sizeof(msg));
250     msg.base.rid = rid;
251     msg.base.type = CSERVE2_OPENED;
252     msg.image.w = entry->w;
253     msg.image.h = entry->h;
254     msg.image.frame_count = entry->frame_count;
255     msg.image.loop_count = entry->loop_count;
256     msg.image.loop_hint = entry->loop_hint;
257     msg.image.alpha = entry->alpha;
258
259     size = sizeof(msg);
260     cserve2_client_send(client, &size, sizeof(size));
261     cserve2_client_send(client, &msg, sizeof(msg));
262     // _cserve2_cache_load_requests_process();
263 }
264
265 static void
266 _image_loaded_send(Client *client, Image_Data *entry, unsigned int rid)
267 {
268    int size;
269    const char *shmpath = cserve2_shm_name_get(entry->shm);
270    Msg_Loaded msg;
271    int path_len;
272    char *buf;
273
274    DBG("Sending LOADED reply for entry %d and RID: %d.", entry->base.id, rid);
275    path_len = strlen(shmpath) + 1;
276
277    memset(&msg, 0, sizeof(msg));
278    msg.base.rid = rid;
279    msg.base.type = CSERVE2_LOADED;
280
281    msg.shm.mmap_offset = cserve2_shm_map_offset_get(entry->shm);
282    msg.shm.use_offset = cserve2_shm_offset_get(entry->shm);
283    msg.shm.mmap_size = cserve2_shm_map_size_get(entry->shm);
284    msg.shm.image_size = cserve2_shm_size_get(entry->shm);
285    msg.alpha_sparse = entry->alpha_sparse;
286
287    buf = malloc(sizeof(msg) + path_len);
288
289    memcpy(buf, &msg, sizeof(msg));
290    memcpy(buf + sizeof(msg), shmpath, path_len);
291
292    size = sizeof(msg) + path_len;
293
294    cserve2_client_send(client, &size, sizeof(size));
295    cserve2_client_send(client, buf, size);
296
297    free(buf);
298 }
299
300 static void
301 _image_preloaded_send(Client *client, unsigned int rid)
302 {
303    int size;
304    Msg_Preloaded msg;
305
306    DBG("Sending PRELOADED reply for RID: %d.", rid);
307    memset(&msg, 0, sizeof(msg));
308    msg.base.rid = rid;
309    msg.base.type = CSERVE2_PRELOADED;
310
311    size = sizeof(msg);
312    cserve2_client_send(client, &size, sizeof(size));
313    cserve2_client_send(client, &msg, size);
314 }
315
316 static void
317 _font_loaded_send(Client *client, unsigned int rid)
318 {
319    int size;
320    Msg_Font_Loaded msg;
321
322    DBG("Sending FONT_LOADED reply for RID: %d.", rid);
323
324    size = sizeof(msg);
325    memset(&msg, 0, size);
326    msg.base.rid = rid;
327    msg.base.type = CSERVE2_FONT_LOADED;
328
329    size = sizeof(msg);
330    cserve2_client_send(client, &size, sizeof(size));
331    cserve2_client_send(client, &msg, size);
332 }
333
334 static void *
335 _open_request_build(File_Data *f, int *bufsize)
336 {
337    char *buf;
338    int size, pathlen, keylen;
339    Slave_Msg_Image_Open msg;
340
341    pathlen = strlen(f->path) + 1;
342    keylen = strlen(f->key) + 1;
343
344    size = sizeof(msg) + pathlen + keylen;
345    buf = malloc(size);
346    if (!buf) return NULL;
347
348    memset(&msg, 0, sizeof(msg));
349    memcpy(buf, &msg, sizeof(msg));
350    memcpy(buf + sizeof(msg), f->path, pathlen);
351    memcpy(buf + sizeof(msg) + pathlen, f->key, keylen);
352
353    *bufsize = size;
354
355    _entry_load_start(&f->base);
356
357    return buf;
358 }
359
360 static void
361 _request_failed(Entry *e, Error_Type type)
362 {
363    Waiter *w;
364    Eina_List *l;
365    Reference *ref;
366
367    DBG("Request for entry %p failed with error %d", e, type);
368    EINA_LIST_FREE(e->request->waiters, w)
369      {
370         cserve2_client_error_send(w->ref->client, w->rid, type);
371
372         w->ref->count--;
373         free(w);
374      }
375
376    EINA_LIST_FOREACH(e->references, l, ref)
377      {
378         Eina_Hash *hash = NULL;
379         if (e->type == CSERVE2_IMAGE_FILE)
380           hash = ref->client->files.referencing;
381         else if (e->type == CSERVE2_IMAGE_DATA)
382           hash = ref->client->images.referencing;
383         else
384           continue;
385
386         eina_hash_del_by_key(hash, &(ref->client_entry_id));
387      }
388 }
389
390 static void
391 _open_request_response(File_Data *e, Slave_Msg_Image_Opened *resp)
392 {
393    Waiter *w;
394
395    _entry_load_finish(&e->base);
396    e->w = resp->w;
397    e->h = resp->h;
398    e->frame_count = resp->frame_count;
399    e->loop_count = resp->loop_count;
400    e->loop_hint = resp->loop_hint;
401    e->alpha = resp->alpha;
402    if (resp->has_loader_data)
403      {
404         const char *ldata = (const char *)resp +
405                                            sizeof(Slave_Msg_Image_Opened);
406         e->loader_data = eina_stringshare_add(ldata);
407      }
408
409    DBG("Finished opening file %d. Notifying %d waiters.", e->base.id,
410        e->base.request->waiters ? eina_list_count(e->base.request->waiters) : 0);
411    EINA_LIST_FREE(e->base.request->waiters, w)
412      {
413         _image_opened_send(w->ref->client, e, w->rid);
414         free(w);
415      }
416 }
417
418 static Request_Funcs _open_funcs = {
419    .msg_create = (Request_Msg_Create)_open_request_build,
420    .response = (Request_Response)_open_request_response,
421    .error = (Request_Error)_request_failed
422 };
423
424 static void *
425 _load_request_build(Image_Data *i, int *bufsize)
426 {
427    char *buf, *ptr;
428    const char *shmpath;
429    int size;
430    int shmlen, filelen, keylen, loaderlen;
431    Slave_Msg_Image_Load msg;
432
433    // opening shm for this file
434    i->shm = cserve2_shm_request(i->file->w * i->file->h * 4);
435
436    shmpath = cserve2_shm_name_get(i->shm);
437
438    shmlen = strlen(shmpath) + 1;
439    filelen = strlen(i->file->path) + 1;
440    keylen = strlen(i->file->key) + 1;
441    if (i->file->loader_data)
442      loaderlen = strlen(i->file->loader_data) + 1;
443    else
444      loaderlen = 0;
445
446    size = sizeof(msg) + shmlen + filelen + keylen + loaderlen;
447    buf = malloc(size);
448    if (!buf) return NULL;
449
450    memset(&msg, 0, sizeof(msg));
451    msg.w = i->file->w;
452    msg.h = i->file->h;
453    msg.alpha = i->file->alpha;
454    msg.opts.w = i->opts.w;
455    msg.opts.h = i->opts.h;
456    msg.opts.rx = i->opts.rx;
457    msg.opts.ry = i->opts.ry;
458    msg.opts.rw = i->opts.rw;
459    msg.opts.rh = i->opts.rh;
460    msg.opts.scale_down_by = i->opts.scale_down;
461    msg.opts.dpi = i->opts.dpi;
462    msg.opts.orientation = i->opts.orientation;
463
464    msg.shm.mmap_offset = cserve2_shm_map_offset_get(i->shm);
465    msg.shm.image_offset = cserve2_shm_offset_get(i->shm);
466    msg.shm.mmap_size = cserve2_shm_map_size_get(i->shm);
467    msg.shm.image_size = cserve2_shm_size_get(i->shm);
468
469    msg.has_loader_data = !!loaderlen;
470
471    memcpy(buf, &msg, sizeof(msg));
472    ptr = buf + sizeof(msg);
473
474    memcpy(ptr, shmpath, shmlen);
475    ptr += shmlen;
476    memcpy(ptr, i->file->path, filelen);
477    ptr += filelen;
478    memcpy(ptr, i->file->key, keylen);
479    ptr += keylen;
480    memcpy(ptr, i->file->loader_data, loaderlen);
481
482    *bufsize = size;
483
484    _entry_load_start(&i->base);
485
486    return buf;
487 }
488
489 static void
490 _load_request_response(Image_Data *e, Slave_Msg_Image_Loaded *resp)
491 {
492    Waiter *w;
493
494    _entry_load_start(&e->base);
495
496    e->alpha_sparse = resp->alpha_sparse;
497    if (!e->doload)
498      DBG("Entry %d loaded by speculative preload.", e->base.id);
499
500    DBG("Finished loading image %d. Notifying %d waiters.", e->base.id,
501        e->base.request->waiters ? eina_list_count(e->base.request->waiters) : 0);
502    EINA_LIST_FREE(e->base.request->waiters, w)
503      {
504         if (w->type == CSERVE2_LOAD)
505           _image_loaded_send(w->ref->client, e, w->rid);
506         else if (w->type == CSERVE2_PRELOAD)
507           _image_preloaded_send(w->ref->client, w->rid);
508         // else w->type == CSERVE2_SETOPTS --> do nothing
509
510         free(w);
511      }
512 }
513
514 static Request_Funcs _load_funcs = {
515    .msg_create = (Request_Msg_Create)_load_request_build,
516    .response = (Request_Response)_load_request_response,
517    .error = (Request_Error)_request_failed
518 };
519
520 static unsigned int
521 _img_opts_id_get(Image_Data *im, char *buf, int size)
522 {
523    uintptr_t image_id;
524
525    snprintf(buf, size, "%u:%0.3f:%dx%d:%d:%d,%d+%dx%d:%d",
526             im->file_id, im->opts.dpi, im->opts.w, im->opts.h,
527             im->opts.scale_down, im->opts.rx, im->opts.ry,
528             im->opts.rw, im->opts.rh, im->opts.orientation);
529
530    image_id = (uintptr_t)eina_hash_find(image_ids, buf);
531
532    return image_id;
533 }
534
535 static int
536 _image_entry_size_get(Image_Data *e)
537 {
538    int size = sizeof(Image_Data);
539    /* XXX: get the overhead of the shm handler too */
540    if (e->shm)
541      size += cserve2_shm_size_get(e->shm);
542    return size / 1024;
543 }
544
545 static void
546 _file_id_free(File_Data *entry)
547 {
548    char buf[4096];
549
550    DBG("Removing entry file id: %d, file: \"%s:%s\"",
551        entry->base.id, entry->path, entry->key);
552    snprintf(buf, sizeof(buf), "%s:%s", entry->path, entry->key);
553    eina_hash_del_by_key(file_ids, buf);
554 }
555
556 static void
557 _image_id_free(Image_Data *entry)
558 {
559    char buf[4096];
560
561    DBG("Removing entry image id: %d", entry->base.id);
562
563    _img_opts_id_get(entry, buf, sizeof(buf));
564    eina_hash_del_by_key(image_ids, buf);
565 }
566
567 static void
568 _image_entry_free(Image_Data *entry)
569 {
570    File_Data *fentry = entry->file;
571
572    if (entry->base.request)
573      {
574         if (entry->base.request->processing)
575           entry->base.request->entry = NULL;
576         else if (!entry->base.request->waiters)
577           {
578              if (entry->doload)
579                load_requests = eina_list_remove(load_requests,
580                                                 entry->base.request);
581              else
582                spload_requests = eina_list_remove(spload_requests,
583                                                   entry->base.request);
584           }
585      }
586
587    if (entry->unused)
588      {
589         image_entries_lru = eina_list_remove(image_entries_lru, entry);
590         unused_mem_usage -= _image_entry_size_get(entry);
591      }
592
593    if (fentry)
594      fentry->images = eina_list_remove(fentry->images, entry);
595    if (entry->shm)
596      cserve2_shm_unref(entry->shm);
597    free(entry);
598 }
599
600 static void
601 _hash_image_entry_free(void *data)
602 {
603    Image_Data *entry = data;
604
605    _image_id_free(entry);
606    _image_entry_free(entry);
607 }
608
609 static void
610 _file_entry_free(File_Data *entry)
611 {
612    File_Watch *fw;
613
614    // Should we call free for each of the images too?
615    // If everything goes fine, it's not necessary.
616    if (entry->images)
617      {
618         ERR("Freeing file %d (\"%s:%s\") image data still referenced.",
619             entry->base.id, entry->path, entry->key);
620         eina_list_free(entry->images);
621      }
622
623    if (entry->base.request)
624      {
625         if (entry->base.request->processing)
626           entry->base.request->entry = NULL;
627         else if (!entry->base.request->waiters)
628           {
629              open_requests = eina_list_remove(open_requests,
630                                               entry->base.request);
631              free(entry->base.request);
632           }
633      }
634
635    if ((fw = entry->watcher))
636      {
637         fw->entries = eina_list_remove(fw->entries, entry);
638         if (!fw->entries)
639           eina_hash_del_by_key(file_watch, fw->path);
640      }
641
642    free(entry->key);
643    free(entry->path);
644    eina_stringshare_del(entry->loader_data);
645    free(entry);
646 }
647
648 static void
649 _hash_file_entry_free(void *data)
650 {
651    File_Data *entry = data;
652    // TODO: Add some checks to make sure that we are freeing an
653    // unused entry.
654
655    _file_id_free(entry);
656    _file_entry_free(entry);
657 }
658
659 static void
660 _file_watch_free(void *data)
661 {
662    File_Watch *fw = data;
663    cserve2_file_change_watch_del(fw->path);
664    eina_stringshare_del(fw->path);
665    eina_list_free(fw->entries);
666    free(fw);
667 }
668
669 static int
670 _font_entry_cmp(const Font_Entry *k1, int k1_length __UNUSED__, const Font_Entry *k2, int k2_length __UNUSED__)
671 {
672    if (k1->src->key == k2->src->key)
673      {
674         if (k1->size == k2->size)
675           {
676              if (k1->rend_flags == k2->rend_flags)
677                return k1->dpi - k2->dpi;
678              return k1->rend_flags - k2->rend_flags;
679           }
680         return k1->size - k2->size;
681      }
682    return strcmp(k1->src->key, k2->src->key);
683 }
684
685 static int
686 _font_entry_key_hash(const Font_Entry *key, int key_length __UNUSED__)
687 {
688    int hash;
689    hash = eina_hash_djb2(key->src->key, eina_stringshare_strlen(key->src->key) + 1);
690    hash ^= eina_hash_int32(&key->rend_flags, sizeof(int));
691    hash ^= eina_hash_int32(&key->size, sizeof(int));
692    hash ^= eina_hash_int32(&key->dpi, sizeof(int));
693
694    return hash;
695 }
696
697 static void
698 _font_entry_free(Font_Entry *fe)
699 {
700    fash_gl_free(fe->glyphs);
701    fe->src->references--;
702    if (fe->ft) cserve2_font_ft_free(fe->ft);
703    if (fe->src->references <= 0)
704      eina_hash_del_by_key(font_sources, fe->src->key);
705    free(fe);
706 }
707
708 static void
709 _glyph_free_cb(void *data)
710 {
711    Glyph_Entry *gl = data;
712    free(gl);
713 }
714
715 static void
716 _font_source_free(Font_Source *fs)
717 {
718    eina_stringshare_del(fs->key);
719    eina_stringshare_del(fs->name);
720    eina_stringshare_del(fs->file);
721    if (fs->ft) cserve2_font_source_ft_free(fs->ft);
722
723    free(fs);
724 }
725
726 static void
727 _font_shm_promote(Font_Cache *fc)
728 {
729    Eina_List *l;
730    l = eina_list_data_find_list(font_shm_lru, fc);
731    font_shm_lru = eina_list_demote_list(font_shm_lru, l);
732 }
733
734 static int
735 _font_shm_size_get(Font_Cache *fc)
736 {
737    int size;
738
739    size = sizeof(*fc) + cserve2_shm_size_get(fc->shm);
740
741    return size;
742 }
743
744 static void
745 _font_shm_free(Font_Cache *fc)
746 {
747    Font_Entry *fe = fc->fe;
748    fe->caches = eina_inlist_remove(fe->caches, EINA_INLIST_GET(fc));
749    if (fc == fe->last_cache)
750      fe->last_cache = NULL;
751
752    while (fc->glyphs)
753      {
754         Glyph_Entry *gl = EINA_INLIST_CONTAINER_GET(fc->glyphs, Glyph_Entry);
755         fc->glyphs = eina_inlist_remove(fc->glyphs, fc->glyphs);
756         fash_gl_del(fe->glyphs, gl->index);
757      }
758
759    cserve2_shm_unref(fc->shm);
760    free(fc);
761
762    if (!fe->caches)
763      eina_hash_del_by_key(font_entries, fe);
764 }
765
766 static void
767 _font_shm_lru_flush(void)
768 {
769    Eina_List *l, *l_next;
770
771    l = font_shm_lru;
772    l_next = eina_list_next(l);
773
774    while (l && font_mem_usage > max_font_usage)
775      {
776         int size;
777         Font_Cache *fc;
778
779         fc = eina_list_data_get(l);
780         if (fc->fe->unused && fc->inuse == 0)
781           {
782              font_shm_lru = eina_list_remove_list(font_shm_lru, l);
783              size = _font_shm_size_get(fc);
784              size += fc->nglyphs * sizeof(Glyph_Entry);
785              _font_shm_free(fc);
786              font_mem_usage -= size;
787           }
788
789         l = l_next;
790         l_next = eina_list_next(l);
791      }
792 }
793
794 void
795 cserve2_cache_init(void)
796 {
797    file_ids = eina_hash_string_superfast_new(NULL);
798    file_entries = eina_hash_int32_new(_hash_file_entry_free);
799    image_ids = eina_hash_string_superfast_new(NULL);
800    image_entries = eina_hash_string_superfast_new(_hash_image_entry_free);
801    file_watch = eina_hash_string_superfast_new(_file_watch_free);
802
803    font_sources = eina_hash_string_small_new(EINA_FREE_CB(_font_source_free));
804    font_entries = eina_hash_new(NULL,
805                                 EINA_KEY_CMP(_font_entry_cmp),
806                                 EINA_KEY_HASH(_font_entry_key_hash),
807                                 EINA_FREE_CB(_font_entry_free),
808                                 5);
809 }
810
811 void
812 cserve2_cache_shutdown(void)
813 {
814    Font_Cache *fc;
815
816    EINA_LIST_FREE(font_shm_lru, fc)
817      _font_shm_free(fc);
818
819    eina_hash_free(image_entries);
820    eina_hash_free(image_ids);
821    eina_hash_free(file_entries);
822    eina_hash_free(file_ids);
823    eina_hash_free(file_watch);
824
825    eina_hash_free(font_entries);
826    eina_hash_free(font_sources);
827 }
828
829 static void
830 _request_answer_del(Eina_List **requests, Request *req, Client *client, Error_Type err)
831 {
832    Eina_List *l, *l_next;
833    Waiter *it;
834
835    DBG("Removing answer requests from entry: %d, client: %d",
836        req->entry->id, client->id);
837
838    EINA_LIST_FOREACH_SAFE(req->waiters, l, l_next, it)
839      {
840         if (it->ref->client->id == client->id)
841           {
842              cserve2_client_error_send(client, it->rid, err);
843              req->waiters = eina_list_remove_list(req->waiters, l);
844              free(it);
845           }
846      }
847
848    // FIXME: Should this be really here? I guess that it should be in the
849    // entry_free_cb function, or entry_reference_del, when there are no more
850    // references
851    if (!req->entry && !req->waiters)
852      {
853         *requests = eina_list_remove(*requests, req);
854         free(req);
855      }
856 }
857
858 static void
859 _request_answer_all_del(Eina_List **requests, Request *req, Error_Type err)
860 {
861    Waiter *it;
862
863    DBG("Removing all answer requests from entry: %d", req->entry->id);
864
865    EINA_LIST_FREE(req->waiters, it)
866      {
867         cserve2_client_error_send(it->ref->client, it->rid, err);
868         free(it);
869      }
870
871    *requests = eina_list_remove(*requests, req);
872    free(req);
873 }
874
875 static void
876 _request_answer_add(Request *req, Reference *ref, unsigned int rid, Message_Type type)
877 {
878    Waiter *w = malloc(sizeof(*w));
879
880    w->ref = ref;
881    w->rid = rid;
882    w->type = type;
883
884    DBG("Add answer request for entry id: %d, client: %d, rid: %d",
885        req->entry->id, ref->client->id, rid);
886    req->waiters = eina_list_append(req->waiters, w);
887 }
888
889 static void
890 _request_add(Eina_List **requests, Entry *entry, Reference *ref, unsigned int rid, Message_Type type)
891 {
892    Request *req;
893
894    // add the request if it doesn't exist yet
895    if (!entry->request)
896      {
897         req = malloc(sizeof(*req));
898         req->entry = entry;
899         req->waiters = NULL;
900         req->processing = EINA_FALSE;
901         entry->request = req;
902         if (type == CSERVE2_OPEN)
903           req->funcs = &_open_funcs;
904         else
905           req->funcs = &_load_funcs;
906         *requests = eina_list_append(*requests, req);
907         DBG("Add request for entry id: %d, client: %d, rid: %d",
908             req->entry->id, ref->client->id, rid);
909      }
910    else
911      req = entry->request;
912
913    if (type != CSERVE2_SETOPTS)
914      _request_answer_add(req, ref, rid, type);
915    else
916      DBG("Adding entry for speculative preload: id=%d", req->entry->id);
917 }
918
919 static Reference *
920 _entry_reference_add(Entry *entry, Client *client, unsigned int client_entry_id)
921 {
922    Reference *ref;
923
924    // increase reference for this file
925    ref = malloc(sizeof(*ref));
926    ref->client = client;
927    ref->entry = entry;
928    ref->client_entry_id = client_entry_id;
929    ref->count = 1;
930    entry->references = eina_list_append(entry->references, ref);
931
932    return ref;
933 }
934
935 static int
936 _cserve2_cache_open_requests_process(int nloaders)
937 {
938    Request *req;
939    char *slave_cmd_data;
940    int slave_cmd_size;
941
942    while ((nloaders > 0) && (open_requests))
943      {
944         // remove the first element from the list and process this element
945         req = eina_list_data_get(open_requests);
946         open_requests = eina_list_remove_list(open_requests, open_requests);
947
948         DBG("Processing OPEN request for file entry: %d", req->entry->id);
949
950         slave_cmd_data = req->funcs->msg_create(req->entry, &slave_cmd_size);
951
952         cserve2_slave_cmd_dispatch(req, IMAGE_OPEN, slave_cmd_data,
953                                    slave_cmd_size);
954
955         free(slave_cmd_data);
956
957         req->processing = EINA_TRUE;
958         nloaders--;
959      }
960
961    return nloaders;
962 }
963
964 static int
965 _cserve2_cache_load_requests_list_process(Eina_List **queue, int nloaders)
966 {
967    Eina_List *skipped = NULL;
968    Request *req;
969
970    while ((nloaders > 0) && (*queue))
971      {
972         Image_Data *ientry;
973         char *buf;
974         int size;
975
976         // remove the first element from the list and process this element
977         req = eina_list_data_get(*queue);
978         *queue = eina_list_remove_list(*queue, *queue);
979
980         ientry = (Image_Data *)req->entry;
981         if (!ientry->file)
982           {
983              ERR("File entry doesn't exist for entry id %d", req->entry->id);
984              _request_failed(req->entry, CSERVE2_INVALID_CACHE);
985              continue;
986           }
987
988         if (ientry->file->base.request)
989           {
990              /* OPEN still pending, skip this request */
991              skipped = eina_list_append(skipped, req);
992              continue;
993           }
994
995         DBG("Processing LOAD request for image entry: %d", req->entry->id);
996
997         buf = req->funcs->msg_create(req->entry, &size);
998
999         cserve2_slave_cmd_dispatch(req, IMAGE_LOAD, buf, size);
1000
1001         free(buf);
1002
1003         req->processing = EINA_TRUE;
1004
1005         nloaders--;
1006      }
1007
1008    EINA_LIST_FREE(skipped, req)
1009       *queue = eina_list_append(*queue, req);
1010
1011    return nloaders;
1012 }
1013
1014 static void
1015 _cserve2_cache_load_requests_process(int nloaders)
1016 {
1017    nloaders = _cserve2_cache_load_requests_list_process(&load_requests,
1018                                                         nloaders);
1019    _cserve2_cache_load_requests_list_process(&spload_requests, nloaders - 1);
1020 }
1021
1022
1023 void
1024 cserve2_cache_requests_process(void)
1025 {
1026    int avail_loaders;
1027
1028    avail_loaders = cserve2_slave_available_get();
1029    avail_loaders = _cserve2_cache_open_requests_process(avail_loaders);
1030    _cserve2_cache_load_requests_process(avail_loaders);
1031 }
1032
1033 static void
1034 _entry_unused_push(Image_Data *e)
1035 {
1036    int size = _image_entry_size_get(e);
1037
1038    if ((size > max_unused_mem_usage) || !(e->doload))
1039      {
1040         eina_hash_del_by_key(image_entries, &e->base.id);
1041         return;
1042      }
1043    while (size > (max_unused_mem_usage - unused_mem_usage))
1044      {
1045         Entry *ie = eina_list_data_get(eina_list_last(image_entries_lru));
1046         eina_hash_del_by_key(image_entries, &ie->id);
1047      }
1048    image_entries_lru = eina_list_append(image_entries_lru, e);
1049    e->unused = EINA_TRUE;
1050    unused_mem_usage += size;
1051 }
1052
1053 static void
1054 _entry_reference_del(Entry *entry, Reference *ref)
1055 {
1056    entry->references = eina_list_remove(entry->references, ref);
1057
1058    if (entry->references)
1059      goto free_ref;
1060
1061    if (entry->type == CSERVE2_IMAGE_FILE)
1062      {
1063         File_Data *fentry = (File_Data *)entry;
1064
1065         if (fentry->invalid)
1066           _file_entry_free(fentry);
1067         else
1068           {
1069              Image_Data *ie;
1070              EINA_LIST_FREE(fentry->images, ie)
1071                 ie->file = NULL;
1072              eina_hash_del_by_key(file_entries, &entry->id);
1073           }
1074      }
1075    else if (entry->type == CSERVE2_IMAGE_DATA)
1076      {
1077         Image_Data *ientry = (Image_Data *)entry;
1078
1079         if (!ientry->file)
1080           eina_hash_del_by_key(image_entries, &entry->id);
1081         else if (ientry->file->invalid)
1082           _image_entry_free(ientry);
1083         else
1084           _entry_unused_push(ientry);
1085      }
1086    else if (entry->type == CSERVE2_FONT_ENTRY)
1087      {
1088         Font_Entry *fe = (Font_Entry *)entry;
1089         fe->unused = EINA_TRUE;
1090         if (!fe->caches)
1091           eina_hash_del_by_key(font_entries, fe);
1092      }
1093    else
1094      ERR("Wrong type of entry.");
1095
1096 free_ref:
1097    free(ref);
1098 }
1099
1100 static void
1101 _entry_free_cb(void *data)
1102 {
1103    Reference *ref = data;
1104    Entry *entry;
1105
1106    DBG("Removing client reference for entry id: %d, client: %d",
1107        ref->entry->id, ref->client->id);
1108
1109    entry = ref->entry;
1110
1111    if (entry->request && !entry->request->processing)
1112      {
1113         if (entry->type == CSERVE2_IMAGE_FILE)
1114           _request_answer_del(&open_requests, entry->request, ref->client,
1115                               CSERVE2_REQUEST_CANCEL);
1116         else if (entry->type == CSERVE2_IMAGE_DATA)
1117           {
1118              if (((Image_Data *)entry)->doload)
1119                _request_answer_del(&load_requests, entry->request,
1120                                    ref->client, CSERVE2_REQUEST_CANCEL);
1121              else
1122                _request_answer_del(&spload_requests, entry->request,
1123                                    ref->client, CSERVE2_REQUEST_CANCEL);
1124           }
1125      }
1126
1127    _entry_reference_del(entry, ref);
1128 }
1129
1130 static void
1131 _font_entry_reference_del(Client *client, Font_Entry *fe)
1132 {
1133    Eina_List *l;
1134    Reference *ref;
1135
1136    EINA_LIST_FOREACH(client->fonts.referencing, l, ref)
1137      {
1138         if (ref->entry == (Entry *)fe)
1139           {
1140              ref->count--;
1141              if (ref->count > 0)
1142                break;
1143
1144              client->fonts.referencing = eina_list_remove_list(
1145                  client->fonts.referencing, l);
1146              _entry_reference_del(&fe->base, ref);
1147              return;
1148           }
1149      }
1150 }
1151
1152 void
1153 cserve2_cache_client_new(Client *client)
1154 {
1155    client->files.referencing = eina_hash_int32_new(_entry_free_cb);
1156    client->images.referencing = eina_hash_int32_new(_entry_free_cb);
1157    client->fonts.referencing = NULL;
1158 }
1159
1160 void
1161 cserve2_cache_client_del(Client *client)
1162 {
1163    Reference *ref;
1164
1165    // will call _entry_free_cb() for every entry
1166    eina_hash_free(client->images.referencing);
1167    // will call _entry_free_cb() for every entry
1168    eina_hash_free(client->files.referencing);
1169
1170    EINA_LIST_FREE(client->fonts.referencing, ref)
1171      {
1172         _entry_reference_del(ref->entry, ref);
1173      }
1174 }
1175
1176 static Image_Data *
1177 _image_msg_new(Client *client, Msg_Setopts *msg)
1178 {
1179    Reference *ref;
1180    Image_Data *im_entry;
1181
1182    ref = eina_hash_find(client->files.referencing, &msg->file_id);
1183    if (!ref)
1184      {
1185         ERR("Couldn't find file id: %d, for image id: %d",
1186             msg->file_id, msg->image_id);
1187         cserve2_client_error_send(client, msg->base.rid,
1188                                   CSERVE2_INVALID_CACHE);
1189         return NULL;
1190      }
1191    if (((File_Data *)ref->entry)->invalid)
1192      {
1193         cserve2_client_error_send(client, msg->base.rid,
1194                                   CSERVE2_FILE_CHANGED);
1195         return NULL;
1196      }
1197
1198    im_entry = calloc(1, sizeof(*im_entry));
1199    im_entry->base.type = CSERVE2_IMAGE_DATA;
1200    im_entry->file_id = ref->entry->id;
1201    im_entry->file = (File_Data *)ref->entry;
1202    im_entry->opts.dpi = msg->opts.dpi;
1203    im_entry->opts.w = msg->opts.w;
1204    im_entry->opts.h = msg->opts.h;
1205    im_entry->opts.scale_down = msg->opts.scale_down;
1206    im_entry->opts.rx = msg->opts.rx;
1207    im_entry->opts.ry = msg->opts.ry;
1208    im_entry->opts.rw = msg->opts.rw;
1209    im_entry->opts.rh = msg->opts.rh;
1210    im_entry->opts.orientation = msg->opts.orientation;
1211
1212    return im_entry;
1213 }
1214
1215 static void
1216 _file_changed_cb(const char *path __UNUSED__, Eina_Bool deleted __UNUSED__, void *data)
1217 {
1218    File_Watch *fw = data;
1219    File_Data *e;
1220    Eina_List *l;
1221
1222    EINA_LIST_FOREACH(fw->entries, l, e)
1223      {
1224         Eina_List *ll;
1225         Image_Data *ie;
1226
1227         e->invalid = EINA_TRUE;
1228         e->watcher = NULL;
1229
1230         EINA_LIST_FOREACH(e->images, ll, ie)
1231           {
1232              _image_id_free(ie);
1233              eina_hash_set(image_entries, &ie->base.id, NULL);
1234              if (ie->base.request && !ie->base.request->processing)
1235                {
1236                   if (ie->doload)
1237                     _request_answer_all_del(&load_requests, ie->base.request,
1238                                             CSERVE2_FILE_CHANGED);
1239                   else
1240                     _request_answer_all_del(&spload_requests, ie->base.request,
1241                                             CSERVE2_FILE_CHANGED);
1242                }
1243              ie->base.request = NULL;
1244              if (ie->unused)
1245                _image_entry_free(ie);
1246           }
1247
1248         _file_id_free(e);
1249         eina_hash_set(file_entries, &e->base.id, NULL);
1250         if (e->base.request && !e->base.request->processing)
1251           _request_answer_all_del(&open_requests, e->base.request,
1252                                   CSERVE2_FILE_CHANGED);
1253         e->base.request = NULL;
1254         if (!e->images && !e->base.references)
1255           _file_entry_free(e);
1256      }
1257
1258    eina_hash_del_by_key(file_watch, fw->path);
1259 }
1260
1261 static Font_Source *
1262 _cserve2_font_source_find(const char *name)
1263 {
1264    Font_Source *fs;
1265
1266    fs = eina_hash_find(font_sources, name);
1267
1268    return fs;
1269 }
1270
1271 static Font_Entry *
1272 _cserve2_font_entry_find(const char *name, unsigned int namelen, unsigned int size, unsigned int rend_flags, unsigned int dpi)
1273 {
1274    Font_Entry tmp_fe;
1275    Font_Source tmp_fs;
1276    Font_Entry *fe;
1277
1278    tmp_fs.key = eina_stringshare_add_length(name, namelen);
1279    tmp_fe.src = &tmp_fs;
1280    tmp_fe.size = size;
1281    tmp_fe.rend_flags = rend_flags;
1282    tmp_fe.dpi = dpi;
1283
1284    fe = eina_hash_find(font_entries, &tmp_fe);
1285    eina_stringshare_del(tmp_fs.key);
1286
1287    return fe;
1288 }
1289
1290 static void *
1291 _font_load_request_build(void *data, int *size)
1292 {
1293    Font_Entry *fe = data;
1294    Slave_Msg_Font_Load *msg = calloc(1, sizeof(*msg));
1295
1296    msg->ftdata1 = fe->src->ft;
1297    msg->ftdata2 = fe->ft;
1298    msg->rend_flags = fe->rend_flags;
1299    msg->size = fe->size;
1300    msg->dpi = fe->dpi;
1301    msg->name = fe->src->name;
1302    msg->file = fe->src->file;
1303
1304    *size = 0;
1305
1306    _entry_load_start(&fe->base);
1307
1308    return msg;
1309 }
1310
1311 static void
1312 _font_load_request_free(void *msg, void *data)
1313 {
1314    free(msg);
1315 }
1316
1317 static void
1318 _font_load_request_response(Client *client __UNUSED__, void *data, void *resp, unsigned int rid __UNUSED__)
1319 {
1320    Slave_Msg_Font_Loaded *msg = resp;
1321    Font_Entry *fe = data;
1322
1323    DBG("request %d answered.", rid);
1324
1325    if (!fe->src->ft)
1326      fe->src->ft = msg->ftdata1;
1327
1328    if (!fe->ft)
1329      {
1330         fe->ft = msg->ftdata2;
1331         _entry_load_finish(&fe->base);
1332      }
1333
1334    if (fe->request) fe->request = NULL;
1335
1336    _font_loaded_send(client, rid);
1337 }
1338
1339 static void
1340 _font_load_request_failed(Client *client __UNUSED__, void *data __UNUSED__, Error_Type error __UNUSED__, unsigned int rid __UNUSED__)
1341 {
1342    Font_Entry *fe = data;
1343    DBG("request %d error answered.", rid);
1344
1345    cserve2_client_error_send(client, rid, error);
1346
1347    if (fe->request) fe->request = NULL;
1348
1349    _font_entry_reference_del(client, fe);
1350 }
1351
1352 static Font_Request_Funcs _font_load_funcs = {
1353    .msg_create = (Font_Request_Msg_Create)_font_load_request_build,
1354    .msg_free = (Font_Request_Msg_Free)_font_load_request_free,
1355    .response = (Font_Request_Response)_font_load_request_response,
1356    .error = (Font_Request_Error)_font_load_request_failed
1357 };
1358
1359 static Eina_Bool
1360 _glyphs_request_check(Glyphs_Request *req)
1361 {
1362    int i;
1363    Font_Entry *fe = req->fe;
1364
1365    req->answer = malloc(sizeof(*req->answer) * req->nglyphs);
1366    req->nanswer = 0;
1367
1368    for (i = req->current; i < req->nglyphs; i++)
1369      {
1370         Glyph_Entry *ge;
1371         ge = fash_gl_find(fe->glyphs, req->glyphs[i]);
1372         if (ge)
1373           {
1374              req->answer[req->nanswer++] = ge;
1375              ge->fc->inuse++;
1376           }
1377         else
1378           break;
1379      }
1380
1381    req->current = i;
1382
1383    // No glyphs need to be rendered.
1384    return (req->nanswer == req->nglyphs);
1385 }
1386
1387 /* organize answer (cache1{gl1, gl2,}, cache2{gl3,gl4,gl5}, cache3{gl6})
1388  */
1389 static Eina_List *
1390 _glyphs_group_create(Glyphs_Request *req)
1391 {
1392    Eina_List *groups = NULL;
1393    int i;
1394
1395    for (i = 0; i < req->nanswer; i++)
1396      {
1397         Eina_List *l;
1398         Glyphs_Group *iter, *gg = NULL;
1399         Font_Cache *fc = req->answer[i]->fc;
1400
1401         EINA_LIST_FOREACH(groups, l, iter)
1402           {
1403              if (iter->fc == fc)
1404                {
1405                   gg = iter;
1406                   break;
1407                }
1408           }
1409
1410         if (!gg)
1411           {
1412              gg = calloc(1, sizeof(*gg));
1413              gg->fc = fc;
1414              groups = eina_list_append(groups, gg);
1415           }
1416         gg->glyphs = eina_list_append(gg->glyphs, req->answer[i]);
1417      }
1418
1419    return groups;
1420 }
1421
1422 static void
1423 _glyphs_loaded_send(Glyphs_Request *req, unsigned int rid)
1424 {
1425    Msg_Font_Glyphs_Loaded msg;
1426    unsigned int size;
1427    Eina_List *ll, *answers = NULL;
1428    const char *shmname;
1429    unsigned int shmsize;
1430    unsigned int intsize;
1431    char *resp, *buf;
1432    Glyphs_Group *iter;
1433
1434    memset(&msg, 0, sizeof(msg));
1435    msg.base.rid = rid;
1436    msg.base.type = CSERVE2_FONT_GLYPHS_LOADED;
1437
1438    answers = _glyphs_group_create(req);
1439    msg.ncaches = eina_list_count(answers);
1440    size = sizeof(msg);
1441
1442    // calculate size of message
1443    // ncaches * sizeof(cache) + nglyphs1 * sizeof(glyph) +
1444    //   nglyphs2 * sizeof(glyph)...
1445
1446    intsize = sizeof(unsigned int);
1447
1448    EINA_LIST_FOREACH(answers, ll, iter)
1449      {
1450         shmname = cserve2_shm_name_get(iter->fc->shm);
1451         shmsize = eina_stringshare_strlen(shmname) + 1;
1452         // shm namelen + name
1453         size += intsize + shmsize;
1454
1455         // nglyphs
1456         size += intsize;
1457         // nglyphs * (index + offset + size + rows + width + pitch +
1458         //            num_grays + pixel_mode)
1459         size += eina_list_count(iter->glyphs) * 8 * intsize;
1460      }
1461
1462    resp = malloc(size);
1463    memcpy(resp, &msg, sizeof(msg));
1464    buf = resp + sizeof(msg);
1465
1466    EINA_LIST_FREE(answers, iter)
1467      {
1468         Glyph_Entry *gl;
1469         unsigned int nglyphs;
1470
1471         shmname = cserve2_shm_name_get(iter->fc->shm);
1472         shmsize = eina_stringshare_strlen(shmname) + 1;
1473         memcpy(buf, &shmsize, intsize);
1474         buf += intsize;
1475         memcpy(buf, shmname, shmsize);
1476         buf += shmsize;
1477
1478         nglyphs = eina_list_count(iter->glyphs);
1479         memcpy(buf, &nglyphs, intsize);
1480         buf += intsize;
1481
1482         iter->fc->inuse -= eina_list_count(iter->glyphs);
1483
1484         EINA_LIST_FREE(iter->glyphs, gl)
1485           {
1486              memcpy(buf, &gl->index, intsize);
1487              buf += intsize;
1488              memcpy(buf, &gl->offset, intsize);
1489              buf += intsize;
1490              memcpy(buf, &gl->size, intsize);
1491              buf += intsize;
1492              memcpy(buf, &gl->rows, intsize);
1493              buf += intsize;
1494              memcpy(buf, &gl->width, intsize);
1495              buf += intsize;
1496              memcpy(buf, &gl->pitch, intsize);
1497              buf += intsize;
1498              memcpy(buf, &gl->num_grays, intsize);
1499              buf += intsize;
1500              memcpy(buf, &gl->pixel_mode, intsize);
1501              buf += intsize;
1502           }
1503
1504         /* We are removing SHMs from the beginning of the list, so this
1505          * gives a higher priority to them */
1506         _font_shm_promote(iter->fc);
1507         eina_list_free(iter->glyphs);
1508         free(iter);
1509      }
1510
1511    cserve2_client_send(req->client, &size, sizeof(size));
1512    cserve2_client_send(req->client, resp, size);
1513
1514    free(resp);
1515 }
1516
1517 /*
1518  * taken from evas_path.c. It would be good to clean up those utils to
1519  * have cserve link against them easily without dragging unneeded dependencies
1520  */
1521 #ifdef _WIN32
1522 # define EVAS_PATH_SEPARATOR "\\"
1523 #else
1524 # define EVAS_PATH_SEPARATOR "/"
1525 #endif
1526
1527 static char *
1528 _file_path_join(const char *path, const char *end)
1529 {
1530    char *res = NULL;
1531    size_t len;
1532
1533    if ((!path) && (!end)) return NULL;
1534    if (!path) return strdup(end);
1535    if (!end) return strdup(path);
1536    len = strlen(path);
1537    len += strlen(end);
1538    len += strlen(EVAS_PATH_SEPARATOR);
1539    res = malloc(len + 1);
1540    if (!res) return NULL;
1541    strcpy(res, path);
1542    strcat(res, EVAS_PATH_SEPARATOR);
1543    strcat(res, end);
1544    return res;
1545 }
1546
1547 static Glyphs_Request *
1548 _glyphs_request_create(Client *client, const char *source, unsigned int sourcelen, const char *name, unsigned int namelen, unsigned int hint, unsigned int rend_flags, unsigned int size, unsigned int dpi, unsigned int *glyphs, unsigned int nglyphs)
1549 {
1550    char *fullname;
1551    Glyphs_Request *req = calloc(1, sizeof(*req));
1552
1553    if (sourcelen == 0)
1554      source = NULL;
1555    if (namelen == 0)
1556      name = NULL;
1557
1558    fullname = _file_path_join(source, name);
1559    req->fe = _cserve2_font_entry_find(fullname, strlen(fullname) + 1, size,
1560                                       rend_flags, dpi);
1561    free(fullname);
1562    if (!req->fe)
1563      {
1564         ERR("No font entry found: source %s, name %s, rendflags: %d, hint: %d,"
1565             " size: %d, dpi: %d", source, name, rend_flags, hint, size, dpi);
1566         free(req);
1567         return NULL;
1568      }
1569
1570    req->client = client;
1571
1572    req->nglyphs = nglyphs;
1573    req->current = 0;
1574    req->glyphs = glyphs;
1575    req->hint = hint;
1576
1577    return req;
1578 }
1579
1580 static void
1581 _glyphs_request_free(Glyphs_Request *req)
1582 {
1583    free(req->glyphs);
1584    free(req->render);
1585    free(req->answer);
1586    free(req);
1587 }
1588
1589 /* add glyphs that are already in cache to the "answers" array, and the ones
1590  * that are not cached to the "render" array.
1591  */
1592 static void
1593 _glyphs_load_request_prepare(Glyphs_Request *req)
1594 {
1595    int i, max;
1596    req->nrender = 0;
1597    Font_Entry *fe = req->fe;
1598
1599    if (!fe)
1600      {
1601         ERR("No font entry for this request.");
1602         return;
1603      }
1604
1605    // Won't render more than this number of glyphs
1606    max = req->nglyphs - req->nanswer;
1607    req->render = malloc(sizeof(*req->render) * max);
1608
1609    for (i = req->current; i < req->nglyphs; i++)
1610      {
1611         Glyph_Entry *ge;
1612         ge = fash_gl_find(fe->glyphs, req->glyphs[i]);
1613         if (ge)
1614           {
1615              req->answer[req->nanswer++] = ge;
1616              ge->fc->inuse++;
1617           }
1618         else
1619           req->render[req->nrender++] = req->glyphs[i];
1620      }
1621 }
1622
1623 static void *
1624 _glyphs_load_request_build(void *data, int *size)
1625 {
1626    Glyphs_Request *req = data;
1627    Slave_Msg_Font_Glyphs_Load *msg = NULL;
1628    Font_Entry *fe = req->fe;
1629    Font_Cache *fc;
1630
1631    _glyphs_load_request_prepare(req);
1632
1633    msg = calloc(1, sizeof(*msg));
1634
1635    msg->font.ftdata1 = fe->src->ft;
1636    msg->font.ftdata2 = fe->ft;
1637    msg->font.hint = req->hint;
1638    msg->font.rend_flags = fe->rend_flags;
1639    msg->glyphs.nglyphs = req->nrender;
1640    msg->glyphs.glyphs = req->render;
1641
1642    // Trying to reuse last filled cache.
1643    fc = fe->last_cache;
1644    if (fc)
1645      {
1646         msg->cache.shm = fc->shm;
1647         msg->cache.usage = fc->usage;
1648         msg->cache.nglyphs = fc->nglyphs;
1649      }
1650
1651 #ifdef DEBUG_LOAD_TIME
1652    gettimeofday(&fe->load_start, NULL);
1653 #endif
1654
1655    return msg;
1656 }
1657
1658 static void
1659 _glyphs_load_request_free(void *msg, void *data)
1660 {
1661    _glyphs_request_free(data);
1662    free(msg);
1663 }
1664
1665 static void
1666 _glyphs_load_request_response(Client *client, void *data, void *resp, unsigned int rid)
1667 {
1668    Glyphs_Request *req = data;
1669    Slave_Msg_Font_Glyphs_Loaded *msg = resp;
1670    Font_Entry *fe = req->fe;
1671    Font_Cache *fc = NULL;
1672    int i = 0;
1673
1674    if (fe->last_cache && fe->last_cache->shm == msg->caches[0]->shm)
1675      fc = fe->last_cache;
1676
1677    while (i < msg->ncaches)
1678      {
1679         int j;
1680         Slave_Msg_Font_Cache *c = msg->caches[i++];
1681
1682         if (!fc)
1683           {
1684              fc = malloc(sizeof(*fc));
1685              fe->caches = eina_inlist_append(fe->caches, EINA_INLIST_GET(fc));
1686              fe->last_cache = fc;
1687              fc->fe = fe;
1688              fc->shm = c->shm;
1689              fc->glyphs = NULL;
1690              fc->nglyphs = 0;
1691              fc->inuse = 0;
1692              font_shm_lru = eina_list_append(font_shm_lru, fc);
1693              font_mem_usage += _font_shm_size_get(fc);
1694           }
1695         fc->usage = c->usage;
1696         for (j = 0; j < c->nglyphs; j++)
1697           {
1698              Glyph_Entry *gl = malloc(sizeof(*gl));
1699              gl->fe = fe;
1700              gl->fc = fc;
1701              gl->index = c->glyphs[j].index;
1702              gl->offset = c->glyphs[j].offset;
1703              gl->size = c->glyphs[j].size;
1704              gl->rows = c->glyphs[j].rows;
1705              gl->width = c->glyphs[j].width;
1706              gl->pitch = c->glyphs[j].pitch;
1707              gl->num_grays = c->glyphs[j].num_grays;
1708              gl->pixel_mode = c->glyphs[j].pixel_mode;
1709              font_mem_usage += sizeof(*gl);
1710              fc->glyphs = eina_inlist_append(fc->glyphs, EINA_INLIST_GET(gl));
1711              fc->nglyphs++;
1712              fash_gl_add(fe->glyphs, gl->index, gl);
1713              req->answer[req->nanswer++] = gl;
1714              gl->fc->inuse++;
1715           }
1716
1717         free(c); // FIXME: We are freeing this here because we only do a
1718                  // simple free on the response message. Later we need to
1719                  // setup a free callback for the slave response.
1720         fc = NULL;
1721      }
1722
1723 #ifdef DEBUG_LOAD_TIME
1724    int load_time;
1725    gettimeofday(&fe->load_finish, NULL);
1726    load_time = _timeval_sub(&fe->load_finish, &fe->load_start);
1727    fe->gl_load_time += load_time;
1728 #endif
1729
1730    _glyphs_loaded_send(req, rid);
1731    _font_shm_lru_flush();
1732 }
1733
1734 static void
1735 _glyphs_load_request_failed(Client *client, void *data, Error_Type error, unsigned int rid)
1736 {
1737 }
1738
1739 static Font_Request_Funcs _glyphs_load_funcs = {
1740    .msg_create = (Font_Request_Msg_Create)_glyphs_load_request_build,
1741    .msg_free = (Font_Request_Msg_Free)_glyphs_load_request_free,
1742    .response = (Font_Request_Response)_glyphs_load_request_response,
1743    .error = (Font_Request_Error)_glyphs_load_request_failed
1744 };
1745
1746 static Eina_Bool
1747 _font_entry_stats_cb(const Eina_Hash *hash __UNUSED__, const void *key __UNUSED__, void *data, void *fdata)
1748 {
1749    Font_Entry *fe = data;
1750    Msg_Stats *msg = fdata;
1751    Font_Cache *fc;
1752    int load_time;
1753    int nrefs = eina_list_count(fe->base.references);
1754
1755    // accounting size
1756    EINA_INLIST_FOREACH(fe->caches, fc)
1757      {
1758         /* This is not real requested usage, but an approximation. We don't
1759          * know how many times each glyph would be used by each client, but
1760          * assume that a similar set of glyphs from a given font would be used
1761          * by each client, thus counting them one time per client referencing
1762          * them.
1763          */
1764         msg->fonts.requested_usage += fc->usage * nrefs;
1765         msg->fonts.real_usage += cserve2_shm_size_get(fc->shm);
1766      }
1767
1768 #ifdef DEBUG_LOAD_TIME
1769    // accounting fonts load time
1770    load_time = _timeval_sub(&fe->base.load_finish, &fe->base.load_start);
1771    msg->fonts.fonts_load += load_time;
1772    if (fe->caches) msg->fonts.fonts_used_load += load_time;
1773
1774    // accounting glyphs load time
1775    msg->fonts.glyphs_load += fe->gl_load_time;
1776 #endif
1777
1778    return EINA_TRUE;
1779 }
1780
1781 static void
1782 _cserve2_cache_image_stats_get(Msg_Stats *msg)
1783 {
1784 }
1785
1786 static void
1787 _cserve2_cache_font_stats_get(Msg_Stats *msg)
1788 {
1789    eina_hash_foreach(font_entries, _font_entry_stats_cb, msg);
1790 }
1791
1792 struct _debug_info
1793 {
1794    unsigned int size;
1795    unsigned int nfonts;
1796 };
1797
1798 static Eina_Bool
1799 _font_entry_debug_size_cb(const Eina_Hash *hash __UNUSED__, const void *key __UNUSED__, void *data, void *fdata)
1800 {
1801    struct _debug_info *di = fdata;
1802    unsigned int size = di->size;
1803    Font_Entry *fe = data;
1804    Font_Cache *fc;
1805    unsigned int intsize = sizeof(unsigned int);
1806
1807    // filelen
1808    size += intsize;
1809
1810    // file
1811    if (fe->src->file)
1812      size += strlen(fe->src->file) + 1;
1813
1814    // namelen
1815    size += intsize;
1816
1817    // name
1818    if (fe->src->name)
1819      size += strlen(fe->src->name) + 1;
1820
1821    // rend_flags, size, dpi
1822    size += 3 * intsize;
1823
1824    // unused
1825    size += intsize;
1826
1827    // ncaches
1828    size += intsize;
1829
1830    EINA_INLIST_FOREACH(fe->caches, fc)
1831      {
1832         Glyph_Entry *gl;
1833
1834         // shmnamelen + shmname
1835         size += intsize;
1836         size += strlen(cserve2_shm_name_get(fc->shm)) + 1;
1837
1838         // size + usage
1839         size += 2 * intsize;
1840
1841         // nglyphs
1842         size += intsize;
1843
1844         EINA_INLIST_FOREACH(fc->glyphs, gl)
1845           {
1846              // index, offset, size
1847              size += 3 * intsize;
1848
1849              // rows, width, pitch
1850              size += 3 * intsize;
1851
1852              // num_grays, pixel_mode
1853              size += 2 * intsize;
1854           }
1855      }
1856
1857    di->size = size;
1858    di->nfonts++;
1859
1860    return EINA_TRUE;
1861 }
1862
1863 static Eina_Bool
1864 _font_entry_debug_cb(const Eina_Hash *hash __UNUSED__, const void *key __UNUSED__, void *data, void *fdata)
1865 {
1866    char *buf = fdata;
1867    Font_Entry *fe = data;
1868    Font_Cache *fc;
1869    unsigned int len;
1870    unsigned int unused;
1871    unsigned int ncaches;
1872    unsigned int intsize = sizeof(unsigned int);
1873
1874    // filelen + file
1875    len = 0;
1876    if (fe->src->file)
1877      len = strlen(fe->src->file) + 1;
1878    memcpy(buf, &len, intsize);
1879    buf += intsize;
1880    memcpy(buf, fe->src->file, len);
1881    buf += len;
1882
1883    // namelen + name
1884    len = 0;
1885    if (fe->src->name)
1886      len = strlen(fe->src->name) + 1;
1887    memcpy(buf, &len, intsize);
1888    buf += intsize;
1889    memcpy(buf, fe->src->name, len);
1890    buf += len;
1891
1892    // rend_flags, size, dpi
1893    memcpy(buf, &fe->rend_flags, intsize);
1894    buf += intsize;
1895    memcpy(buf, &fe->size, intsize);
1896    buf += intsize;
1897    memcpy(buf, &fe->dpi, intsize);
1898    buf += intsize;
1899
1900    // unused
1901    unused = fe->unused;
1902    memcpy(buf, &unused, intsize);
1903    buf += intsize;
1904
1905    // ncaches
1906    ncaches = eina_inlist_count(fe->caches);
1907    memcpy(buf, &ncaches, intsize);
1908    buf += intsize;
1909
1910    EINA_INLIST_FOREACH(fe->caches, fc)
1911      {
1912         Glyph_Entry *gl;
1913         const char *shmname;
1914         unsigned int shmsize;
1915
1916         // shmnamelen + shmname
1917         shmname = cserve2_shm_name_get(fc->shm);
1918         len = strlen(shmname) + 1;
1919         memcpy(buf, &len, intsize);
1920         buf += intsize;
1921         memcpy(buf, shmname, len);
1922         buf += len;
1923
1924         // size, usage, nglyphs
1925         shmsize = cserve2_shm_size_get(fc->shm);
1926         memcpy(buf, &shmsize, intsize);
1927         buf += intsize;
1928         memcpy(buf, &fc->usage, intsize);
1929         buf += intsize;
1930         memcpy(buf, &fc->nglyphs, intsize);
1931         buf += intsize;
1932
1933         EINA_INLIST_FOREACH(fc->glyphs, gl)
1934           {
1935              // index, offset, size
1936              memcpy(buf, &gl->index, intsize);
1937              buf += intsize;
1938              memcpy(buf, &gl->offset, intsize);
1939              buf += intsize;
1940              memcpy(buf, &gl->size, intsize);
1941              buf += intsize;
1942
1943              // rows, width, pitch
1944              memcpy(buf, &gl->rows, intsize);
1945              buf += intsize;
1946              memcpy(buf, &gl->width, intsize);
1947              buf += intsize;
1948              memcpy(buf, &gl->pitch, intsize);
1949              buf += intsize;
1950
1951              // num_grays, pixel_mode
1952              memcpy(buf, &gl->num_grays, intsize);
1953              buf += intsize;
1954              memcpy(buf, &gl->pixel_mode, intsize);
1955              buf += intsize;
1956           }
1957      }
1958
1959    return EINA_TRUE;
1960 }
1961
1962 static void *
1963 _cserve2_cache_font_debug(unsigned int rid, unsigned int *size)
1964 {
1965    Msg_Font_Debug msg;
1966    char *buf, *pos;
1967    struct _debug_info di;
1968    di.size = sizeof(msg);
1969
1970    memset(&msg, 0, sizeof(msg));
1971
1972    msg.base.type = CSERVE2_FONT_DEBUG;
1973    msg.base.rid = rid;
1974
1975    // First calculate how much size is needed for this message:
1976
1977    // nfonts
1978    di.size += sizeof(unsigned int);
1979
1980    // size needed for each font entry
1981    eina_hash_foreach(font_entries, _font_entry_debug_size_cb, &di);
1982
1983    // Now really create the message
1984    buf = malloc(di.size);
1985    pos = buf;
1986
1987    // msg base
1988    memcpy(buf, &msg, sizeof(msg));
1989    pos += sizeof(msg);
1990
1991    // nfonts
1992    memcpy(pos, &di.nfonts, sizeof(unsigned int));
1993    pos += sizeof(unsigned int);
1994
1995    eina_hash_foreach(font_entries, _font_entry_debug_cb, pos);
1996
1997    *size = di.size;
1998    return buf;
1999 }
2000
2001 int
2002 cserve2_cache_file_open(Client *client, unsigned int client_file_id, const char *path, const char *key, unsigned int rid)
2003 {
2004    unsigned int file_id;
2005    File_Data *entry;
2006    Reference *ref;
2007    File_Watch *fw;
2008    char buf[4906];
2009
2010    // look for this file on client references
2011    ref = eina_hash_find(client->files.referencing, &client_file_id);
2012    if (ref)
2013      {
2014         entry = (File_Data *)ref->entry;
2015
2016         if (entry->invalid)
2017           {
2018              cserve2_client_error_send(client, rid, CSERVE2_FILE_CHANGED);
2019              return -1;
2020           }
2021
2022         DBG("found client file id: %d", client_file_id);
2023         ref->count++;
2024
2025         // File already being loaded, just add the request to be replied
2026         if (entry->base.request)
2027           _request_answer_add(entry->base.request, ref, rid, CSERVE2_OPEN);
2028         else
2029           _image_opened_send(client, entry, rid);
2030         return 0;
2031      }
2032
2033    // search whether the file is already opened by another client
2034    snprintf(buf, sizeof(buf), "%s:%s", path, key);
2035    file_id = (unsigned int)eina_hash_find(file_ids, buf);
2036    if (file_id)
2037      {
2038         DBG("found file_id %u for client file id %d",
2039                 file_id, client_file_id);
2040         entry = eina_hash_find(file_entries, &file_id);
2041         if (!entry)
2042           {
2043              ERR("file \"%s\" is in file_ids hash but not in entries hash.",
2044                  buf);
2045              cserve2_client_error_send(client, rid, CSERVE2_INVALID_CACHE);
2046              return -1;
2047           }
2048         ref = _entry_reference_add((Entry *)entry, client, client_file_id);
2049         eina_hash_add(client->files.referencing, &client_file_id, ref);
2050         if (entry->base.request)
2051           _request_answer_add(entry->base.request, ref, rid, CSERVE2_OPEN);
2052         else // File already loaded, otherwise there would be a request
2053           _image_opened_send(client, entry, rid);
2054         return 0;
2055      }
2056
2057    file_id = _file_id++;
2058    while ((file_id == 0) || (eina_hash_find(file_entries, &file_id)))
2059      file_id = _file_id++;
2060
2061    DBG("Creating new entry with file_id: %u for file \"%s:%s\"",
2062        file_id, path, key);
2063    entry = calloc(1, sizeof(*entry));
2064    entry->base.type = CSERVE2_IMAGE_FILE;
2065    entry->path = strdup(path);
2066    entry->key = strdup(key);
2067    entry->base.id = file_id;
2068    eina_hash_add(file_entries, &file_id, entry);
2069    eina_hash_add(file_ids, buf, (void *)file_id);
2070    ref = _entry_reference_add((Entry *)entry, client, client_file_id);
2071    eina_hash_add(client->files.referencing, &client_file_id, ref);
2072
2073    fw = eina_hash_find(file_watch, entry->path);
2074    if (!fw)
2075      {
2076         fw = calloc(1, sizeof(File_Watch));
2077         fw->path = eina_stringshare_add(entry->path);
2078         cserve2_file_change_watch_add(fw->path, _file_changed_cb, fw);
2079         eina_hash_direct_add(file_watch, fw->path, fw);
2080      }
2081    fw->entries = eina_list_append(fw->entries, entry);
2082    entry->watcher = fw;
2083
2084    _request_add(&open_requests, (Entry *)entry, ref, rid, CSERVE2_OPEN);
2085
2086    // _open_image_default_set(entry);
2087
2088    return 0;
2089 }
2090
2091 void
2092 cserve2_cache_file_close(Client *client, unsigned int client_file_id)
2093 {
2094    Reference *ref = eina_hash_find(client->files.referencing,
2095                                         &client_file_id);
2096    if (!ref)
2097      {
2098         ERR("Couldn't find file %d in client hash.", client_file_id);
2099         return;
2100      }
2101
2102    ref->count--;
2103    if (ref->count <= 0)
2104      // will call _entry_free_cb() for this entry
2105      eina_hash_del_by_key(client->files.referencing, &client_file_id);
2106 }
2107
2108 int
2109 cserve2_cache_image_opts_set(Client *client, Msg_Setopts *msg)
2110 {
2111    Image_Data *entry;
2112    File_Data *fentry = NULL;
2113    Reference *ref, *oldref;
2114    unsigned int image_id;
2115    char buf[4096];
2116
2117    oldref = eina_hash_find(client->images.referencing, &msg->image_id);
2118
2119    // search whether the image is already loaded by another client
2120    entry = _image_msg_new(client, msg);
2121    if (!entry)
2122      return -1;
2123    image_id = _img_opts_id_get(entry, buf, sizeof(buf));
2124    if (image_id)
2125      {  // if so, just update the references
2126         free(entry);
2127         DBG("found image_id %d for client image id %d",
2128             image_id, msg->image_id);
2129         entry = eina_hash_find(image_entries, &image_id);
2130         if (!entry)
2131           {
2132              ERR("image id %d is in file_ids hash, but not in entries hash"
2133                  "with entry id %d.", msg->image_id, image_id);
2134              cserve2_client_error_send(client, msg->base.rid,
2135                                        CSERVE2_INVALID_CACHE);
2136              return -1;
2137           }
2138
2139         if (entry->unused)
2140           {
2141              DBG("Re-using old image entry (id: %d) from the LRU list.",
2142                  entry->base.id);
2143              entry->unused = EINA_FALSE;
2144              image_entries_lru = eina_list_remove(image_entries_lru, entry);
2145              unused_mem_usage -= _image_entry_size_get(entry);
2146           }
2147
2148         if (oldref && (oldref->entry->id == image_id))
2149           return 0;
2150
2151         ref = _entry_reference_add((Entry *)entry, client, msg->image_id);
2152
2153         if (oldref)
2154           eina_hash_del_by_key(client->images.referencing, &msg->image_id);
2155
2156         eina_hash_add(client->images.referencing, &msg->image_id, ref);
2157
2158         return 0;
2159      }
2160
2161    image_id = _image_id++;
2162    while ((image_id == 0) || (eina_hash_find(image_entries, &image_id)))
2163      image_id = _image_id++;
2164
2165    entry->base.id = image_id;
2166    eina_hash_add(image_entries, &image_id, entry);
2167    eina_hash_add(image_ids, buf, (void *)image_id);
2168    ref = _entry_reference_add((Entry *)entry, client, msg->image_id);
2169
2170    if (oldref)
2171      eina_hash_del_by_key(client->images.referencing, &msg->image_id);
2172    eina_hash_add(client->images.referencing, &msg->image_id, ref);
2173
2174    fentry = entry->file;
2175    fentry->images = eina_list_append(fentry->images, entry);
2176
2177    _request_add(&spload_requests, (Entry *)entry, ref, msg->base.rid,
2178                 CSERVE2_SETOPTS);
2179    return 0;
2180 }
2181
2182 void
2183 cserve2_cache_image_load(Client *client, unsigned int client_image_id, unsigned int rid)
2184 {
2185    Image_Data *entry;
2186    Reference *ref;
2187
2188    ref = eina_hash_find(client->images.referencing, &client_image_id);
2189    if (!ref)
2190      {
2191         ERR("Can't load: client %d has no image id %d",
2192             client->id, client_image_id);
2193         return;
2194      }
2195
2196    entry = (Image_Data *)ref->entry;
2197
2198    if (entry->file->invalid)
2199      {
2200         cserve2_client_error_send(client, rid, CSERVE2_FILE_CHANGED);
2201         return;
2202      }
2203
2204    DBG("Loading image id: %d", ref->entry->id);
2205
2206    // File already being loaded, just add the request to be replied
2207    if (entry->base.request)
2208      {
2209         _request_answer_add(entry->base.request, ref, rid, CSERVE2_LOAD);
2210         if ((!entry->base.request->processing) && (!entry->doload))
2211           {
2212              DBG("Removing entry %d from speculative preload and adding "
2213                  "to normal load queue.", entry->base.id);
2214              spload_requests = eina_list_remove(spload_requests,
2215                                                 entry->base.request);
2216              load_requests = eina_list_append(load_requests,
2217                                               entry->base.request);
2218           }
2219      }
2220    else if (entry->shm)
2221      _image_loaded_send(client, entry, rid);
2222    else
2223      _request_add(&load_requests, (Entry *)entry, ref, rid, CSERVE2_LOAD);
2224
2225    entry->doload = EINA_TRUE;
2226 }
2227
2228 void
2229 cserve2_cache_image_preload(Client *client, unsigned int client_image_id, unsigned int rid)
2230 {
2231    Image_Data *entry;
2232    Reference *ref;
2233
2234    ref = eina_hash_find(client->images.referencing, &client_image_id);
2235    if (!ref)
2236      {
2237         ERR("Can't load: client %d has no image id %d",
2238             client->id, client_image_id);
2239         return;
2240      }
2241
2242    entry = (Image_Data *)ref->entry;
2243
2244    if (entry->file->invalid)
2245      {
2246         cserve2_client_error_send(client, rid, CSERVE2_FILE_CHANGED);
2247         return;
2248      }
2249
2250    DBG("Loading image id: %d", ref->entry->id);
2251
2252    // File already being loaded, just add the request to be replied
2253    if (entry->base.request)
2254      {
2255         _request_answer_add(entry->base.request, ref, rid, CSERVE2_PRELOAD);
2256         if ((!entry->base.request->processing) && (!entry->doload))
2257           {
2258              DBG("Removing entry %d from speculative preload and adding "
2259                  "to normal (pre)load queue.", entry->base.id);
2260              spload_requests = eina_list_remove(spload_requests,
2261                                                 entry->base.request);
2262              load_requests = eina_list_append(load_requests,
2263                                               entry->base.request);
2264           }
2265      }
2266    else if (entry->shm)
2267      _image_preloaded_send(client, rid);
2268    else
2269      _request_add(&load_requests, (Entry *)entry, ref, rid, CSERVE2_PRELOAD);
2270
2271    entry->doload = EINA_TRUE;
2272 }
2273
2274 void
2275 cserve2_cache_image_unload(Client *client, unsigned int client_image_id)
2276 {
2277    Reference *ref = eina_hash_find(client->images.referencing,
2278                                    &client_image_id);
2279    if (!ref)
2280      {
2281         ERR("Couldn't find file %d in client hash.", client_image_id);
2282         return;
2283      }
2284
2285    ref->count--;
2286    if (ref->count <= 0)
2287      // will call _entry_free_cb() for this entry
2288      eina_hash_del_by_key(client->images.referencing, &client_image_id);
2289 }
2290
2291 int
2292 cserve2_cache_font_load(Client *client, const char *source, unsigned int sourcelen, const char *name, unsigned int namelen, unsigned int rend_flags, unsigned int size, unsigned int dpi, unsigned int rid)
2293 {
2294    Reference *ref;
2295    Font_Source *fs;
2296    Font_Entry *fe;
2297    char *fullname;
2298
2299    if (sourcelen == 0)
2300      source = NULL;
2301    if (namelen == 0)
2302      name = NULL;
2303
2304    fullname = _file_path_join(source, name);
2305    fe = _cserve2_font_entry_find(fullname, strlen(fullname) + 1, size,
2306                                  rend_flags, dpi);
2307    if (fe)
2308      {
2309         DBG("found font entry %s, rendflags: %d, size: %d, dpi: %d",
2310             name, rend_flags, size, dpi);
2311
2312         ref = _entry_reference_add((Entry *)fe, client, 0);
2313         client->fonts.referencing = eina_list_append(
2314            client->fonts.referencing, ref);
2315
2316         fe->unused = EINA_FALSE;
2317
2318         if (fe->request)
2319           cserve2_request_waiter_add(fe->request, rid, client);
2320         else
2321           _font_loaded_send(client, rid);
2322         free(fullname);
2323         return 0;
2324      }
2325
2326    fe = calloc(1, sizeof(*fe));
2327    fe->rend_flags = rend_flags;
2328    fe->size = size;
2329    fe->dpi = dpi;
2330    fe->base.type = CSERVE2_FONT_ENTRY;
2331    fe->glyphs = fash_gl_new(_glyph_free_cb);
2332    ref = _entry_reference_add((Entry *)fe, client, 0);
2333    client->fonts.referencing = eina_list_append(
2334       client->fonts.referencing, ref);
2335    fe->unused = EINA_FALSE;
2336
2337    fs = _cserve2_font_source_find(fullname);
2338    if (!fs)
2339      {
2340         fs = calloc(1, sizeof(*fs));
2341         if (source)
2342           {
2343              fs->key = eina_stringshare_add(fullname);
2344              fs->name = eina_stringshare_add_length(name, namelen);
2345              fs->file = eina_stringshare_add_length(source, sourcelen);
2346           }
2347         else
2348           {
2349              fs->file = eina_stringshare_add_length(name, namelen);
2350              fs->key = eina_stringshare_ref(fs->file);
2351           }
2352         eina_hash_direct_add(font_sources, fs->key, fs);
2353      }
2354
2355    fe->src = fs;
2356    fs->references++;
2357    DBG("adding FONT_LOAD '%s' request.", fs->name);
2358    fe->request = cserve2_request_add(CSERVE2_REQ_FONT_LOAD, rid,
2359                                      client, &_font_load_funcs, fe);
2360
2361    eina_hash_direct_add(font_entries, fe, fe);
2362
2363    free(fullname);
2364
2365    return 0;
2366 }
2367
2368 int
2369 cserve2_cache_font_unload(Client *client, const char *source, unsigned int sourcelen, const char *name, unsigned int namelen, unsigned int rend_flags, unsigned int size, unsigned int dpi, unsigned int rid)
2370 {
2371    Font_Entry *fe;
2372    char *fullname;
2373
2374    if (sourcelen == 0)
2375      source = NULL;
2376    if (namelen == 0)
2377      name = NULL;
2378
2379    fullname = _file_path_join(source, name);
2380    fe = _cserve2_font_entry_find(fullname, strlen(fullname) + 1, size,
2381                                  rend_flags, dpi);
2382    free(fullname);
2383
2384    if (!fe)
2385      {
2386         ERR("Unreferencing font not found: '%s:%s'.", source, name);
2387         return -1;
2388      }
2389
2390    _font_entry_reference_del(client, fe);
2391
2392    return 0;
2393 }
2394
2395 int
2396 cserve2_cache_font_glyphs_load(Client *client, const char *source, unsigned int sourcelen, const char *name, unsigned int namelen, unsigned int hint, unsigned int rend_flags, unsigned int size, unsigned int dpi, unsigned int *glyphs, unsigned int nglyphs, unsigned int rid)
2397 {
2398    Glyphs_Request *req;
2399
2400    req = _glyphs_request_create(client, source, sourcelen, name, namelen,
2401                                 hint, rend_flags, size, dpi, glyphs, nglyphs);
2402    if (!req)
2403      {
2404         free(glyphs);
2405         return -1;
2406      }
2407
2408    if (_glyphs_request_check(req))
2409      {
2410         INF("Glyphs already loaded. Sending answer.");
2411         _glyphs_loaded_send(req, rid);
2412         _glyphs_request_free(req);
2413      }
2414    else
2415      {
2416         cserve2_request_add(CSERVE2_REQ_FONT_GLYPHS_LOAD, rid,
2417                             client, &_glyphs_load_funcs, req);
2418      }
2419    return 0;
2420 }
2421
2422 int
2423 cserve2_cache_font_glyphs_used(Client *client, const char *source, unsigned int sourcelen, const char *name, unsigned int namelen, unsigned int hint, unsigned int rend_flags, unsigned int size, unsigned int dpi, unsigned int *glyphs, unsigned int nglyphs, unsigned int rid __UNUSED__)
2424 {
2425    Glyphs_Group *gg;
2426    Eina_List *groups;
2427    Glyphs_Request *req;
2428
2429    DBG("Received report of used glyphs from client %d", client->id);
2430    req = _glyphs_request_create(client, source, sourcelen, name, namelen,
2431                                 hint, rend_flags, size, dpi, glyphs, nglyphs);
2432    if (!req)
2433      {
2434         free(glyphs);
2435         return 0;
2436      }
2437
2438    _glyphs_load_request_prepare(req);
2439    groups = _glyphs_group_create(req);
2440
2441    // Promote SHMs which are still cached and in use
2442    // TODO: We can use later the information from request_prepare to preload
2443    // glyphs which are not cached anymore, but are in use on the client.
2444    EINA_LIST_FREE(groups, gg)
2445      {
2446         _font_shm_promote(gg->fc);
2447         eina_list_free(gg->glyphs);
2448      }
2449
2450    _glyphs_request_free(req);
2451    return 0;
2452 }
2453
2454 void
2455 cserve2_cache_requests_response(Slave_Command type, void *msg, void *data)
2456 {
2457    Request *req = data;
2458
2459    if (!req->entry)
2460      {
2461         Waiter *w;
2462         DBG("Request finished but it has no entry anymore.");
2463         EINA_LIST_FREE(req->waiters, w)
2464           {
2465              cserve2_client_error_send(w->ref->client, w->rid,
2466                                        CSERVE2_REQUEST_CANCEL);
2467
2468              w->ref->count--;
2469              free(w);
2470           }
2471      }
2472    else if (type == ERROR)
2473      {
2474         Error_Type *error = msg;
2475         req->funcs->error(req->entry, *error);
2476      }
2477    else
2478      req->funcs->response(req->entry, msg);
2479
2480    if (req->entry)
2481      req->entry->request = NULL;
2482    free(req);
2483 }
2484
2485 void
2486 cserve2_cache_stats_get(Client *client, unsigned int rid)
2487 {
2488    Msg_Stats msg;
2489    int size;
2490
2491    memset(&msg, 0, sizeof(msg));
2492
2493    msg.base.type = CSERVE2_STATS;
2494    msg.base.rid = rid;
2495
2496    _cserve2_cache_image_stats_get(&msg);
2497    _cserve2_cache_font_stats_get(&msg);
2498
2499    size = sizeof(msg);
2500    cserve2_client_send(client, &size, sizeof(size));
2501    cserve2_client_send(client, &msg, size);
2502 }
2503
2504 void
2505 cserve2_cache_font_debug(Client *client, unsigned int rid)
2506 {
2507    void *msg;
2508    unsigned int size;
2509
2510    msg = _cserve2_cache_font_debug(rid, &size);
2511
2512    cserve2_client_send(client, &size, sizeof(size));
2513    cserve2_client_send(client, msg, size);
2514
2515    free(msg);
2516 }