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);
229 _timeval_sub(const struct timeval *tv2, const struct timeval *tv1)
233 t1 = tv1->tv_usec + tv1->tv_sec * 1000000;
234 t2 = tv2->tv_usec + tv2->tv_sec * 1000000;
240 _image_opened_send(Client *client, File_Data *entry, unsigned int rid)
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));
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;
258 cserve2_client_send(client, &size, sizeof(size));
259 cserve2_client_send(client, &msg, sizeof(msg));
260 // _cserve2_cache_load_requests_process();
264 _image_loaded_send(Client *client, Image_Data *entry, unsigned int rid)
267 const char *shmpath = cserve2_shm_name_get(entry->shm);
272 DBG("Sending LOADED reply for entry %d and RID: %d.", entry->base.id, rid);
273 path_len = strlen(shmpath) + 1;
275 memset(&msg, 0, sizeof(msg));
277 msg.base.type = CSERVE2_LOADED;
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;
285 buf = malloc(sizeof(msg) + path_len);
287 memcpy(buf, &msg, sizeof(msg));
288 memcpy(buf + sizeof(msg), shmpath, path_len);
290 size = sizeof(msg) + path_len;
292 cserve2_client_send(client, &size, sizeof(size));
293 cserve2_client_send(client, buf, size);
299 _image_preloaded_send(Client *client, unsigned int rid)
304 DBG("Sending PRELOADED reply for RID: %d.", rid);
305 memset(&msg, 0, sizeof(msg));
307 msg.base.type = CSERVE2_PRELOADED;
310 cserve2_client_send(client, &size, sizeof(size));
311 cserve2_client_send(client, &msg, size);
315 _font_loaded_send(Client *client, unsigned int rid)
320 DBG("Sending FONT_LOADED reply for RID: %d.", rid);
323 memset(&msg, 0, size);
325 msg.base.type = CSERVE2_FONT_LOADED;
328 cserve2_client_send(client, &size, sizeof(size));
329 cserve2_client_send(client, &msg, size);
333 _open_request_build(File_Data *f, int *bufsize)
336 int size, pathlen, keylen;
337 Slave_Msg_Image_Open msg;
339 pathlen = strlen(f->path) + 1;
340 keylen = strlen(f->key) + 1;
342 size = sizeof(msg) + pathlen + keylen;
344 if (!buf) return NULL;
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);
353 _entry_load_start(&f->base);
359 _request_failed(Entry *e, Error_Type type)
365 DBG("Request for entry %p failed with error %d", e, type);
366 EINA_LIST_FREE(e->request->waiters, w)
368 cserve2_client_error_send(w->ref->client, w->rid, type);
374 EINA_LIST_FOREACH(e->references, l, ref)
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;
384 eina_hash_del_by_key(hash, &(ref->client_entry_id));
389 _open_request_response(File_Data *e, Slave_Msg_Image_Opened *resp)
393 _entry_load_finish(&e->base);
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)
402 const char *ldata = (const char *)resp +
403 sizeof(Slave_Msg_Image_Opened);
404 e->loader_data = eina_stringshare_add(ldata);
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)
411 _image_opened_send(w->ref->client, e, w->rid);
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
423 _load_request_build(Image_Data *i, int *bufsize)
428 int shmlen, filelen, keylen, loaderlen;
429 Slave_Msg_Image_Load msg;
431 // opening shm for this file
432 i->shm = cserve2_shm_request(i->file->w * i->file->h * 4);
434 shmpath = cserve2_shm_name_get(i->shm);
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;
444 size = sizeof(msg) + shmlen + filelen + keylen + loaderlen;
446 if (!buf) return NULL;
448 memset(&msg, 0, sizeof(msg));
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;
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);
467 msg.has_loader_data = !!loaderlen;
469 memcpy(buf, &msg, sizeof(msg));
470 ptr = buf + sizeof(msg);
472 memcpy(ptr, shmpath, shmlen);
474 memcpy(ptr, i->file->path, filelen);
476 memcpy(ptr, i->file->key, keylen);
478 memcpy(ptr, i->file->loader_data, loaderlen);
482 _entry_load_start(&i->base);
488 _load_request_response(Image_Data *e, Slave_Msg_Image_Loaded *resp)
492 _entry_load_start(&e->base);
494 e->alpha_sparse = resp->alpha_sparse;
496 DBG("Entry %d loaded by speculative preload.", e->base.id);
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)
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
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
519 _img_opts_id_get(Image_Data *im, char *buf, int size)
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);
528 image_id = (uintptr_t)eina_hash_find(image_ids, buf);
534 _image_entry_size_get(Image_Data *e)
536 int size = sizeof(Image_Data);
537 /* XXX: get the overhead of the shm handler too */
539 size += cserve2_shm_size_get(e->shm);
544 _file_id_free(File_Data *entry)
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);
555 _image_id_free(Image_Data *entry)
559 DBG("Removing entry image id: %d", entry->base.id);
561 _img_opts_id_get(entry, buf, sizeof(buf));
562 eina_hash_del_by_key(image_ids, buf);
566 _image_entry_free(Image_Data *entry)
568 File_Data *fentry = entry->file;
570 if (entry->base.request)
572 if (entry->base.request->processing)
573 entry->base.request->entry = NULL;
574 else if (!entry->base.request->waiters)
577 load_requests = eina_list_remove(load_requests,
578 entry->base.request);
580 spload_requests = eina_list_remove(spload_requests,
581 entry->base.request);
587 image_entries_lru = eina_list_remove(image_entries_lru, entry);
588 unused_mem_usage -= _image_entry_size_get(entry);
592 fentry->images = eina_list_remove(fentry->images, entry);
594 cserve2_shm_unref(entry->shm);
599 _hash_image_entry_free(void *data)
601 Image_Data *entry = data;
603 _image_id_free(entry);
604 _image_entry_free(entry);
608 _file_entry_free(File_Data *entry)
612 // Should we call free for each of the images too?
613 // If everything goes fine, it's not necessary.
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);
621 if (entry->base.request)
623 if (entry->base.request->processing)
624 entry->base.request->entry = NULL;
625 else if (!entry->base.request->waiters)
627 open_requests = eina_list_remove(open_requests,
628 entry->base.request);
629 free(entry->base.request);
633 if ((fw = entry->watcher))
635 fw->entries = eina_list_remove(fw->entries, entry);
637 eina_hash_del_by_key(file_watch, fw->path);
642 eina_stringshare_del(entry->loader_data);
647 _hash_file_entry_free(void *data)
649 File_Data *entry = data;
650 // TODO: Add some checks to make sure that we are freeing an
653 _file_id_free(entry);
654 _file_entry_free(entry);
658 _file_watch_free(void *data)
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);
668 _font_entry_cmp(const Font_Entry *k1, int k1_length __UNUSED__, const Font_Entry *k2, int k2_length __UNUSED__)
670 if (k1->src->key == k2->src->key)
672 if (k1->size == k2->size)
674 if (k1->rend_flags == k2->rend_flags)
675 return k1->dpi - k2->dpi;
676 return k1->rend_flags - k2->rend_flags;
678 return k1->size - k2->size;
680 return strcmp(k1->src->key, k2->src->key);
684 _font_entry_key_hash(const Font_Entry *key, int key_length __UNUSED__)
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));
696 _font_entry_free(Font_Entry *fe)
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);
707 _glyph_free_cb(void *data)
709 Glyph_Entry *gl = data;
714 _font_source_free(Font_Source *fs)
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);
725 _font_shm_promote(Font_Cache *fc)
728 l = eina_list_data_find_list(font_shm_lru, fc);
729 font_shm_lru = eina_list_demote_list(font_shm_lru, l);
733 _font_shm_size_get(Font_Cache *fc)
737 size = sizeof(*fc) + cserve2_shm_size_get(fc->shm);
743 _font_shm_free(Font_Cache *fc)
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;
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);
757 cserve2_shm_unref(fc->shm);
761 eina_hash_del_by_key(font_entries, fe);
765 _font_shm_lru_flush(void)
767 Eina_List *l, *l_next;
770 l_next = eina_list_next(l);
772 while (l && font_mem_usage > max_font_usage)
777 fc = eina_list_data_get(l);
778 if (fc->fe->unused && fc->inuse == 0)
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);
784 font_mem_usage -= size;
788 l_next = eina_list_next(l);
793 cserve2_cache_init(void)
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);
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),
810 cserve2_cache_shutdown(void)
814 EINA_LIST_FREE(font_shm_lru, fc)
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);
823 eina_hash_free(font_entries);
824 eina_hash_free(font_sources);
828 _request_answer_del(Eina_List **requests, Request *req, Client *client, Error_Type err)
830 Eina_List *l, *l_next;
833 DBG("Removing answer requests from entry: %d, client: %d",
834 req->entry->id, client->id);
836 EINA_LIST_FOREACH_SAFE(req->waiters, l, l_next, it)
838 if (it->ref->client->id == client->id)
840 cserve2_client_error_send(client, it->rid, err);
841 req->waiters = eina_list_remove_list(req->waiters, l);
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
849 if (!req->entry && !req->waiters)
851 *requests = eina_list_remove(*requests, req);
857 _request_answer_all_del(Eina_List **requests, Request *req, Error_Type err)
861 DBG("Removing all answer requests from entry: %d", req->entry->id);
863 EINA_LIST_FREE(req->waiters, it)
865 cserve2_client_error_send(it->ref->client, it->rid, err);
869 *requests = eina_list_remove(*requests, req);
874 _request_answer_add(Request *req, Reference *ref, unsigned int rid, Message_Type type)
876 Waiter *w = malloc(sizeof(*w));
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);
888 _request_add(Eina_List **requests, Entry *entry, Reference *ref, unsigned int rid, Message_Type type)
892 // add the request if it doesn't exist yet
895 req = malloc(sizeof(*req));
898 req->processing = EINA_FALSE;
899 entry->request = req;
900 if (type == CSERVE2_OPEN)
901 req->funcs = &_open_funcs;
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);
909 req = entry->request;
911 if (type != CSERVE2_SETOPTS)
912 _request_answer_add(req, ref, rid, type);
914 DBG("Adding entry for speculative preload: id=%d", req->entry->id);
918 _entry_reference_add(Entry *entry, Client *client, unsigned int client_entry_id)
922 // increase reference for this file
923 ref = malloc(sizeof(*ref));
924 ref->client = client;
926 ref->client_entry_id = client_entry_id;
928 entry->references = eina_list_append(entry->references, ref);
934 _cserve2_cache_open_requests_process(int nloaders)
937 char *slave_cmd_data;
940 while ((nloaders > 0) && (open_requests))
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);
946 DBG("Processing OPEN request for file entry: %d", req->entry->id);
948 slave_cmd_data = req->funcs->msg_create(req->entry, &slave_cmd_size);
950 cserve2_slave_cmd_dispatch(req, IMAGE_OPEN, slave_cmd_data,
953 free(slave_cmd_data);
955 req->processing = EINA_TRUE;
963 _cserve2_cache_load_requests_list_process(Eina_List **queue, int nloaders)
965 Eina_List *skipped = NULL;
968 while ((nloaders > 0) && (*queue))
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);
978 ientry = (Image_Data *)req->entry;
981 ERR("File entry doesn't exist for entry id %d", req->entry->id);
982 _request_failed(req->entry, CSERVE2_INVALID_CACHE);
986 if (ientry->file->base.request)
988 /* OPEN still pending, skip this request */
989 skipped = eina_list_append(skipped, req);
993 DBG("Processing LOAD request for image entry: %d", req->entry->id);
995 buf = req->funcs->msg_create(req->entry, &size);
997 cserve2_slave_cmd_dispatch(req, IMAGE_LOAD, buf, size);
1001 req->processing = EINA_TRUE;
1006 EINA_LIST_FREE(skipped, req)
1007 *queue = eina_list_append(*queue, req);
1013 _cserve2_cache_load_requests_process(int nloaders)
1015 nloaders = _cserve2_cache_load_requests_list_process(&load_requests,
1017 _cserve2_cache_load_requests_list_process(&spload_requests, nloaders - 1);
1022 cserve2_cache_requests_process(void)
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);
1032 _entry_unused_push(Image_Data *e)
1034 int size = _image_entry_size_get(e);
1036 if ((size > max_unused_mem_usage) || !(e->doload))
1038 eina_hash_del_by_key(image_entries, &e->base.id);
1041 while (size > (max_unused_mem_usage - unused_mem_usage))
1043 Entry *ie = eina_list_data_get(eina_list_last(image_entries_lru));
1044 eina_hash_del_by_key(image_entries, &ie->id);
1046 image_entries_lru = eina_list_append(image_entries_lru, e);
1047 e->unused = EINA_TRUE;
1048 unused_mem_usage += size;
1052 _entry_reference_del(Entry *entry, Reference *ref)
1054 entry->references = eina_list_remove(entry->references, ref);
1056 if (entry->references)
1059 if (entry->type == CSERVE2_IMAGE_FILE)
1061 File_Data *fentry = (File_Data *)entry;
1063 if (fentry->invalid)
1064 _file_entry_free(fentry);
1068 EINA_LIST_FREE(fentry->images, ie)
1070 eina_hash_del_by_key(file_entries, &entry->id);
1073 else if (entry->type == CSERVE2_IMAGE_DATA)
1075 Image_Data *ientry = (Image_Data *)entry;
1078 eina_hash_del_by_key(image_entries, &entry->id);
1079 else if (ientry->file->invalid)
1080 _image_entry_free(ientry);
1082 _entry_unused_push(ientry);
1084 else if (entry->type == CSERVE2_FONT_ENTRY)
1086 Font_Entry *fe = (Font_Entry *)entry;
1087 fe->unused = EINA_TRUE;
1089 eina_hash_del_by_key(font_entries, fe);
1092 ERR("Wrong type of entry.");
1099 _entry_free_cb(void *data)
1101 Reference *ref = data;
1104 DBG("Removing client reference for entry id: %d, client: %d",
1105 ref->entry->id, ref->client->id);
1109 if (entry->request && !entry->request->processing)
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)
1116 if (((Image_Data *)entry)->doload)
1117 _request_answer_del(&load_requests, entry->request,
1118 ref->client, CSERVE2_REQUEST_CANCEL);
1120 _request_answer_del(&spload_requests, entry->request,
1121 ref->client, CSERVE2_REQUEST_CANCEL);
1125 _entry_reference_del(entry, ref);
1129 _font_entry_reference_del(Client *client, Font_Entry *fe)
1134 EINA_LIST_FOREACH(client->fonts.referencing, l, ref)
1136 if (ref->entry == (Entry *)fe)
1142 client->fonts.referencing = eina_list_remove_list(
1143 client->fonts.referencing, l);
1144 _entry_reference_del(&fe->base, ref);
1151 cserve2_cache_client_new(Client *client)
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;
1159 cserve2_cache_client_del(Client *client)
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);
1168 EINA_LIST_FREE(client->fonts.referencing, ref)
1170 _entry_reference_del(ref->entry, ref);
1175 _image_msg_new(Client *client, Msg_Setopts *msg)
1178 Image_Data *im_entry;
1180 ref = eina_hash_find(client->files.referencing, &msg->file_id);
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);
1189 if (((File_Data *)ref->entry)->invalid)
1191 cserve2_client_error_send(client, msg->base.rid,
1192 CSERVE2_FILE_CHANGED);
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;
1214 _file_changed_cb(const char *path __UNUSED__, Eina_Bool deleted __UNUSED__, void *data)
1216 File_Watch *fw = data;
1220 EINA_LIST_FOREACH(fw->entries, l, e)
1225 e->invalid = EINA_TRUE;
1228 EINA_LIST_FOREACH(e->images, ll, ie)
1231 eina_hash_set(image_entries, &ie->base.id, NULL);
1232 if (ie->base.request && !ie->base.request->processing)
1235 _request_answer_all_del(&load_requests, ie->base.request,
1236 CSERVE2_FILE_CHANGED);
1238 _request_answer_all_del(&spload_requests, ie->base.request,
1239 CSERVE2_FILE_CHANGED);
1241 ie->base.request = NULL;
1243 _image_entry_free(ie);
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);
1256 eina_hash_del_by_key(file_watch, fw->path);
1259 static Font_Source *
1260 _cserve2_font_source_find(const char *name)
1264 fs = eina_hash_find(font_sources, name);
1270 _cserve2_font_entry_find(const char *name, unsigned int namelen, unsigned int size, unsigned int rend_flags, unsigned int dpi)
1276 tmp_fs.key = eina_stringshare_add_length(name, namelen);
1277 tmp_fe.src = &tmp_fs;
1279 tmp_fe.rend_flags = rend_flags;
1282 fe = eina_hash_find(font_entries, &tmp_fe);
1283 eina_stringshare_del(tmp_fs.key);
1289 _font_load_request_build(void *data, int *size)
1291 Font_Entry *fe = data;
1292 Slave_Msg_Font_Load *msg = calloc(1, sizeof(*msg));
1294 msg->ftdata1 = fe->src->ft;
1295 msg->ftdata2 = fe->ft;
1296 msg->rend_flags = fe->rend_flags;
1297 msg->size = fe->size;
1299 msg->name = fe->src->name;
1300 msg->file = fe->src->file;
1304 _entry_load_start(&fe->base);
1310 _font_load_request_free(void *msg, void *data)
1316 _font_load_request_response(Client *client __UNUSED__, void *data, void *resp, unsigned int rid __UNUSED__)
1318 Slave_Msg_Font_Loaded *msg = resp;
1319 Font_Entry *fe = data;
1321 DBG("request %d answered.", rid);
1324 fe->src->ft = msg->ftdata1;
1328 fe->ft = msg->ftdata2;
1329 _entry_load_finish(&fe->base);
1332 if (fe->request) fe->request = NULL;
1334 _font_loaded_send(client, rid);
1338 _font_load_request_failed(Client *client __UNUSED__, void *data __UNUSED__, Error_Type error __UNUSED__, unsigned int rid __UNUSED__)
1340 Font_Entry *fe = data;
1341 DBG("request %d error answered.", rid);
1343 cserve2_client_error_send(client, rid, error);
1345 if (fe->request) fe->request = NULL;
1347 _font_entry_reference_del(client, fe);
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
1358 _glyphs_request_check(Glyphs_Request *req)
1361 Font_Entry *fe = req->fe;
1363 req->answer = malloc(sizeof(*req->answer) * req->nglyphs);
1366 for (i = req->current; i < req->nglyphs; i++)
1369 ge = fash_gl_find(fe->glyphs, req->glyphs[i]);
1372 req->answer[req->nanswer++] = ge;
1381 // No glyphs need to be rendered.
1382 return (req->nanswer == req->nglyphs);
1385 /* organize answer (cache1{gl1, gl2,}, cache2{gl3,gl4,gl5}, cache3{gl6})
1388 _glyphs_group_create(Glyphs_Request *req)
1390 Eina_List *groups = NULL;
1393 for (i = 0; i < req->nanswer; i++)
1396 Glyphs_Group *iter, *gg = NULL;
1397 Font_Cache *fc = req->answer[i]->fc;
1399 EINA_LIST_FOREACH(groups, l, iter)
1410 gg = calloc(1, sizeof(*gg));
1412 groups = eina_list_append(groups, gg);
1414 gg->glyphs = eina_list_append(gg->glyphs, req->answer[i]);
1421 _glyphs_loaded_send(Glyphs_Request *req, unsigned int rid)
1423 Msg_Font_Glyphs_Loaded msg;
1425 Eina_List *ll, *answers = NULL;
1426 const char *shmname;
1427 unsigned int shmsize;
1428 unsigned int intsize;
1432 memset(&msg, 0, sizeof(msg));
1434 msg.base.type = CSERVE2_FONT_GLYPHS_LOADED;
1436 answers = _glyphs_group_create(req);
1437 msg.ncaches = eina_list_count(answers);
1440 // calculate size of message
1441 // ncaches * sizeof(cache) + nglyphs1 * sizeof(glyph) +
1442 // nglyphs2 * sizeof(glyph)...
1444 intsize = sizeof(unsigned int);
1446 EINA_LIST_FOREACH(answers, ll, iter)
1448 shmname = cserve2_shm_name_get(iter->fc->shm);
1449 shmsize = eina_stringshare_strlen(shmname) + 1;
1450 // shm namelen + name
1451 size += intsize + shmsize;
1455 // nglyphs * (index + offset + size + rows + width + pitch +
1456 // num_grays + pixel_mode)
1457 size += eina_list_count(iter->glyphs) * 8 * intsize;
1460 resp = malloc(size);
1461 memcpy(resp, &msg, sizeof(msg));
1462 buf = resp + sizeof(msg);
1464 EINA_LIST_FREE(answers, iter)
1467 unsigned int nglyphs;
1469 shmname = cserve2_shm_name_get(iter->fc->shm);
1470 shmsize = eina_stringshare_strlen(shmname) + 1;
1471 memcpy(buf, &shmsize, intsize);
1473 memcpy(buf, shmname, shmsize);
1476 nglyphs = eina_list_count(iter->glyphs);
1477 memcpy(buf, &nglyphs, intsize);
1480 iter->fc->inuse -= eina_list_count(iter->glyphs);
1482 EINA_LIST_FREE(iter->glyphs, gl)
1484 memcpy(buf, &gl->index, intsize);
1486 memcpy(buf, &gl->offset, intsize);
1488 memcpy(buf, &gl->size, intsize);
1490 memcpy(buf, &gl->rows, intsize);
1492 memcpy(buf, &gl->width, intsize);
1494 memcpy(buf, &gl->pitch, intsize);
1496 memcpy(buf, &gl->num_grays, intsize);
1498 memcpy(buf, &gl->pixel_mode, intsize);
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);
1509 cserve2_client_send(req->client, &size, sizeof(size));
1510 cserve2_client_send(req->client, resp, size);
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
1520 # define EVAS_PATH_SEPARATOR "\\"
1522 # define EVAS_PATH_SEPARATOR "/"
1526 _file_path_join(const char *path, const char *end)
1531 if ((!path) && (!end)) return NULL;
1532 if (!path) return strdup(end);
1533 if (!end) return strdup(path);
1536 len += strlen(EVAS_PATH_SEPARATOR);
1537 res = malloc(len + 1);
1538 if (!res) return NULL;
1540 strcat(res, EVAS_PATH_SEPARATOR);
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)
1549 Glyphs_Request *req = calloc(1, sizeof(*req));
1556 fullname = _file_path_join(source, name);
1557 req->fe = _cserve2_font_entry_find(fullname, strlen(fullname) + 1, size,
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);
1568 req->client = client;
1570 req->nglyphs = nglyphs;
1572 req->glyphs = glyphs;
1579 _glyphs_request_free(Glyphs_Request *req)
1587 /* add glyphs that are already in cache to the "answers" array, and the ones
1588 * that are not cached to the "render" array.
1591 _glyphs_load_request_prepare(Glyphs_Request *req)
1595 Font_Entry *fe = req->fe;
1599 ERR("No font entry for this request.");
1603 // Won't render more than this number of glyphs
1604 max = req->nglyphs - req->nanswer;
1605 req->render = malloc(sizeof(*req->render) * max);
1607 for (i = req->current; i < req->nglyphs; i++)
1610 ge = fash_gl_find(fe->glyphs, req->glyphs[i]);
1613 req->answer[req->nanswer++] = ge;
1617 req->render[req->nrender++] = req->glyphs[i];
1622 _glyphs_load_request_build(void *data, int *size)
1624 Glyphs_Request *req = data;
1625 Slave_Msg_Font_Glyphs_Load *msg = NULL;
1626 Font_Entry *fe = req->fe;
1629 _glyphs_load_request_prepare(req);
1631 msg = calloc(1, sizeof(*msg));
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;
1640 // Trying to reuse last filled cache.
1641 fc = fe->last_cache;
1644 msg->cache.shm = fc->shm;
1645 msg->cache.usage = fc->usage;
1646 msg->cache.nglyphs = fc->nglyphs;
1649 #ifdef DEBUG_LOAD_TIME
1650 gettimeofday(&fe->load_start, NULL);
1657 _glyphs_load_request_free(void *msg, void *data)
1659 _glyphs_request_free(data);
1664 _glyphs_load_request_response(Client *client, void *data, void *resp, unsigned int rid)
1666 Glyphs_Request *req = data;
1667 Slave_Msg_Font_Glyphs_Loaded *msg = resp;
1668 Font_Entry *fe = req->fe;
1669 Font_Cache *fc = NULL;
1672 if (fe->last_cache && fe->last_cache->shm == msg->caches[0]->shm)
1673 fc = fe->last_cache;
1675 while (i < msg->ncaches)
1678 Slave_Msg_Font_Cache *c = msg->caches[i++];
1682 fc = malloc(sizeof(*fc));
1683 fe->caches = eina_inlist_append(fe->caches, EINA_INLIST_GET(fc));
1684 fe->last_cache = fc;
1690 font_shm_lru = eina_list_append(font_shm_lru, fc);
1691 font_mem_usage += _font_shm_size_get(fc);
1693 fc->usage = c->usage;
1694 for (j = 0; j < c->nglyphs; j++)
1696 Glyph_Entry *gl = malloc(sizeof(*gl));
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));
1710 fash_gl_add(fe->glyphs, gl->index, gl);
1711 req->answer[req->nanswer++] = gl;
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.
1721 #ifdef DEBUG_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;
1728 _glyphs_loaded_send(req, rid);
1729 _font_shm_lru_flush();
1733 _glyphs_load_request_failed(Client *client, void *data, Error_Type error, unsigned int rid)
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
1745 _font_entry_stats_cb(const Eina_Hash *hash __UNUSED__, const void *key __UNUSED__, void *data, void *fdata)
1747 Font_Entry *fe = data;
1748 Msg_Stats *msg = fdata;
1751 int nrefs = eina_list_count(fe->base.references);
1754 EINA_INLIST_FOREACH(fe->caches, fc)
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
1762 msg->fonts.requested_usage += fc->usage * nrefs;
1763 msg->fonts.real_usage += cserve2_shm_size_get(fc->shm);
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;
1772 // accounting glyphs load time
1773 msg->fonts.glyphs_load += fe->gl_load_time;
1780 _cserve2_cache_image_stats_get(Msg_Stats *msg)
1785 _cserve2_cache_font_stats_get(Msg_Stats *msg)
1787 eina_hash_foreach(font_entries, _font_entry_stats_cb, msg);
1793 unsigned int nfonts;
1797 _font_entry_debug_size_cb(const Eina_Hash *hash __UNUSED__, const void *key __UNUSED__, void *data, void *fdata)
1799 struct _debug_info *di = fdata;
1800 unsigned int size = di->size;
1801 Font_Entry *fe = data;
1803 unsigned int intsize = sizeof(unsigned int);
1810 size += strlen(fe->src->file) + 1;
1817 size += strlen(fe->src->name) + 1;
1819 // rend_flags, size, dpi
1820 size += 3 * intsize;
1828 EINA_INLIST_FOREACH(fe->caches, fc)
1832 // shmnamelen + shmname
1834 size += strlen(cserve2_shm_name_get(fc->shm)) + 1;
1837 size += 2 * intsize;
1842 EINA_INLIST_FOREACH(fc->glyphs, gl)
1844 // index, offset, size
1845 size += 3 * intsize;
1847 // rows, width, pitch
1848 size += 3 * intsize;
1850 // num_grays, pixel_mode
1851 size += 2 * intsize;
1862 _font_entry_debug_cb(const Eina_Hash *hash __UNUSED__, const void *key __UNUSED__, void *data, void *fdata)
1865 Font_Entry *fe = data;
1868 unsigned int unused;
1869 unsigned int ncaches;
1870 unsigned int intsize = sizeof(unsigned int);
1875 len = strlen(fe->src->file) + 1;
1876 memcpy(buf, &len, intsize);
1878 memcpy(buf, fe->src->file, len);
1884 len = strlen(fe->src->name) + 1;
1885 memcpy(buf, &len, intsize);
1887 memcpy(buf, fe->src->name, len);
1890 // rend_flags, size, dpi
1891 memcpy(buf, &fe->rend_flags, intsize);
1893 memcpy(buf, &fe->size, intsize);
1895 memcpy(buf, &fe->dpi, intsize);
1899 unused = fe->unused;
1900 memcpy(buf, &unused, intsize);
1904 ncaches = eina_inlist_count(fe->caches);
1905 memcpy(buf, &ncaches, intsize);
1908 EINA_INLIST_FOREACH(fe->caches, fc)
1911 const char *shmname;
1912 unsigned int shmsize;
1914 // shmnamelen + shmname
1915 shmname = cserve2_shm_name_get(fc->shm);
1916 len = strlen(shmname) + 1;
1917 memcpy(buf, &len, intsize);
1919 memcpy(buf, shmname, len);
1922 // size, usage, nglyphs
1923 shmsize = cserve2_shm_size_get(fc->shm);
1924 memcpy(buf, &shmsize, intsize);
1926 memcpy(buf, &fc->usage, intsize);
1928 memcpy(buf, &fc->nglyphs, intsize);
1931 EINA_INLIST_FOREACH(fc->glyphs, gl)
1933 // index, offset, size
1934 memcpy(buf, &gl->index, intsize);
1936 memcpy(buf, &gl->offset, intsize);
1938 memcpy(buf, &gl->size, intsize);
1941 // rows, width, pitch
1942 memcpy(buf, &gl->rows, intsize);
1944 memcpy(buf, &gl->width, intsize);
1946 memcpy(buf, &gl->pitch, intsize);
1949 // num_grays, pixel_mode
1950 memcpy(buf, &gl->num_grays, intsize);
1952 memcpy(buf, &gl->pixel_mode, intsize);
1961 _cserve2_cache_font_debug(unsigned int rid, unsigned int *size)
1965 struct _debug_info di;
1966 di.size = sizeof(msg);
1968 memset(&msg, 0, sizeof(msg));
1970 msg.base.type = CSERVE2_FONT_DEBUG;
1973 // First calculate how much size is needed for this message:
1976 di.size += sizeof(unsigned int);
1978 // size needed for each font entry
1979 eina_hash_foreach(font_entries, _font_entry_debug_size_cb, &di);
1981 // Now really create the message
1982 buf = malloc(di.size);
1986 memcpy(buf, &msg, sizeof(msg));
1990 memcpy(pos, &di.nfonts, sizeof(unsigned int));
1991 pos += sizeof(unsigned int);
1993 eina_hash_foreach(font_entries, _font_entry_debug_cb, pos);
2000 cserve2_cache_file_open(Client *client, unsigned int client_file_id, const char *path, const char *key, unsigned int rid)
2002 unsigned int file_id;
2008 // look for this file on client references
2009 ref = eina_hash_find(client->files.referencing, &client_file_id);
2012 entry = (File_Data *)ref->entry;
2016 cserve2_client_error_send(client, rid, CSERVE2_FILE_CHANGED);
2020 DBG("found client file id: %d", client_file_id);
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);
2027 _image_opened_send(client, entry, rid);
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);
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);
2041 ERR("file \"%s\" is in file_ids hash but not in entries hash.",
2043 cserve2_client_error_send(client, rid, CSERVE2_INVALID_CACHE);
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);
2055 file_id = _file_id++;
2056 while ((file_id == 0) || (eina_hash_find(file_entries, &file_id)))
2057 file_id = _file_id++;
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);
2071 fw = eina_hash_find(file_watch, entry->path);
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);
2079 fw->entries = eina_list_append(fw->entries, entry);
2080 entry->watcher = fw;
2082 _request_add(&open_requests, (Entry *)entry, ref, rid, CSERVE2_OPEN);
2084 // _open_image_default_set(entry);
2090 cserve2_cache_file_close(Client *client, unsigned int client_file_id)
2092 Reference *ref = eina_hash_find(client->files.referencing,
2096 ERR("Couldn't find file %d in client hash.", client_file_id);
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);
2107 cserve2_cache_image_opts_set(Client *client, Msg_Setopts *msg)
2110 File_Data *fentry = NULL;
2111 Reference *ref, *oldref;
2112 unsigned int image_id;
2115 oldref = eina_hash_find(client->images.referencing, &msg->image_id);
2117 // search whether the image is already loaded by another client
2118 entry = _image_msg_new(client, msg);
2121 image_id = _img_opts_id_get(entry, buf, sizeof(buf));
2123 { // if so, just update the references
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);
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);
2139 DBG("Re-using old image entry (id: %d) from the LRU list.",
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);
2146 if (oldref && (oldref->entry->id == image_id))
2149 ref = _entry_reference_add((Entry *)entry, client, msg->image_id);
2152 eina_hash_del_by_key(client->images.referencing, &msg->image_id);
2154 eina_hash_add(client->images.referencing, &msg->image_id, ref);
2159 image_id = _image_id++;
2160 while ((image_id == 0) || (eina_hash_find(image_entries, &image_id)))
2161 image_id = _image_id++;
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);
2169 eina_hash_del_by_key(client->images.referencing, &msg->image_id);
2170 eina_hash_add(client->images.referencing, &msg->image_id, ref);
2172 fentry = entry->file;
2173 fentry->images = eina_list_append(fentry->images, entry);
2175 _request_add(&spload_requests, (Entry *)entry, ref, msg->base.rid,
2181 cserve2_cache_image_load(Client *client, unsigned int client_image_id, unsigned int rid)
2186 ref = eina_hash_find(client->images.referencing, &client_image_id);
2189 ERR("Can't load: client %d has no image id %d",
2190 client->id, client_image_id);
2194 entry = (Image_Data *)ref->entry;
2196 if (entry->file->invalid)
2198 cserve2_client_error_send(client, rid, CSERVE2_FILE_CHANGED);
2202 DBG("Loading image id: %d", ref->entry->id);
2204 // File already being loaded, just add the request to be replied
2205 if (entry->base.request)
2207 _request_answer_add(entry->base.request, ref, rid, CSERVE2_LOAD);
2208 if ((!entry->base.request->processing) && (!entry->doload))
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);
2218 else if (entry->shm)
2219 _image_loaded_send(client, entry, rid);
2221 _request_add(&load_requests, (Entry *)entry, ref, rid, CSERVE2_LOAD);
2223 entry->doload = EINA_TRUE;
2227 cserve2_cache_image_preload(Client *client, unsigned int client_image_id, unsigned int rid)
2232 ref = eina_hash_find(client->images.referencing, &client_image_id);
2235 ERR("Can't load: client %d has no image id %d",
2236 client->id, client_image_id);
2240 entry = (Image_Data *)ref->entry;
2242 if (entry->file->invalid)
2244 cserve2_client_error_send(client, rid, CSERVE2_FILE_CHANGED);
2248 DBG("Loading image id: %d", ref->entry->id);
2250 // File already being loaded, just add the request to be replied
2251 if (entry->base.request)
2253 _request_answer_add(entry->base.request, ref, rid, CSERVE2_PRELOAD);
2254 if ((!entry->base.request->processing) && (!entry->doload))
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);
2264 else if (entry->shm)
2265 _image_preloaded_send(client, rid);
2267 _request_add(&load_requests, (Entry *)entry, ref, rid, CSERVE2_PRELOAD);
2269 entry->doload = EINA_TRUE;
2273 cserve2_cache_image_unload(Client *client, unsigned int client_image_id)
2275 Reference *ref = eina_hash_find(client->images.referencing,
2279 ERR("Couldn't find file %d in client hash.", client_image_id);
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);
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)
2302 fullname = _file_path_join(source, name);
2303 fe = _cserve2_font_entry_find(fullname, strlen(fullname) + 1, size,
2307 DBG("found font entry %s, rendflags: %d, size: %d, dpi: %d",
2308 name, rend_flags, size, dpi);
2310 ref = _entry_reference_add((Entry *)fe, client, 0);
2311 client->fonts.referencing = eina_list_append(
2312 client->fonts.referencing, ref);
2314 fe->unused = EINA_FALSE;
2317 cserve2_request_waiter_add(fe->request, rid, client);
2319 _font_loaded_send(client, rid);
2324 fe = calloc(1, sizeof(*fe));
2325 fe->rend_flags = rend_flags;
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;
2335 fs = _cserve2_font_source_find(fullname);
2338 fs = calloc(1, sizeof(*fs));
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);
2347 fs->file = eina_stringshare_add_length(name, namelen);
2348 fs->key = eina_stringshare_ref(fs->file);
2350 eina_hash_direct_add(font_sources, fs->key, fs);
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);
2359 eina_hash_direct_add(font_entries, fe, fe);
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)
2377 fullname = _file_path_join(source, name);
2378 fe = _cserve2_font_entry_find(fullname, strlen(fullname) + 1, size,
2384 ERR("Unreferencing font not found: '%s:%s'.", source, name);
2388 _font_entry_reference_del(client, fe);
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)
2396 Glyphs_Request *req;
2398 req = _glyphs_request_create(client, source, sourcelen, name, namelen,
2399 hint, rend_flags, size, dpi, glyphs, nglyphs);
2406 if (_glyphs_request_check(req))
2408 INF("Glyphs already loaded. Sending answer.");
2409 _glyphs_loaded_send(req, rid);
2410 _glyphs_request_free(req);
2414 cserve2_request_add(CSERVE2_REQ_FONT_GLYPHS_LOAD, rid,
2415 client, &_glyphs_load_funcs, req);
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__)
2425 Glyphs_Request *req;
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);
2436 _glyphs_load_request_prepare(req);
2437 groups = _glyphs_group_create(req);
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)
2444 _font_shm_promote(gg->fc);
2445 eina_list_free(gg->glyphs);
2448 _glyphs_request_free(req);
2453 cserve2_cache_requests_response(Slave_Command type, void *msg, void *data)
2455 Request *req = data;
2460 DBG("Request finished but it has no entry anymore.");
2461 EINA_LIST_FREE(req->waiters, w)
2463 cserve2_client_error_send(w->ref->client, w->rid,
2464 CSERVE2_REQUEST_CANCEL);
2470 else if (type == ERROR)
2472 Error_Type *error = msg;
2473 req->funcs->error(req->entry, *error);
2476 req->funcs->response(req->entry, msg);
2479 req->entry->request = NULL;
2484 cserve2_cache_stats_get(Client *client, unsigned int rid)
2489 memset(&msg, 0, sizeof(msg));
2491 msg.base.type = CSERVE2_STATS;
2494 _cserve2_cache_image_stats_get(&msg);
2495 _cserve2_cache_font_stats_get(&msg);
2498 cserve2_client_send(client, &size, sizeof(size));
2499 cserve2_client_send(client, &msg, size);
2503 cserve2_cache_font_debug(Client *client, unsigned int rid)
2508 msg = _cserve2_cache_font_debug(rid, &size);
2510 cserve2_client_send(client, &size, sizeof(size));
2511 cserve2_client_send(client, msg, size);