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