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;
72 const char *loader_data;
76 Eina_Bool invalid : 1;
79 // Default values for load options commented below
85 double dpi; // dpi < -1
86 int w, h; // w and h < -1
87 int scale_down; // scale_down < -1
88 int rx, ry, rw, rh; // rx, ry, rw, rh < -1
89 Eina_Bool orientation; // orientation == 0
92 Eina_Bool alpha_sparse : 1;
107 Font_Request *request;
108 unsigned int rend_flags;
114 unsigned int nglyphs;
116 Font_Cache *last_cache;
117 Eina_Bool unused : 1;
118 #ifdef DEBUG_LOAD_TIME
119 struct timeval load_start;
120 struct timeval load_finish;
133 unsigned int nglyphs;
136 struct _Glyph_Entry {
146 unsigned int num_grays;
147 unsigned int pixel_mode;
150 struct _Glyphs_Request {
153 unsigned int current;
154 unsigned int nglyphs;
155 unsigned int *glyphs;
156 unsigned int nrender;
157 unsigned int *render;
158 unsigned int nanswer;
159 Glyph_Entry **answer;
163 typedef struct _Glyphs_Request Glyphs_Request;
165 struct _Glyphs_Group {
170 typedef struct _Glyphs_Group Glyphs_Group;
175 unsigned int client_entry_id; // for reverse lookup
190 static Eina_List *open_requests = NULL;
191 static Eina_List *load_requests = NULL;
192 static Eina_List *spload_requests = NULL; // speculative preload requests
194 static unsigned int _file_id = 0; // id unique number
195 static unsigned int _image_id = 0; // id unique number
196 static Eina_Hash *file_ids = NULL; // maps path + key --> file_id
197 static Eina_Hash *file_entries = NULL; // maps file_id --> entry
199 static Eina_Hash *image_ids = NULL; // maps file id + load opts --> image id
200 static Eina_Hash *image_entries = NULL; // maps image_id --> entry
202 static Eina_Hash *font_sources = NULL; // font path --> font source
203 static Eina_Hash *font_entries = NULL; // maps font path + options --> entry
205 static Eina_Hash *file_watch = NULL;
207 static Eina_List *image_entries_lru = NULL;
209 static Eina_List *font_shm_lru = NULL;
211 static int max_unused_mem_usage = 5 * 1024; /* in kbytes */
212 static int unused_mem_usage = 0;
213 static int max_font_usage = 10 * 4 * 1024; /* in kbytes */
214 static int font_mem_usage = 0;
216 #ifdef DEBUG_LOAD_TIME
218 _timeval_sub(const struct timeval *tv2, const struct timeval *tv1)
222 t1 = tv1->tv_usec + tv1->tv_sec * 1000000;
223 t2 = tv2->tv_usec + tv2->tv_sec * 1000000;
225 // Make sure that we don't add negative values. Some images may have
226 // been not loaded yet, so it would mess with the stats.
235 _entry_load_start(Entry *e)
237 #ifdef DEBUG_LOAD_TIME
238 gettimeofday(&e->load_start, NULL);
243 _entry_load_finish(Entry *e)
245 #ifdef DEBUG_LOAD_TIME
246 gettimeofday(&e->load_finish, NULL);
247 e->load_time = _timeval_sub(&e->load_finish, &e->load_start);
252 _entry_load_reused(Entry *e)
254 #ifdef DEBUG_LOAD_TIME
255 e->saved_time += e->load_time;
260 _image_opened_send(Client *client, File_Data *entry, unsigned int rid)
265 DBG("Sending OPENED reply for entry: %d and RID: %d.", entry->base.id, rid);
266 // clear the struct with possible paddings, since it is not aligned.
267 memset(&msg, 0, sizeof(msg));
269 msg.base.type = CSERVE2_OPENED;
270 msg.image.w = entry->w;
271 msg.image.h = entry->h;
272 msg.image.frame_count = entry->frame_count;
273 msg.image.loop_count = entry->loop_count;
274 msg.image.loop_hint = entry->loop_hint;
275 msg.image.alpha = entry->alpha;
278 cserve2_client_send(client, &size, sizeof(size));
279 cserve2_client_send(client, &msg, sizeof(msg));
280 // _cserve2_cache_load_requests_process();
284 _image_loaded_send(Client *client, Image_Data *entry, unsigned int rid)
287 const char *shmpath = cserve2_shm_name_get(entry->shm);
292 DBG("Sending LOADED reply for entry %d and RID: %d.", entry->base.id, rid);
293 path_len = strlen(shmpath) + 1;
295 memset(&msg, 0, sizeof(msg));
297 msg.base.type = CSERVE2_LOADED;
299 msg.shm.mmap_offset = cserve2_shm_map_offset_get(entry->shm);
300 msg.shm.use_offset = cserve2_shm_offset_get(entry->shm);
301 msg.shm.mmap_size = cserve2_shm_map_size_get(entry->shm);
302 msg.shm.image_size = cserve2_shm_size_get(entry->shm);
303 msg.alpha_sparse = entry->alpha_sparse;
305 buf = malloc(sizeof(msg) + path_len);
307 memcpy(buf, &msg, sizeof(msg));
308 memcpy(buf + sizeof(msg), shmpath, path_len);
310 size = sizeof(msg) + path_len;
312 cserve2_client_send(client, &size, sizeof(size));
313 cserve2_client_send(client, buf, size);
319 _image_preloaded_send(Client *client, unsigned int rid)
324 DBG("Sending PRELOADED reply for RID: %d.", rid);
325 memset(&msg, 0, sizeof(msg));
327 msg.base.type = CSERVE2_PRELOADED;
330 cserve2_client_send(client, &size, sizeof(size));
331 cserve2_client_send(client, &msg, size);
335 _font_loaded_send(Client *client, unsigned int rid)
340 DBG("Sending FONT_LOADED reply for RID: %d.", rid);
343 memset(&msg, 0, size);
345 msg.base.type = CSERVE2_FONT_LOADED;
348 cserve2_client_send(client, &size, sizeof(size));
349 cserve2_client_send(client, &msg, size);
353 _open_request_build(File_Data *f, int *bufsize)
356 int size, pathlen, keylen;
357 Slave_Msg_Image_Open msg;
359 pathlen = strlen(f->path) + 1;
360 keylen = strlen(f->key) + 1;
362 size = sizeof(msg) + pathlen + keylen;
364 if (!buf) return NULL;
366 memset(&msg, 0, sizeof(msg));
367 memcpy(buf, &msg, sizeof(msg));
368 memcpy(buf + sizeof(msg), f->path, pathlen);
369 memcpy(buf + sizeof(msg) + pathlen, f->key, keylen);
373 _entry_load_start(&f->base);
379 _request_failed(Entry *e, Error_Type type)
385 DBG("Request for entry %p failed with error %d", e, type);
386 EINA_LIST_FREE(e->request->waiters, w)
388 cserve2_client_error_send(w->ref->client, w->rid, type);
394 EINA_LIST_FOREACH(e->references, l, ref)
396 Eina_Hash *hash = NULL;
397 if (e->type == CSERVE2_IMAGE_FILE)
398 hash = ref->client->files.referencing;
399 else if (e->type == CSERVE2_IMAGE_DATA)
400 hash = ref->client->images.referencing;
404 eina_hash_del_by_key(hash, &(ref->client_entry_id));
409 _open_request_response(File_Data *e, Slave_Msg_Image_Opened *resp)
413 _entry_load_finish(&e->base);
416 e->frame_count = resp->frame_count;
417 e->loop_count = resp->loop_count;
418 e->loop_hint = resp->loop_hint;
419 e->alpha = resp->alpha;
420 if (resp->has_loader_data)
422 const char *ldata = (const char *)resp +
423 sizeof(Slave_Msg_Image_Opened);
424 e->loader_data = eina_stringshare_add(ldata);
427 DBG("Finished opening file %d. Notifying %d waiters.", e->base.id,
428 e->base.request->waiters ? eina_list_count(e->base.request->waiters) : 0);
429 EINA_LIST_FREE(e->base.request->waiters, w)
431 _image_opened_send(w->ref->client, e, w->rid);
436 static Request_Funcs _open_funcs = {
437 .msg_create = (Request_Msg_Create)_open_request_build,
438 .response = (Request_Response)_open_request_response,
439 .error = (Request_Error)_request_failed
443 _load_request_build(Image_Data *i, int *bufsize)
448 int shmlen, filelen, keylen, loaderlen;
449 Slave_Msg_Image_Load msg;
451 // opening shm for this file
452 i->shm = cserve2_shm_request(i->file->w * i->file->h * 4);
454 shmpath = cserve2_shm_name_get(i->shm);
456 shmlen = strlen(shmpath) + 1;
457 filelen = strlen(i->file->path) + 1;
458 keylen = strlen(i->file->key) + 1;
459 if (i->file->loader_data)
460 loaderlen = strlen(i->file->loader_data) + 1;
464 size = sizeof(msg) + shmlen + filelen + keylen + loaderlen;
466 if (!buf) return NULL;
468 memset(&msg, 0, sizeof(msg));
471 msg.alpha = i->file->alpha;
472 msg.opts.w = i->opts.w;
473 msg.opts.h = i->opts.h;
474 msg.opts.rx = i->opts.rx;
475 msg.opts.ry = i->opts.ry;
476 msg.opts.rw = i->opts.rw;
477 msg.opts.rh = i->opts.rh;
478 msg.opts.scale_down_by = i->opts.scale_down;
479 msg.opts.dpi = i->opts.dpi;
480 msg.opts.orientation = i->opts.orientation;
482 msg.shm.mmap_offset = cserve2_shm_map_offset_get(i->shm);
483 msg.shm.image_offset = cserve2_shm_offset_get(i->shm);
484 msg.shm.mmap_size = cserve2_shm_map_size_get(i->shm);
485 msg.shm.image_size = cserve2_shm_size_get(i->shm);
487 msg.has_loader_data = !!loaderlen;
489 memcpy(buf, &msg, sizeof(msg));
490 ptr = buf + sizeof(msg);
492 memcpy(ptr, shmpath, shmlen);
494 memcpy(ptr, i->file->path, filelen);
496 memcpy(ptr, i->file->key, keylen);
498 memcpy(ptr, i->file->loader_data, loaderlen);
502 _entry_load_start(&i->base);
508 _load_request_response(Image_Data *e, Slave_Msg_Image_Loaded *resp)
512 _entry_load_finish(&e->base);
514 e->alpha_sparse = resp->alpha_sparse;
516 DBG("Entry %d loaded by speculative preload.", e->base.id);
518 DBG("Finished loading image %d. Notifying %d waiters.", e->base.id,
519 e->base.request->waiters ? eina_list_count(e->base.request->waiters) : 0);
520 EINA_LIST_FREE(e->base.request->waiters, w)
522 if (w->type == CSERVE2_LOAD)
523 _image_loaded_send(w->ref->client, e, w->rid);
524 else if (w->type == CSERVE2_PRELOAD)
525 _image_preloaded_send(w->ref->client, w->rid);
526 // else w->type == CSERVE2_SETOPTS --> do nothing
532 static Request_Funcs _load_funcs = {
533 .msg_create = (Request_Msg_Create)_load_request_build,
534 .response = (Request_Response)_load_request_response,
535 .error = (Request_Error)_request_failed
539 _img_opts_id_get(Image_Data *im, char *buf, int size)
543 snprintf(buf, size, "%u:%0.3f:%dx%d:%d:%d,%d+%dx%d:%d",
544 im->file_id, im->opts.dpi, im->opts.w, im->opts.h,
545 im->opts.scale_down, im->opts.rx, im->opts.ry,
546 im->opts.rw, im->opts.rh, im->opts.orientation);
548 image_id = (uintptr_t)eina_hash_find(image_ids, buf);
554 _image_entry_size_get(Image_Data *e)
556 int size = sizeof(Image_Data);
557 /* XXX: get the overhead of the shm handler too */
559 size += cserve2_shm_size_get(e->shm);
564 _file_id_free(File_Data *entry)
568 DBG("Removing entry file id: %d, file: \"%s:%s\"",
569 entry->base.id, entry->path, entry->key);
570 snprintf(buf, sizeof(buf), "%s:%s", entry->path, entry->key);
571 eina_hash_del_by_key(file_ids, buf);
575 _image_id_free(Image_Data *entry)
579 DBG("Removing entry image id: %d", entry->base.id);
581 _img_opts_id_get(entry, buf, sizeof(buf));
582 eina_hash_del_by_key(image_ids, buf);
586 _image_entry_free(Image_Data *entry)
588 File_Data *fentry = entry->file;
590 if (entry->base.request)
592 if (entry->base.request->processing)
593 entry->base.request->entry = NULL;
594 else if (!entry->base.request->waiters)
597 load_requests = eina_list_remove(load_requests,
598 entry->base.request);
600 spload_requests = eina_list_remove(spload_requests,
601 entry->base.request);
607 image_entries_lru = eina_list_remove(image_entries_lru, entry);
608 unused_mem_usage -= _image_entry_size_get(entry);
613 fentry->images = eina_list_remove(fentry->images, entry);
614 if (!fentry->images && !fentry->base.references)
615 eina_hash_del_by_key(file_entries, &fentry->base.id);
618 cserve2_shm_unref(entry->shm);
623 _hash_image_entry_free(void *data)
625 Image_Data *entry = data;
627 _image_id_free(entry);
628 _image_entry_free(entry);
632 _file_entry_free(File_Data *entry)
636 // Should we call free for each of the images too?
637 // If everything goes fine, it's not necessary.
640 ERR("Freeing file %d (\"%s:%s\") image data still referenced.",
641 entry->base.id, entry->path, entry->key);
642 eina_list_free(entry->images);
645 if (entry->base.request)
647 if (entry->base.request->processing)
648 entry->base.request->entry = NULL;
649 else if (!entry->base.request->waiters)
651 open_requests = eina_list_remove(open_requests,
652 entry->base.request);
653 free(entry->base.request);
657 if ((fw = entry->watcher))
659 fw->entries = eina_list_remove(fw->entries, entry);
661 eina_hash_del_by_key(file_watch, fw->path);
666 eina_stringshare_del(entry->loader_data);
671 _hash_file_entry_free(void *data)
673 File_Data *entry = data;
674 // TODO: Add some checks to make sure that we are freeing an
677 _file_id_free(entry);
678 _file_entry_free(entry);
682 _file_watch_free(void *data)
684 File_Watch *fw = data;
685 cserve2_file_change_watch_del(fw->path);
686 eina_stringshare_del(fw->path);
687 eina_list_free(fw->entries);
692 _font_entry_cmp(const Font_Entry *k1, int k1_length __UNUSED__, const Font_Entry *k2, int k2_length __UNUSED__)
694 if (k1->src->key == k2->src->key)
696 if (k1->size == k2->size)
698 if (k1->rend_flags == k2->rend_flags)
699 return k1->dpi - k2->dpi;
700 return k1->rend_flags - k2->rend_flags;
702 return k1->size - k2->size;
704 return strcmp(k1->src->key, k2->src->key);
708 _font_entry_key_hash(const Font_Entry *key, int key_length __UNUSED__)
711 hash = eina_hash_djb2(key->src->key, eina_stringshare_strlen(key->src->key) + 1);
712 hash ^= eina_hash_int32(&key->rend_flags, sizeof(int));
713 hash ^= eina_hash_int32(&key->size, sizeof(int));
714 hash ^= eina_hash_int32(&key->dpi, sizeof(int));
720 _font_entry_free(Font_Entry *fe)
722 fash_gl_free(fe->glyphs);
723 fe->src->references--;
724 if (fe->ft) cserve2_font_ft_free(fe->ft);
725 if (fe->src->references <= 0)
726 eina_hash_del_by_key(font_sources, fe->src->key);
731 _glyph_free_cb(void *data)
733 Glyph_Entry *gl = data;
738 _font_source_free(Font_Source *fs)
740 eina_stringshare_del(fs->key);
741 eina_stringshare_del(fs->name);
742 eina_stringshare_del(fs->file);
743 if (fs->ft) cserve2_font_source_ft_free(fs->ft);
749 _font_shm_promote(Font_Cache *fc)
752 l = eina_list_data_find_list(font_shm_lru, fc);
753 font_shm_lru = eina_list_demote_list(font_shm_lru, l);
757 _font_shm_size_get(Font_Cache *fc)
761 size = sizeof(*fc) + cserve2_shm_size_get(fc->shm);
767 _font_shm_free(Font_Cache *fc)
769 Font_Entry *fe = fc->fe;
770 fe->caches = eina_inlist_remove(fe->caches, EINA_INLIST_GET(fc));
771 if (fc == fe->last_cache)
772 fe->last_cache = NULL;
776 Glyph_Entry *gl = EINA_INLIST_CONTAINER_GET(fc->glyphs, Glyph_Entry);
777 fc->glyphs = eina_inlist_remove(fc->glyphs, fc->glyphs);
778 fash_gl_del(fe->glyphs, gl->index);
781 cserve2_shm_unref(fc->shm);
785 eina_hash_del_by_key(font_entries, fe);
789 _font_shm_lru_flush(void)
791 Eina_List *l, *l_next;
794 l_next = eina_list_next(l);
796 while (l && font_mem_usage > max_font_usage)
801 fc = eina_list_data_get(l);
802 if (fc->fe->unused && fc->inuse == 0)
804 font_shm_lru = eina_list_remove_list(font_shm_lru, l);
805 size = _font_shm_size_get(fc);
806 size += fc->nglyphs * sizeof(Glyph_Entry);
808 font_mem_usage -= size;
812 l_next = eina_list_next(l);
817 cserve2_cache_init(void)
819 file_ids = eina_hash_string_superfast_new(NULL);
820 file_entries = eina_hash_int32_new(_hash_file_entry_free);
821 image_ids = eina_hash_string_superfast_new(NULL);
822 image_entries = eina_hash_string_superfast_new(_hash_image_entry_free);
823 file_watch = eina_hash_string_superfast_new(_file_watch_free);
825 font_sources = eina_hash_string_small_new(EINA_FREE_CB(_font_source_free));
826 font_entries = eina_hash_new(NULL,
827 EINA_KEY_CMP(_font_entry_cmp),
828 EINA_KEY_HASH(_font_entry_key_hash),
829 EINA_FREE_CB(_font_entry_free),
834 cserve2_cache_shutdown(void)
838 EINA_LIST_FREE(font_shm_lru, fc)
841 eina_hash_free(image_entries);
842 eina_hash_free(image_ids);
843 eina_hash_free(file_entries);
844 eina_hash_free(file_ids);
845 eina_hash_free(file_watch);
847 eina_hash_free(font_entries);
848 eina_hash_free(font_sources);
852 _request_answer_del(Eina_List **requests, Request *req, Client *client, Error_Type err)
854 Eina_List *l, *l_next;
857 DBG("Removing answer requests from entry: %d, client: %d",
858 req->entry->id, client->id);
860 EINA_LIST_FOREACH_SAFE(req->waiters, l, l_next, it)
862 if (it->ref->client->id == client->id)
864 cserve2_client_error_send(client, it->rid, err);
865 req->waiters = eina_list_remove_list(req->waiters, l);
870 // FIXME: Should this be really here? I guess that it should be in the
871 // entry_free_cb function, or entry_reference_del, when there are no more
873 if (!req->entry && !req->waiters)
875 *requests = eina_list_remove(*requests, req);
881 _request_answer_all_del(Eina_List **requests, Request *req, Error_Type err)
885 DBG("Removing all answer requests from entry: %d", req->entry->id);
887 EINA_LIST_FREE(req->waiters, it)
889 cserve2_client_error_send(it->ref->client, it->rid, err);
893 *requests = eina_list_remove(*requests, req);
898 _request_answer_add(Request *req, Reference *ref, unsigned int rid, Message_Type type)
900 Waiter *w = malloc(sizeof(*w));
906 DBG("Add answer request for entry id: %d, client: %d, rid: %d",
907 req->entry->id, ref->client->id, rid);
908 req->waiters = eina_list_append(req->waiters, w);
912 _request_add(Eina_List **requests, Entry *entry, Reference *ref, unsigned int rid, Message_Type type)
916 // add the request if it doesn't exist yet
919 req = malloc(sizeof(*req));
922 req->processing = EINA_FALSE;
923 entry->request = req;
924 if (type == CSERVE2_OPEN)
925 req->funcs = &_open_funcs;
927 req->funcs = &_load_funcs;
928 *requests = eina_list_append(*requests, req);
929 DBG("Add request for entry id: %d, client: %d, rid: %d",
930 req->entry->id, ref->client->id, rid);
933 req = entry->request;
935 if (type != CSERVE2_SETOPTS)
936 _request_answer_add(req, ref, rid, type);
938 DBG("Adding entry for speculative preload: id=%d", req->entry->id);
942 _entry_reference_add(Entry *entry, Client *client, unsigned int client_entry_id)
946 // increase reference for this file
947 ref = malloc(sizeof(*ref));
948 ref->client = client;
950 ref->client_entry_id = client_entry_id;
952 entry->references = eina_list_append(entry->references, ref);
958 _cserve2_cache_open_requests_process(int nloaders)
961 char *slave_cmd_data;
964 while ((nloaders > 0) && (open_requests))
966 // remove the first element from the list and process this element
967 req = eina_list_data_get(open_requests);
968 open_requests = eina_list_remove_list(open_requests, open_requests);
970 DBG("Processing OPEN request for file entry: %d", req->entry->id);
972 slave_cmd_data = req->funcs->msg_create(req->entry, &slave_cmd_size);
974 cserve2_slave_cmd_dispatch(req, IMAGE_OPEN, slave_cmd_data,
977 free(slave_cmd_data);
979 req->processing = EINA_TRUE;
987 _cserve2_cache_load_requests_list_process(Eina_List **queue, int nloaders)
989 Eina_List *skipped = NULL;
992 while ((nloaders > 0) && (*queue))
998 // remove the first element from the list and process this element
999 req = eina_list_data_get(*queue);
1000 *queue = eina_list_remove_list(*queue, *queue);
1002 ientry = (Image_Data *)req->entry;
1005 ERR("File entry doesn't exist for entry id %d", req->entry->id);
1006 _request_failed(req->entry, CSERVE2_INVALID_CACHE);
1010 if (ientry->file->base.request)
1012 /* OPEN still pending, skip this request */
1013 skipped = eina_list_append(skipped, req);
1017 DBG("Processing LOAD request for image entry: %d", req->entry->id);
1019 buf = req->funcs->msg_create(req->entry, &size);
1021 cserve2_slave_cmd_dispatch(req, IMAGE_LOAD, buf, size);
1025 req->processing = EINA_TRUE;
1030 EINA_LIST_FREE(skipped, req)
1031 *queue = eina_list_append(*queue, req);
1037 _cserve2_cache_load_requests_process(int nloaders)
1039 nloaders = _cserve2_cache_load_requests_list_process(&load_requests,
1041 _cserve2_cache_load_requests_list_process(&spload_requests, nloaders - 1);
1046 cserve2_cache_requests_process(void)
1050 avail_loaders = cserve2_slave_available_get();
1051 avail_loaders = _cserve2_cache_open_requests_process(avail_loaders);
1052 _cserve2_cache_load_requests_process(avail_loaders);
1056 _entry_unused_push(Image_Data *e)
1058 int size = _image_entry_size_get(e);
1060 if ((size > max_unused_mem_usage) || !(e->doload))
1062 eina_hash_del_by_key(image_entries, &e->base.id);
1065 while (size > (max_unused_mem_usage - unused_mem_usage))
1067 Entry *ie = eina_list_data_get(eina_list_last(image_entries_lru));
1068 eina_hash_del_by_key(image_entries, &ie->id);
1070 image_entries_lru = eina_list_append(image_entries_lru, e);
1071 e->unused = EINA_TRUE;
1072 unused_mem_usage += size;
1076 _entry_reference_del(Entry *entry, Reference *ref)
1078 entry->references = eina_list_remove(entry->references, ref);
1080 if (entry->references)
1083 if (entry->type == CSERVE2_IMAGE_FILE)
1085 File_Data *fentry = (File_Data *)entry;
1087 if (fentry->invalid)
1088 _file_entry_free(fentry);
1089 else if (!fentry->images)
1090 eina_hash_del_by_key(file_entries, &entry->id);
1091 /* don't free file entries that have images attached to it, they will
1092 * be freed when the last unused image is freed */
1094 else if (entry->type == CSERVE2_IMAGE_DATA)
1096 Image_Data *ientry = (Image_Data *)entry;
1099 eina_hash_del_by_key(image_entries, &entry->id);
1100 else if (ientry->file->invalid)
1101 _image_entry_free(ientry);
1103 _entry_unused_push(ientry);
1105 else if (entry->type == CSERVE2_FONT_ENTRY)
1107 Font_Entry *fe = (Font_Entry *)entry;
1108 fe->unused = EINA_TRUE;
1110 eina_hash_del_by_key(font_entries, fe);
1113 ERR("Wrong type of entry.");
1120 _entry_free_cb(void *data)
1122 Reference *ref = data;
1125 DBG("Removing client reference for entry id: %d, client: %d",
1126 ref->entry->id, ref->client->id);
1130 if (entry->request && !entry->request->processing)
1132 if (entry->type == CSERVE2_IMAGE_FILE)
1133 _request_answer_del(&open_requests, entry->request, ref->client,
1134 CSERVE2_REQUEST_CANCEL);
1135 else if (entry->type == CSERVE2_IMAGE_DATA)
1137 if (((Image_Data *)entry)->doload)
1138 _request_answer_del(&load_requests, entry->request,
1139 ref->client, CSERVE2_REQUEST_CANCEL);
1141 _request_answer_del(&spload_requests, entry->request,
1142 ref->client, CSERVE2_REQUEST_CANCEL);
1146 _entry_reference_del(entry, ref);
1150 _font_entry_reference_del(Client *client, Font_Entry *fe)
1155 EINA_LIST_FOREACH(client->fonts.referencing, l, ref)
1157 if (ref->entry == (Entry *)fe)
1163 client->fonts.referencing = eina_list_remove_list(
1164 client->fonts.referencing, l);
1165 _entry_reference_del(&fe->base, ref);
1172 cserve2_cache_client_new(Client *client)
1174 client->files.referencing = eina_hash_int32_new(_entry_free_cb);
1175 client->images.referencing = eina_hash_int32_new(_entry_free_cb);
1176 client->fonts.referencing = NULL;
1180 cserve2_cache_client_del(Client *client)
1184 // will call _entry_free_cb() for every entry
1185 eina_hash_free(client->images.referencing);
1186 // will call _entry_free_cb() for every entry
1187 eina_hash_free(client->files.referencing);
1189 EINA_LIST_FREE(client->fonts.referencing, ref)
1191 _entry_reference_del(ref->entry, ref);
1196 _image_msg_new(Client *client, Msg_Setopts *msg)
1199 Image_Data *im_entry;
1201 ref = eina_hash_find(client->files.referencing, &msg->file_id);
1204 ERR("Couldn't find file id: %d, for image id: %d",
1205 msg->file_id, msg->image_id);
1206 cserve2_client_error_send(client, msg->base.rid,
1207 CSERVE2_INVALID_CACHE);
1210 if (((File_Data *)ref->entry)->invalid)
1212 cserve2_client_error_send(client, msg->base.rid,
1213 CSERVE2_FILE_CHANGED);
1217 im_entry = calloc(1, sizeof(*im_entry));
1218 im_entry->base.type = CSERVE2_IMAGE_DATA;
1219 im_entry->file_id = ref->entry->id;
1220 im_entry->file = (File_Data *)ref->entry;
1221 im_entry->opts.dpi = msg->opts.dpi;
1222 im_entry->opts.w = msg->opts.w;
1223 im_entry->opts.h = msg->opts.h;
1224 im_entry->opts.scale_down = msg->opts.scale_down;
1225 im_entry->opts.rx = msg->opts.rx;
1226 im_entry->opts.ry = msg->opts.ry;
1227 im_entry->opts.rw = msg->opts.rw;
1228 im_entry->opts.rh = msg->opts.rh;
1229 im_entry->opts.orientation = msg->opts.orientation;
1235 _file_changed_cb(const char *path __UNUSED__, Eina_Bool deleted __UNUSED__, void *data)
1237 File_Watch *fw = data;
1241 EINA_LIST_FOREACH(fw->entries, l, e)
1246 e->invalid = EINA_TRUE;
1249 EINA_LIST_FOREACH(e->images, ll, ie)
1252 eina_hash_set(image_entries, &ie->base.id, NULL);
1253 if (ie->base.request && !ie->base.request->processing)
1256 _request_answer_all_del(&load_requests, ie->base.request,
1257 CSERVE2_FILE_CHANGED);
1259 _request_answer_all_del(&spload_requests, ie->base.request,
1260 CSERVE2_FILE_CHANGED);
1262 ie->base.request = NULL;
1264 _image_entry_free(ie);
1268 eina_hash_set(file_entries, &e->base.id, NULL);
1269 if (e->base.request && !e->base.request->processing)
1270 _request_answer_all_del(&open_requests, e->base.request,
1271 CSERVE2_FILE_CHANGED);
1272 e->base.request = NULL;
1273 if (!e->images && !e->base.references)
1274 _file_entry_free(e);
1277 eina_hash_del_by_key(file_watch, fw->path);
1280 static Font_Source *
1281 _cserve2_font_source_find(const char *name)
1285 fs = eina_hash_find(font_sources, name);
1291 _cserve2_font_entry_find(const char *name, unsigned int namelen, unsigned int size, unsigned int rend_flags, unsigned int dpi)
1297 tmp_fs.key = eina_stringshare_add_length(name, namelen);
1298 tmp_fe.src = &tmp_fs;
1300 tmp_fe.rend_flags = rend_flags;
1303 fe = eina_hash_find(font_entries, &tmp_fe);
1304 eina_stringshare_del(tmp_fs.key);
1310 _font_load_request_build(void *data, int *size)
1312 Font_Entry *fe = data;
1313 Slave_Msg_Font_Load *msg = calloc(1, sizeof(*msg));
1315 msg->ftdata1 = fe->src->ft;
1316 msg->ftdata2 = fe->ft;
1317 msg->rend_flags = fe->rend_flags;
1318 msg->size = fe->size;
1320 msg->name = fe->src->name;
1321 msg->file = fe->src->file;
1325 _entry_load_start(&fe->base);
1331 _font_load_request_free(void *msg, void *data __UNUSED__)
1337 _font_load_request_response(Client *client __UNUSED__, void *data, void *resp, unsigned int rid __UNUSED__)
1339 Slave_Msg_Font_Loaded *msg = resp;
1340 Font_Entry *fe = data;
1342 DBG("request %d answered.", rid);
1345 fe->src->ft = msg->ftdata1;
1349 fe->ft = msg->ftdata2;
1350 _entry_load_finish(&fe->base);
1353 if (fe->request) fe->request = NULL;
1355 _font_loaded_send(client, rid);
1359 _font_load_request_failed(Client *client __UNUSED__, void *data __UNUSED__, Error_Type error __UNUSED__, unsigned int rid __UNUSED__)
1361 Font_Entry *fe = data;
1362 DBG("request %d error answered.", rid);
1364 cserve2_client_error_send(client, rid, error);
1366 if (fe->request) fe->request = NULL;
1368 _font_entry_reference_del(client, fe);
1371 static Font_Request_Funcs _font_load_funcs = {
1372 .msg_create = (Font_Request_Msg_Create)_font_load_request_build,
1373 .msg_free = (Font_Request_Msg_Free)_font_load_request_free,
1374 .response = (Font_Request_Response)_font_load_request_response,
1375 .error = (Font_Request_Error)_font_load_request_failed
1379 _glyphs_request_check(Glyphs_Request *req, Eina_Bool report_load)
1382 Font_Entry *fe = req->fe;
1384 req->answer = malloc(sizeof(*req->answer) * req->nglyphs);
1387 for (i = req->current; i < req->nglyphs; i++)
1390 ge = fash_gl_find(fe->glyphs, req->glyphs[i]);
1393 req->answer[req->nanswer++] = ge;
1394 #ifdef DEBUG_LOAD_TIME
1395 // calculate average time saved when loading glyphs
1397 fe->gl_saved_time +=
1398 (fe->gl_load_time / fe->nglyphs);
1408 // No glyphs need to be rendered.
1409 return (req->nanswer == req->nglyphs);
1412 /* organize answer (cache1{gl1, gl2,}, cache2{gl3,gl4,gl5}, cache3{gl6})
1415 _glyphs_group_create(Glyphs_Request *req)
1417 Eina_List *groups = NULL;
1420 for (i = 0; i < req->nanswer; i++)
1423 Glyphs_Group *iter, *gg = NULL;
1424 Font_Cache *fc = req->answer[i]->fc;
1426 EINA_LIST_FOREACH(groups, l, iter)
1437 gg = calloc(1, sizeof(*gg));
1439 groups = eina_list_append(groups, gg);
1441 gg->glyphs = eina_list_append(gg->glyphs, req->answer[i]);
1448 _glyphs_loaded_send(Glyphs_Request *req, unsigned int rid)
1450 Msg_Font_Glyphs_Loaded msg;
1452 Eina_List *ll, *answers = NULL;
1453 const char *shmname;
1454 unsigned int shmsize;
1455 unsigned int intsize;
1459 memset(&msg, 0, sizeof(msg));
1461 msg.base.type = CSERVE2_FONT_GLYPHS_LOADED;
1463 answers = _glyphs_group_create(req);
1464 msg.ncaches = eina_list_count(answers);
1467 // calculate size of message
1468 // ncaches * sizeof(cache) + nglyphs1 * sizeof(glyph) +
1469 // nglyphs2 * sizeof(glyph)...
1471 intsize = sizeof(unsigned int);
1473 EINA_LIST_FOREACH(answers, ll, iter)
1475 shmname = cserve2_shm_name_get(iter->fc->shm);
1476 shmsize = eina_stringshare_strlen(shmname) + 1;
1477 // shm namelen + name
1478 size += intsize + shmsize;
1482 // nglyphs * (index + offset + size + rows + width + pitch +
1483 // num_grays + pixel_mode)
1484 size += eina_list_count(iter->glyphs) * 8 * intsize;
1487 resp = malloc(size);
1488 memcpy(resp, &msg, sizeof(msg));
1489 buf = resp + sizeof(msg);
1491 EINA_LIST_FREE(answers, iter)
1494 unsigned int nglyphs;
1496 shmname = cserve2_shm_name_get(iter->fc->shm);
1497 shmsize = eina_stringshare_strlen(shmname) + 1;
1498 memcpy(buf, &shmsize, intsize);
1500 memcpy(buf, shmname, shmsize);
1503 nglyphs = eina_list_count(iter->glyphs);
1504 memcpy(buf, &nglyphs, intsize);
1507 iter->fc->inuse -= eina_list_count(iter->glyphs);
1509 EINA_LIST_FREE(iter->glyphs, gl)
1511 memcpy(buf, &gl->index, intsize);
1513 memcpy(buf, &gl->offset, intsize);
1515 memcpy(buf, &gl->size, intsize);
1517 memcpy(buf, &gl->rows, intsize);
1519 memcpy(buf, &gl->width, intsize);
1521 memcpy(buf, &gl->pitch, intsize);
1523 memcpy(buf, &gl->num_grays, intsize);
1525 memcpy(buf, &gl->pixel_mode, intsize);
1529 /* We are removing SHMs from the beginning of the list, so this
1530 * gives a higher priority to them */
1531 _font_shm_promote(iter->fc);
1532 eina_list_free(iter->glyphs);
1536 cserve2_client_send(req->client, &size, sizeof(size));
1537 cserve2_client_send(req->client, resp, size);
1543 * taken from evas_path.c. It would be good to clean up those utils to
1544 * have cserve link against them easily without dragging unneeded dependencies
1547 # define EVAS_PATH_SEPARATOR "\\"
1549 # define EVAS_PATH_SEPARATOR "/"
1553 _file_path_join(const char *path, const char *end)
1558 if ((!path) && (!end)) return NULL;
1559 if (!path) return strdup(end);
1560 if (!end) return strdup(path);
1563 len += strlen(EVAS_PATH_SEPARATOR);
1564 res = malloc(len + 1);
1565 if (!res) return NULL;
1567 strcat(res, EVAS_PATH_SEPARATOR);
1572 static Glyphs_Request *
1573 _glyphs_request_create(Client *client, const char *source, unsigned int sourcelen, const char *name, unsigned int namelen, unsigned int hint, unsigned int rend_flags, unsigned int size, unsigned int dpi, unsigned int *glyphs, unsigned int nglyphs)
1576 Glyphs_Request *req = calloc(1, sizeof(*req));
1583 fullname = _file_path_join(source, name);
1584 req->fe = _cserve2_font_entry_find(fullname, strlen(fullname) + 1, size,
1589 ERR("No font entry found: source %s, name %s, rendflags: %d, hint: %d,"
1590 " size: %d, dpi: %d", source, name, rend_flags, hint, size, dpi);
1595 req->client = client;
1597 req->nglyphs = nglyphs;
1599 req->glyphs = glyphs;
1606 _glyphs_request_free(Glyphs_Request *req)
1614 /* add glyphs that are already in cache to the "answers" array, and the ones
1615 * that are not cached to the "render" array.
1618 _glyphs_load_request_prepare(Glyphs_Request *req)
1620 unsigned int i, max;
1622 Font_Entry *fe = req->fe;
1626 ERR("No font entry for this request.");
1630 // Won't render more than this number of glyphs
1631 max = req->nglyphs - req->nanswer;
1632 req->render = malloc(sizeof(*req->render) * max);
1634 for (i = req->current; i < req->nglyphs; i++)
1637 ge = fash_gl_find(fe->glyphs, req->glyphs[i]);
1640 req->answer[req->nanswer++] = ge;
1642 #ifdef DEBUG_LOAD_TIME
1643 // calculate average time saved when loading glyphs
1644 fe->gl_saved_time +=
1645 (fe->gl_load_time / fe->nglyphs);
1650 req->render[req->nrender++] = req->glyphs[i];
1655 _glyphs_load_request_build(void *data, int *size __UNUSED__)
1657 Glyphs_Request *req = data;
1658 Slave_Msg_Font_Glyphs_Load *msg = NULL;
1659 Font_Entry *fe = req->fe;
1662 _glyphs_load_request_prepare(req);
1664 msg = calloc(1, sizeof(*msg));
1666 msg->font.ftdata1 = fe->src->ft;
1667 msg->font.ftdata2 = fe->ft;
1668 msg->font.hint = req->hint;
1669 msg->font.rend_flags = fe->rend_flags;
1670 msg->glyphs.nglyphs = req->nrender;
1671 msg->glyphs.glyphs = req->render;
1673 // Trying to reuse last filled cache.
1674 fc = fe->last_cache;
1677 msg->cache.shm = fc->shm;
1678 msg->cache.usage = fc->usage;
1679 msg->cache.nglyphs = fc->nglyphs;
1682 #ifdef DEBUG_LOAD_TIME
1683 gettimeofday(&fe->load_start, NULL);
1690 _glyphs_load_request_free(void *msg, void *data)
1692 _glyphs_request_free(data);
1697 _glyphs_load_request_response(Client *client __UNUSED__, void *data, void *resp, unsigned int rid)
1699 Glyphs_Request *req = data;
1700 Slave_Msg_Font_Glyphs_Loaded *msg = resp;
1701 Font_Entry *fe = req->fe;
1702 Font_Cache *fc = NULL;
1705 if (fe->last_cache && fe->last_cache->shm == msg->caches[0]->shm)
1706 fc = fe->last_cache;
1708 while (i < msg->ncaches)
1711 Slave_Msg_Font_Cache *c = msg->caches[i++];
1715 fc = malloc(sizeof(*fc));
1716 fe->caches = eina_inlist_append(fe->caches, EINA_INLIST_GET(fc));
1717 fe->last_cache = fc;
1723 font_shm_lru = eina_list_append(font_shm_lru, fc);
1724 font_mem_usage += _font_shm_size_get(fc);
1726 fc->usage = c->usage;
1727 for (j = 0; j < c->nglyphs; j++)
1729 Glyph_Entry *gl = malloc(sizeof(*gl));
1732 gl->index = c->glyphs[j].index;
1733 gl->offset = c->glyphs[j].offset;
1734 gl->size = c->glyphs[j].size;
1735 gl->rows = c->glyphs[j].rows;
1736 gl->width = c->glyphs[j].width;
1737 gl->pitch = c->glyphs[j].pitch;
1738 gl->num_grays = c->glyphs[j].num_grays;
1739 gl->pixel_mode = c->glyphs[j].pixel_mode;
1740 font_mem_usage += sizeof(*gl);
1741 fc->glyphs = eina_inlist_append(fc->glyphs, EINA_INLIST_GET(gl));
1744 fash_gl_add(fe->glyphs, gl->index, gl);
1745 req->answer[req->nanswer++] = gl;
1749 free(c); // FIXME: We are freeing this here because we only do a
1750 // simple free on the response message. Later we need to
1751 // setup a free callback for the slave response.
1755 #ifdef DEBUG_LOAD_TIME
1757 gettimeofday(&fe->load_finish, NULL);
1758 load_time = _timeval_sub(&fe->load_finish, &fe->load_start);
1759 fe->gl_load_time += load_time;
1762 _glyphs_loaded_send(req, rid);
1763 _font_shm_lru_flush();
1767 _glyphs_load_request_failed(Client *client __UNUSED__, void *data __UNUSED__, Error_Type error __UNUSED__, unsigned int rid __UNUSED__)
1771 static Font_Request_Funcs _glyphs_load_funcs = {
1772 .msg_create = (Font_Request_Msg_Create)_glyphs_load_request_build,
1773 .msg_free = (Font_Request_Msg_Free)_glyphs_load_request_free,
1774 .response = (Font_Request_Response)_glyphs_load_request_response,
1775 .error = (Font_Request_Error)_glyphs_load_request_failed
1779 _font_entry_stats_cb(const Eina_Hash *hash __UNUSED__, const void *key __UNUSED__, void *data, void *fdata)
1781 Font_Entry *fe = data;
1782 Msg_Stats *msg = fdata;
1784 int nrefs = eina_list_count(fe->base.references);
1786 msg->fonts.fonts_loaded++;
1787 if (fe->unused) msg->fonts.fonts_unused++;
1790 EINA_INLIST_FOREACH(fe->caches, fc)
1792 unsigned int fc_usage, shmsize;
1793 /* This is not real requested usage, but an approximation. We don't
1794 * know how many times each glyph would be used by each client, but
1795 * assume that a similar set of glyphs from a given font would be used
1796 * by each client, thus counting them one time per client referencing
1799 fc_usage = fc->usage * nrefs;
1800 shmsize = cserve2_shm_size_get(fc->shm);
1802 msg->fonts.requested_size += fc_usage;
1803 msg->fonts.real_size += shmsize;
1804 if (fe->unused) msg->fonts.unused_size += shmsize;
1807 #ifdef DEBUG_LOAD_TIME
1808 // accounting fonts load time
1809 msg->fonts.fonts_load_time += fe->base.load_time;
1812 msg->fonts.fonts_used_load_time += fe->base.load_time;
1813 msg->fonts.fonts_used_saved_time += fe->base.saved_time;
1816 // accounting glyphs load time
1817 msg->fonts.glyphs_load_time += fe->gl_load_time;
1818 msg->fonts.glyphs_saved_time += fe->gl_saved_time;
1825 _image_file_entry_stats_cb(const Eina_Hash *hash __UNUSED__, const void *key __UNUSED__, void *data, void *fdata)
1827 Msg_Stats *msg = fdata;
1828 File_Data *fd = data;
1830 // accounting numbers
1831 msg->images.files_loaded++;
1834 msg->images.files_size += sizeof(File_Data) +
1835 eina_list_count(fd->images) * sizeof(Eina_List *) +
1836 eina_list_count(fd->base.references) *
1837 (sizeof(Request) + sizeof(Eina_List *));
1839 #ifdef DEBUG_LOAD_TIME
1840 // accounting file entries load time
1841 msg->images.files_load_time += fd->base.load_time;
1842 msg->images.files_saved_time += fd->base.saved_time;
1849 _image_data_entry_stats_cb(const Eina_Hash *hash __UNUSED__, const void *key __UNUSED__, void *data, void *fdata)
1851 Msg_Stats *msg = fdata;
1852 Image_Data *id = data;
1853 unsigned int image_size;
1855 // accounting numbers
1856 msg->images.images_loaded++;
1857 if (id->unused) msg->images.images_unused++;
1860 msg->images.images_size += _image_entry_size_get(id) * 1024;
1861 if (id->unused) msg->images.unused_size += _image_entry_size_get(id) * 1024;
1863 image_size = id->file->w * id->file->h * 4;
1864 msg->images.requested_size +=
1865 (image_size * eina_list_count(id->base.references));
1867 #ifdef DEBUG_LOAD_TIME
1868 // accounting image entries load time
1869 msg->images.images_load_time += id->base.load_time;
1870 msg->images.images_saved_time += id->base.saved_time;
1877 _cserve2_cache_image_stats_get(Msg_Stats *msg)
1879 eina_hash_foreach(file_entries, _image_file_entry_stats_cb, msg);
1880 eina_hash_foreach(image_entries, _image_data_entry_stats_cb, msg);
1884 _cserve2_cache_font_stats_get(Msg_Stats *msg)
1886 eina_hash_foreach(font_entries, _font_entry_stats_cb, msg);
1892 unsigned int nfonts;
1896 _font_entry_debug_size_cb(const Eina_Hash *hash __UNUSED__, const void *key __UNUSED__, void *data, void *fdata)
1898 struct _debug_info *di = fdata;
1899 unsigned int size = di->size;
1900 Font_Entry *fe = data;
1902 unsigned int intsize = sizeof(unsigned int);
1909 size += strlen(fe->src->file) + 1;
1916 size += strlen(fe->src->name) + 1;
1918 // rend_flags, size, dpi
1919 size += 3 * intsize;
1927 EINA_INLIST_FOREACH(fe->caches, fc)
1931 // shmnamelen + shmname
1933 size += strlen(cserve2_shm_name_get(fc->shm)) + 1;
1936 size += 2 * intsize;
1941 EINA_INLIST_FOREACH(fc->glyphs, gl)
1943 // index, offset, size
1944 size += 3 * intsize;
1946 // rows, width, pitch
1947 size += 3 * intsize;
1949 // num_grays, pixel_mode
1950 size += 2 * intsize;
1961 _font_entry_debug_cb(const Eina_Hash *hash __UNUSED__, const void *key __UNUSED__, void *data, void *fdata)
1965 Font_Entry *fe = data;
1968 unsigned int unused;
1969 unsigned int ncaches;
1970 unsigned int intsize = sizeof(unsigned int);
1975 len = strlen(fe->src->file) + 1;
1976 memcpy(buf, &len, intsize);
1978 memcpy(buf, fe->src->file, len);
1984 len = strlen(fe->src->name) + 1;
1985 memcpy(buf, &len, intsize);
1987 memcpy(buf, fe->src->name, len);
1990 // rend_flags, size, dpi
1991 memcpy(buf, &fe->rend_flags, intsize);
1993 memcpy(buf, &fe->size, intsize);
1995 memcpy(buf, &fe->dpi, intsize);
1999 unused = fe->unused;
2000 memcpy(buf, &unused, intsize);
2004 ncaches = eina_inlist_count(fe->caches);
2005 memcpy(buf, &ncaches, intsize);
2008 EINA_INLIST_FOREACH(fe->caches, fc)
2011 const char *shmname;
2012 unsigned int shmsize;
2014 // shmnamelen + shmname
2015 shmname = cserve2_shm_name_get(fc->shm);
2016 len = strlen(shmname) + 1;
2017 memcpy(buf, &len, intsize);
2019 memcpy(buf, shmname, len);
2022 // size, usage, nglyphs
2023 shmsize = cserve2_shm_size_get(fc->shm);
2024 memcpy(buf, &shmsize, intsize);
2026 memcpy(buf, &fc->usage, intsize);
2028 memcpy(buf, &fc->nglyphs, intsize);
2031 EINA_INLIST_FOREACH(fc->glyphs, gl)
2033 // index, offset, size
2034 memcpy(buf, &gl->index, intsize);
2036 memcpy(buf, &gl->offset, intsize);
2038 memcpy(buf, &gl->size, intsize);
2041 // rows, width, pitch
2042 memcpy(buf, &gl->rows, intsize);
2044 memcpy(buf, &gl->width, intsize);
2046 memcpy(buf, &gl->pitch, intsize);
2049 // num_grays, pixel_mode
2050 memcpy(buf, &gl->num_grays, intsize);
2052 memcpy(buf, &gl->pixel_mode, intsize);
2062 _cserve2_cache_font_debug(unsigned int rid, unsigned int *size)
2066 struct _debug_info di;
2067 di.size = sizeof(msg);
2070 memset(&msg, 0, sizeof(msg));
2072 msg.base.type = CSERVE2_FONT_DEBUG;
2075 // First calculate how much size is needed for this message:
2078 di.size += sizeof(unsigned int);
2080 // size needed for each font entry
2081 eina_hash_foreach(font_entries, _font_entry_debug_size_cb, &di);
2083 // Now really create the message
2084 buf = malloc(di.size);
2088 memcpy(buf, &msg, sizeof(msg));
2092 memcpy(pos, &di.nfonts, sizeof(unsigned int));
2093 pos += sizeof(unsigned int);
2095 eina_hash_foreach(font_entries, _font_entry_debug_cb, &pos);
2102 cserve2_cache_file_open(Client *client, unsigned int client_file_id, const char *path, const char *key, unsigned int rid)
2104 unsigned int file_id;
2110 // look for this file on client references
2111 ref = eina_hash_find(client->files.referencing, &client_file_id);
2114 entry = (File_Data *)ref->entry;
2115 _entry_load_reused(ref->entry);
2119 cserve2_client_error_send(client, rid, CSERVE2_FILE_CHANGED);
2123 DBG("found client file id: %d", client_file_id);
2126 // File already being loaded, just add the request to be replied
2127 if (entry->base.request)
2128 _request_answer_add(entry->base.request, ref, rid, CSERVE2_OPEN);
2130 _image_opened_send(client, entry, rid);
2134 // search whether the file is already opened by another client
2135 snprintf(buf, sizeof(buf), "%s:%s", path, key);
2136 file_id = (unsigned int)eina_hash_find(file_ids, buf);
2139 DBG("found file_id %u for client file id %d",
2140 file_id, client_file_id);
2141 entry = eina_hash_find(file_entries, &file_id);
2144 ERR("file \"%s\" is in file_ids hash but not in entries hash.",
2146 cserve2_client_error_send(client, rid, CSERVE2_INVALID_CACHE);
2149 ref = _entry_reference_add((Entry *)entry, client, client_file_id);
2150 _entry_load_reused(ref->entry);
2151 eina_hash_add(client->files.referencing, &client_file_id, ref);
2152 if (entry->base.request)
2153 _request_answer_add(entry->base.request, ref, rid, CSERVE2_OPEN);
2154 else // File already loaded, otherwise there would be a request
2155 _image_opened_send(client, entry, rid);
2159 file_id = _file_id++;
2160 while ((file_id == 0) || (eina_hash_find(file_entries, &file_id)))
2161 file_id = _file_id++;
2163 DBG("Creating new entry with file_id: %u for file \"%s:%s\"",
2164 file_id, path, key);
2165 entry = calloc(1, sizeof(*entry));
2166 entry->base.type = CSERVE2_IMAGE_FILE;
2167 entry->path = strdup(path);
2168 entry->key = strdup(key);
2169 entry->base.id = file_id;
2170 eina_hash_add(file_entries, &file_id, entry);
2171 eina_hash_add(file_ids, buf, (void *)file_id);
2172 ref = _entry_reference_add((Entry *)entry, client, client_file_id);
2173 eina_hash_add(client->files.referencing, &client_file_id, ref);
2175 fw = eina_hash_find(file_watch, entry->path);
2178 fw = calloc(1, sizeof(File_Watch));
2179 fw->path = eina_stringshare_add(entry->path);
2180 cserve2_file_change_watch_add(fw->path, _file_changed_cb, fw);
2181 eina_hash_direct_add(file_watch, fw->path, fw);
2183 fw->entries = eina_list_append(fw->entries, entry);
2184 entry->watcher = fw;
2186 _request_add(&open_requests, (Entry *)entry, ref, rid, CSERVE2_OPEN);
2188 // _open_image_default_set(entry);
2194 cserve2_cache_file_close(Client *client, unsigned int client_file_id)
2196 Reference *ref = eina_hash_find(client->files.referencing,
2200 ERR("Couldn't find file %d in client hash.", client_file_id);
2205 if (ref->count <= 0)
2206 // will call _entry_free_cb() for this entry
2207 eina_hash_del_by_key(client->files.referencing, &client_file_id);
2211 cserve2_cache_image_opts_set(Client *client, Msg_Setopts *msg)
2214 File_Data *fentry = NULL;
2215 Reference *ref, *oldref;
2216 unsigned int image_id;
2219 oldref = eina_hash_find(client->images.referencing, &msg->image_id);
2221 // search whether the image is already loaded by another client
2222 entry = _image_msg_new(client, msg);
2225 image_id = _img_opts_id_get(entry, buf, sizeof(buf));
2227 { // if so, just update the references
2229 DBG("found image_id %d for client image id %d",
2230 image_id, msg->image_id);
2231 entry = eina_hash_find(image_entries, &image_id);
2234 ERR("image id %d is in file_ids hash, but not in entries hash"
2235 "with entry id %d.", msg->image_id, image_id);
2236 cserve2_client_error_send(client, msg->base.rid,
2237 CSERVE2_INVALID_CACHE);
2243 DBG("Re-using old image entry (id: %d) from the LRU list.",
2245 entry->unused = EINA_FALSE;
2246 image_entries_lru = eina_list_remove(image_entries_lru, entry);
2247 unused_mem_usage -= _image_entry_size_get(entry);
2249 _entry_load_reused(&entry->base);
2251 if (oldref && (oldref->entry->id == image_id))
2254 ref = _entry_reference_add((Entry *)entry, client, msg->image_id);
2257 eina_hash_del_by_key(client->images.referencing, &msg->image_id);
2259 eina_hash_add(client->images.referencing, &msg->image_id, ref);
2264 image_id = _image_id++;
2265 while ((image_id == 0) || (eina_hash_find(image_entries, &image_id)))
2266 image_id = _image_id++;
2268 entry->base.id = image_id;
2269 eina_hash_add(image_entries, &image_id, entry);
2270 eina_hash_add(image_ids, buf, (void *)image_id);
2271 ref = _entry_reference_add((Entry *)entry, client, msg->image_id);
2274 eina_hash_del_by_key(client->images.referencing, &msg->image_id);
2275 eina_hash_add(client->images.referencing, &msg->image_id, ref);
2277 fentry = entry->file;
2278 fentry->images = eina_list_append(fentry->images, entry);
2280 _request_add(&spload_requests, (Entry *)entry, ref, msg->base.rid,
2286 cserve2_cache_image_load(Client *client, unsigned int client_image_id, unsigned int rid)
2291 ref = eina_hash_find(client->images.referencing, &client_image_id);
2294 ERR("Can't load: client %d has no image id %d",
2295 client->id, client_image_id);
2299 entry = (Image_Data *)ref->entry;
2301 if (entry->file->invalid)
2303 cserve2_client_error_send(client, rid, CSERVE2_FILE_CHANGED);
2307 DBG("Loading image id: %d", ref->entry->id);
2309 // File already being loaded, just add the request to be replied
2310 if (entry->base.request)
2312 _request_answer_add(entry->base.request, ref, rid, CSERVE2_LOAD);
2313 if ((!entry->base.request->processing) && (!entry->doload))
2315 DBG("Removing entry %d from speculative preload and adding "
2316 "to normal load queue.", entry->base.id);
2317 spload_requests = eina_list_remove(spload_requests,
2318 entry->base.request);
2319 load_requests = eina_list_append(load_requests,
2320 entry->base.request);
2323 else if (entry->shm)
2324 _image_loaded_send(client, entry, rid);
2326 _request_add(&load_requests, (Entry *)entry, ref, rid, CSERVE2_LOAD);
2328 entry->doload = EINA_TRUE;
2332 cserve2_cache_image_preload(Client *client, unsigned int client_image_id, unsigned int rid)
2337 ref = eina_hash_find(client->images.referencing, &client_image_id);
2340 ERR("Can't load: client %d has no image id %d",
2341 client->id, client_image_id);
2345 entry = (Image_Data *)ref->entry;
2347 if (entry->file->invalid)
2349 cserve2_client_error_send(client, rid, CSERVE2_FILE_CHANGED);
2353 DBG("Loading image id: %d", ref->entry->id);
2355 // File already being loaded, just add the request to be replied
2356 if (entry->base.request)
2358 _request_answer_add(entry->base.request, ref, rid, CSERVE2_PRELOAD);
2359 if ((!entry->base.request->processing) && (!entry->doload))
2361 DBG("Removing entry %d from speculative preload and adding "
2362 "to normal (pre)load queue.", entry->base.id);
2363 spload_requests = eina_list_remove(spload_requests,
2364 entry->base.request);
2365 load_requests = eina_list_append(load_requests,
2366 entry->base.request);
2369 else if (entry->shm)
2370 _image_preloaded_send(client, rid);
2372 _request_add(&load_requests, (Entry *)entry, ref, rid, CSERVE2_PRELOAD);
2374 entry->doload = EINA_TRUE;
2378 cserve2_cache_image_unload(Client *client, unsigned int client_image_id)
2380 Reference *ref = eina_hash_find(client->images.referencing,
2384 ERR("Couldn't find file %d in client hash.", client_image_id);
2389 if (ref->count <= 0)
2390 // will call _entry_free_cb() for this entry
2391 eina_hash_del_by_key(client->images.referencing, &client_image_id);
2395 cserve2_cache_font_load(Client *client, const char *source, unsigned int sourcelen, const char *name, unsigned int namelen, unsigned int rend_flags, unsigned int size, unsigned int dpi, unsigned int rid)
2407 fullname = _file_path_join(source, name);
2408 fe = _cserve2_font_entry_find(fullname, strlen(fullname) + 1, size,
2412 DBG("found font entry %s, rendflags: %d, size: %d, dpi: %d",
2413 name, rend_flags, size, dpi);
2415 ref = _entry_reference_add((Entry *)fe, client, 0);
2416 client->fonts.referencing = eina_list_append(
2417 client->fonts.referencing, ref);
2419 _entry_load_reused(&fe->base);
2420 fe->unused = EINA_FALSE;
2423 cserve2_request_waiter_add(fe->request, rid, client);
2425 _font_loaded_send(client, rid);
2430 fe = calloc(1, sizeof(*fe));
2431 fe->rend_flags = rend_flags;
2434 fe->base.type = CSERVE2_FONT_ENTRY;
2435 fe->glyphs = fash_gl_new(_glyph_free_cb);
2436 ref = _entry_reference_add((Entry *)fe, client, 0);
2437 client->fonts.referencing = eina_list_append(
2438 client->fonts.referencing, ref);
2439 fe->unused = EINA_FALSE;
2441 fs = _cserve2_font_source_find(fullname);
2444 fs = calloc(1, sizeof(*fs));
2447 fs->key = eina_stringshare_add(fullname);
2448 fs->name = eina_stringshare_add_length(name, namelen);
2449 fs->file = eina_stringshare_add_length(source, sourcelen);
2453 fs->file = eina_stringshare_add_length(name, namelen);
2454 fs->key = eina_stringshare_ref(fs->file);
2456 eina_hash_direct_add(font_sources, fs->key, fs);
2461 DBG("adding FONT_LOAD '%s' request.", fs->name);
2462 fe->request = cserve2_request_add(CSERVE2_REQ_FONT_LOAD, rid,
2463 client, &_font_load_funcs, fe);
2465 eina_hash_direct_add(font_entries, fe, fe);
2473 cserve2_cache_font_unload(Client *client, const char *source, unsigned int sourcelen, const char *name, unsigned int namelen, unsigned int rend_flags, unsigned int size, unsigned int dpi, unsigned int rid __UNUSED__)
2483 fullname = _file_path_join(source, name);
2484 fe = _cserve2_font_entry_find(fullname, strlen(fullname) + 1, size,
2490 ERR("Unreferencing font not found: '%s:%s'.", source, name);
2494 _font_entry_reference_del(client, fe);
2500 cserve2_cache_font_glyphs_load(Client *client, const char *source, unsigned int sourcelen, const char *name, unsigned int namelen, unsigned int hint, unsigned int rend_flags, unsigned int size, unsigned int dpi, unsigned int *glyphs, unsigned int nglyphs, unsigned int rid)
2502 Glyphs_Request *req;
2504 req = _glyphs_request_create(client, source, sourcelen, name, namelen,
2505 hint, rend_flags, size, dpi, glyphs, nglyphs);
2512 if (_glyphs_request_check(req, EINA_TRUE))
2514 INF("Glyphs already loaded. Sending answer.");
2515 _glyphs_loaded_send(req, rid);
2516 _glyphs_request_free(req);
2520 cserve2_request_add(CSERVE2_REQ_FONT_GLYPHS_LOAD, rid,
2521 client, &_glyphs_load_funcs, req);
2527 cserve2_cache_font_glyphs_used(Client *client, const char *source, unsigned int sourcelen, const char *name, unsigned int namelen, unsigned int hint, unsigned int rend_flags, unsigned int size, unsigned int dpi, unsigned int *glyphs, unsigned int nglyphs, unsigned int rid __UNUSED__)
2531 Glyphs_Request *req;
2533 DBG("Received report of used glyphs from client %d", client->id);
2534 req = _glyphs_request_create(client, source, sourcelen, name, namelen,
2535 hint, rend_flags, size, dpi, glyphs, nglyphs);
2542 _glyphs_request_check(req, EINA_FALSE);
2543 groups = _glyphs_group_create(req);
2545 // Promote SHMs which are still cached and in use
2546 // TODO: We can use later the information from request_prepare to preload
2547 // glyphs which are not cached anymore, but are in use on the client.
2548 EINA_LIST_FREE(groups, gg)
2550 _font_shm_promote(gg->fc);
2551 eina_list_free(gg->glyphs);
2555 _glyphs_request_free(req);
2560 cserve2_cache_requests_response(Slave_Command type, void *msg, void *data)
2562 Request *req = data;
2567 DBG("Request finished but it has no entry anymore.");
2568 EINA_LIST_FREE(req->waiters, w)
2570 cserve2_client_error_send(w->ref->client, w->rid,
2571 CSERVE2_REQUEST_CANCEL);
2577 else if (type == ERROR)
2579 Error_Type *error = msg;
2580 req->funcs->error(req->entry, *error);
2583 req->funcs->response(req->entry, msg);
2586 req->entry->request = NULL;
2591 cserve2_cache_stats_get(Client *client, unsigned int rid)
2596 memset(&msg, 0, sizeof(msg));
2598 msg.base.type = CSERVE2_STATS;
2601 _cserve2_cache_image_stats_get(&msg);
2602 _cserve2_cache_font_stats_get(&msg);
2605 cserve2_client_send(client, &size, sizeof(size));
2606 cserve2_client_send(client, &msg, size);
2610 cserve2_cache_font_debug(Client *client, unsigned int rid)
2615 msg = _cserve2_cache_font_debug(rid, &size);
2617 cserve2_client_send(client, &size, sizeof(size));
2618 cserve2_client_send(client, msg, size);