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