SUBDIRS = loaders
libexec_PROGRAMS = evas_cserve2 evas_cserve2_slave dummy_slave
-bin_PROGRAMS = evas_cserve2_client
+bin_PROGRAMS = evas_cserve2_client evas_cserve2_usage
AM_CPPFLAGS = \
-I. \
-DPACKAGE_LIB_DIR=\"$(libdir)\" \
-DPACKAGE_LIBEXEC_DIR=\"$(libexecdir)\" \
@FREETYPE_CFLAGS@ \
-@EINA_CFLAGS@
+@EINA_CFLAGS@ \
+@EET_CFLAGS@
evas_cserve2_SOURCES = \
evas_cserve2.h \
evas_cserve2_fonts.c \
evas_cserve2_main_loop_linux.c
+libevas_cserve2_utils_la = $(top_builddir)/src/lib/cserve2/libevas_cserve2_utils.la
+
evas_cserve2_LDADD = \
@FREETYPE_LIBS@ \
@EINA_LIBS@ \
-@EFL_SHM_OPEN_LIBS@
+@EFL_SHM_OPEN_LIBS@ \
+@EET_LIBS@ \
+$(libevas_cserve2_utils_la)
evas_cserve2_client_SOURCES = \
evas_cserve2_client.c
+evas_cserve2_usage_SOURCES = \
+evas_cserve2_usage.c
+
+evas_cserve2_usage_LDADD = \
+@EINA_LIBS@
+
evas_cserve2_slave_SOURCES = \
evas_cserve2_slave.c \
evas_cserve2_utils.c
return response_send(fd, ERROR, &err, sizeof(Error_Type));
}
-void *
-cserve2_shm_map(const char *name, size_t length, off_t offset)
+static void *
+_cserve2_shm_map(const char *name, size_t length, off_t offset)
{
void *map;
int fd;
return map;
}
-void
-cserve2_shm_unmap(void *map, size_t length)
+/*
+static void
+_cserve2_shm_unmap(void *map, size_t length)
{
munmap(map, length);
}
+*/
static Error_Type
image_open(const char *file __UNUSED__, const char *key __UNUSED__, Slave_Msg_Image_Opened *result)
static Error_Type
image_load(const char *shmfile, Slave_Msg_Image_Load *params)
{
- char *map = cserve2_shm_map(shmfile, params->shm.mmap_size,
- params->shm.mmap_offset);
+ char *map = _cserve2_shm_map(shmfile, params->shm.mmap_size,
+ params->shm.mmap_offset);
if (map == MAP_FAILED)
return CSERVE2_RESOURCE_ALLOCATION_FAILED;
#endif
#define INF(...) EINA_LOG_DOM_INFO(_evas_cserve2_bin_log_dom, __VA_ARGS__)
+#define DEBUG_LOAD_TIME 1
+
extern int _evas_cserve2_bin_log_dom;
typedef struct _Slave Slave;
void *ftdata1; // Freetype file source info comes here
void *ftdata2; // Freetype font info comes here
unsigned int rend_flags;
- unsigned int hint;
unsigned int size;
unsigned int dpi;
const char *name;
const char *file;
+ void *data;
+ int datasize;
};
struct _Slave_Msg_Font_Loaded {
void *ftdata2;
};
+struct _Slave_Msg_Font_Glyphs_Load {
+ struct {
+ void *ftdata1;
+ void *ftdata2;
+ unsigned int rend_flags;
+ unsigned int hint;
+ } font;
+ struct {
+ unsigned int nglyphs;
+ unsigned int *glyphs;
+ } glyphs;
+ struct {
+ Shm_Handle *shm;
+ unsigned int usage;
+ unsigned int nglyphs;
+ } cache;
+};
+
+struct _Slave_Msg_Glyph {
+ unsigned int index;
+ unsigned int offset;
+ unsigned int size;
+ unsigned int rows;
+ unsigned int width;
+ unsigned int pitch;
+ unsigned int num_grays;
+ unsigned int pixel_mode;
+};
+
+typedef struct _Slave_Msg_Glyph Slave_Msg_Glyph;
+
+struct _Slave_Msg_Font_Cache {
+ unsigned int nglyphs;
+ Slave_Msg_Glyph *glyphs;
+ Shm_Handle *shm;
+ unsigned int usage;
+};
+
+typedef struct _Slave_Msg_Font_Cache Slave_Msg_Font_Cache;
+
+struct _Slave_Msg_Font_Glyphs_Loaded {
+ unsigned int ncaches;
+ Slave_Msg_Font_Cache **caches;
+};
+
typedef struct _Slave_Msg_Font_Load Slave_Msg_Font_Load;
typedef struct _Slave_Msg_Font_Loaded Slave_Msg_Font_Loaded;
+typedef struct _Slave_Msg_Font_Glyphs_Load Slave_Msg_Font_Glyphs_Load;
+typedef struct _Slave_Msg_Font_Glyphs_Loaded Slave_Msg_Font_Glyphs_Loaded;
typedef void *(*Font_Request_Msg_Create)(void *data, int *size);
-typedef void (*Font_Request_Msg_Free)(void *data);
+typedef void (*Font_Request_Msg_Free)(void *msg, void *data);
typedef void (*Font_Request_Response)(Client *c, void *data, void *resp, unsigned int rid);
typedef void (*Font_Request_Error)(Client *c, void *data, Error_Type error, unsigned int rid);
CSERVE2_REQ_LAST
} Font_Request_Type;
+typedef struct _Glyph_Entry Glyph_Entry;
+
typedef void (*Fd_Watch_Cb)(int fd, Fd_Flags flags, void *data);
typedef void (*Timeout_Cb)(void); /* void* for compat? */
typedef void (*Main_Loop_Child_Dead_Cb)(int pid, int status); /* void* for compat? */
off_t cserve2_shm_offset_get(const Shm_Handle *shm);
size_t cserve2_shm_map_size_get(const Shm_Handle *shm);
size_t cserve2_shm_size_get(const Shm_Handle *shm);
+void *cserve2_shm_map(Shm_Handle *shm);
+void cserve2_shm_unmap(Shm_Handle *shm);
+size_t cserve2_shm_size_normalize(size_t size);
void cserve2_command_run(Client *client, Message_Type type);
void cserve2_cache_image_preload(Client *client, unsigned int client_image_id, unsigned int rid);
void cserve2_cache_image_unload(Client *client, unsigned int client_image_id);
-int cserve2_cache_font_load(Client *client, const char *name, unsigned int namelen, unsigned int rend_flags, unsigned int hint, unsigned int size, unsigned int dpi, unsigned int rid);
+int 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);
+int 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);
+int cserve2_cache_font_glyphs_load(Client *client, const char *source, unsigned int sourcelen, const char *name, unsigned int namelen, unsigned int rend_flags, unsigned int hint, unsigned int size, unsigned int dpi, unsigned int *glyphs, unsigned int nglyphs, unsigned int rid);
+int 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);
+void cserve2_cache_stats_get(Client *client, unsigned int rid);
+void cserve2_cache_font_debug(Client *client, unsigned int rid);
Font_Request *cserve2_request_add(Font_Request_Type type, unsigned int rid, Client *client, Font_Request_Funcs *funcs, void *data);
+void cserve2_request_waiter_add(Font_Request *req, unsigned int rid, Client *client);
void cserve2_request_cancel(Font_Request *req, Client *client, Error_Type err);
void cserve2_request_cancel_all(Font_Request *req, Error_Type err);
void cserve2_requests_init(void);
void cserve2_font_init(void);
void cserve2_font_shutdown(void);
void *cserve2_font_slave_cb(Slave_Thread_Data *sd, Slave_Command *cmd, const void *cmddata, void *data);
+void cserve2_font_source_ft_free(void *fontsource);
+void cserve2_font_ft_free(void *fontinfo);
#endif /* _EVAS_CSERVE2_H */
#include <string.h>
+#ifdef DEBUG_LOAD_TIME
+ #include <sys/time.h>
+#endif
+
#include "evas_cserve2.h"
+#include "evas_cs2_utils.h"
typedef struct _Request_Funcs Request_Funcs;
typedef struct _Request Request;
typedef struct _Font_Source Font_Source;
typedef struct _Font_Entry Font_Entry;
typedef struct _Font_Cache Font_Cache;
-typedef struct _Glyph_Entry Glyph_Entry;
typedef void *(*Request_Msg_Create)(Entry *e, int *size);
typedef void (*Request_Response)(Entry *e, void *resp);
Eina_List *references;
Request *request;
Entry_Type type;
+#ifdef DEBUG_LOAD_TIME
+ struct timeval load_start;
+ struct timeval load_finish;
+#endif
};
struct _File_Data {
};
struct _Font_Source {
+ const char *key;
const char *name;
const char *file;
int references;
Entry base;
Font_Request *request;
unsigned int rend_flags;
- unsigned int hint;
unsigned int size;
unsigned int dpi;
Font_Source *src;
void *ft;
+ Fash_Glyph *glyphs;
+ Eina_Inlist *caches;
+ Font_Cache *last_cache;
+ Eina_Bool unused : 1;
+#ifdef DEBUG_LOAD_TIME
+ struct timeval load_start;
+ struct timeval load_finish;
+ int gl_load_time;
+#endif
};
struct _Font_Cache {
+ EINA_INLIST;
Font_Entry *fe;
- struct {
- const char *name;
- void *data;
- unsigned int size;
- unsigned int usage;
- } shm;
+ Shm_Handle *shm;
+ unsigned int usage;
+ int inuse;
Eina_Inlist *glyphs;
+ unsigned int nglyphs;
};
struct _Glyph_Entry {
EINA_INLIST;
Font_Entry *fe;
- Font_Cache *fi;
+ Font_Cache *fc;
unsigned int index;
unsigned int offset;
+ unsigned int size;
+ unsigned int rows;
+ unsigned int width;
+ unsigned int pitch;
+ unsigned int num_grays;
+ unsigned int pixel_mode;
+};
+
+struct _Glyphs_Request {
+ Client *client;
+ Font_Entry *fe;
+ unsigned int current;
+ unsigned int nglyphs;
+ unsigned int *glyphs;
+ unsigned int nrender;
+ unsigned int *render;
+ unsigned int nanswer;
+ Glyph_Entry **answer;
+ unsigned int hint;
+};
+
+typedef struct _Glyphs_Request Glyphs_Request;
+
+struct _Glyphs_Group {
+ Font_Cache *fc;
+ Eina_List *glyphs;
};
+typedef struct _Glyphs_Group Glyphs_Group;
+
struct _Reference {
Client *client;
Entry *entry;
static Eina_List *image_entries_lru = NULL;
+static Eina_List *font_shm_lru = NULL;
+
static int max_unused_mem_usage = 5 * 1024; /* in kbytes */
static int unused_mem_usage = 0;
+static int max_font_usage = 10 * 4 * 1024; /* in kbytes */
+static int font_mem_usage = 0;
+
+static inline void
+_entry_load_start(Entry *e)
+{
+#ifdef DEBUG_LOAD_TIME
+ gettimeofday(&e->load_start, NULL);
+#endif
+}
+
+static inline void
+_entry_load_finish(Entry *e)
+{
+#ifdef DEBUG_LOAD_TIME
+ gettimeofday(&e->load_finish, NULL);
+#endif
+}
+
+static int
+_timeval_sub(const struct timeval *tv2, const struct timeval *tv1)
+{
+ int t1, t2;
+
+ t1 = tv1->tv_usec + tv1->tv_sec * 1000000;
+ t2 = tv2->tv_usec + tv2->tv_sec * 1000000;
+
+ return t2 - t1;
+}
static void
_image_opened_send(Client *client, File_Data *entry, unsigned int rid)
}
static void
-_font_loaded_send(Client *client, Font_Entry *fe __UNUSED__, unsigned int rid)
+_font_loaded_send(Client *client, unsigned int rid)
{
int size;
Msg_Font_Loaded msg;
DBG("Sending FONT_LOADED reply for RID: %d.", rid);
- memset(&msg, 0, sizeof(msg));
+
+ size = sizeof(msg);
+ memset(&msg, 0, size);
msg.base.rid = rid;
msg.base.type = CSERVE2_FONT_LOADED;
memcpy(buf + sizeof(msg) + pathlen, f->key, keylen);
*bufsize = size;
+
+ _entry_load_start(&f->base);
+
return buf;
}
{
Waiter *w;
+ _entry_load_finish(&e->base);
e->w = resp->w;
e->h = resp->h;
e->frame_count = resp->frame_count;
memcpy(ptr, i->file->loader_data, loaderlen);
*bufsize = size;
+
+ _entry_load_start(&i->base);
+
return buf;
}
{
Waiter *w;
+ _entry_load_start(&e->base);
+
e->alpha_sparse = resp->alpha_sparse;
if (!e->doload)
DBG("Entry %d loaded by speculative preload.", e->base.id);
static int
_font_entry_cmp(const Font_Entry *k1, int k1_length __UNUSED__, const Font_Entry *k2, int k2_length __UNUSED__)
{
- if (k1->src->name == k2->src->name)
+ if (k1->src->key == k2->src->key)
{
if (k1->size == k2->size)
{
if (k1->rend_flags == k2->rend_flags)
- {
- if (k1->hint == k2->hint)
- return k1->dpi - k2->dpi;
- return k1->hint - k2->hint;
- }
+ return k1->dpi - k2->dpi;
return k1->rend_flags - k2->rend_flags;
}
return k1->size - k2->size;
}
- return strcmp(k1->src->name, k2->src->name);
+ return strcmp(k1->src->key, k2->src->key);
}
static int
_font_entry_key_hash(const Font_Entry *key, int key_length __UNUSED__)
{
int hash;
- hash = eina_hash_djb2(key->src->name, eina_stringshare_strlen(key->src->name) + 1);
+ hash = eina_hash_djb2(key->src->key, eina_stringshare_strlen(key->src->key) + 1);
hash ^= eina_hash_int32(&key->rend_flags, sizeof(int));
hash ^= eina_hash_int32(&key->size, sizeof(int));
hash ^= eina_hash_int32(&key->dpi, sizeof(int));
static void
_font_entry_free(Font_Entry *fe)
{
+ fash_gl_free(fe->glyphs);
+ fe->src->references--;
+ if (fe->ft) cserve2_font_ft_free(fe->ft);
+ if (fe->src->references <= 0)
+ eina_hash_del_by_key(font_sources, fe->src->key);
free(fe);
}
static void
+_glyph_free_cb(void *data)
+{
+ Glyph_Entry *gl = data;
+ free(gl);
+}
+
+static void
_font_source_free(Font_Source *fs)
{
- if (fs->name) eina_stringshare_del(fs->name);
- if (fs->file) eina_stringshare_del(fs->file);
+ eina_stringshare_del(fs->key);
+ eina_stringshare_del(fs->name);
+ eina_stringshare_del(fs->file);
+ if (fs->ft) cserve2_font_source_ft_free(fs->ft);
free(fs);
}
+static void
+_font_shm_promote(Font_Cache *fc)
+{
+ Eina_List *l;
+ l = eina_list_data_find_list(font_shm_lru, fc);
+ font_shm_lru = eina_list_demote_list(font_shm_lru, l);
+}
+
+static int
+_font_shm_size_get(Font_Cache *fc)
+{
+ int size;
+
+ size = sizeof(*fc) + cserve2_shm_size_get(fc->shm);
+
+ return size;
+}
+
+static void
+_font_shm_free(Font_Cache *fc)
+{
+ Font_Entry *fe = fc->fe;
+ fe->caches = eina_inlist_remove(fe->caches, EINA_INLIST_GET(fc));
+ if (fc == fe->last_cache)
+ fe->last_cache = NULL;
+
+ while (fc->glyphs)
+ {
+ Glyph_Entry *gl = EINA_INLIST_CONTAINER_GET(fc->glyphs, Glyph_Entry);
+ fc->glyphs = eina_inlist_remove(fc->glyphs, fc->glyphs);
+ fash_gl_del(fe->glyphs, gl->index);
+ }
+
+ cserve2_shm_unref(fc->shm);
+ free(fc);
+
+ if (!fe->caches)
+ eina_hash_del_by_key(font_entries, fe);
+}
+
+static void
+_font_shm_lru_flush(void)
+{
+ Eina_List *l, *l_next;
+
+ l = font_shm_lru;
+ l_next = eina_list_next(l);
+
+ while (l && font_mem_usage > max_font_usage)
+ {
+ int size;
+ Font_Cache *fc;
+
+ fc = eina_list_data_get(l);
+ if (fc->fe->unused && fc->inuse == 0)
+ {
+ font_shm_lru = eina_list_remove_list(font_shm_lru, l);
+ size = _font_shm_size_get(fc);
+ size += fc->nglyphs * sizeof(Glyph_Entry);
+ _font_shm_free(fc);
+ font_mem_usage -= size;
+ }
+
+ l = l_next;
+ l_next = eina_list_next(l);
+ }
+}
+
void
cserve2_cache_init(void)
{
void
cserve2_cache_shutdown(void)
{
+ Font_Cache *fc;
+
+ EINA_LIST_FREE(font_shm_lru, fc)
+ _font_shm_free(fc);
+
eina_hash_free(image_entries);
eina_hash_free(image_ids);
eina_hash_free(file_entries);
else if (entry->type == CSERVE2_FONT_ENTRY)
{
Font_Entry *fe = (Font_Entry *)entry;
- eina_hash_del_by_key(font_entries, fe);
+ fe->unused = EINA_TRUE;
+ if (!fe->caches)
+ eina_hash_del_by_key(font_entries, fe);
}
else
ERR("Wrong type of entry.");
}
static void
-_font_entry_reference_del(Client *client __UNUSED__, Reference *ref)
+_font_entry_reference_del(Client *client, Font_Entry *fe)
{
- Entry *entry = ref->entry;
+ Eina_List *l;
+ Reference *ref;
- _entry_reference_del(entry, ref);
+ EINA_LIST_FOREACH(client->fonts.referencing, l, ref)
+ {
+ if (ref->entry == (Entry *)fe)
+ {
+ ref->count--;
+ if (ref->count > 0)
+ break;
+
+ client->fonts.referencing = eina_list_remove_list(
+ client->fonts.referencing, l);
+ _entry_reference_del(&fe->base, ref);
+ return;
+ }
+ }
}
void
EINA_LIST_FREE(client->fonts.referencing, ref)
{
- _font_entry_reference_del(client, ref);
+ _entry_reference_del(ref->entry, ref);
}
}
}
static Font_Entry *
-_cserve2_font_entry_find(const char *name, unsigned int namelen, unsigned int size, unsigned int rend_flags, unsigned int hint, unsigned int dpi)
+_cserve2_font_entry_find(const char *name, unsigned int namelen, unsigned int size, unsigned int rend_flags, unsigned int dpi)
{
Font_Entry tmp_fe;
Font_Source tmp_fs;
Font_Entry *fe;
- tmp_fs.name = eina_stringshare_add_length(name, namelen);
+ tmp_fs.key = eina_stringshare_add_length(name, namelen);
tmp_fe.src = &tmp_fs;
tmp_fe.size = size;
tmp_fe.rend_flags = rend_flags;
- tmp_fe.hint = hint;
tmp_fe.dpi = dpi;
fe = eina_hash_find(font_entries, &tmp_fe);
- eina_stringshare_del(tmp_fs.name);
+ eina_stringshare_del(tmp_fs.key);
return fe;
}
msg->ftdata1 = fe->src->ft;
msg->ftdata2 = fe->ft;
msg->rend_flags = fe->rend_flags;
- msg->hint = fe->hint;
msg->size = fe->size;
msg->dpi = fe->dpi;
msg->name = fe->src->name;
*size = 0;
+ _entry_load_start(&fe->base);
+
return msg;
}
static void
-_font_load_request_free(void *data)
+_font_load_request_free(void *msg, void *data)
{
- free(data);
+ free(msg);
}
static void
Slave_Msg_Font_Loaded *msg = resp;
Font_Entry *fe = data;
+ DBG("request %d answered.", rid);
+
if (!fe->src->ft)
fe->src->ft = msg->ftdata1;
if (!fe->ft)
- fe->ft = msg->ftdata2;
+ {
+ fe->ft = msg->ftdata2;
+ _entry_load_finish(&fe->base);
+ }
if (fe->request) fe->request = NULL;
+
+ _font_loaded_send(client, rid);
}
static void
_font_load_request_failed(Client *client __UNUSED__, void *data __UNUSED__, Error_Type error __UNUSED__, unsigned int rid __UNUSED__)
{
+ Font_Entry *fe = data;
+ DBG("request %d error answered.", rid);
+
+ cserve2_client_error_send(client, rid, error);
+
+ if (fe->request) fe->request = NULL;
+
+ _font_entry_reference_del(client, fe);
}
static Font_Request_Funcs _font_load_funcs = {
.error = (Font_Request_Error)_font_load_request_failed
};
+static Eina_Bool
+_glyphs_request_check(Glyphs_Request *req)
+{
+ int i;
+ Font_Entry *fe = req->fe;
+
+ req->answer = malloc(sizeof(*req->answer) * req->nglyphs);
+ req->nanswer = 0;
+
+ for (i = req->current; i < req->nglyphs; i++)
+ {
+ Glyph_Entry *ge;
+ ge = fash_gl_find(fe->glyphs, req->glyphs[i]);
+ if (ge)
+ {
+ req->answer[req->nanswer++] = ge;
+ ge->fc->inuse++;
+ }
+ else
+ break;
+ }
+
+ req->current = i;
+
+ // No glyphs need to be rendered.
+ return (req->nanswer == req->nglyphs);
+}
+
+/* organize answer (cache1{gl1, gl2,}, cache2{gl3,gl4,gl5}, cache3{gl6})
+ */
+static Eina_List *
+_glyphs_group_create(Glyphs_Request *req)
+{
+ Eina_List *groups = NULL;
+ int i;
+
+ for (i = 0; i < req->nanswer; i++)
+ {
+ Eina_List *l;
+ Glyphs_Group *iter, *gg = NULL;
+ Font_Cache *fc = req->answer[i]->fc;
+
+ EINA_LIST_FOREACH(groups, l, iter)
+ {
+ if (iter->fc == fc)
+ {
+ gg = iter;
+ break;
+ }
+ }
+
+ if (!gg)
+ {
+ gg = calloc(1, sizeof(*gg));
+ gg->fc = fc;
+ groups = eina_list_append(groups, gg);
+ }
+ gg->glyphs = eina_list_append(gg->glyphs, req->answer[i]);
+ }
+
+ return groups;
+}
+
+static void
+_glyphs_loaded_send(Glyphs_Request *req, unsigned int rid)
+{
+ Msg_Font_Glyphs_Loaded msg;
+ unsigned int size;
+ Eina_List *ll, *answers = NULL;
+ const char *shmname;
+ unsigned int shmsize;
+ unsigned int intsize;
+ char *resp, *buf;
+ Glyphs_Group *iter;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.base.rid = rid;
+ msg.base.type = CSERVE2_FONT_GLYPHS_LOADED;
+
+ answers = _glyphs_group_create(req);
+ msg.ncaches = eina_list_count(answers);
+ size = sizeof(msg);
+
+ // calculate size of message
+ // ncaches * sizeof(cache) + nglyphs1 * sizeof(glyph) +
+ // nglyphs2 * sizeof(glyph)...
+
+ intsize = sizeof(unsigned int);
+
+ EINA_LIST_FOREACH(answers, ll, iter)
+ {
+ shmname = cserve2_shm_name_get(iter->fc->shm);
+ shmsize = eina_stringshare_strlen(shmname) + 1;
+ // shm namelen + name
+ size += intsize + shmsize;
+
+ // nglyphs
+ size += intsize;
+ // nglyphs * (index + offset + size + rows + width + pitch +
+ // num_grays + pixel_mode)
+ size += eina_list_count(iter->glyphs) * 8 * intsize;
+ }
+
+ resp = malloc(size);
+ memcpy(resp, &msg, sizeof(msg));
+ buf = resp + sizeof(msg);
+
+ EINA_LIST_FREE(answers, iter)
+ {
+ Glyph_Entry *gl;
+ unsigned int nglyphs;
+
+ shmname = cserve2_shm_name_get(iter->fc->shm);
+ shmsize = eina_stringshare_strlen(shmname) + 1;
+ memcpy(buf, &shmsize, intsize);
+ buf += intsize;
+ memcpy(buf, shmname, shmsize);
+ buf += shmsize;
+
+ nglyphs = eina_list_count(iter->glyphs);
+ memcpy(buf, &nglyphs, intsize);
+ buf += intsize;
+
+ iter->fc->inuse -= eina_list_count(iter->glyphs);
+
+ EINA_LIST_FREE(iter->glyphs, gl)
+ {
+ memcpy(buf, &gl->index, intsize);
+ buf += intsize;
+ memcpy(buf, &gl->offset, intsize);
+ buf += intsize;
+ memcpy(buf, &gl->size, intsize);
+ buf += intsize;
+ memcpy(buf, &gl->rows, intsize);
+ buf += intsize;
+ memcpy(buf, &gl->width, intsize);
+ buf += intsize;
+ memcpy(buf, &gl->pitch, intsize);
+ buf += intsize;
+ memcpy(buf, &gl->num_grays, intsize);
+ buf += intsize;
+ memcpy(buf, &gl->pixel_mode, intsize);
+ buf += intsize;
+ }
+
+ /* We are removing SHMs from the beginning of the list, so this
+ * gives a higher priority to them */
+ _font_shm_promote(iter->fc);
+ eina_list_free(iter->glyphs);
+ free(iter);
+ }
+
+ cserve2_client_send(req->client, &size, sizeof(size));
+ cserve2_client_send(req->client, resp, size);
+
+ free(resp);
+}
+
+/*
+ * taken from evas_path.c. It would be good to clean up those utils to
+ * have cserve link against them easily without dragging unneeded dependencies
+ */
+#ifdef _WIN32
+# define EVAS_PATH_SEPARATOR "\\"
+#else
+# define EVAS_PATH_SEPARATOR "/"
+#endif
+
+static char *
+_file_path_join(const char *path, const char *end)
+{
+ char *res = NULL;
+ size_t len;
+
+ if ((!path) && (!end)) return NULL;
+ if (!path) return strdup(end);
+ if (!end) return strdup(path);
+ len = strlen(path);
+ len += strlen(end);
+ len += strlen(EVAS_PATH_SEPARATOR);
+ res = malloc(len + 1);
+ if (!res) return NULL;
+ strcpy(res, path);
+ strcat(res, EVAS_PATH_SEPARATOR);
+ strcat(res, end);
+ return res;
+}
+
+static Glyphs_Request *
+_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)
+{
+ char *fullname;
+ Glyphs_Request *req = calloc(1, sizeof(*req));
+
+ if (sourcelen == 0)
+ source = NULL;
+ if (namelen == 0)
+ name = NULL;
+
+ fullname = _file_path_join(source, name);
+ req->fe = _cserve2_font_entry_find(fullname, strlen(fullname) + 1, size,
+ rend_flags, dpi);
+ free(fullname);
+ if (!req->fe)
+ {
+ ERR("No font entry found: source %s, name %s, rendflags: %d, hint: %d,"
+ " size: %d, dpi: %d", source, name, rend_flags, hint, size, dpi);
+ free(req);
+ return NULL;
+ }
+
+ req->client = client;
+
+ req->nglyphs = nglyphs;
+ req->current = 0;
+ req->glyphs = glyphs;
+ req->hint = hint;
+
+ return req;
+}
+
+static void
+_glyphs_request_free(Glyphs_Request *req)
+{
+ free(req->glyphs);
+ free(req->render);
+ free(req->answer);
+ free(req);
+}
+
+/* add glyphs that are already in cache to the "answers" array, and the ones
+ * that are not cached to the "render" array.
+ */
+static void
+_glyphs_load_request_prepare(Glyphs_Request *req)
+{
+ int i, max;
+ req->nrender = 0;
+ Font_Entry *fe = req->fe;
+
+ if (!fe)
+ {
+ ERR("No font entry for this request.");
+ return;
+ }
+
+ // Won't render more than this number of glyphs
+ max = req->nglyphs - req->nanswer;
+ req->render = malloc(sizeof(*req->render) * max);
+
+ for (i = req->current; i < req->nglyphs; i++)
+ {
+ Glyph_Entry *ge;
+ ge = fash_gl_find(fe->glyphs, req->glyphs[i]);
+ if (ge)
+ {
+ req->answer[req->nanswer++] = ge;
+ ge->fc->inuse++;
+ }
+ else
+ req->render[req->nrender++] = req->glyphs[i];
+ }
+}
+
+static void *
+_glyphs_load_request_build(void *data, int *size)
+{
+ Glyphs_Request *req = data;
+ Slave_Msg_Font_Glyphs_Load *msg = NULL;
+ Font_Entry *fe = req->fe;
+ Font_Cache *fc;
+
+ _glyphs_load_request_prepare(req);
+
+ msg = calloc(1, sizeof(*msg));
+
+ msg->font.ftdata1 = fe->src->ft;
+ msg->font.ftdata2 = fe->ft;
+ msg->font.hint = req->hint;
+ msg->font.rend_flags = fe->rend_flags;
+ msg->glyphs.nglyphs = req->nrender;
+ msg->glyphs.glyphs = req->render;
+
+ // Trying to reuse last filled cache.
+ fc = fe->last_cache;
+ if (fc)
+ {
+ msg->cache.shm = fc->shm;
+ msg->cache.usage = fc->usage;
+ msg->cache.nglyphs = fc->nglyphs;
+ }
+
+#ifdef DEBUG_LOAD_TIME
+ gettimeofday(&fe->load_start, NULL);
+#endif
+
+ return msg;
+}
+
+static void
+_glyphs_load_request_free(void *msg, void *data)
+{
+ _glyphs_request_free(data);
+ free(msg);
+}
+
+static void
+_glyphs_load_request_response(Client *client, void *data, void *resp, unsigned int rid)
+{
+ Glyphs_Request *req = data;
+ Slave_Msg_Font_Glyphs_Loaded *msg = resp;
+ Font_Entry *fe = req->fe;
+ Font_Cache *fc = NULL;
+ int i = 0;
+
+ if (fe->last_cache && fe->last_cache->shm == msg->caches[0]->shm)
+ fc = fe->last_cache;
+
+ while (i < msg->ncaches)
+ {
+ int j;
+ Slave_Msg_Font_Cache *c = msg->caches[i++];
+
+ if (!fc)
+ {
+ fc = malloc(sizeof(*fc));
+ fe->caches = eina_inlist_append(fe->caches, EINA_INLIST_GET(fc));
+ fe->last_cache = fc;
+ fc->fe = fe;
+ fc->shm = c->shm;
+ fc->glyphs = NULL;
+ fc->nglyphs = 0;
+ fc->inuse = 0;
+ font_shm_lru = eina_list_append(font_shm_lru, fc);
+ font_mem_usage += _font_shm_size_get(fc);
+ }
+ fc->usage = c->usage;
+ for (j = 0; j < c->nglyphs; j++)
+ {
+ Glyph_Entry *gl = malloc(sizeof(*gl));
+ gl->fe = fe;
+ gl->fc = fc;
+ gl->index = c->glyphs[j].index;
+ gl->offset = c->glyphs[j].offset;
+ gl->size = c->glyphs[j].size;
+ gl->rows = c->glyphs[j].rows;
+ gl->width = c->glyphs[j].width;
+ gl->pitch = c->glyphs[j].pitch;
+ gl->num_grays = c->glyphs[j].num_grays;
+ gl->pixel_mode = c->glyphs[j].pixel_mode;
+ font_mem_usage += sizeof(*gl);
+ fc->glyphs = eina_inlist_append(fc->glyphs, EINA_INLIST_GET(gl));
+ fc->nglyphs++;
+ fash_gl_add(fe->glyphs, gl->index, gl);
+ req->answer[req->nanswer++] = gl;
+ gl->fc->inuse++;
+ }
+
+ free(c); // FIXME: We are freeing this here because we only do a
+ // simple free on the response message. Later we need to
+ // setup a free callback for the slave response.
+ fc = NULL;
+ }
+
+#ifdef DEBUG_LOAD_TIME
+ int load_time;
+ gettimeofday(&fe->load_finish, NULL);
+ load_time = _timeval_sub(&fe->load_finish, &fe->load_start);
+ fe->gl_load_time += load_time;
+#endif
+
+ _glyphs_loaded_send(req, rid);
+ _font_shm_lru_flush();
+}
+
+static void
+_glyphs_load_request_failed(Client *client, void *data, Error_Type error, unsigned int rid)
+{
+}
+
+static Font_Request_Funcs _glyphs_load_funcs = {
+ .msg_create = (Font_Request_Msg_Create)_glyphs_load_request_build,
+ .msg_free = (Font_Request_Msg_Free)_glyphs_load_request_free,
+ .response = (Font_Request_Response)_glyphs_load_request_response,
+ .error = (Font_Request_Error)_glyphs_load_request_failed
+};
+
+static Eina_Bool
+_font_entry_stats_cb(const Eina_Hash *hash __UNUSED__, const void *key __UNUSED__, void *data, void *fdata)
+{
+ Font_Entry *fe = data;
+ Msg_Stats *msg = fdata;
+ Font_Cache *fc;
+ int load_time;
+ int nrefs = eina_list_count(fe->base.references);
+
+ // accounting size
+ EINA_INLIST_FOREACH(fe->caches, fc)
+ {
+ /* This is not real requested usage, but an approximation. We don't
+ * know how many times each glyph would be used by each client, but
+ * assume that a similar set of glyphs from a given font would be used
+ * by each client, thus counting them one time per client referencing
+ * them.
+ */
+ msg->fonts.requested_usage += fc->usage * nrefs;
+ msg->fonts.real_usage += cserve2_shm_size_get(fc->shm);
+ }
+
+#ifdef DEBUG_LOAD_TIME
+ // accounting fonts load time
+ load_time = _timeval_sub(&fe->base.load_finish, &fe->base.load_start);
+ msg->fonts.fonts_load += load_time;
+ if (fe->caches) msg->fonts.fonts_used_load += load_time;
+
+ // accounting glyphs load time
+ msg->fonts.glyphs_load += fe->gl_load_time;
+#endif
+
+ return EINA_TRUE;
+}
+
+static void
+_cserve2_cache_image_stats_get(Msg_Stats *msg)
+{
+}
+
+static void
+_cserve2_cache_font_stats_get(Msg_Stats *msg)
+{
+ eina_hash_foreach(font_entries, _font_entry_stats_cb, msg);
+}
+
+struct _debug_info
+{
+ unsigned int size;
+ unsigned int nfonts;
+};
+
+static Eina_Bool
+_font_entry_debug_size_cb(const Eina_Hash *hash __UNUSED__, const void *key __UNUSED__, void *data, void *fdata)
+{
+ struct _debug_info *di = fdata;
+ unsigned int size = di->size;
+ Font_Entry *fe = data;
+ Font_Cache *fc;
+ unsigned int intsize = sizeof(unsigned int);
+
+ // filelen
+ size += intsize;
+
+ // file
+ if (fe->src->file)
+ size += strlen(fe->src->file) + 1;
+
+ // namelen
+ size += intsize;
+
+ // name
+ if (fe->src->name)
+ size += strlen(fe->src->name) + 1;
+
+ // rend_flags, size, dpi
+ size += 3 * intsize;
+
+ // unused
+ size += intsize;
+
+ // ncaches
+ size += intsize;
+
+ EINA_INLIST_FOREACH(fe->caches, fc)
+ {
+ Glyph_Entry *gl;
+
+ // shmnamelen + shmname
+ size += intsize;
+ size += strlen(cserve2_shm_name_get(fc->shm)) + 1;
+
+ // size + usage
+ size += 2 * intsize;
+
+ // nglyphs
+ size += intsize;
+
+ EINA_INLIST_FOREACH(fc->glyphs, gl)
+ {
+ // index, offset, size
+ size += 3 * intsize;
+
+ // rows, width, pitch
+ size += 3 * intsize;
+
+ // num_grays, pixel_mode
+ size += 2 * intsize;
+ }
+ }
+
+ di->size = size;
+ di->nfonts++;
+
+ return EINA_TRUE;
+}
+
+static Eina_Bool
+_font_entry_debug_cb(const Eina_Hash *hash __UNUSED__, const void *key __UNUSED__, void *data, void *fdata)
+{
+ char *buf = fdata;
+ Font_Entry *fe = data;
+ Font_Cache *fc;
+ unsigned int len;
+ unsigned int unused;
+ unsigned int ncaches;
+ unsigned int intsize = sizeof(unsigned int);
+
+ // filelen + file
+ len = 0;
+ if (fe->src->file)
+ len = strlen(fe->src->file) + 1;
+ memcpy(buf, &len, intsize);
+ buf += intsize;
+ memcpy(buf, fe->src->file, len);
+ buf += len;
+
+ // namelen + name
+ len = 0;
+ if (fe->src->name)
+ len = strlen(fe->src->name) + 1;
+ memcpy(buf, &len, intsize);
+ buf += intsize;
+ memcpy(buf, fe->src->name, len);
+ buf += len;
+
+ // rend_flags, size, dpi
+ memcpy(buf, &fe->rend_flags, intsize);
+ buf += intsize;
+ memcpy(buf, &fe->size, intsize);
+ buf += intsize;
+ memcpy(buf, &fe->dpi, intsize);
+ buf += intsize;
+
+ // unused
+ unused = fe->unused;
+ memcpy(buf, &unused, intsize);
+ buf += intsize;
+
+ // ncaches
+ ncaches = eina_inlist_count(fe->caches);
+ memcpy(buf, &ncaches, intsize);
+ buf += intsize;
+
+ EINA_INLIST_FOREACH(fe->caches, fc)
+ {
+ Glyph_Entry *gl;
+ const char *shmname;
+ unsigned int shmsize;
+
+ // shmnamelen + shmname
+ shmname = cserve2_shm_name_get(fc->shm);
+ len = strlen(shmname) + 1;
+ memcpy(buf, &len, intsize);
+ buf += intsize;
+ memcpy(buf, shmname, len);
+ buf += len;
+
+ // size, usage, nglyphs
+ shmsize = cserve2_shm_size_get(fc->shm);
+ memcpy(buf, &shmsize, intsize);
+ buf += intsize;
+ memcpy(buf, &fc->usage, intsize);
+ buf += intsize;
+ memcpy(buf, &fc->nglyphs, intsize);
+ buf += intsize;
+
+ EINA_INLIST_FOREACH(fc->glyphs, gl)
+ {
+ // index, offset, size
+ memcpy(buf, &gl->index, intsize);
+ buf += intsize;
+ memcpy(buf, &gl->offset, intsize);
+ buf += intsize;
+ memcpy(buf, &gl->size, intsize);
+ buf += intsize;
+
+ // rows, width, pitch
+ memcpy(buf, &gl->rows, intsize);
+ buf += intsize;
+ memcpy(buf, &gl->width, intsize);
+ buf += intsize;
+ memcpy(buf, &gl->pitch, intsize);
+ buf += intsize;
+
+ // num_grays, pixel_mode
+ memcpy(buf, &gl->num_grays, intsize);
+ buf += intsize;
+ memcpy(buf, &gl->pixel_mode, intsize);
+ buf += intsize;
+ }
+ }
+
+ return EINA_TRUE;
+}
+
+static void *
+_cserve2_cache_font_debug(unsigned int rid, unsigned int *size)
+{
+ Msg_Font_Debug msg;
+ char *buf, *pos;
+ struct _debug_info di;
+ di.size = sizeof(msg);
+
+ memset(&msg, 0, sizeof(msg));
+
+ msg.base.type = CSERVE2_FONT_DEBUG;
+ msg.base.rid = rid;
+
+ // First calculate how much size is needed for this message:
+
+ // nfonts
+ di.size += sizeof(unsigned int);
+
+ // size needed for each font entry
+ eina_hash_foreach(font_entries, _font_entry_debug_size_cb, &di);
+
+ // Now really create the message
+ buf = malloc(di.size);
+ pos = buf;
+
+ // msg base
+ memcpy(buf, &msg, sizeof(msg));
+ pos += sizeof(msg);
+
+ // nfonts
+ memcpy(pos, &di.nfonts, sizeof(unsigned int));
+ pos += sizeof(unsigned int);
+
+ eina_hash_foreach(font_entries, _font_entry_debug_cb, pos);
+
+ *size = di.size;
+ return buf;
+}
+
int
cserve2_cache_file_open(Client *client, unsigned int client_file_id, const char *path, const char *key, unsigned int rid)
{
}
int
-cserve2_cache_font_load(Client *client, const char *name, unsigned int namelen, unsigned int rend_flags, unsigned int hint, unsigned int size, unsigned int dpi, unsigned int rid)
+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)
{
Reference *ref;
Font_Source *fs;
- Font_Entry *fe = _cserve2_font_entry_find(name, namelen, size,
- rend_flags, hint, dpi);
+ Font_Entry *fe;
+ char *fullname;
+
+ if (sourcelen == 0)
+ source = NULL;
+ if (namelen == 0)
+ name = NULL;
+ fullname = _file_path_join(source, name);
+ fe = _cserve2_font_entry_find(fullname, strlen(fullname) + 1, size,
+ rend_flags, dpi);
if (fe)
{
- DBG("found font entry %s, rendflags: %d, hint: %d, size: %d, dpi: %d",
- name, rend_flags, hint, size, dpi);
+ DBG("found font entry %s, rendflags: %d, size: %d, dpi: %d",
+ name, rend_flags, size, dpi);
ref = _entry_reference_add((Entry *)fe, client, 0);
client->fonts.referencing = eina_list_append(
client->fonts.referencing, ref);
- _font_loaded_send(client, fe, rid);
+ fe->unused = EINA_FALSE;
+
+ if (fe->request)
+ cserve2_request_waiter_add(fe->request, rid, client);
+ else
+ _font_loaded_send(client, rid);
+ free(fullname);
return 0;
}
fe = calloc(1, sizeof(*fe));
fe->rend_flags = rend_flags;
- fe->hint = hint;
fe->size = size;
fe->dpi = dpi;
fe->base.type = CSERVE2_FONT_ENTRY;
+ fe->glyphs = fash_gl_new(_glyph_free_cb);
+ ref = _entry_reference_add((Entry *)fe, client, 0);
+ client->fonts.referencing = eina_list_append(
+ client->fonts.referencing, ref);
+ fe->unused = EINA_FALSE;
- fs = _cserve2_font_source_find(name);
+ fs = _cserve2_font_source_find(fullname);
if (!fs)
{
fs = calloc(1, sizeof(*fs));
- fs->name = eina_stringshare_add_length(name, namelen);
- fs->file = eina_stringshare_ref(fs->name);
- eina_hash_direct_add(font_sources, fs->name, fs);
+ if (source)
+ {
+ fs->key = eina_stringshare_add(fullname);
+ fs->name = eina_stringshare_add_length(name, namelen);
+ fs->file = eina_stringshare_add_length(source, sourcelen);
+ }
+ else
+ {
+ fs->file = eina_stringshare_add_length(name, namelen);
+ fs->key = eina_stringshare_ref(fs->file);
+ }
+ eina_hash_direct_add(font_sources, fs->key, fs);
}
-
fe->src = fs;
fs->references++;
+ DBG("adding FONT_LOAD '%s' request.", fs->name);
fe->request = cserve2_request_add(CSERVE2_REQ_FONT_LOAD, rid,
client, &_font_load_funcs, fe);
eina_hash_direct_add(font_entries, fe, fe);
+ free(fullname);
+
+ return 0;
+}
+
+int
+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)
+{
+ Font_Entry *fe;
+ char *fullname;
+
+ if (sourcelen == 0)
+ source = NULL;
+ if (namelen == 0)
+ name = NULL;
+
+ fullname = _file_path_join(source, name);
+ fe = _cserve2_font_entry_find(fullname, strlen(fullname) + 1, size,
+ rend_flags, dpi);
+ free(fullname);
+
+ if (!fe)
+ {
+ ERR("Unreferencing font not found: '%s:%s'.", source, name);
+ return -1;
+ }
+
+ _font_entry_reference_del(client, fe);
+
+ return 0;
+}
+
+int
+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)
+{
+ Glyphs_Request *req;
+
+ req = _glyphs_request_create(client, source, sourcelen, name, namelen,
+ hint, rend_flags, size, dpi, glyphs, nglyphs);
+ if (!req)
+ {
+ free(glyphs);
+ return -1;
+ }
+
+ if (_glyphs_request_check(req))
+ {
+ INF("Glyphs already loaded. Sending answer.");
+ _glyphs_loaded_send(req, rid);
+ _glyphs_request_free(req);
+ }
+ else
+ {
+ cserve2_request_add(CSERVE2_REQ_FONT_GLYPHS_LOAD, rid,
+ client, &_glyphs_load_funcs, req);
+ }
+ return 0;
+}
+
+int
+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__)
+{
+ Glyphs_Group *gg;
+ Eina_List *groups;
+ Glyphs_Request *req;
+
+ DBG("Received report of used glyphs from client %d", client->id);
+ req = _glyphs_request_create(client, source, sourcelen, name, namelen,
+ hint, rend_flags, size, dpi, glyphs, nglyphs);
+ if (!req)
+ {
+ free(glyphs);
+ return 0;
+ }
+
+ _glyphs_load_request_prepare(req);
+ groups = _glyphs_group_create(req);
+
+ // Promote SHMs which are still cached and in use
+ // TODO: We can use later the information from request_prepare to preload
+ // glyphs which are not cached anymore, but are in use on the client.
+ EINA_LIST_FREE(groups, gg)
+ {
+ _font_shm_promote(gg->fc);
+ eina_list_free(gg->glyphs);
+ }
+
+ _glyphs_request_free(req);
return 0;
}
req->entry->request = NULL;
free(req);
}
+
+void
+cserve2_cache_stats_get(Client *client, unsigned int rid)
+{
+ Msg_Stats msg;
+ int size;
+
+ memset(&msg, 0, sizeof(msg));
+
+ msg.base.type = CSERVE2_STATS;
+ msg.base.rid = rid;
+
+ _cserve2_cache_image_stats_get(&msg);
+ _cserve2_cache_font_stats_get(&msg);
+
+ size = sizeof(msg);
+ cserve2_client_send(client, &size, sizeof(size));
+ cserve2_client_send(client, &msg, size);
+}
+
+void
+cserve2_cache_font_debug(Client *client, unsigned int rid)
+{
+ void *msg;
+ unsigned int size;
+
+ msg = _cserve2_cache_font_debug(rid, &size);
+
+ cserve2_client_send(client, &size, sizeof(size));
+ cserve2_client_send(client, msg, size);
+
+ free(msg);
+}
#include "evas_cserve2.h"
-static const char *SOCK_PATH = "/tmp/cserve2.socket";
static unsigned int _rid_count = 0;
static struct sockaddr_un socket_local;
# include "config.h"
#endif
+#ifdef BUILD_FONT_LOADER_EET
+#include <Eet.h>
+#endif
+
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_GLYPH_H
#include FT_SIZES_H
+#include FT_TYPES_H
#include FT_MODULE_H
+#include FT_OUTLINE_H
#include "evas_cserve2.h"
+#define CACHESIZE 4 * 1024
+
+/* The tangent of the slant angle we do on runtime. This value was
+ * retrieved from engines/common/evas_font.h */
+#define _EVAS_FONT_SLANT_TAN 0.221694663
+
+#define CHECK_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
+#define MIN_GLYPHS 50
+#define MAX_CACHE_SIZE 1 * 1024 * 1024 // 1MB
+
+#define EVAS_FONT_ROUND_26_6_TO_INT(x) \
+ (((x + 0x20) & -0x40) >> 6)
+
static FT_Library cserve2_ft_lib = 0;
static int initialised = 0;
+typedef struct _Font_Info Font_Info;
+typedef struct _Font_Source_Info Font_Source_Info;
+
struct _Font_Info
{
+ Font_Source_Info *fsi;
FT_Size size;
- int real_size;
+ int real_size; // this is probably useless, not used even on client
+ int fsize;
+ int dpi;
int max_h;
unsigned int runtime_rend;
+ int shmsize;
};
struct _Font_Source_Info
FT_Face face;
int orig_upem;
int current_size;
+ int current_dpi;
+ void *data;
+ int datasize;
};
-typedef struct _Font_Info Font_Info;
-typedef struct _Font_Source_Info Font_Source_Info;
-
static void *
_font_slave_error_send(Error_Type error)
{
return e;
}
+static void
+_font_slave_size_use(Font_Info *fi)
+{
+ Font_Source_Info *fsi = fi->fsi;
+// if ((fsi->current_size != fi->fsize)
+// || (fsi->current_dpi != fi->dpi))
+ {
+ FT_Activate_Size(fi->size);
+ fsi->current_size = fi->fsize;
+ fsi->current_dpi = fi->dpi;
+ }
+}
+
static Font_Source_Info *
-_font_slave_source_load(const char *file)
+_font_slave_source_load(const char *file, const char *name)
{
int error;
- Font_Source_Info *fsi = malloc(sizeof(*fsi));
+ Font_Source_Info *fsi = calloc(1, sizeof(*fsi));
- error = FT_New_Face(cserve2_ft_lib, file, 0, &(fsi->face));
- if (error)
+ if (!name)
{
- free(fsi);
- return NULL;
+ error = FT_New_Face(cserve2_ft_lib, file, 0, &(fsi->face));
+ if (error)
+ {
+ free(fsi);
+ return NULL;
+ }
}
+#ifdef BUILD_FONT_LOADER_EET
+ else
+ {
+ Eet_File *ef;
+ void *fdata;
+ int fsize = 0;
+
+ ef = eet_open(file, EET_FILE_MODE_READ);
+ if (!ef)
+ {
+ free(fsi);
+ return NULL;
+ }
+ fdata = eet_read(ef, name, &fsize);
+ eet_close(ef);
+ if (!fdata)
+ {
+ free(fsi);
+ return NULL;
+ }
+ fsi->data = fdata;
+ fsi->datasize = fsize;
+
+ error = FT_New_Memory_Face(cserve2_ft_lib, fsi->data, fsi->datasize,
+ 0, &(fsi->face));
+ if (error)
+ {
+ free(fsi->data);
+ free(fsi);
+ return NULL;
+ }
+ }
+#endif
error = FT_Select_Charmap(fsi->face, ft_encoding_unicode);
if (error)
{
FT_Done_Face(fsi->face);
+ free(fsi->data);
free(fsi);
return NULL;
}
fsi->orig_upem = fsi->face->units_per_EM;
fsi->current_size = 0;
+ fsi->current_dpi = 0;
return fsi;
}
if (!error)
FT_Activate_Size(fi->size);
+ fi->fsize = msg->size;
+ fi->dpi = msg->dpi;
fi->real_size = msg->size * 64;
+ fi->fsi = fsi;
error = FT_Set_Char_Size(fsi->face, 0, fi->real_size, msg->dpi, msg->dpi);
if (error)
- {
- fi->real_size = msg->size;
- error = FT_Set_Pixel_Sizes(fsi->face, 0, fi->real_size);
- }
+ error = FT_Set_Pixel_Sizes(fsi->face, 0, fi->real_size);
if (error)
{
- int i;
+ int i, maxd = 0x7fffffff;
int chosen_size = 0;
- int chosen_width = 0;
+ int chosen_size2 = 0;
for (i = 0; i < fsi->face->num_fixed_sizes; i++)
{
- int s;
- int d, cd;
+ int s, cd;
- s = fsi->face->available_sizes[i].height;
- cd = chosen_size - msg->size;
+ s = fsi->face->available_sizes[i].size;
+ cd = chosen_size - fi->real_size;
if (cd < 0) cd = -cd;
- d = s - msg->size;
- if (d < 0) d = -d;
- if (d < cd)
+ if (cd < maxd)
{
- chosen_width = fsi->face->available_sizes[i].width;
+ maxd = cd;
chosen_size = s;
+ chosen_size2 = fsi->face->available_sizes[i].y_ppem;
+ if (maxd == 0) break;
}
- if (d == 0) break;
}
fi->real_size = chosen_size;
- error = FT_Set_Pixel_Sizes(fsi->face, chosen_width, fi->real_size);
+ error = FT_Set_Pixel_Sizes(fsi->face, 0, fi->real_size);
if (error)
{
- ERR("Could not choose the font size for font: '%s'.", msg->name);
- FT_Done_Size(fi->size);
- free(fi);
- return NULL;
+ error = FT_Set_Char_Size(fsi->face, 0, fi->real_size, fi->dpi, fi->dpi);
+ if (error)
+ {
+ /* hack around broken fonts */
+ fi->real_size = (chosen_size2 / 64) * 60;
+ error = FT_Set_Char_Size(fsi->face, 0, fi->real_size, fi->dpi, fi->dpi);
+ if (error)
+ {
+ ERR("Could not choose the font size for font: '%s:%s'.",
+ msg->file, msg->name);
+ FT_Done_Size(fi->size);
+ free(fi);
+ return NULL;
+ }
+ }
}
}
Font_Source_Info *fsi;
Font_Info *fi;
+ DBG("slave received FONT_LOAD: '%s'", msg->name);
fsi = msg->ftdata1;
/* Loading Font Source */
if (!fsi)
- fsi = _font_slave_source_load(msg->file);
+ fsi = _font_slave_source_load(msg->file, msg->name);
// FIXME: Return correct error message
if (!fsi)
fi = _font_slave_int_load(msg, fsi);
if (!fi)
{
- FT_Done_Face(fsi->face);
- free(fsi);
+ if (!msg->ftdata1)
+ cserve2_font_source_ft_free(fsi);
return NULL;
}
return response;
}
+static Shm_Handle *
+_font_slave_memory_alloc(Font_Info *fi)
+{
+ Shm_Handle *shm = cserve2_shm_request(fi->shmsize);
+
+ return shm;
+}
+
+/* This function will load the "index" glyph to the glyph slot of the font.
+ * In order to use or render it, one should access it from the glyph slot,
+ * or get the glyph using FT_Get_Glyph().
+ */
+static Eina_Bool
+_font_slave_glyph_load(Font_Info *fi, unsigned int index, unsigned int hint)
+{
+ Font_Source_Info *fsi = fi->fsi;
+ FT_Error error;
+ const FT_Int32 hintflags[3] =
+ { FT_LOAD_NO_HINTING, FT_LOAD_FORCE_AUTOHINT, FT_LOAD_NO_AUTOHINT };
+ static FT_Matrix transform = {0x10000, _EVAS_FONT_SLANT_TAN * 0x10000,
+ 0x00000, 0x10000};
+
+ error = FT_Load_Glyph(fsi->face, index,
+ FT_LOAD_DEFAULT | FT_LOAD_NO_BITMAP |
+ hintflags[hint]);
+ if (error)
+ {
+ ERR("Could not load glyph %d", index);
+ return EINA_FALSE;
+ }
+
+ /* Transform the outline of Glyph according to runtime_rend. */
+ if (fi->runtime_rend & FONT_REND_SLANT)
+ FT_Outline_Transform(&fsi->face->glyph->outline, &transform);
+ /* Embolden the outline of Glyph according to rundtime_rend. */
+ if (fi->runtime_rend & FONT_REND_WEIGHT)
+ FT_Outline_Embolden(&fsi->face->glyph->outline,
+ (fsi->face->size->metrics.x_ppem * 5 * 64) /
+ 100);
+
+ return EINA_TRUE;
+}
+
+/* This function will render the glyph currently in the glyph slot into the
+ * given Font Cache.
+ */
+static Eina_Bool
+_font_slave_glyph_render(Font_Info *fi, Slave_Msg_Font_Cache *c, unsigned int index)
+{
+ Font_Source_Info *fsi = fi->fsi;
+ unsigned int glyphsize;
+ char *cachedata = cserve2_shm_map(c->shm);
+ FT_Glyph glyph;
+ FT_BitmapGlyph bglyph;
+
+ FT_Get_Glyph(fsi->face->glyph, &glyph);
+ FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 1);
+ bglyph = (FT_BitmapGlyph)glyph;
+
+ glyphsize = bglyph->bitmap.pitch * bglyph->bitmap.rows;
+
+ if (c->usage + glyphsize > cserve2_shm_size_get(c->shm))
+ {
+ FT_Done_Glyph(glyph);
+ return EINA_FALSE;
+ }
+
+ memcpy(cachedata + c->usage, bglyph->bitmap.buffer, glyphsize);
+
+ // TODO: Check if we have problems with alignment
+ c->glyphs[c->nglyphs].index = index;
+ c->glyphs[c->nglyphs].offset = c->usage;
+ c->glyphs[c->nglyphs].size = glyphsize;
+ c->glyphs[c->nglyphs].rows = bglyph->bitmap.rows;
+ c->glyphs[c->nglyphs].width = bglyph->bitmap.width;
+ c->glyphs[c->nglyphs].pitch = bglyph->bitmap.pitch;
+ c->glyphs[c->nglyphs].num_grays = bglyph->bitmap.num_grays;
+ c->glyphs[c->nglyphs].pixel_mode = bglyph->bitmap.pixel_mode;
+ c->usage += glyphsize;
+ c->nglyphs++;
+
+ FT_Done_Glyph(glyph);
+
+ return EINA_TRUE;
+}
+
+static void
+_font_slave_int_metrics_get(Font_Info *fi, unsigned int hint, unsigned int c, int *width, int *height, int *depth)
+{
+ unsigned int idx;
+ Font_Source_Info *fsi = fi->fsi;
+ FT_BBox outbox;
+ FT_Glyph glyph;
+
+ idx = FT_Get_Char_Index(fsi->face, c);
+ if (!idx)
+ goto end;
+
+ if (!_font_slave_glyph_load(fi, idx, hint))
+ goto end;
+
+ FT_Get_Glyph(fsi->face->glyph, &glyph);
+ FT_Glyph_Get_CBox(glyph,
+ ((hint == 0) ? FT_GLYPH_BBOX_UNSCALED :
+ FT_GLYPH_BBOX_GRIDFIT),
+ &outbox);
+ if (width) *width = EVAS_FONT_ROUND_26_6_TO_INT(outbox.xMax - outbox.xMin);
+ if (height)
+ *height = EVAS_FONT_ROUND_26_6_TO_INT(outbox.yMax - outbox.xMin);
+ if (depth) *depth = 1; // FIXME: Do we need to check this?
+ FT_Done_Glyph(glyph);
+ return;
+
+end:
+ if (width) *width = 0;
+ if (height) *height = 0;
+ if (depth) *depth = 0;
+}
+
+static unsigned int
+_font_slave_int_shm_prev_calculate(unsigned int size, unsigned int nglyphs)
+{
+ unsigned int average = size / nglyphs;
+ unsigned int newsize;
+
+ newsize = MIN_GLYPHS * average;
+ newsize = cserve2_shm_size_normalize(newsize);
+
+ if (newsize > MAX_CACHE_SIZE)
+ return MAX_CACHE_SIZE;
+
+ return newsize;
+}
+
+static unsigned int
+_font_slave_int_shm_calculate(Font_Info *fi, unsigned int hint)
+{
+ const char *c;
+ int i;
+ int size = 0;
+ int average;
+
+ for (c = CHECK_CHARS, i = 0; *c != '\0'; c++, i++)
+ {
+ int w, h, depth;
+ _font_slave_int_metrics_get(fi, hint, *c, &w, &h, &depth);
+ size += w * h * depth;
+ }
+
+ average = size / i; // average glyph size
+ size = MIN_GLYPHS * average;
+
+ size = cserve2_shm_size_normalize(size);
+
+ if (size > MAX_CACHE_SIZE)
+ return MAX_CACHE_SIZE; // Assumes no glyph will be bigger than this
+
+ return size;
+}
+
+static Slave_Msg_Font_Glyphs_Loaded *
+_font_slave_glyphs_load(const void *cmddata, void *data __UNUSED__)
+{
+ const Slave_Msg_Font_Glyphs_Load *msg = cmddata;
+ Slave_Msg_Font_Glyphs_Loaded *response;
+ Font_Info *fi;
+ unsigned int i;
+ unsigned int total_glyphs;
+ Eina_List *caches = NULL;
+ Slave_Msg_Font_Cache *c = NULL;
+
+ fi = msg->font.ftdata2;
+
+ _font_slave_size_use(fi);
+
+ if (msg->cache.shm)
+ {
+ c = malloc(sizeof(*c) + sizeof(Slave_Msg_Glyph) *
+ (msg->glyphs.nglyphs));
+ c->nglyphs = 0;
+ c->glyphs = (void *)(c + 1);
+ c->shm = msg->cache.shm;
+ c->usage = msg->cache.usage;
+ caches = eina_list_append(caches, c);
+ total_glyphs = msg->cache.nglyphs;
+ }
+
+ if (!fi->shmsize)
+ fi->shmsize = _font_slave_int_shm_calculate(fi, msg->font.hint);
+
+ i = 0;
+
+ while (i < msg->glyphs.nglyphs)
+ {
+ Eina_Bool r = EINA_TRUE;
+
+ if (!c)
+ {
+ Shm_Handle *shm;
+ shm = _font_slave_memory_alloc(fi);
+ c = malloc(sizeof(*c) + sizeof(Slave_Msg_Glyph) *
+ (msg->glyphs.nglyphs - i));
+ c->nglyphs = 0;
+ c->glyphs = (void *)(c + 1);
+ c->shm = shm;
+ c->usage = 0;
+ caches = eina_list_append(caches, c);
+ total_glyphs = 0;
+ }
+
+ if (_font_slave_glyph_load(fi, msg->glyphs.glyphs[i], msg->font.hint))
+ r = _font_slave_glyph_render(fi, c, msg->glyphs.glyphs[i]);
+ if (!r) // SHM is full
+ {
+ fi->shmsize = _font_slave_int_shm_prev_calculate
+ (c->usage, total_glyphs);
+ c = NULL;
+ continue;
+ }
+ i++;
+ total_glyphs++;
+ }
+
+ response = malloc(sizeof(*response) +
+ sizeof(c) * eina_list_count(caches));
+ response->ncaches = eina_list_count(caches);
+ response->caches = (void *)(response + 1);
+
+ i = 0;
+ EINA_LIST_FREE(caches, c)
+ response->caches[i++] = c;
+
+ return response;
+}
+
void *
cserve2_font_slave_cb(Slave_Thread_Data *sd __UNUSED__, Slave_Command *cmd, const void *cmddata, void *data)
{
response = _font_slave_load(cmddata, data);
break;
case FONT_GLYPHS_LOAD:
- // command for FONT_GLYPHS_LOAD
+ response = _font_slave_glyphs_load(cmddata, data);
break;
default:
ERR("Invalid command for font slave: %d", *cmd);
error = FT_Init_FreeType(&cserve2_ft_lib);
if (error) return;
+
+#ifdef BUILD_FONT_LOADER_EET
+ eet_init();
+#endif
}
void
FT_Done_FreeType(cserve2_ft_lib);
cserve2_ft_lib = 0;
+
+#ifdef BUILD_FONT_LOADER_EET
+ eet_shutdown();
+#endif
+}
+
+void
+cserve2_font_source_ft_free(void *fontsource)
+{
+ Font_Source_Info *fsi = fontsource;
+
+ FT_Done_Face(fsi->face);
+ free(fsi->data);
+ free(fsi);
+}
+
+void
+cserve2_font_ft_free(void *fontinfo)
+{
+ Font_Info *fi = fontinfo;
+
+ FT_Done_Size(fi->size);
+ free(fi);
}
static Eina_Inlist *slaves_idle = NULL;
static Eina_Inlist *slaves_working = NULL;
-struct _Glyph_Request {
- unsigned int index;
- unsigned int offset;
-};
-
-typedef struct _Glyph_Request Glyph_Request;
-
void
cserve2_client_error_send(Client *client, unsigned int rid, int error_code)
{
_cserve2_client_font_load(Client *client)
{
Msg_Font_Load *msg = (Msg_Font_Load *)client->msg.buf;
- char name[PATH_MAX];
+ char name[PATH_MAX], source[PATH_MAX], *buf;
- memcpy(name, msg + 1, msg->pathlen);
+ buf = ((char *)msg) + sizeof(*msg);
+ memcpy(source, buf, msg->sourcelen);
+ buf += msg->sourcelen;
+ memcpy(name, buf, msg->pathlen);
INF("Received %s command: RID=%d",
(msg->base.type == CSERVE2_FONT_LOAD) ? "FONT_LOAD" : "FONT_UNLOAD",
msg->base.rid);
- INF("Font: %s, rend_flags: %d, hint: %d, size: %d, dpi: %d",
- name, msg->rend_flags, msg->hint, msg->size, msg->dpi);
+ INF("Font: %s, rend_flags: %d, size: %d, dpi: %d",
+ name, msg->rend_flags, msg->size, msg->dpi);
- cserve2_cache_font_load(client, name, msg->pathlen, msg->rend_flags,
- msg->hint, msg->size, msg->dpi, msg->base.rid);
+ if (msg->base.type == CSERVE2_FONT_LOAD)
+ cserve2_cache_font_load(client, source, msg->sourcelen, name,
+ msg->pathlen, msg->rend_flags, msg->size,
+ msg->dpi, msg->base.rid);
+ else
+ cserve2_cache_font_unload(client, source, msg->sourcelen, name,
+ msg->pathlen, msg->rend_flags, msg->size,
+ msg->dpi, msg->base.rid);
}
static void
_cserve2_client_font_glyphs_request(Client *client)
{
Msg_Font_Glyphs_Request *msg = (Msg_Font_Glyphs_Request *)client->msg.buf;
- char fontpath[PATH_MAX];
- Glyph_Request *glyphs;
- unsigned int i;
- const char *bufpos = client->msg.buf;
+ char source[PATH_MAX], fontpath[PATH_MAX], *buf;
+ unsigned int *glyphs;
- memcpy(fontpath, msg + 1, msg->pathlen);
+ buf = ((char *)msg) + sizeof(*msg);
+ memcpy(source, buf, msg->sourcelen);
+ buf += msg->sourcelen;
+ memcpy(fontpath, buf, msg->pathlen);
+ buf += msg->pathlen;
- bufpos = bufpos + sizeof(msg) + msg->pathlen;
glyphs = malloc(sizeof(*glyphs) * msg->nglyphs);
-
- for (i = 0; i < msg->nglyphs; i++)
- {
- memcpy(&glyphs[i], bufpos, sizeof(*glyphs));
- bufpos += sizeof(*glyphs);
- }
+ memcpy(glyphs, buf, sizeof(*glyphs) * msg->nglyphs);
if (msg->base.type == CSERVE2_FONT_GLYPHS_LOAD)
{
INF("Received CSERVE2_FONT_GLYPHS_LOAD command: RID=%d",
msg->base.rid);
+ cserve2_cache_font_glyphs_load(client, source, msg->sourcelen,
+ fontpath, msg->pathlen,
+ msg->hint, msg->rend_flags, msg->size,
+ msg->dpi, glyphs, msg->nglyphs,
+ msg->base.rid);
}
else
{
INF("Received CSERVE2_FONT_GLYPHS_USED command: RID=%d",
msg->base.rid);
+ /*
+ cserve2_cache_font_glyphs_used(client, source, msg->sourcelen,
+ fontpath, msg->pathlen,
+ msg->hint, msg->rend_flags, msg->size,
+ msg->dpi, glyphs, msg->nglyphs,
+ msg->base.rid);
+ */
}
}
+static void
+_cserve2_client_stats_request(Client *client)
+{
+ Msg_Base *msg = (Msg_Base *)client->msg.buf;
+ cserve2_cache_stats_get(client, msg->rid);
+}
+
+static void
+_cserve2_client_font_debug_request(Client *client)
+{
+ Msg_Base *msg = (Msg_Base *)client->msg.buf;
+ cserve2_cache_font_debug(client, msg->rid);
+}
+
void
cserve2_command_run(Client *client, Message_Type type)
{
case CSERVE2_FONT_GLYPHS_USED:
_cserve2_client_font_glyphs_request(client);
break;
+ case CSERVE2_STATS:
+ _cserve2_client_stats_request(client);
+ break;
+ case CSERVE2_FONT_DEBUG:
+ _cserve2_client_font_debug_request(client);
+ break;
default:
WRN("Unhandled message");
}
struct _Request_Queue
{
Eina_Inlist *waiting;
- Eina_Inlist *processing;
+ Eina_Inlist *processing; // TODO: Check if is there any use for this list.
};
typedef struct _Request_Queue Request_Queue;
static Request_Queue *requests = NULL;
// static Eina_List *processing = NULL;
+static void _cserve2_requests_process(void);
+
static void
_request_waiter_add(Font_Request *req, Client *client, unsigned int rid)
{
req = NULL;
+ /* Check if this request was already being processed. */
EINA_INLIST_FOREACH(requests[type].processing, r)
{
- if (r->data == data)
+ if (r->data != data)
continue;
req = r;
break;
}
+ /* Check if this request was already waiting to be processed. */
if (!req)
{
EINA_INLIST_FOREACH(requests[type].waiting, r)
}
}
+ /* create new request */
if (!req)
{
DBG("Add request for rid: %d", rid);
req = malloc(sizeof(*req));
+ req->type = type;
req->data = data;
req->waiters = NULL;
req->processing = EINA_FALSE;
+ req->funcs = funcs;
requests[type].waiting = eina_inlist_append(requests[type].waiting,
EINA_INLIST_GET(req));
}
_request_waiter_add(req, client, rid);
+ _cserve2_requests_process();
+
return req;
}
void
+cserve2_request_waiter_add(Font_Request *req, unsigned int rid, Client *client)
+{
+ _request_waiter_add(req, client, rid);
+}
+
+void
cserve2_request_cancel(Font_Request *req, Client *client, Error_Type err)
{
Eina_List *l, *l_next;
// TODO: When we have speculative preload, there may be no waiters,
// so we need a flag or something else to make things still load.
- if (!req->waiters)
+ if ((!req->waiters) && (!req->processing))
{
Eina_Inlist **reqlist = &requests[req->type].waiting;
*reqlist = eina_inlist_remove(*reqlist, EINA_INLIST_GET(req));
// TODO: If the request is being processed, it can't be deleted. Must
// be marked as delete_me instead.
- req->funcs->msg_free(req->msg);
+ req->funcs->msg_free(req->msg, req->data);
free(req);
}
free(w);
}
+ if (req->processing)
+ return;
+
requests[req->type].waiting = eina_inlist_remove(
requests[req->type].waiting, EINA_INLIST_GET(req));
- req->funcs->msg_free(req->msg);
+ req->funcs->msg_free(req->msg, req->data);
free(req);
}
free(w);
}
- req->funcs->msg_free(req->msg);
+ req->funcs->msg_free(req->msg, req->data);
requests[req->type].processing = eina_inlist_remove(
requests[req->type].processing, EINA_INLIST_GET(req));
free(req);
free(w);
}
- req->funcs->msg_free(req->msg);
+ req->funcs->msg_free(req->msg, req->data);
// FIXME: We shouldn't free this message directly, it must be freed by a
// callback.
free(msg);
idle = &_workers[sw->type].idle;
*working = eina_list_remove(*working, sw);
*idle = eina_list_append(*idle, sw);
+
+ _cserve2_requests_process();
}
static void
int size;
char *slave_msg = req->funcs->msg_create(req->data, &size);
+
+ DBG("dispatching message of type %d to slave.", req->type);
if (!slave_msg)
{
ERR("Could not create slave message for request type %d.", req->type);
return EINA_TRUE;
}
-void
-cserve2_requests_process(void)
+static void
+_cserve2_requests_process(void)
{
int rtype, j;
for (j = 0; _request_match[j].rtype != CSERVE2_REQ_LAST; j++)
{
- if (_request_match[j].rtype == (unsigned int)j)
+ if (_request_match[j].rtype == rtype)
{
type = _request_match[j].stype;
ctype = _request_match[j].ctype;
idle = &_workers[type].idle;
working = &_workers[type].working;
- while (requests[j].waiting &&
+ while (requests[rtype].waiting &&
(eina_list_count(*working) < max_workers))
{
Slave_Worker *sw;
off_t image_offset;
size_t map_size;
size_t image_size;
+ int refcount;
+ void *data;
};
static int id = 0;
+size_t
+cserve2_shm_size_normalize(size_t size)
+{
+ long pagesize;
+ size_t normalized;
+
+ pagesize = sysconf(_SC_PAGESIZE);
+ if (pagesize < 1)
+ {
+ ERR("sysconf() reported weird value for PAGESIZE, assuming 4096.");
+ pagesize = 4096;
+ }
+
+ normalized = ((size + pagesize - 1) / pagesize) * pagesize;
+
+ return normalized;
+}
+
Shm_Handle *
cserve2_shm_request(size_t size)
{
Shm_Handle *shm;
char shmname[NAME_MAX];
size_t map_size;
- long pagesize;
int fd;
map = calloc(1, sizeof(Shm_Mapping));
}
} while (fd == -1);
- pagesize = sysconf(_SC_PAGESIZE);
- if (pagesize < 1)
- {
- ERR("sysconf() reported weird value for PAGESIZE, assuming 4096.");
- pagesize = 4096;
- }
-
- map_size = ((size + pagesize - 1) / pagesize) * pagesize;
+ map_size = cserve2_shm_size_normalize(size);
if (ftruncate(fd, map_size) == -1)
{
Shm_Mapping *map = shm->mapping;
map->segments = eina_inlist_remove(map->segments, EINA_INLIST_GET(shm));
+
+ if (shm->data)
+ munmap(shm->data, shm->image_size);
free(shm);
if (map->segments)
{
return shm->image_size;
}
+
+void *
+cserve2_shm_map(Shm_Handle *shm)
+{
+ int fd;
+ const char *name;
+
+ if (shm->refcount++)
+ return shm->data;
+
+ name = cserve2_shm_name_get(shm);
+
+ fd = shm_open(name, O_RDWR, S_IWUSR);
+ if (fd == -1)
+ return MAP_FAILED;
+
+ shm->data = mmap(NULL, shm->image_size, PROT_WRITE, MAP_SHARED,
+ fd, shm->image_offset);
+
+ close(fd);
+
+ return shm->data;
+}
+
+void
+cserve2_shm_unmap(Shm_Handle *shm)
+{
+ if (--shm->refcount)
+ return;
+
+ munmap(shm->data, shm->image_size);
+ shm->data = NULL;
+}
return response_send(fd, ERROR, &err, sizeof(Error_Type));
}
-void *
-cserve2_shm_map(const char *name, size_t length, off_t offset)
+static void *
+_cserve2_shm_map(const char *name, size_t length, off_t offset)
{
void *map;
int fd;
return map;
}
-void
-cserve2_shm_unmap(void *map, size_t length)
+static void
+_cserve2_shm_unmap(void *map, size_t length)
{
munmap(map, length);
}
Evas_Loader_Module_Api *api;
int err;
Error_Type ret = CSERVE2_NONE;
- char *map = cserve2_shm_map(shmfile, params->shm.mmap_size,
- params->shm.mmap_offset);
+ char *map = _cserve2_shm_map(shmfile, params->shm.mmap_size,
+ params->shm.mmap_offset);
if (map == MAP_FAILED)
return CSERVE2_RESOURCE_ALLOCATION_FAILED;
result->alpha_sparse = ilp.alpha_sparse;
done:
- cserve2_shm_unmap(map, params->shm.mmap_size);
+ _cserve2_shm_unmap(map, params->shm.mmap_size);
return ret;
}
--- /dev/null
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <Eina.h>
+
+#include "evas_cs2.h"
+
+static int socketfd = -1;
+static unsigned int _rid_count = 1;
+static int _evas_cserve2_usage_log_dom = -1;
+
+static struct sockaddr_un socksize;
+#ifndef UNIX_PATH_MAX
+#define UNIX_PATH_MAX sizeof(socksize.sun_path)
+#endif
+
+#ifdef ERR
+#undef ERR
+#endif
+#define ERR(...) EINA_LOG_DOM_ERR(_evas_cserve2_usage_log_dom, __VA_ARGS__)
+#ifdef DBG
+#undef DBG
+#endif
+#define DBG(...) EINA_LOG_DOM_DBG(_evas_cserve2_usage_log_dom, __VA_ARGS__)
+#ifdef WRN
+#undef WRN
+#endif
+#define WRN(...) EINA_LOG_DOM_WARN(_evas_cserve2_usage_log_dom, __VA_ARGS__)
+#ifdef INF
+#undef INF
+#endif
+#define INF(...) EINA_LOG_DOM_INFO(_evas_cserve2_usage_log_dom, __VA_ARGS__)
+
+static void
+_socket_path_set(char *path)
+{
+ char *env;
+ char buf[UNIX_PATH_MAX];
+
+ env = getenv("EVAS_CSERVE2_SOCKET");
+ if (env && env[0])
+ {
+ strncpy(path, env, UNIX_PATH_MAX - 1);
+ return;
+ }
+
+ snprintf(buf, sizeof(buf), "/tmp/.evas-cserve2-%x.socket", (int)getuid());
+ /* FIXME: check we can actually create this socket */
+ strcpy(path, buf);
+}
+
+static Eina_Bool
+_server_connect(void)
+{
+ int s, len;
+ struct sockaddr_un remote;
+
+ if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
+ {
+ ERR("socket");
+ return EINA_FALSE;
+ }
+
+ remote.sun_family = AF_UNIX;
+ _socket_path_set(remote.sun_path);
+ len = strlen(remote.sun_path) + sizeof(remote.sun_family);
+ if (connect(s, (struct sockaddr *)&remote, len) == -1)
+ {
+ ERR("connect");
+ return EINA_FALSE;
+ }
+
+ fcntl(s, F_SETFL, O_NONBLOCK);
+
+ socketfd = s;
+
+ DBG("connected to cserve2 server.");
+ return EINA_TRUE;
+}
+
+static void
+_server_disconnect(void)
+{
+ close(socketfd);
+ socketfd = -1;
+}
+
+static Eina_Bool
+_server_send(const void *data, int size)
+{
+ int sent = 0;
+ ssize_t ret;
+ const char *msg = data;
+
+ while (sent < size)
+ {
+ ret = send(socketfd, msg + sent, size - sent, MSG_NOSIGNAL);
+ if (ret < 0)
+ {
+ if ((errno == EAGAIN) || (errno == EINTR))
+ continue;
+ return EINA_FALSE;
+ }
+ sent += ret;
+ }
+
+ return EINA_TRUE;
+}
+
+static int sr_size = 0;
+static int sr_got = 0;
+static char *sr_buf = NULL;
+
+static void *
+_server_read(int *size)
+{
+ int n;
+ void *ret;
+
+ if (sr_size)
+ goto get_data;
+
+ n = recv(socketfd, &sr_size, sizeof(sr_size), 0);
+ if (n < 0)
+ return NULL;
+
+ sr_buf = malloc(sr_size);
+
+get_data:
+ n = recv(socketfd, sr_buf + sr_got, sr_size - sr_got, 0);
+ if (n < 0)
+ return NULL;
+
+ sr_got += n;
+ if (sr_got < sr_size)
+ return NULL;
+
+ *size = sr_size;
+ sr_size = 0;
+ sr_got = 0;
+ ret = sr_buf;
+ sr_buf = NULL;
+
+ return ret;
+}
+
+static void
+_usage_msg_send(void)
+{
+ Msg_Base msg;
+ int size;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.type = CSERVE2_STATS;
+ msg.rid = _rid_count++;
+
+ size = sizeof(msg);
+
+ if (!_server_send(&size, sizeof(size)))
+ {
+ ERR("Could not send usage msg size to server.");
+ return;
+ }
+ if (!_server_send(&msg, size))
+ {
+ ERR("Could not send usage msg body to server.");
+ return;
+ }
+}
+
+static void
+_usage_msg_read(void)
+{
+ Msg_Stats *msg = NULL;
+ int size;
+
+ printf("Requesting server statistics.\n\n");
+ while (!msg)
+ msg = _server_read(&size);
+
+ if (msg->base.type != CSERVE2_STATS)
+ {
+ ERR("Invalid message received from server."
+ "Something went badly wrong.");
+ return;
+ }
+
+ printf("Printing server usage.\n");
+ printf("======================\n\n");
+ printf("Font Usage Statistics:\n");
+ printf("Requested usage: %d bytes\n", msg->fonts.requested_usage);
+ printf("Real usage: %d bytes\n", msg->fonts.real_usage);
+ printf("Fonts load time: %dus\n", msg->fonts.fonts_load);
+ printf("Fonts used load time: %dus\n", msg->fonts.fonts_used_load);
+ printf("Glyphs load time: %dus\n", msg->fonts.glyphs_load);
+
+ printf("\n");
+}
+
+int
+main(void)
+{
+ eina_init();
+
+ _evas_cserve2_usage_log_dom = eina_log_domain_register
+ ("evas_cserve2_usage", EINA_COLOR_BLUE);
+
+ if (!_server_connect())
+ {
+ ERR("Could not connect to server.");
+ return -1;
+ }
+
+ _usage_msg_send();
+
+ _usage_msg_read();
+
+ _server_disconnect();
+
+ eina_shutdown();
+}
CSERVE2_FONT_GLYPHS_LOAD,
CSERVE2_FONT_GLYPHS_LOADED,
CSERVE2_FONT_GLYPHS_USED,
+ CSERVE2_STATS,
+ CSERVE2_FONT_DEBUG,
CSERVE2_ERROR
} Message_Type;
*/
struct _Msg_Font_Load {
Msg_Base base;
+ unsigned int sourcelen; // font id
unsigned int pathlen; // font id
unsigned int rend_flags; // font id
- unsigned int hint; // font id
unsigned int size; // font id
unsigned int dpi; // font id
};
/**
* @struct _Msg_Font_Glyphs_Request
*
- * Message from client to request load of glyphs, of inform usage of them.
+ * Message from client to request load of glyphs, or inform usage of them.
*
* The path strings follow the struct inside the message, as well as
* the list of glyphs to be loaded.
*/
struct _Msg_Font_Glyphs_Request {
Msg_Base base;
+ unsigned int sourcelen; // font id
unsigned int pathlen; // font id
unsigned int rend_flags; // font id
- unsigned int hint; // font id
unsigned int size; // font id
unsigned int dpi; // font id
+ unsigned int hint;
unsigned int nglyphs;
};
* - struct {
* unsigned int index;
* unsigned int offset;
+ * unsigned int size;
+ * unsigned int rows;
+ * unsigned int width;
+ * unsigned int pitch;
+ * unsigned int num_grays;
+ * unsigned int pixel_mode;
* } glarray[];
*/
struct _Msg_Font_Glyphs_Loaded {
unsigned int ncaches;
};
+struct _Msg_Stats {
+ Msg_Base base;
+ struct {
+ unsigned int requested_usage;
+ unsigned int real_usage;
+ int fonts_load; // total time spent loading fonts
+ int fonts_used_load; // total time spent loading fonts that are
+ // really used
+ int glyphs_load; // total time spent loading glyphs
+ } fonts;
+};
+
+/*
+ * @struct _Msg_Font_Debug
+ *
+ * Message from server containing all font cache info.
+ *
+ * Content of the message follows:
+ *
+ * * number of font entries;
+ * * each font entry:
+ * - unsigned int filelen
+ * - const char file
+ * - unsigned int namelen
+ * - const char name
+ * - unsigned int rend_flags;
+ * - unsigned int size;
+ * - unsigned int dpi;
+ * - unsigned int unused;
+ * - ncaches:
+ * - each cache:
+ * * usigned int shmnamelen;
+ * * const char shmname;
+ * * unsigned int size;
+ * * unsigned int usage;
+ * * unsigned int nglyphs;
+ * * each glyph:
+ * - unsigned int index;
+ * - unsigned int offset;
+ * - unsigned int size;
+ * - unsigned int rows;
+ * - unsigned int width;
+ * - unsigned int pitch;
+ * - unsigned int num_grays;
+ * - unsigned int pixel_mode;
+ */
+struct _Msg_Font_Debug {
+ Msg_Base base;
+};
+
struct _Msg_Error {
Msg_Base base;
int error;
typedef struct _Msg_Font_Loaded Msg_Font_Loaded;
typedef struct _Msg_Font_Glyphs_Request Msg_Font_Glyphs_Request;
typedef struct _Msg_Font_Glyphs_Loaded Msg_Font_Glyphs_Loaded;
+typedef struct _Msg_Stats Msg_Stats;
+typedef struct _Msg_Font_Debug Msg_Font_Debug;
typedef struct _Msg_Error Msg_Error;
#endif