11 #include "evas_cserve2.h"
12 #include "evas_cs2_utils.h"
14 typedef struct _Request_Funcs Request_Funcs;
15 typedef struct _Request Request;
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;
24 typedef struct _Font_Source Font_Source;
25 typedef struct _Font_Entry Font_Entry;
26 typedef struct _Font_Cache Font_Cache;
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);
32 struct _Request_Funcs {
33 Request_Msg_Create msg_create;
34 Request_Response response;
53 Eina_List *references;
56 #ifdef DEBUG_LOAD_TIME
57 struct timeval load_start;
58 struct timeval load_finish;
70 const char *loader_data;
74 Eina_Bool invalid : 1;
77 // Default values for load options commented below
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
90 Eina_Bool alpha_sparse : 1;
105 Font_Request *request;
106 unsigned int rend_flags;
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;
129 unsigned int nglyphs;
132 struct _Glyph_Entry {
142 unsigned int num_grays;
143 unsigned int pixel_mode;
146 struct _Glyphs_Request {
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;
159 typedef struct _Glyphs_Request Glyphs_Request;
161 struct _Glyphs_Group {
166 typedef struct _Glyphs_Group Glyphs_Group;
171 unsigned int client_entry_id; // for reverse lookup
186 static Eina_List *open_requests = NULL;
187 static Eina_List *load_requests = NULL;
188 static Eina_List *spload_requests = NULL; // speculative preload requests
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
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
198 static Eina_Hash *font_sources = NULL; // font path --> font source
199 static Eina_Hash *font_entries = NULL; // maps font path + options --> entry
201 static Eina_Hash *file_watch = NULL;
203 static Eina_List *image_entries_lru = NULL;
205 static Eina_List *font_shm_lru = NULL;
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;
213 _entry_load_start(Entry *e)
215 #ifdef DEBUG_LOAD_TIME
216 gettimeofday(&e->load_start, NULL);
221 _entry_load_finish(Entry *e)
223 #ifdef DEBUG_LOAD_TIME
224 gettimeofday(&e->load_finish, NULL);
228 #ifdef DEBUG_LOAD_TIME
230 _timeval_sub(const struct timeval *tv2, const struct timeval *tv1)
234 t1 = tv1->tv_usec + tv1->tv_sec * 1000000;
235 t2 = tv2->tv_usec + tv2->tv_sec * 1000000;
242 _image_opened_send(Client *client, File_Data *entry, unsigned int rid)
247 DBG("Sending OPENED reply for entry: %d and RID: %d.", entry->base.id, rid);
248 // clear the struct with possible paddings, since it is not aligned.
249 memset(&msg, 0, sizeof(msg));
251 msg.base.type = CSERVE2_OPENED;
252 msg.image.w = entry->w;
253 msg.image.h = entry->h;
254 msg.image.frame_count = entry->frame_count;
255 msg.image.loop_count = entry->loop_count;
256 msg.image.loop_hint = entry->loop_hint;
257 msg.image.alpha = entry->alpha;
260 cserve2_client_send(client, &size, sizeof(size));
261 cserve2_client_send(client, &msg, sizeof(msg));
262 // _cserve2_cache_load_requests_process();
266 _image_loaded_send(Client *client, Image_Data *entry, unsigned int rid)
269 const char *shmpath = cserve2_shm_name_get(entry->shm);
274 DBG("Sending LOADED reply for entry %d and RID: %d.", entry->base.id, rid);
275 path_len = strlen(shmpath) + 1;
277 memset(&msg, 0, sizeof(msg));
279 msg.base.type = CSERVE2_LOADED;
281 msg.shm.mmap_offset = cserve2_shm_map_offset_get(entry->shm);
282 msg.shm.use_offset = cserve2_shm_offset_get(entry->shm);
283 msg.shm.mmap_size = cserve2_shm_map_size_get(entry->shm);
284 msg.shm.image_size = cserve2_shm_size_get(entry->shm);
285 msg.alpha_sparse = entry->alpha_sparse;
287 buf = malloc(sizeof(msg) + path_len);
289 memcpy(buf, &msg, sizeof(msg));
290 memcpy(buf + sizeof(msg), shmpath, path_len);
292 size = sizeof(msg) + path_len;
294 cserve2_client_send(client, &size, sizeof(size));
295 cserve2_client_send(client, buf, size);
301 _image_preloaded_send(Client *client, unsigned int rid)
306 DBG("Sending PRELOADED reply for RID: %d.", rid);
307 memset(&msg, 0, sizeof(msg));
309 msg.base.type = CSERVE2_PRELOADED;
312 cserve2_client_send(client, &size, sizeof(size));
313 cserve2_client_send(client, &msg, size);
317 _font_loaded_send(Client *client, unsigned int rid)
322 DBG("Sending FONT_LOADED reply for RID: %d.", rid);
325 memset(&msg, 0, size);
327 msg.base.type = CSERVE2_FONT_LOADED;
330 cserve2_client_send(client, &size, sizeof(size));
331 cserve2_client_send(client, &msg, size);
335 _open_request_build(File_Data *f, int *bufsize)
338 int size, pathlen, keylen;
339 Slave_Msg_Image_Open msg;
341 pathlen = strlen(f->path) + 1;
342 keylen = strlen(f->key) + 1;
344 size = sizeof(msg) + pathlen + keylen;
346 if (!buf) return NULL;
348 memset(&msg, 0, sizeof(msg));
349 memcpy(buf, &msg, sizeof(msg));
350 memcpy(buf + sizeof(msg), f->path, pathlen);
351 memcpy(buf + sizeof(msg) + pathlen, f->key, keylen);
355 _entry_load_start(&f->base);
361 _request_failed(Entry *e, Error_Type type)
367 DBG("Request for entry %p failed with error %d", e, type);
368 EINA_LIST_FREE(e->request->waiters, w)
370 cserve2_client_error_send(w->ref->client, w->rid, type);
376 EINA_LIST_FOREACH(e->references, l, ref)
378 Eina_Hash *hash = NULL;
379 if (e->type == CSERVE2_IMAGE_FILE)
380 hash = ref->client->files.referencing;
381 else if (e->type == CSERVE2_IMAGE_DATA)
382 hash = ref->client->images.referencing;
386 eina_hash_del_by_key(hash, &(ref->client_entry_id));
391 _open_request_response(File_Data *e, Slave_Msg_Image_Opened *resp)
395 _entry_load_finish(&e->base);
398 e->frame_count = resp->frame_count;
399 e->loop_count = resp->loop_count;
400 e->loop_hint = resp->loop_hint;
401 e->alpha = resp->alpha;
402 if (resp->has_loader_data)
404 const char *ldata = (const char *)resp +
405 sizeof(Slave_Msg_Image_Opened);
406 e->loader_data = eina_stringshare_add(ldata);
409 DBG("Finished opening file %d. Notifying %d waiters.", e->base.id,
410 e->base.request->waiters ? eina_list_count(e->base.request->waiters) : 0);
411 EINA_LIST_FREE(e->base.request->waiters, w)
413 _image_opened_send(w->ref->client, e, w->rid);
418 static Request_Funcs _open_funcs = {
419 .msg_create = (Request_Msg_Create)_open_request_build,
420 .response = (Request_Response)_open_request_response,
421 .error = (Request_Error)_request_failed
425 _load_request_build(Image_Data *i, int *bufsize)
430 int shmlen, filelen, keylen, loaderlen;
431 Slave_Msg_Image_Load msg;
433 // opening shm for this file
434 i->shm = cserve2_shm_request(i->file->w * i->file->h * 4);
436 shmpath = cserve2_shm_name_get(i->shm);
438 shmlen = strlen(shmpath) + 1;
439 filelen = strlen(i->file->path) + 1;
440 keylen = strlen(i->file->key) + 1;
441 if (i->file->loader_data)
442 loaderlen = strlen(i->file->loader_data) + 1;
446 size = sizeof(msg) + shmlen + filelen + keylen + loaderlen;
448 if (!buf) return NULL;
450 memset(&msg, 0, sizeof(msg));
453 msg.alpha = i->file->alpha;
454 msg.opts.w = i->opts.w;
455 msg.opts.h = i->opts.h;
456 msg.opts.rx = i->opts.rx;
457 msg.opts.ry = i->opts.ry;
458 msg.opts.rw = i->opts.rw;
459 msg.opts.rh = i->opts.rh;
460 msg.opts.scale_down_by = i->opts.scale_down;
461 msg.opts.dpi = i->opts.dpi;
462 msg.opts.orientation = i->opts.orientation;
464 msg.shm.mmap_offset = cserve2_shm_map_offset_get(i->shm);
465 msg.shm.image_offset = cserve2_shm_offset_get(i->shm);
466 msg.shm.mmap_size = cserve2_shm_map_size_get(i->shm);
467 msg.shm.image_size = cserve2_shm_size_get(i->shm);
469 msg.has_loader_data = !!loaderlen;
471 memcpy(buf, &msg, sizeof(msg));
472 ptr = buf + sizeof(msg);
474 memcpy(ptr, shmpath, shmlen);
476 memcpy(ptr, i->file->path, filelen);
478 memcpy(ptr, i->file->key, keylen);
480 memcpy(ptr, i->file->loader_data, loaderlen);
484 _entry_load_start(&i->base);
490 _load_request_response(Image_Data *e, Slave_Msg_Image_Loaded *resp)
494 _entry_load_start(&e->base);
496 e->alpha_sparse = resp->alpha_sparse;
498 DBG("Entry %d loaded by speculative preload.", e->base.id);
500 DBG("Finished loading image %d. Notifying %d waiters.", e->base.id,
501 e->base.request->waiters ? eina_list_count(e->base.request->waiters) : 0);
502 EINA_LIST_FREE(e->base.request->waiters, w)
504 if (w->type == CSERVE2_LOAD)
505 _image_loaded_send(w->ref->client, e, w->rid);
506 else if (w->type == CSERVE2_PRELOAD)
507 _image_preloaded_send(w->ref->client, w->rid);
508 // else w->type == CSERVE2_SETOPTS --> do nothing
514 static Request_Funcs _load_funcs = {
515 .msg_create = (Request_Msg_Create)_load_request_build,
516 .response = (Request_Response)_load_request_response,
517 .error = (Request_Error)_request_failed
521 _img_opts_id_get(Image_Data *im, char *buf, int size)
525 snprintf(buf, size, "%u:%0.3f:%dx%d:%d:%d,%d+%dx%d:%d",
526 im->file_id, im->opts.dpi, im->opts.w, im->opts.h,
527 im->opts.scale_down, im->opts.rx, im->opts.ry,
528 im->opts.rw, im->opts.rh, im->opts.orientation);
530 image_id = (uintptr_t)eina_hash_find(image_ids, buf);
536 _image_entry_size_get(Image_Data *e)
538 int size = sizeof(Image_Data);
539 /* XXX: get the overhead of the shm handler too */
541 size += cserve2_shm_size_get(e->shm);
546 _file_id_free(File_Data *entry)
550 DBG("Removing entry file id: %d, file: \"%s:%s\"",
551 entry->base.id, entry->path, entry->key);
552 snprintf(buf, sizeof(buf), "%s:%s", entry->path, entry->key);
553 eina_hash_del_by_key(file_ids, buf);
557 _image_id_free(Image_Data *entry)
561 DBG("Removing entry image id: %d", entry->base.id);
563 _img_opts_id_get(entry, buf, sizeof(buf));
564 eina_hash_del_by_key(image_ids, buf);
568 _image_entry_free(Image_Data *entry)
570 File_Data *fentry = entry->file;
572 if (entry->base.request)
574 if (entry->base.request->processing)
575 entry->base.request->entry = NULL;
576 else if (!entry->base.request->waiters)
579 load_requests = eina_list_remove(load_requests,
580 entry->base.request);
582 spload_requests = eina_list_remove(spload_requests,
583 entry->base.request);
589 image_entries_lru = eina_list_remove(image_entries_lru, entry);
590 unused_mem_usage -= _image_entry_size_get(entry);
594 fentry->images = eina_list_remove(fentry->images, entry);
596 cserve2_shm_unref(entry->shm);
601 _hash_image_entry_free(void *data)
603 Image_Data *entry = data;
605 _image_id_free(entry);
606 _image_entry_free(entry);
610 _file_entry_free(File_Data *entry)
614 // Should we call free for each of the images too?
615 // If everything goes fine, it's not necessary.
618 ERR("Freeing file %d (\"%s:%s\") image data still referenced.",
619 entry->base.id, entry->path, entry->key);
620 eina_list_free(entry->images);
623 if (entry->base.request)
625 if (entry->base.request->processing)
626 entry->base.request->entry = NULL;
627 else if (!entry->base.request->waiters)
629 open_requests = eina_list_remove(open_requests,
630 entry->base.request);
631 free(entry->base.request);
635 if ((fw = entry->watcher))
637 fw->entries = eina_list_remove(fw->entries, entry);
639 eina_hash_del_by_key(file_watch, fw->path);
644 eina_stringshare_del(entry->loader_data);
649 _hash_file_entry_free(void *data)
651 File_Data *entry = data;
652 // TODO: Add some checks to make sure that we are freeing an
655 _file_id_free(entry);
656 _file_entry_free(entry);
660 _file_watch_free(void *data)
662 File_Watch *fw = data;
663 cserve2_file_change_watch_del(fw->path);
664 eina_stringshare_del(fw->path);
665 eina_list_free(fw->entries);
670 _font_entry_cmp(const Font_Entry *k1, int k1_length __UNUSED__, const Font_Entry *k2, int k2_length __UNUSED__)
672 if (k1->src->key == k2->src->key)
674 if (k1->size == k2->size)
676 if (k1->rend_flags == k2->rend_flags)
677 return k1->dpi - k2->dpi;
678 return k1->rend_flags - k2->rend_flags;
680 return k1->size - k2->size;
682 return strcmp(k1->src->key, k2->src->key);
686 _font_entry_key_hash(const Font_Entry *key, int key_length __UNUSED__)
689 hash = eina_hash_djb2(key->src->key, eina_stringshare_strlen(key->src->key) + 1);
690 hash ^= eina_hash_int32(&key->rend_flags, sizeof(int));
691 hash ^= eina_hash_int32(&key->size, sizeof(int));
692 hash ^= eina_hash_int32(&key->dpi, sizeof(int));
698 _font_entry_free(Font_Entry *fe)
700 fash_gl_free(fe->glyphs);
701 fe->src->references--;
702 if (fe->ft) cserve2_font_ft_free(fe->ft);
703 if (fe->src->references <= 0)
704 eina_hash_del_by_key(font_sources, fe->src->key);
709 _glyph_free_cb(void *data)
711 Glyph_Entry *gl = data;
716 _font_source_free(Font_Source *fs)
718 eina_stringshare_del(fs->key);
719 eina_stringshare_del(fs->name);
720 eina_stringshare_del(fs->file);
721 if (fs->ft) cserve2_font_source_ft_free(fs->ft);
727 _font_shm_promote(Font_Cache *fc)
730 l = eina_list_data_find_list(font_shm_lru, fc);
731 font_shm_lru = eina_list_demote_list(font_shm_lru, l);
735 _font_shm_size_get(Font_Cache *fc)
739 size = sizeof(*fc) + cserve2_shm_size_get(fc->shm);
745 _font_shm_free(Font_Cache *fc)
747 Font_Entry *fe = fc->fe;
748 fe->caches = eina_inlist_remove(fe->caches, EINA_INLIST_GET(fc));
749 if (fc == fe->last_cache)
750 fe->last_cache = NULL;
754 Glyph_Entry *gl = EINA_INLIST_CONTAINER_GET(fc->glyphs, Glyph_Entry);
755 fc->glyphs = eina_inlist_remove(fc->glyphs, fc->glyphs);
756 fash_gl_del(fe->glyphs, gl->index);
759 cserve2_shm_unref(fc->shm);
763 eina_hash_del_by_key(font_entries, fe);
767 _font_shm_lru_flush(void)
769 Eina_List *l, *l_next;
772 l_next = eina_list_next(l);
774 while (l && font_mem_usage > max_font_usage)
779 fc = eina_list_data_get(l);
780 if (fc->fe->unused && fc->inuse == 0)
782 font_shm_lru = eina_list_remove_list(font_shm_lru, l);
783 size = _font_shm_size_get(fc);
784 size += fc->nglyphs * sizeof(Glyph_Entry);
786 font_mem_usage -= size;
790 l_next = eina_list_next(l);
795 cserve2_cache_init(void)
797 file_ids = eina_hash_string_superfast_new(NULL);
798 file_entries = eina_hash_int32_new(_hash_file_entry_free);
799 image_ids = eina_hash_string_superfast_new(NULL);
800 image_entries = eina_hash_string_superfast_new(_hash_image_entry_free);
801 file_watch = eina_hash_string_superfast_new(_file_watch_free);
803 font_sources = eina_hash_string_small_new(EINA_FREE_CB(_font_source_free));
804 font_entries = eina_hash_new(NULL,
805 EINA_KEY_CMP(_font_entry_cmp),
806 EINA_KEY_HASH(_font_entry_key_hash),
807 EINA_FREE_CB(_font_entry_free),
812 cserve2_cache_shutdown(void)
816 EINA_LIST_FREE(font_shm_lru, fc)
819 eina_hash_free(image_entries);
820 eina_hash_free(image_ids);
821 eina_hash_free(file_entries);
822 eina_hash_free(file_ids);
823 eina_hash_free(file_watch);
825 eina_hash_free(font_entries);
826 eina_hash_free(font_sources);
830 _request_answer_del(Eina_List **requests, Request *req, Client *client, Error_Type err)
832 Eina_List *l, *l_next;
835 DBG("Removing answer requests from entry: %d, client: %d",
836 req->entry->id, client->id);
838 EINA_LIST_FOREACH_SAFE(req->waiters, l, l_next, it)
840 if (it->ref->client->id == client->id)
842 cserve2_client_error_send(client, it->rid, err);
843 req->waiters = eina_list_remove_list(req->waiters, l);
848 // FIXME: Should this be really here? I guess that it should be in the
849 // entry_free_cb function, or entry_reference_del, when there are no more
851 if (!req->entry && !req->waiters)
853 *requests = eina_list_remove(*requests, req);
859 _request_answer_all_del(Eina_List **requests, Request *req, Error_Type err)
863 DBG("Removing all answer requests from entry: %d", req->entry->id);
865 EINA_LIST_FREE(req->waiters, it)
867 cserve2_client_error_send(it->ref->client, it->rid, err);
871 *requests = eina_list_remove(*requests, req);
876 _request_answer_add(Request *req, Reference *ref, unsigned int rid, Message_Type type)
878 Waiter *w = malloc(sizeof(*w));
884 DBG("Add answer request for entry id: %d, client: %d, rid: %d",
885 req->entry->id, ref->client->id, rid);
886 req->waiters = eina_list_append(req->waiters, w);
890 _request_add(Eina_List **requests, Entry *entry, Reference *ref, unsigned int rid, Message_Type type)
894 // add the request if it doesn't exist yet
897 req = malloc(sizeof(*req));
900 req->processing = EINA_FALSE;
901 entry->request = req;
902 if (type == CSERVE2_OPEN)
903 req->funcs = &_open_funcs;
905 req->funcs = &_load_funcs;
906 *requests = eina_list_append(*requests, req);
907 DBG("Add request for entry id: %d, client: %d, rid: %d",
908 req->entry->id, ref->client->id, rid);
911 req = entry->request;
913 if (type != CSERVE2_SETOPTS)
914 _request_answer_add(req, ref, rid, type);
916 DBG("Adding entry for speculative preload: id=%d", req->entry->id);
920 _entry_reference_add(Entry *entry, Client *client, unsigned int client_entry_id)
924 // increase reference for this file
925 ref = malloc(sizeof(*ref));
926 ref->client = client;
928 ref->client_entry_id = client_entry_id;
930 entry->references = eina_list_append(entry->references, ref);
936 _cserve2_cache_open_requests_process(int nloaders)
939 char *slave_cmd_data;
942 while ((nloaders > 0) && (open_requests))
944 // remove the first element from the list and process this element
945 req = eina_list_data_get(open_requests);
946 open_requests = eina_list_remove_list(open_requests, open_requests);
948 DBG("Processing OPEN request for file entry: %d", req->entry->id);
950 slave_cmd_data = req->funcs->msg_create(req->entry, &slave_cmd_size);
952 cserve2_slave_cmd_dispatch(req, IMAGE_OPEN, slave_cmd_data,
955 free(slave_cmd_data);
957 req->processing = EINA_TRUE;
965 _cserve2_cache_load_requests_list_process(Eina_List **queue, int nloaders)
967 Eina_List *skipped = NULL;
970 while ((nloaders > 0) && (*queue))
976 // remove the first element from the list and process this element
977 req = eina_list_data_get(*queue);
978 *queue = eina_list_remove_list(*queue, *queue);
980 ientry = (Image_Data *)req->entry;
983 ERR("File entry doesn't exist for entry id %d", req->entry->id);
984 _request_failed(req->entry, CSERVE2_INVALID_CACHE);
988 if (ientry->file->base.request)
990 /* OPEN still pending, skip this request */
991 skipped = eina_list_append(skipped, req);
995 DBG("Processing LOAD request for image entry: %d", req->entry->id);
997 buf = req->funcs->msg_create(req->entry, &size);
999 cserve2_slave_cmd_dispatch(req, IMAGE_LOAD, buf, size);
1003 req->processing = EINA_TRUE;
1008 EINA_LIST_FREE(skipped, req)
1009 *queue = eina_list_append(*queue, req);
1015 _cserve2_cache_load_requests_process(int nloaders)
1017 nloaders = _cserve2_cache_load_requests_list_process(&load_requests,
1019 _cserve2_cache_load_requests_list_process(&spload_requests, nloaders - 1);
1024 cserve2_cache_requests_process(void)
1028 avail_loaders = cserve2_slave_available_get();
1029 avail_loaders = _cserve2_cache_open_requests_process(avail_loaders);
1030 _cserve2_cache_load_requests_process(avail_loaders);
1034 _entry_unused_push(Image_Data *e)
1036 int size = _image_entry_size_get(e);
1038 if ((size > max_unused_mem_usage) || !(e->doload))
1040 eina_hash_del_by_key(image_entries, &e->base.id);
1043 while (size > (max_unused_mem_usage - unused_mem_usage))
1045 Entry *ie = eina_list_data_get(eina_list_last(image_entries_lru));
1046 eina_hash_del_by_key(image_entries, &ie->id);
1048 image_entries_lru = eina_list_append(image_entries_lru, e);
1049 e->unused = EINA_TRUE;
1050 unused_mem_usage += size;
1054 _entry_reference_del(Entry *entry, Reference *ref)
1056 entry->references = eina_list_remove(entry->references, ref);
1058 if (entry->references)
1061 if (entry->type == CSERVE2_IMAGE_FILE)
1063 File_Data *fentry = (File_Data *)entry;
1065 if (fentry->invalid)
1066 _file_entry_free(fentry);
1070 EINA_LIST_FREE(fentry->images, ie)
1072 eina_hash_del_by_key(file_entries, &entry->id);
1075 else if (entry->type == CSERVE2_IMAGE_DATA)
1077 Image_Data *ientry = (Image_Data *)entry;
1080 eina_hash_del_by_key(image_entries, &entry->id);
1081 else if (ientry->file->invalid)
1082 _image_entry_free(ientry);
1084 _entry_unused_push(ientry);
1086 else if (entry->type == CSERVE2_FONT_ENTRY)
1088 Font_Entry *fe = (Font_Entry *)entry;
1089 fe->unused = EINA_TRUE;
1091 eina_hash_del_by_key(font_entries, fe);
1094 ERR("Wrong type of entry.");
1101 _entry_free_cb(void *data)
1103 Reference *ref = data;
1106 DBG("Removing client reference for entry id: %d, client: %d",
1107 ref->entry->id, ref->client->id);
1111 if (entry->request && !entry->request->processing)
1113 if (entry->type == CSERVE2_IMAGE_FILE)
1114 _request_answer_del(&open_requests, entry->request, ref->client,
1115 CSERVE2_REQUEST_CANCEL);
1116 else if (entry->type == CSERVE2_IMAGE_DATA)
1118 if (((Image_Data *)entry)->doload)
1119 _request_answer_del(&load_requests, entry->request,
1120 ref->client, CSERVE2_REQUEST_CANCEL);
1122 _request_answer_del(&spload_requests, entry->request,
1123 ref->client, CSERVE2_REQUEST_CANCEL);
1127 _entry_reference_del(entry, ref);
1131 _font_entry_reference_del(Client *client, Font_Entry *fe)
1136 EINA_LIST_FOREACH(client->fonts.referencing, l, ref)
1138 if (ref->entry == (Entry *)fe)
1144 client->fonts.referencing = eina_list_remove_list(
1145 client->fonts.referencing, l);
1146 _entry_reference_del(&fe->base, ref);
1153 cserve2_cache_client_new(Client *client)
1155 client->files.referencing = eina_hash_int32_new(_entry_free_cb);
1156 client->images.referencing = eina_hash_int32_new(_entry_free_cb);
1157 client->fonts.referencing = NULL;
1161 cserve2_cache_client_del(Client *client)
1165 // will call _entry_free_cb() for every entry
1166 eina_hash_free(client->images.referencing);
1167 // will call _entry_free_cb() for every entry
1168 eina_hash_free(client->files.referencing);
1170 EINA_LIST_FREE(client->fonts.referencing, ref)
1172 _entry_reference_del(ref->entry, ref);
1177 _image_msg_new(Client *client, Msg_Setopts *msg)
1180 Image_Data *im_entry;
1182 ref = eina_hash_find(client->files.referencing, &msg->file_id);
1185 ERR("Couldn't find file id: %d, for image id: %d",
1186 msg->file_id, msg->image_id);
1187 cserve2_client_error_send(client, msg->base.rid,
1188 CSERVE2_INVALID_CACHE);
1191 if (((File_Data *)ref->entry)->invalid)
1193 cserve2_client_error_send(client, msg->base.rid,
1194 CSERVE2_FILE_CHANGED);
1198 im_entry = calloc(1, sizeof(*im_entry));
1199 im_entry->base.type = CSERVE2_IMAGE_DATA;
1200 im_entry->file_id = ref->entry->id;
1201 im_entry->file = (File_Data *)ref->entry;
1202 im_entry->opts.dpi = msg->opts.dpi;
1203 im_entry->opts.w = msg->opts.w;
1204 im_entry->opts.h = msg->opts.h;
1205 im_entry->opts.scale_down = msg->opts.scale_down;
1206 im_entry->opts.rx = msg->opts.rx;
1207 im_entry->opts.ry = msg->opts.ry;
1208 im_entry->opts.rw = msg->opts.rw;
1209 im_entry->opts.rh = msg->opts.rh;
1210 im_entry->opts.orientation = msg->opts.orientation;
1216 _file_changed_cb(const char *path __UNUSED__, Eina_Bool deleted __UNUSED__, void *data)
1218 File_Watch *fw = data;
1222 EINA_LIST_FOREACH(fw->entries, l, e)
1227 e->invalid = EINA_TRUE;
1230 EINA_LIST_FOREACH(e->images, ll, ie)
1233 eina_hash_set(image_entries, &ie->base.id, NULL);
1234 if (ie->base.request && !ie->base.request->processing)
1237 _request_answer_all_del(&load_requests, ie->base.request,
1238 CSERVE2_FILE_CHANGED);
1240 _request_answer_all_del(&spload_requests, ie->base.request,
1241 CSERVE2_FILE_CHANGED);
1243 ie->base.request = NULL;
1245 _image_entry_free(ie);
1249 eina_hash_set(file_entries, &e->base.id, NULL);
1250 if (e->base.request && !e->base.request->processing)
1251 _request_answer_all_del(&open_requests, e->base.request,
1252 CSERVE2_FILE_CHANGED);
1253 e->base.request = NULL;
1254 if (!e->images && !e->base.references)
1255 _file_entry_free(e);
1258 eina_hash_del_by_key(file_watch, fw->path);
1261 static Font_Source *
1262 _cserve2_font_source_find(const char *name)
1266 fs = eina_hash_find(font_sources, name);
1272 _cserve2_font_entry_find(const char *name, unsigned int namelen, unsigned int size, unsigned int rend_flags, unsigned int dpi)
1278 tmp_fs.key = eina_stringshare_add_length(name, namelen);
1279 tmp_fe.src = &tmp_fs;
1281 tmp_fe.rend_flags = rend_flags;
1284 fe = eina_hash_find(font_entries, &tmp_fe);
1285 eina_stringshare_del(tmp_fs.key);
1291 _font_load_request_build(void *data, int *size)
1293 Font_Entry *fe = data;
1294 Slave_Msg_Font_Load *msg = calloc(1, sizeof(*msg));
1296 msg->ftdata1 = fe->src->ft;
1297 msg->ftdata2 = fe->ft;
1298 msg->rend_flags = fe->rend_flags;
1299 msg->size = fe->size;
1301 msg->name = fe->src->name;
1302 msg->file = fe->src->file;
1306 _entry_load_start(&fe->base);
1312 _font_load_request_free(void *msg, void *data)
1318 _font_load_request_response(Client *client __UNUSED__, void *data, void *resp, unsigned int rid __UNUSED__)
1320 Slave_Msg_Font_Loaded *msg = resp;
1321 Font_Entry *fe = data;
1323 DBG("request %d answered.", rid);
1326 fe->src->ft = msg->ftdata1;
1330 fe->ft = msg->ftdata2;
1331 _entry_load_finish(&fe->base);
1334 if (fe->request) fe->request = NULL;
1336 _font_loaded_send(client, rid);
1340 _font_load_request_failed(Client *client __UNUSED__, void *data __UNUSED__, Error_Type error __UNUSED__, unsigned int rid __UNUSED__)
1342 Font_Entry *fe = data;
1343 DBG("request %d error answered.", rid);
1345 cserve2_client_error_send(client, rid, error);
1347 if (fe->request) fe->request = NULL;
1349 _font_entry_reference_del(client, fe);
1352 static Font_Request_Funcs _font_load_funcs = {
1353 .msg_create = (Font_Request_Msg_Create)_font_load_request_build,
1354 .msg_free = (Font_Request_Msg_Free)_font_load_request_free,
1355 .response = (Font_Request_Response)_font_load_request_response,
1356 .error = (Font_Request_Error)_font_load_request_failed
1360 _glyphs_request_check(Glyphs_Request *req)
1363 Font_Entry *fe = req->fe;
1365 req->answer = malloc(sizeof(*req->answer) * req->nglyphs);
1368 for (i = req->current; i < req->nglyphs; i++)
1371 ge = fash_gl_find(fe->glyphs, req->glyphs[i]);
1374 req->answer[req->nanswer++] = ge;
1383 // No glyphs need to be rendered.
1384 return (req->nanswer == req->nglyphs);
1387 /* organize answer (cache1{gl1, gl2,}, cache2{gl3,gl4,gl5}, cache3{gl6})
1390 _glyphs_group_create(Glyphs_Request *req)
1392 Eina_List *groups = NULL;
1395 for (i = 0; i < req->nanswer; i++)
1398 Glyphs_Group *iter, *gg = NULL;
1399 Font_Cache *fc = req->answer[i]->fc;
1401 EINA_LIST_FOREACH(groups, l, iter)
1412 gg = calloc(1, sizeof(*gg));
1414 groups = eina_list_append(groups, gg);
1416 gg->glyphs = eina_list_append(gg->glyphs, req->answer[i]);
1423 _glyphs_loaded_send(Glyphs_Request *req, unsigned int rid)
1425 Msg_Font_Glyphs_Loaded msg;
1427 Eina_List *ll, *answers = NULL;
1428 const char *shmname;
1429 unsigned int shmsize;
1430 unsigned int intsize;
1434 memset(&msg, 0, sizeof(msg));
1436 msg.base.type = CSERVE2_FONT_GLYPHS_LOADED;
1438 answers = _glyphs_group_create(req);
1439 msg.ncaches = eina_list_count(answers);
1442 // calculate size of message
1443 // ncaches * sizeof(cache) + nglyphs1 * sizeof(glyph) +
1444 // nglyphs2 * sizeof(glyph)...
1446 intsize = sizeof(unsigned int);
1448 EINA_LIST_FOREACH(answers, ll, iter)
1450 shmname = cserve2_shm_name_get(iter->fc->shm);
1451 shmsize = eina_stringshare_strlen(shmname) + 1;
1452 // shm namelen + name
1453 size += intsize + shmsize;
1457 // nglyphs * (index + offset + size + rows + width + pitch +
1458 // num_grays + pixel_mode)
1459 size += eina_list_count(iter->glyphs) * 8 * intsize;
1462 resp = malloc(size);
1463 memcpy(resp, &msg, sizeof(msg));
1464 buf = resp + sizeof(msg);
1466 EINA_LIST_FREE(answers, iter)
1469 unsigned int nglyphs;
1471 shmname = cserve2_shm_name_get(iter->fc->shm);
1472 shmsize = eina_stringshare_strlen(shmname) + 1;
1473 memcpy(buf, &shmsize, intsize);
1475 memcpy(buf, shmname, shmsize);
1478 nglyphs = eina_list_count(iter->glyphs);
1479 memcpy(buf, &nglyphs, intsize);
1482 iter->fc->inuse -= eina_list_count(iter->glyphs);
1484 EINA_LIST_FREE(iter->glyphs, gl)
1486 memcpy(buf, &gl->index, intsize);
1488 memcpy(buf, &gl->offset, intsize);
1490 memcpy(buf, &gl->size, intsize);
1492 memcpy(buf, &gl->rows, intsize);
1494 memcpy(buf, &gl->width, intsize);
1496 memcpy(buf, &gl->pitch, intsize);
1498 memcpy(buf, &gl->num_grays, intsize);
1500 memcpy(buf, &gl->pixel_mode, intsize);
1504 /* We are removing SHMs from the beginning of the list, so this
1505 * gives a higher priority to them */
1506 _font_shm_promote(iter->fc);
1507 eina_list_free(iter->glyphs);
1511 cserve2_client_send(req->client, &size, sizeof(size));
1512 cserve2_client_send(req->client, resp, size);
1518 * taken from evas_path.c. It would be good to clean up those utils to
1519 * have cserve link against them easily without dragging unneeded dependencies
1522 # define EVAS_PATH_SEPARATOR "\\"
1524 # define EVAS_PATH_SEPARATOR "/"
1528 _file_path_join(const char *path, const char *end)
1533 if ((!path) && (!end)) return NULL;
1534 if (!path) return strdup(end);
1535 if (!end) return strdup(path);
1538 len += strlen(EVAS_PATH_SEPARATOR);
1539 res = malloc(len + 1);
1540 if (!res) return NULL;
1542 strcat(res, EVAS_PATH_SEPARATOR);
1547 static Glyphs_Request *
1548 _glyphs_request_create(Client *client, const char *source, unsigned int sourcelen, const char *name, unsigned int namelen, unsigned int hint, unsigned int rend_flags, unsigned int size, unsigned int dpi, unsigned int *glyphs, unsigned int nglyphs)
1551 Glyphs_Request *req = calloc(1, sizeof(*req));
1558 fullname = _file_path_join(source, name);
1559 req->fe = _cserve2_font_entry_find(fullname, strlen(fullname) + 1, size,
1564 ERR("No font entry found: source %s, name %s, rendflags: %d, hint: %d,"
1565 " size: %d, dpi: %d", source, name, rend_flags, hint, size, dpi);
1570 req->client = client;
1572 req->nglyphs = nglyphs;
1574 req->glyphs = glyphs;
1581 _glyphs_request_free(Glyphs_Request *req)
1589 /* add glyphs that are already in cache to the "answers" array, and the ones
1590 * that are not cached to the "render" array.
1593 _glyphs_load_request_prepare(Glyphs_Request *req)
1597 Font_Entry *fe = req->fe;
1601 ERR("No font entry for this request.");
1605 // Won't render more than this number of glyphs
1606 max = req->nglyphs - req->nanswer;
1607 req->render = malloc(sizeof(*req->render) * max);
1609 for (i = req->current; i < req->nglyphs; i++)
1612 ge = fash_gl_find(fe->glyphs, req->glyphs[i]);
1615 req->answer[req->nanswer++] = ge;
1619 req->render[req->nrender++] = req->glyphs[i];
1624 _glyphs_load_request_build(void *data, int *size)
1626 Glyphs_Request *req = data;
1627 Slave_Msg_Font_Glyphs_Load *msg = NULL;
1628 Font_Entry *fe = req->fe;
1631 _glyphs_load_request_prepare(req);
1633 msg = calloc(1, sizeof(*msg));
1635 msg->font.ftdata1 = fe->src->ft;
1636 msg->font.ftdata2 = fe->ft;
1637 msg->font.hint = req->hint;
1638 msg->font.rend_flags = fe->rend_flags;
1639 msg->glyphs.nglyphs = req->nrender;
1640 msg->glyphs.glyphs = req->render;
1642 // Trying to reuse last filled cache.
1643 fc = fe->last_cache;
1646 msg->cache.shm = fc->shm;
1647 msg->cache.usage = fc->usage;
1648 msg->cache.nglyphs = fc->nglyphs;
1651 #ifdef DEBUG_LOAD_TIME
1652 gettimeofday(&fe->load_start, NULL);
1659 _glyphs_load_request_free(void *msg, void *data)
1661 _glyphs_request_free(data);
1666 _glyphs_load_request_response(Client *client, void *data, void *resp, unsigned int rid)
1668 Glyphs_Request *req = data;
1669 Slave_Msg_Font_Glyphs_Loaded *msg = resp;
1670 Font_Entry *fe = req->fe;
1671 Font_Cache *fc = NULL;
1674 if (fe->last_cache && fe->last_cache->shm == msg->caches[0]->shm)
1675 fc = fe->last_cache;
1677 while (i < msg->ncaches)
1680 Slave_Msg_Font_Cache *c = msg->caches[i++];
1684 fc = malloc(sizeof(*fc));
1685 fe->caches = eina_inlist_append(fe->caches, EINA_INLIST_GET(fc));
1686 fe->last_cache = fc;
1692 font_shm_lru = eina_list_append(font_shm_lru, fc);
1693 font_mem_usage += _font_shm_size_get(fc);
1695 fc->usage = c->usage;
1696 for (j = 0; j < c->nglyphs; j++)
1698 Glyph_Entry *gl = malloc(sizeof(*gl));
1701 gl->index = c->glyphs[j].index;
1702 gl->offset = c->glyphs[j].offset;
1703 gl->size = c->glyphs[j].size;
1704 gl->rows = c->glyphs[j].rows;
1705 gl->width = c->glyphs[j].width;
1706 gl->pitch = c->glyphs[j].pitch;
1707 gl->num_grays = c->glyphs[j].num_grays;
1708 gl->pixel_mode = c->glyphs[j].pixel_mode;
1709 font_mem_usage += sizeof(*gl);
1710 fc->glyphs = eina_inlist_append(fc->glyphs, EINA_INLIST_GET(gl));
1712 fash_gl_add(fe->glyphs, gl->index, gl);
1713 req->answer[req->nanswer++] = gl;
1717 free(c); // FIXME: We are freeing this here because we only do a
1718 // simple free on the response message. Later we need to
1719 // setup a free callback for the slave response.
1723 #ifdef DEBUG_LOAD_TIME
1725 gettimeofday(&fe->load_finish, NULL);
1726 load_time = _timeval_sub(&fe->load_finish, &fe->load_start);
1727 fe->gl_load_time += load_time;
1730 _glyphs_loaded_send(req, rid);
1731 _font_shm_lru_flush();
1735 _glyphs_load_request_failed(Client *client, void *data, Error_Type error, unsigned int rid)
1739 static Font_Request_Funcs _glyphs_load_funcs = {
1740 .msg_create = (Font_Request_Msg_Create)_glyphs_load_request_build,
1741 .msg_free = (Font_Request_Msg_Free)_glyphs_load_request_free,
1742 .response = (Font_Request_Response)_glyphs_load_request_response,
1743 .error = (Font_Request_Error)_glyphs_load_request_failed
1747 _font_entry_stats_cb(const Eina_Hash *hash __UNUSED__, const void *key __UNUSED__, void *data, void *fdata)
1749 Font_Entry *fe = data;
1750 Msg_Stats *msg = fdata;
1753 int nrefs = eina_list_count(fe->base.references);
1756 EINA_INLIST_FOREACH(fe->caches, fc)
1758 /* This is not real requested usage, but an approximation. We don't
1759 * know how many times each glyph would be used by each client, but
1760 * assume that a similar set of glyphs from a given font would be used
1761 * by each client, thus counting them one time per client referencing
1764 msg->fonts.requested_usage += fc->usage * nrefs;
1765 msg->fonts.real_usage += cserve2_shm_size_get(fc->shm);
1768 #ifdef DEBUG_LOAD_TIME
1769 // accounting fonts load time
1770 load_time = _timeval_sub(&fe->base.load_finish, &fe->base.load_start);
1771 msg->fonts.fonts_load += load_time;
1772 if (fe->caches) msg->fonts.fonts_used_load += load_time;
1774 // accounting glyphs load time
1775 msg->fonts.glyphs_load += fe->gl_load_time;
1782 _cserve2_cache_image_stats_get(Msg_Stats *msg)
1787 _cserve2_cache_font_stats_get(Msg_Stats *msg)
1789 eina_hash_foreach(font_entries, _font_entry_stats_cb, msg);
1795 unsigned int nfonts;
1799 _font_entry_debug_size_cb(const Eina_Hash *hash __UNUSED__, const void *key __UNUSED__, void *data, void *fdata)
1801 struct _debug_info *di = fdata;
1802 unsigned int size = di->size;
1803 Font_Entry *fe = data;
1805 unsigned int intsize = sizeof(unsigned int);
1812 size += strlen(fe->src->file) + 1;
1819 size += strlen(fe->src->name) + 1;
1821 // rend_flags, size, dpi
1822 size += 3 * intsize;
1830 EINA_INLIST_FOREACH(fe->caches, fc)
1834 // shmnamelen + shmname
1836 size += strlen(cserve2_shm_name_get(fc->shm)) + 1;
1839 size += 2 * intsize;
1844 EINA_INLIST_FOREACH(fc->glyphs, gl)
1846 // index, offset, size
1847 size += 3 * intsize;
1849 // rows, width, pitch
1850 size += 3 * intsize;
1852 // num_grays, pixel_mode
1853 size += 2 * intsize;
1864 _font_entry_debug_cb(const Eina_Hash *hash __UNUSED__, const void *key __UNUSED__, void *data, void *fdata)
1867 Font_Entry *fe = data;
1870 unsigned int unused;
1871 unsigned int ncaches;
1872 unsigned int intsize = sizeof(unsigned int);
1877 len = strlen(fe->src->file) + 1;
1878 memcpy(buf, &len, intsize);
1880 memcpy(buf, fe->src->file, len);
1886 len = strlen(fe->src->name) + 1;
1887 memcpy(buf, &len, intsize);
1889 memcpy(buf, fe->src->name, len);
1892 // rend_flags, size, dpi
1893 memcpy(buf, &fe->rend_flags, intsize);
1895 memcpy(buf, &fe->size, intsize);
1897 memcpy(buf, &fe->dpi, intsize);
1901 unused = fe->unused;
1902 memcpy(buf, &unused, intsize);
1906 ncaches = eina_inlist_count(fe->caches);
1907 memcpy(buf, &ncaches, intsize);
1910 EINA_INLIST_FOREACH(fe->caches, fc)
1913 const char *shmname;
1914 unsigned int shmsize;
1916 // shmnamelen + shmname
1917 shmname = cserve2_shm_name_get(fc->shm);
1918 len = strlen(shmname) + 1;
1919 memcpy(buf, &len, intsize);
1921 memcpy(buf, shmname, len);
1924 // size, usage, nglyphs
1925 shmsize = cserve2_shm_size_get(fc->shm);
1926 memcpy(buf, &shmsize, intsize);
1928 memcpy(buf, &fc->usage, intsize);
1930 memcpy(buf, &fc->nglyphs, intsize);
1933 EINA_INLIST_FOREACH(fc->glyphs, gl)
1935 // index, offset, size
1936 memcpy(buf, &gl->index, intsize);
1938 memcpy(buf, &gl->offset, intsize);
1940 memcpy(buf, &gl->size, intsize);
1943 // rows, width, pitch
1944 memcpy(buf, &gl->rows, intsize);
1946 memcpy(buf, &gl->width, intsize);
1948 memcpy(buf, &gl->pitch, intsize);
1951 // num_grays, pixel_mode
1952 memcpy(buf, &gl->num_grays, intsize);
1954 memcpy(buf, &gl->pixel_mode, intsize);
1963 _cserve2_cache_font_debug(unsigned int rid, unsigned int *size)
1967 struct _debug_info di;
1968 di.size = sizeof(msg);
1970 memset(&msg, 0, sizeof(msg));
1972 msg.base.type = CSERVE2_FONT_DEBUG;
1975 // First calculate how much size is needed for this message:
1978 di.size += sizeof(unsigned int);
1980 // size needed for each font entry
1981 eina_hash_foreach(font_entries, _font_entry_debug_size_cb, &di);
1983 // Now really create the message
1984 buf = malloc(di.size);
1988 memcpy(buf, &msg, sizeof(msg));
1992 memcpy(pos, &di.nfonts, sizeof(unsigned int));
1993 pos += sizeof(unsigned int);
1995 eina_hash_foreach(font_entries, _font_entry_debug_cb, pos);
2002 cserve2_cache_file_open(Client *client, unsigned int client_file_id, const char *path, const char *key, unsigned int rid)
2004 unsigned int file_id;
2010 // look for this file on client references
2011 ref = eina_hash_find(client->files.referencing, &client_file_id);
2014 entry = (File_Data *)ref->entry;
2018 cserve2_client_error_send(client, rid, CSERVE2_FILE_CHANGED);
2022 DBG("found client file id: %d", client_file_id);
2025 // File already being loaded, just add the request to be replied
2026 if (entry->base.request)
2027 _request_answer_add(entry->base.request, ref, rid, CSERVE2_OPEN);
2029 _image_opened_send(client, entry, rid);
2033 // search whether the file is already opened by another client
2034 snprintf(buf, sizeof(buf), "%s:%s", path, key);
2035 file_id = (unsigned int)eina_hash_find(file_ids, buf);
2038 DBG("found file_id %u for client file id %d",
2039 file_id, client_file_id);
2040 entry = eina_hash_find(file_entries, &file_id);
2043 ERR("file \"%s\" is in file_ids hash but not in entries hash.",
2045 cserve2_client_error_send(client, rid, CSERVE2_INVALID_CACHE);
2048 ref = _entry_reference_add((Entry *)entry, client, client_file_id);
2049 eina_hash_add(client->files.referencing, &client_file_id, ref);
2050 if (entry->base.request)
2051 _request_answer_add(entry->base.request, ref, rid, CSERVE2_OPEN);
2052 else // File already loaded, otherwise there would be a request
2053 _image_opened_send(client, entry, rid);
2057 file_id = _file_id++;
2058 while ((file_id == 0) || (eina_hash_find(file_entries, &file_id)))
2059 file_id = _file_id++;
2061 DBG("Creating new entry with file_id: %u for file \"%s:%s\"",
2062 file_id, path, key);
2063 entry = calloc(1, sizeof(*entry));
2064 entry->base.type = CSERVE2_IMAGE_FILE;
2065 entry->path = strdup(path);
2066 entry->key = strdup(key);
2067 entry->base.id = file_id;
2068 eina_hash_add(file_entries, &file_id, entry);
2069 eina_hash_add(file_ids, buf, (void *)file_id);
2070 ref = _entry_reference_add((Entry *)entry, client, client_file_id);
2071 eina_hash_add(client->files.referencing, &client_file_id, ref);
2073 fw = eina_hash_find(file_watch, entry->path);
2076 fw = calloc(1, sizeof(File_Watch));
2077 fw->path = eina_stringshare_add(entry->path);
2078 cserve2_file_change_watch_add(fw->path, _file_changed_cb, fw);
2079 eina_hash_direct_add(file_watch, fw->path, fw);
2081 fw->entries = eina_list_append(fw->entries, entry);
2082 entry->watcher = fw;
2084 _request_add(&open_requests, (Entry *)entry, ref, rid, CSERVE2_OPEN);
2086 // _open_image_default_set(entry);
2092 cserve2_cache_file_close(Client *client, unsigned int client_file_id)
2094 Reference *ref = eina_hash_find(client->files.referencing,
2098 ERR("Couldn't find file %d in client hash.", client_file_id);
2103 if (ref->count <= 0)
2104 // will call _entry_free_cb() for this entry
2105 eina_hash_del_by_key(client->files.referencing, &client_file_id);
2109 cserve2_cache_image_opts_set(Client *client, Msg_Setopts *msg)
2112 File_Data *fentry = NULL;
2113 Reference *ref, *oldref;
2114 unsigned int image_id;
2117 oldref = eina_hash_find(client->images.referencing, &msg->image_id);
2119 // search whether the image is already loaded by another client
2120 entry = _image_msg_new(client, msg);
2123 image_id = _img_opts_id_get(entry, buf, sizeof(buf));
2125 { // if so, just update the references
2127 DBG("found image_id %d for client image id %d",
2128 image_id, msg->image_id);
2129 entry = eina_hash_find(image_entries, &image_id);
2132 ERR("image id %d is in file_ids hash, but not in entries hash"
2133 "with entry id %d.", msg->image_id, image_id);
2134 cserve2_client_error_send(client, msg->base.rid,
2135 CSERVE2_INVALID_CACHE);
2141 DBG("Re-using old image entry (id: %d) from the LRU list.",
2143 entry->unused = EINA_FALSE;
2144 image_entries_lru = eina_list_remove(image_entries_lru, entry);
2145 unused_mem_usage -= _image_entry_size_get(entry);
2148 if (oldref && (oldref->entry->id == image_id))
2151 ref = _entry_reference_add((Entry *)entry, client, msg->image_id);
2154 eina_hash_del_by_key(client->images.referencing, &msg->image_id);
2156 eina_hash_add(client->images.referencing, &msg->image_id, ref);
2161 image_id = _image_id++;
2162 while ((image_id == 0) || (eina_hash_find(image_entries, &image_id)))
2163 image_id = _image_id++;
2165 entry->base.id = image_id;
2166 eina_hash_add(image_entries, &image_id, entry);
2167 eina_hash_add(image_ids, buf, (void *)image_id);
2168 ref = _entry_reference_add((Entry *)entry, client, msg->image_id);
2171 eina_hash_del_by_key(client->images.referencing, &msg->image_id);
2172 eina_hash_add(client->images.referencing, &msg->image_id, ref);
2174 fentry = entry->file;
2175 fentry->images = eina_list_append(fentry->images, entry);
2177 _request_add(&spload_requests, (Entry *)entry, ref, msg->base.rid,
2183 cserve2_cache_image_load(Client *client, unsigned int client_image_id, unsigned int rid)
2188 ref = eina_hash_find(client->images.referencing, &client_image_id);
2191 ERR("Can't load: client %d has no image id %d",
2192 client->id, client_image_id);
2196 entry = (Image_Data *)ref->entry;
2198 if (entry->file->invalid)
2200 cserve2_client_error_send(client, rid, CSERVE2_FILE_CHANGED);
2204 DBG("Loading image id: %d", ref->entry->id);
2206 // File already being loaded, just add the request to be replied
2207 if (entry->base.request)
2209 _request_answer_add(entry->base.request, ref, rid, CSERVE2_LOAD);
2210 if ((!entry->base.request->processing) && (!entry->doload))
2212 DBG("Removing entry %d from speculative preload and adding "
2213 "to normal load queue.", entry->base.id);
2214 spload_requests = eina_list_remove(spload_requests,
2215 entry->base.request);
2216 load_requests = eina_list_append(load_requests,
2217 entry->base.request);
2220 else if (entry->shm)
2221 _image_loaded_send(client, entry, rid);
2223 _request_add(&load_requests, (Entry *)entry, ref, rid, CSERVE2_LOAD);
2225 entry->doload = EINA_TRUE;
2229 cserve2_cache_image_preload(Client *client, unsigned int client_image_id, unsigned int rid)
2234 ref = eina_hash_find(client->images.referencing, &client_image_id);
2237 ERR("Can't load: client %d has no image id %d",
2238 client->id, client_image_id);
2242 entry = (Image_Data *)ref->entry;
2244 if (entry->file->invalid)
2246 cserve2_client_error_send(client, rid, CSERVE2_FILE_CHANGED);
2250 DBG("Loading image id: %d", ref->entry->id);
2252 // File already being loaded, just add the request to be replied
2253 if (entry->base.request)
2255 _request_answer_add(entry->base.request, ref, rid, CSERVE2_PRELOAD);
2256 if ((!entry->base.request->processing) && (!entry->doload))
2258 DBG("Removing entry %d from speculative preload and adding "
2259 "to normal (pre)load queue.", entry->base.id);
2260 spload_requests = eina_list_remove(spload_requests,
2261 entry->base.request);
2262 load_requests = eina_list_append(load_requests,
2263 entry->base.request);
2266 else if (entry->shm)
2267 _image_preloaded_send(client, rid);
2269 _request_add(&load_requests, (Entry *)entry, ref, rid, CSERVE2_PRELOAD);
2271 entry->doload = EINA_TRUE;
2275 cserve2_cache_image_unload(Client *client, unsigned int client_image_id)
2277 Reference *ref = eina_hash_find(client->images.referencing,
2281 ERR("Couldn't find file %d in client hash.", client_image_id);
2286 if (ref->count <= 0)
2287 // will call _entry_free_cb() for this entry
2288 eina_hash_del_by_key(client->images.referencing, &client_image_id);
2292 cserve2_cache_font_load(Client *client, const char *source, unsigned int sourcelen, const char *name, unsigned int namelen, unsigned int rend_flags, unsigned int size, unsigned int dpi, unsigned int rid)
2304 fullname = _file_path_join(source, name);
2305 fe = _cserve2_font_entry_find(fullname, strlen(fullname) + 1, size,
2309 DBG("found font entry %s, rendflags: %d, size: %d, dpi: %d",
2310 name, rend_flags, size, dpi);
2312 ref = _entry_reference_add((Entry *)fe, client, 0);
2313 client->fonts.referencing = eina_list_append(
2314 client->fonts.referencing, ref);
2316 fe->unused = EINA_FALSE;
2319 cserve2_request_waiter_add(fe->request, rid, client);
2321 _font_loaded_send(client, rid);
2326 fe = calloc(1, sizeof(*fe));
2327 fe->rend_flags = rend_flags;
2330 fe->base.type = CSERVE2_FONT_ENTRY;
2331 fe->glyphs = fash_gl_new(_glyph_free_cb);
2332 ref = _entry_reference_add((Entry *)fe, client, 0);
2333 client->fonts.referencing = eina_list_append(
2334 client->fonts.referencing, ref);
2335 fe->unused = EINA_FALSE;
2337 fs = _cserve2_font_source_find(fullname);
2340 fs = calloc(1, sizeof(*fs));
2343 fs->key = eina_stringshare_add(fullname);
2344 fs->name = eina_stringshare_add_length(name, namelen);
2345 fs->file = eina_stringshare_add_length(source, sourcelen);
2349 fs->file = eina_stringshare_add_length(name, namelen);
2350 fs->key = eina_stringshare_ref(fs->file);
2352 eina_hash_direct_add(font_sources, fs->key, fs);
2357 DBG("adding FONT_LOAD '%s' request.", fs->name);
2358 fe->request = cserve2_request_add(CSERVE2_REQ_FONT_LOAD, rid,
2359 client, &_font_load_funcs, fe);
2361 eina_hash_direct_add(font_entries, fe, fe);
2369 cserve2_cache_font_unload(Client *client, const char *source, unsigned int sourcelen, const char *name, unsigned int namelen, unsigned int rend_flags, unsigned int size, unsigned int dpi, unsigned int rid)
2379 fullname = _file_path_join(source, name);
2380 fe = _cserve2_font_entry_find(fullname, strlen(fullname) + 1, size,
2386 ERR("Unreferencing font not found: '%s:%s'.", source, name);
2390 _font_entry_reference_del(client, fe);
2396 cserve2_cache_font_glyphs_load(Client *client, const char *source, unsigned int sourcelen, const char *name, unsigned int namelen, unsigned int hint, unsigned int rend_flags, unsigned int size, unsigned int dpi, unsigned int *glyphs, unsigned int nglyphs, unsigned int rid)
2398 Glyphs_Request *req;
2400 req = _glyphs_request_create(client, source, sourcelen, name, namelen,
2401 hint, rend_flags, size, dpi, glyphs, nglyphs);
2408 if (_glyphs_request_check(req))
2410 INF("Glyphs already loaded. Sending answer.");
2411 _glyphs_loaded_send(req, rid);
2412 _glyphs_request_free(req);
2416 cserve2_request_add(CSERVE2_REQ_FONT_GLYPHS_LOAD, rid,
2417 client, &_glyphs_load_funcs, req);
2423 cserve2_cache_font_glyphs_used(Client *client, const char *source, unsigned int sourcelen, const char *name, unsigned int namelen, unsigned int hint, unsigned int rend_flags, unsigned int size, unsigned int dpi, unsigned int *glyphs, unsigned int nglyphs, unsigned int rid __UNUSED__)
2427 Glyphs_Request *req;
2429 DBG("Received report of used glyphs from client %d", client->id);
2430 req = _glyphs_request_create(client, source, sourcelen, name, namelen,
2431 hint, rend_flags, size, dpi, glyphs, nglyphs);
2438 _glyphs_load_request_prepare(req);
2439 groups = _glyphs_group_create(req);
2441 // Promote SHMs which are still cached and in use
2442 // TODO: We can use later the information from request_prepare to preload
2443 // glyphs which are not cached anymore, but are in use on the client.
2444 EINA_LIST_FREE(groups, gg)
2446 _font_shm_promote(gg->fc);
2447 eina_list_free(gg->glyphs);
2450 _glyphs_request_free(req);
2455 cserve2_cache_requests_response(Slave_Command type, void *msg, void *data)
2457 Request *req = data;
2462 DBG("Request finished but it has no entry anymore.");
2463 EINA_LIST_FREE(req->waiters, w)
2465 cserve2_client_error_send(w->ref->client, w->rid,
2466 CSERVE2_REQUEST_CANCEL);
2472 else if (type == ERROR)
2474 Error_Type *error = msg;
2475 req->funcs->error(req->entry, *error);
2478 req->funcs->response(req->entry, msg);
2481 req->entry->request = NULL;
2486 cserve2_cache_stats_get(Client *client, unsigned int rid)
2491 memset(&msg, 0, sizeof(msg));
2493 msg.base.type = CSERVE2_STATS;
2496 _cserve2_cache_image_stats_get(&msg);
2497 _cserve2_cache_font_stats_get(&msg);
2500 cserve2_client_send(client, &size, sizeof(size));
2501 cserve2_client_send(client, &msg, size);
2505 cserve2_cache_font_debug(Client *client, unsigned int rid)
2510 msg = _cserve2_cache_font_debug(rid, &size);
2512 cserve2_client_send(client, &size, sizeof(size));
2513 cserve2_client_send(client, msg, size);