evas/cserve2: Add font and glyphs loading implementation
authorantognolli <antognolli@7cbeb6ba-43b4-40fd-8cce-4c39aea84d33>
Fri, 22 Jun 2012 20:31:17 +0000 (20:31 +0000)
committerantognolli <antognolli@7cbeb6ba-43b4-40fd-8cce-4c39aea84d33>
Fri, 22 Jun 2012 20:31:17 +0000 (20:31 +0000)
 to cserve2.
Big commit that implements the entire functionality. Besides loading
fonts and glyphs, and sharing the glyph bitmaps with clients, it also
adds:
- new request system, with a much better abstraction;
- new working slaves management;
- slaves can be threads or process now;
- started a debugging and statistics implementation on server.

The image caching part still uses the old request and slaves system, but
should be changed to use the new one soon and get more easily
maintainable.

git-svn-id: svn+ssh://svn.enlightenment.org/var/svn/e/trunk/evas@72699 7cbeb6ba-43b4-40fd-8cce-4c39aea84d33

12 files changed:
src/bin/Makefile.am
src/bin/dummy_slave.c
src/bin/evas_cserve2.h
src/bin/evas_cserve2_cache.c
src/bin/evas_cserve2_client.c
src/bin/evas_cserve2_fonts.c
src/bin/evas_cserve2_main.c
src/bin/evas_cserve2_requests.c
src/bin/evas_cserve2_shm.c
src/bin/evas_cserve2_slave.c
src/bin/evas_cserve2_usage.c [new file with mode: 0644]
src/lib/cserve2/evas_cs2.h

index 815defc..0a2d6ef 100644 (file)
@@ -45,7 +45,7 @@ if EVAS_CSERVE2
 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. \
@@ -56,7 +56,8 @@ AM_CPPFLAGS = \
 -DPACKAGE_LIB_DIR=\"$(libdir)\" \
 -DPACKAGE_LIBEXEC_DIR=\"$(libexecdir)\" \
 @FREETYPE_CFLAGS@ \
-@EINA_CFLAGS@
+@EINA_CFLAGS@ \
+@EET_CFLAGS@
 
 evas_cserve2_SOURCES = \
 evas_cserve2.h \
@@ -70,14 +71,24 @@ evas_cserve2_requests.c \
 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
index a150d67..398d86e 100644 (file)
@@ -89,8 +89,8 @@ error_send(int fd, Error_Type err)
    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;
@@ -106,11 +106,13 @@ cserve2_shm_map(const char *name, size_t length, off_t offset)
    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)
@@ -128,8 +130,8 @@ image_open(const char *file __UNUSED__, const char *key __UNUSED__, Slave_Msg_Im
 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;
 
index a1008ac..98c6547 100644 (file)
@@ -21,6 +21,8 @@
 #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;
@@ -119,11 +121,12 @@ struct _Slave_Msg_Font_Load {
    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 {
@@ -131,11 +134,58 @@ 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);
 
@@ -161,6 +211,8 @@ typedef enum {
    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? */
@@ -211,6 +263,9 @@ off_t cserve2_shm_map_offset_get(const Shm_Handle *shm);
 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);
 
@@ -225,10 +280,16 @@ void cserve2_cache_image_load(Client *client, unsigned int client_image_id, unsi
 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);
@@ -240,5 +301,7 @@ void cserve2_cache_requests_response(Slave_Command type, void *msg, void *data);
 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 */
index 62bc9b5..23a6902 100644 (file)
@@ -4,7 +4,12 @@
 
 #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;
@@ -19,7 +24,6 @@ typedef struct _File_Watch File_Watch;
 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);
@@ -49,6 +53,10 @@ struct _Entry {
    Eina_List *references;
    Request *request;
    Entry_Type type;
+#ifdef DEBUG_LOAD_TIME
+   struct timeval load_start;
+   struct timeval load_finish;
+#endif
 };
 
 struct _File_Data {
@@ -85,6 +93,7 @@ struct _Image_Data {
 };
 
 struct _Font_Source {
+   const char *key;
    const char *name;
    const char *file;
    int references;
@@ -95,32 +104,67 @@ struct _Font_Entry {
    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;
@@ -158,8 +202,39 @@ static Eina_Hash *file_watch = NULL;
 
 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)
@@ -237,13 +312,15 @@ _image_preloaded_send(Client *client, 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;
 
@@ -272,6 +349,9 @@ _open_request_build(File_Data *f, int *bufsize)
    memcpy(buf + sizeof(msg) + pathlen, f->key, keylen);
 
    *bufsize = size;
+
+   _entry_load_start(&f->base);
+
    return buf;
 }
 
@@ -310,6 +390,7 @@ _open_request_response(File_Data *e, Slave_Msg_Image_Opened *resp)
 {
    Waiter *w;
 
+   _entry_load_finish(&e->base);
    e->w = resp->w;
    e->h = resp->h;
    e->frame_count = resp->frame_count;
@@ -397,6 +478,9 @@ _load_request_build(Image_Data *i, int *bufsize)
    memcpy(ptr, i->file->loader_data, loaderlen);
 
    *bufsize = size;
+
+   _entry_load_start(&i->base);
+
    return buf;
 }
 
@@ -405,6 +489,8 @@ _load_request_response(Image_Data *e, Slave_Msg_Image_Loaded *resp)
 {
    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);
@@ -581,28 +667,24 @@ _file_watch_free(void *data)
 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));
@@ -613,18 +695,100 @@ _font_entry_key_hash(const Font_Entry *key, int key_length __UNUSED__)
 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)
 {
@@ -645,6 +809,11 @@ 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);
@@ -915,7 +1084,9 @@ _entry_reference_del(Entry *entry, Reference *ref)
    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.");
@@ -955,11 +1126,25 @@ _entry_free_cb(void *data)
 }
 
 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
@@ -982,7 +1167,7 @@ cserve2_cache_client_del(Client *client)
 
    EINA_LIST_FREE(client->fonts.referencing, ref)
      {
-        _font_entry_reference_del(client, ref);
+        _entry_reference_del(ref->entry, ref);
      }
 }
 
@@ -1082,21 +1267,20 @@ _cserve2_font_source_find(const char *name)
 }
 
 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;
 }
@@ -1110,7 +1294,6 @@ _font_load_request_build(void *data, int *size)
    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;
@@ -1118,13 +1301,15 @@ _font_load_request_build(void *data, int *size)
 
    *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
@@ -1133,18 +1318,33 @@ _font_load_request_response(Client *client __UNUSED__, void *data, void *resp, u
    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 = {
@@ -1154,6 +1354,648 @@ 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)
 {
@@ -1445,50 +2287,165 @@ 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)
+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;
 }
 
@@ -1522,3 +2479,36 @@ cserve2_cache_requests_response(Slave_Command type, void *msg, void *data)
      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);
+}
index d8add9c..b2a33ac 100644 (file)
@@ -11,7 +11,6 @@
 
 #include "evas_cserve2.h"
 
-static const char *SOCK_PATH = "/tmp/cserve2.socket";
 static unsigned int _rid_count = 0;
 
 static struct sockaddr_un socket_local;
index 36dc140..cb5355b 100644 (file)
@@ -2,23 +2,49 @@
 # 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
@@ -26,11 +52,11 @@ 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)
 {
@@ -40,29 +66,80 @@ _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;
 }
@@ -79,46 +156,55 @@ _font_slave_int_load(const Slave_Msg_Font_Load *msg, Font_Source_Info *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;
+                    }
+               }
           }
      }
 
@@ -159,11 +245,12 @@ _font_slave_load(const void *cmddata, void *data __UNUSED__)
    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)
@@ -175,8 +262,8 @@ _font_slave_load(const void *cmddata, void *data __UNUSED__)
    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;
      }
 
@@ -187,6 +274,241 @@ _font_slave_load(const void *cmddata, void *data __UNUSED__)
    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)
 {
@@ -198,7 +520,7 @@ cserve2_font_slave_cb(Slave_Thread_Data *sd __UNUSED__, Slave_Command *cmd, cons
          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);
@@ -224,6 +546,10 @@ cserve2_font_init(void)
 
    error = FT_Init_FreeType(&cserve2_ft_lib);
    if (error) return;
+
+#ifdef BUILD_FONT_LOADER_EET
+   eet_init();
+#endif
 }
 
 void
@@ -239,4 +565,27 @@ cserve2_font_shutdown(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);
 }
index 582fbe0..a0c909a 100644 (file)
@@ -33,13 +33,6 @@ static Eina_Hash *client_list = NULL;
 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)
 {
@@ -270,52 +263,83 @@ static void
 _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)
 {
@@ -347,6 +371,12 @@ 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");
      }
index dd1dd07..c0777e4 100644 (file)
@@ -81,7 +81,7 @@ typedef struct _Waiter Waiter;
 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;
@@ -89,6 +89,8 @@ 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)
 {
@@ -109,15 +111,17 @@ cserve2_request_add(Font_Request_Type type, unsigned int rid, Client *client, Fo
 
    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)
@@ -130,23 +134,34 @@ cserve2_request_add(Font_Request_Type type, unsigned int rid, Client *client, Fo
           }
      }
 
+   /* 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;
@@ -167,13 +182,13 @@ cserve2_request_cancel(Font_Request *req, Client *client, Error_Type err)
 
    // 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);
      }
 
@@ -195,9 +210,12 @@ cserve2_request_cancel_all(Font_Request *req, Error_Type err)
         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);
 }
 
@@ -226,7 +244,7 @@ _cserve2_request_failed(Font_Request *req, Error_Type type)
         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);
@@ -252,7 +270,7 @@ _slave_read_cb(Slave *s __UNUSED__, Slave_Command cmd, void *msg, void *data)
         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);
@@ -265,6 +283,8 @@ _slave_read_cb(Slave *s __UNUSED__, Slave_Command cmd, void *msg, void *data)
    idle = &_workers[sw->type].idle;
    *working = eina_list_remove(*working, sw);
    *idle = eina_list_append(*idle, sw);
+
+   _cserve2_requests_process();
 }
 
 static void
@@ -338,6 +358,8 @@ _cserve2_request_dispatch(Slave_Worker *sw, Slave_Command ctype, Font_Request *r
    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);
@@ -352,8 +374,8 @@ _cserve2_request_dispatch(Slave_Worker *sw, Slave_Command ctype, Font_Request *r
    return EINA_TRUE;
 }
 
-void
-cserve2_requests_process(void)
+static void
+_cserve2_requests_process(void)
 {
     int rtype, j;
 
@@ -366,7 +388,7 @@ cserve2_requests_process(void)
 
          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;
@@ -387,7 +409,7 @@ cserve2_requests_process(void)
          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;
index 36291f9..5e60138 100644 (file)
@@ -27,10 +27,30 @@ struct _Shm_Handle
    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)
 {
@@ -38,7 +58,6 @@ 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));
@@ -59,14 +78,7 @@ cserve2_shm_request(size_t size)
           }
    } 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)
      {
@@ -106,6 +118,9 @@ cserve2_shm_unref(Shm_Handle *shm)
    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)
@@ -145,3 +160,36 @@ cserve2_shm_size_get(const Shm_Handle *shm)
 {
    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;
+}
index b6e4610..a11c635 100644 (file)
@@ -187,8 +187,8 @@ error_send(int fd, Error_Type err)
    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;
@@ -204,8 +204,8 @@ cserve2_shm_map(const char *name, size_t length, off_t offset)
    return map;
 }
 
-void
-cserve2_shm_unmap(void *map, size_t length)
+static void
+_cserve2_shm_unmap(void *map, size_t length)
 {
    munmap(map, length);
 }
@@ -316,8 +316,8 @@ image_load(const char *file, const char *key, const char *shmfile, Slave_Msg_Ima
    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;
 
@@ -352,7 +352,7 @@ image_load(const char *file, const char *key, const char *shmfile, Slave_Msg_Ima
    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;
 }
diff --git a/src/bin/evas_cserve2_usage.c b/src/bin/evas_cserve2_usage.c
new file mode 100644 (file)
index 0000000..fcf6720
--- /dev/null
@@ -0,0 +1,230 @@
+#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();
+}
index 77e7f8c..d58b866 100644 (file)
@@ -22,6 +22,8 @@ typedef enum {
    CSERVE2_FONT_GLYPHS_LOAD,
    CSERVE2_FONT_GLYPHS_LOADED,
    CSERVE2_FONT_GLYPHS_USED,
+   CSERVE2_STATS,
+   CSERVE2_FONT_DEBUG,
    CSERVE2_ERROR
 } Message_Type;
 
@@ -127,9 +129,9 @@ struct _Msg_Close {
  */
 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
 };
@@ -146,18 +148,19 @@ struct _Msg_Font_Loaded {
 /**
  * @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;
 };
 
@@ -180,6 +183,12 @@ struct _Msg_Font_Glyphs_Request {
  *  - 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 {
@@ -187,6 +196,56 @@ 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;
@@ -206,6 +265,8 @@ typedef struct _Msg_Font_Load Msg_Font_Load;
 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