forked from enlightenment/efl
* Evas: Improve RGBA_Font_* cache using hash and stringshare correctly.
Note: This should not impact font rendering, but if you notice anything wrong, please don't hesitate to report this to me. SVN revision: 41050
This commit is contained in:
parent
fdca8644a2
commit
0839ec65ea
|
@ -68,4 +68,7 @@ EAPI int evas_common_font_query_char_coords (RGBA_Font *fn, con
|
||||||
EAPI int evas_common_font_query_text_at_pos (RGBA_Font *fn, const char *text, int x, int y, int *cx, int *cy, int *cw, int *ch);
|
EAPI int evas_common_font_query_text_at_pos (RGBA_Font *fn, const char *text, int x, int y, int *cx, int *cy, int *cw, int *ch);
|
||||||
EAPI int evas_common_font_query_last_up_to_pos (RGBA_Font *fn, const char *text, int x, int y);
|
EAPI int evas_common_font_query_last_up_to_pos (RGBA_Font *fn, const char *text, int x, int y);
|
||||||
|
|
||||||
|
void evas_common_font_load_init(void);
|
||||||
|
void evas_common_font_load_shutdown(void);
|
||||||
|
|
||||||
#endif /* _EVAS_FONT_H */
|
#endif /* _EVAS_FONT_H */
|
||||||
|
|
|
@ -5,22 +5,127 @@
|
||||||
#include "evas_common.h"
|
#include "evas_common.h"
|
||||||
#include "evas_private.h"
|
#include "evas_private.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
extern FT_Library evas_ft_lib;
|
extern FT_Library evas_ft_lib;
|
||||||
|
|
||||||
static int font_cache_usage = 0;
|
static int font_cache_usage = 0;
|
||||||
static int font_cache = 0;
|
static int font_cache = 0;
|
||||||
static Eina_Inlist * fonts_src = NULL;
|
|
||||||
static Eina_Inlist * fonts = NULL;
|
static Eina_Hash * fonts_src = NULL;
|
||||||
|
static Eina_Hash * fonts = NULL;
|
||||||
|
static Eina_List * fonts_lru = NULL;
|
||||||
|
|
||||||
static Evas_Bool font_modify_cache_cb(const Eina_Hash *hash, const void *key, void *data, void *fdata);
|
static Evas_Bool font_modify_cache_cb(const Eina_Hash *hash, const void *key, void *data, void *fdata);
|
||||||
static Evas_Bool font_flush_free_glyph_cb(const Eina_Hash *hash, const void *key, void *data, void *fdata);
|
static Evas_Bool font_flush_free_glyph_cb(const Eina_Hash *hash, const void *key, void *data, void *fdata);
|
||||||
|
|
||||||
|
static unsigned int
|
||||||
|
_evas_font_cache_int_length(const RGBA_Font_Int *key)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
_evas_font_cache_int_cmp(const RGBA_Font_Int *k1, int k1_length,
|
||||||
|
const RGBA_Font_Int *k2, int k2_length)
|
||||||
|
{
|
||||||
|
/* RGBA_Font_Source->name is a stringshare */
|
||||||
|
if (k1->src->name == k2->src->name)
|
||||||
|
return k1->size - k2->size;
|
||||||
|
return strcmp(k1->src->name, k2->src->name);;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
_evas_font_cache_int_hash(const RGBA_Font_Int *key, int key_length)
|
||||||
|
{
|
||||||
|
int hash;
|
||||||
|
|
||||||
|
hash = eina_hash_djb2(key->src->name, eina_stringshare_strlen(key->src->name) + 1);
|
||||||
|
hash ^= eina_hash_int32(&key->size, sizeof (int));
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_evas_common_font_source_free(RGBA_Font_Source *fs)
|
||||||
|
{
|
||||||
|
FT_Done_Face(fs->ft.face);
|
||||||
|
if (fs->charmap) evas_array_hash_free(fs->charmap);
|
||||||
|
if (fs->name) eina_stringshare_del(fs->name);
|
||||||
|
free(fs);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static Eina_Bool
|
||||||
|
font_flush_free_glyph_cb(const Eina_Hash *hash, const void *key, void *data, void *fdata)
|
||||||
|
{
|
||||||
|
RGBA_Font_Glyph *fg;
|
||||||
|
|
||||||
|
fg = data;
|
||||||
|
FT_Done_Glyph(fg->glyph);
|
||||||
|
/* extension calls */
|
||||||
|
if (fg->ext_dat_free) fg->ext_dat_free(fg->ext_dat);
|
||||||
|
free(fg);
|
||||||
|
return 1;
|
||||||
|
hash = 0;
|
||||||
|
key = 0;
|
||||||
|
fdata = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_evas_common_font_int_free(RGBA_Font_Int *fi)
|
||||||
|
{
|
||||||
|
FT_Done_Size(fi->ft.size);
|
||||||
|
|
||||||
|
evas_common_font_int_modify_cache_by(fi, -1);
|
||||||
|
|
||||||
|
eina_hash_foreach(fi->glyphs, font_flush_free_glyph_cb, NULL);
|
||||||
|
eina_hash_free(fi->glyphs);
|
||||||
|
|
||||||
|
eina_hash_free(fi->kerning);
|
||||||
|
eina_hash_free(fi->indexes);
|
||||||
|
|
||||||
|
#ifdef HAVE_PTHREAD
|
||||||
|
pthread_mutex_destroy(&fi->ft_mutex);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
evas_common_font_source_free(fi->src);
|
||||||
|
|
||||||
|
if (fi->references == 0)
|
||||||
|
fonts_lru = eina_list_remove(fonts_lru, fi);
|
||||||
|
|
||||||
|
free(fi);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
evas_common_font_load_init(void)
|
||||||
|
{
|
||||||
|
fonts_src = eina_hash_string_small_new(EINA_FREE_CB(_evas_common_font_source_free));
|
||||||
|
fonts = eina_hash_new(EINA_KEY_LENGTH(_evas_font_cache_int_length),
|
||||||
|
EINA_KEY_CMP(_evas_font_cache_int_cmp),
|
||||||
|
EINA_KEY_HASH(_evas_font_cache_int_hash),
|
||||||
|
EINA_FREE_CB(_evas_common_font_int_free),
|
||||||
|
5);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
evas_common_font_load_shutdown(void)
|
||||||
|
{
|
||||||
|
eina_hash_free(fonts);
|
||||||
|
fonts = NULL;
|
||||||
|
|
||||||
|
eina_hash_free(fonts_src);
|
||||||
|
fonts_src = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
EAPI RGBA_Font_Source *
|
EAPI RGBA_Font_Source *
|
||||||
evas_common_font_source_memory_load(const char *name, const void *data, int data_size)
|
evas_common_font_source_memory_load(const char *name, const void *data, int data_size)
|
||||||
{
|
{
|
||||||
int error;
|
int error;
|
||||||
RGBA_Font_Source *fs;
|
RGBA_Font_Source *fs;
|
||||||
|
|
||||||
|
assert(name != NULL);
|
||||||
|
|
||||||
fs = calloc(1, sizeof(RGBA_Font_Source) + data_size);
|
fs = calloc(1, sizeof(RGBA_Font_Source) + data_size);
|
||||||
if (!fs) return NULL;
|
if (!fs) return NULL;
|
||||||
fs->data = ((unsigned char *)fs) + sizeof(RGBA_Font_Source);
|
fs->data = ((unsigned char *)fs) + sizeof(RGBA_Font_Source);
|
||||||
|
@ -38,7 +143,8 @@ evas_common_font_source_memory_load(const char *name, const void *data, int data
|
||||||
error = FT_Select_Charmap(fs->ft.face, ft_encoding_unicode);
|
error = FT_Select_Charmap(fs->ft.face, ft_encoding_unicode);
|
||||||
fs->ft.orig_upem = fs->ft.face->units_per_EM;
|
fs->ft.orig_upem = fs->ft.face->units_per_EM;
|
||||||
fs->references = 1;
|
fs->references = 1;
|
||||||
fonts_src = eina_inlist_prepend(fonts_src, EINA_INLIST_GET(fs));
|
|
||||||
|
eina_hash_direct_add(fonts_src, fs->name, fs);
|
||||||
return fs;
|
return fs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,6 +153,8 @@ evas_common_font_source_load(const char *name)
|
||||||
{
|
{
|
||||||
RGBA_Font_Source *fs;
|
RGBA_Font_Source *fs;
|
||||||
|
|
||||||
|
assert(name != NULL);
|
||||||
|
|
||||||
fs = calloc(1, sizeof(RGBA_Font_Source));
|
fs = calloc(1, sizeof(RGBA_Font_Source));
|
||||||
if (!fs) return NULL;
|
if (!fs) return NULL;
|
||||||
fs->data = NULL;
|
fs->data = NULL;
|
||||||
|
@ -60,7 +168,8 @@ evas_common_font_source_load(const char *name)
|
||||||
fs->ft.orig_upem = 0;
|
fs->ft.orig_upem = 0;
|
||||||
|
|
||||||
fs->references = 1;
|
fs->references = 1;
|
||||||
fonts_src = eina_inlist_prepend(fonts_src, EINA_INLIST_GET(fs));
|
|
||||||
|
eina_hash_direct_add(fonts_src, fs->name, fs);
|
||||||
return fs;
|
return fs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,14 +203,11 @@ evas_common_font_source_find(const char *name)
|
||||||
RGBA_Font_Source *fs;
|
RGBA_Font_Source *fs;
|
||||||
|
|
||||||
if (!name) return NULL;
|
if (!name) return NULL;
|
||||||
EINA_INLIST_FOREACH(fonts_src, fs)
|
fs = eina_hash_find(fonts_src, name);
|
||||||
|
if (fs)
|
||||||
{
|
{
|
||||||
if ((fs->name) && (!strcmp(name, fs->name)))
|
fs->references++;
|
||||||
{
|
return fs;
|
||||||
fs->references++;
|
|
||||||
fonts_src = eina_inlist_demote(fonts_src, EINA_INLIST_GET(fs));
|
|
||||||
return fs;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -112,24 +218,17 @@ evas_common_font_source_free(RGBA_Font_Source *fs)
|
||||||
fs->references--;
|
fs->references--;
|
||||||
if (fs->references > 0) return;
|
if (fs->references > 0) return;
|
||||||
|
|
||||||
fonts_src = eina_inlist_remove(fonts_src, EINA_INLIST_GET(fs));
|
eina_hash_del(fonts_src, fs->name, fs);
|
||||||
FT_Done_Face(fs->ft.face);
|
|
||||||
if (fs->charmap) evas_array_hash_free(fs->charmap);
|
|
||||||
if (fs->name) eina_stringshare_del(fs->name);
|
|
||||||
free(fs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EAPI void
|
EAPI void
|
||||||
evas_common_font_size_use(RGBA_Font *fn)
|
evas_common_font_size_use(RGBA_Font *fn)
|
||||||
{
|
{
|
||||||
|
RGBA_Font_Int *fi;
|
||||||
Eina_List *l;
|
Eina_List *l;
|
||||||
|
|
||||||
for (l = fn->fonts; l; l = l->next)
|
EINA_LIST_FOREACH(fn->fonts, l, fi)
|
||||||
{
|
{
|
||||||
RGBA_Font_Int *fi;
|
|
||||||
|
|
||||||
fi = l->data;
|
|
||||||
|
|
||||||
if (fi->src->current_size != fi->size)
|
if (fi->src->current_size != fi->size)
|
||||||
{
|
{
|
||||||
FT_Activate_Size(fi->ft.size);
|
FT_Activate_Size(fi->ft.size);
|
||||||
|
@ -178,7 +277,7 @@ _evas_common_font_double_int_hash(const unsigned int key[2], int key_length)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
_evas_commont_font_int_cache_init(RGBA_Font_Int *fi)
|
_evas_common_font_int_cache_init(RGBA_Font_Int *fi)
|
||||||
{
|
{
|
||||||
/* Add some font kerning cache. */
|
/* Add some font kerning cache. */
|
||||||
fi->indexes = eina_hash_new(EINA_KEY_LENGTH(_evas_common_font_int_length),
|
fi->indexes = eina_hash_new(EINA_KEY_LENGTH(_evas_common_font_int_length),
|
||||||
|
@ -194,7 +293,6 @@ _evas_commont_font_int_cache_init(RGBA_Font_Int *fi)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
EAPI RGBA_Font_Int *
|
EAPI RGBA_Font_Int *
|
||||||
evas_common_font_int_memory_load(const char *name, int size, const void *data, int data_size)
|
evas_common_font_int_memory_load(const char *name, int size, const void *data, int data_size)
|
||||||
{
|
{
|
||||||
|
@ -218,7 +316,7 @@ evas_common_font_int_memory_load(const char *name, int size, const void *data, i
|
||||||
|
|
||||||
fi->size = size;
|
fi->size = size;
|
||||||
|
|
||||||
_evas_commont_font_int_cache_init(fi);
|
_evas_common_font_int_cache_init(fi);
|
||||||
|
|
||||||
fi = evas_common_font_int_load_init(fi);
|
fi = evas_common_font_int_load_init(fi);
|
||||||
evas_common_font_int_load_complete(fi);
|
evas_common_font_int_load_complete(fi);
|
||||||
|
@ -249,7 +347,7 @@ evas_common_font_int_load(const char *name, int size)
|
||||||
|
|
||||||
fi->size = size;
|
fi->size = size;
|
||||||
|
|
||||||
_evas_commont_font_int_cache_init(fi);
|
_evas_common_font_int_cache_init(fi);
|
||||||
|
|
||||||
return evas_common_font_int_load_init(fi);
|
return evas_common_font_int_load_init(fi);
|
||||||
}
|
}
|
||||||
|
@ -261,7 +359,8 @@ evas_common_font_int_load_init(RGBA_Font_Int *fi)
|
||||||
fi->glyphs = eina_hash_int32_new(NULL);
|
fi->glyphs = eina_hash_int32_new(NULL);
|
||||||
fi->usage = 0;
|
fi->usage = 0;
|
||||||
fi->references = 1;
|
fi->references = 1;
|
||||||
fonts = eina_inlist_prepend(fonts, EINA_INLIST_GET(fi));
|
|
||||||
|
eina_hash_direct_add(fonts, fi, fi);
|
||||||
|
|
||||||
return fi;
|
return fi;
|
||||||
}
|
}
|
||||||
|
@ -359,6 +458,7 @@ evas_common_font_load(const char *name, int size)
|
||||||
fi->references--;
|
fi->references--;
|
||||||
if (fi->references == 0)
|
if (fi->references == 0)
|
||||||
{
|
{
|
||||||
|
fonts_lru = eina_list_prepend(fonts_lru, fi);
|
||||||
evas_common_font_int_modify_cache_by(fi, 1);
|
evas_common_font_int_modify_cache_by(fi, 1);
|
||||||
evas_common_font_flush();
|
evas_common_font_flush();
|
||||||
}
|
}
|
||||||
|
@ -374,6 +474,7 @@ evas_common_font_load(const char *name, int size)
|
||||||
fi->references--;
|
fi->references--;
|
||||||
if (fi->references == 0)
|
if (fi->references == 0)
|
||||||
{
|
{
|
||||||
|
fonts_lru = eina_list_prepend(fonts_lru, fi);
|
||||||
evas_common_font_int_modify_cache_by(fi, 1);
|
evas_common_font_int_modify_cache_by(fi, 1);
|
||||||
evas_common_font_flush();
|
evas_common_font_flush();
|
||||||
}
|
}
|
||||||
|
@ -436,10 +537,11 @@ evas_common_font_free(RGBA_Font *fn)
|
||||||
fi->references--;
|
fi->references--;
|
||||||
if (fi->references == 0)
|
if (fi->references == 0)
|
||||||
{
|
{
|
||||||
|
fonts_lru = eina_list_append(fonts_lru, fi);
|
||||||
evas_common_font_int_modify_cache_by(fi, 1);
|
evas_common_font_int_modify_cache_by(fi, 1);
|
||||||
evas_common_font_flush();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
evas_common_font_flush();
|
||||||
eina_list_free(fn->fonts);
|
eina_list_free(fn->fonts);
|
||||||
LKD(fn->lock);
|
LKD(fn->lock);
|
||||||
free(fn);
|
free(fn);
|
||||||
|
@ -580,71 +682,44 @@ evas_common_font_flush(void)
|
||||||
while (font_cache_usage > font_cache) evas_common_font_flush_last();
|
while (font_cache_usage > font_cache) evas_common_font_flush_last();
|
||||||
}
|
}
|
||||||
|
|
||||||
static Eina_Bool
|
|
||||||
font_flush_free_glyph_cb(const Eina_Hash *hash, const void *key, void *data, void *fdata)
|
|
||||||
{
|
|
||||||
RGBA_Font_Glyph *fg;
|
|
||||||
|
|
||||||
fg = data;
|
|
||||||
FT_Done_Glyph(fg->glyph);
|
|
||||||
/* extension calls */
|
|
||||||
if (fg->ext_dat_free) fg->ext_dat_free(fg->ext_dat);
|
|
||||||
free(fg);
|
|
||||||
return 1;
|
|
||||||
hash = 0;
|
|
||||||
key = 0;
|
|
||||||
fdata = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We run this when the cache gets larger than allowed size
|
/* We run this when the cache gets larger than allowed size
|
||||||
* We check cache size each time a fi->references goes to 0
|
* We check cache size each time a fi->references goes to 0
|
||||||
* PERFORMS: Find font_int(s) with references == 0 and delete them */
|
* PERFORMS: Find font_int(s) with references == 0 and delete them */
|
||||||
EAPI void
|
EAPI void
|
||||||
evas_common_font_flush_last(void)
|
evas_common_font_flush_last(void)
|
||||||
{
|
{
|
||||||
RGBA_Font_Int *fi_tmp;
|
|
||||||
RGBA_Font_Int *fi = NULL;
|
RGBA_Font_Int *fi = NULL;
|
||||||
|
|
||||||
EINA_INLIST_FOREACH(fonts, fi_tmp)
|
if (!fonts_lru) return ;
|
||||||
{
|
|
||||||
if (fi_tmp->references == 0) fi = fi_tmp;
|
|
||||||
}
|
|
||||||
if (!fi) return;
|
|
||||||
|
|
||||||
FT_Done_Size(fi->ft.size);
|
fi = eina_list_data_get(fonts_lru);
|
||||||
|
fonts_lru = eina_list_remove_list(fonts_lru, fonts_lru);
|
||||||
|
|
||||||
fonts = eina_inlist_remove(fonts, EINA_INLIST_GET(fi));
|
eina_hash_del(fonts, fi, fi);
|
||||||
evas_common_font_int_modify_cache_by(fi, -1);
|
|
||||||
|
|
||||||
eina_hash_foreach(fi->glyphs, font_flush_free_glyph_cb, NULL);
|
|
||||||
eina_hash_free(fi->glyphs);
|
|
||||||
|
|
||||||
eina_hash_free(fi->kerning);
|
|
||||||
eina_hash_free(fi->indexes);
|
|
||||||
|
|
||||||
#ifdef HAVE_PTHREAD
|
|
||||||
pthread_mutex_destroy(&fi->ft_mutex);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
evas_common_font_source_free(fi->src);
|
|
||||||
|
|
||||||
free(fi);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EAPI RGBA_Font_Int *
|
EAPI RGBA_Font_Int *
|
||||||
evas_common_font_int_find(const char *name, int size)
|
evas_common_font_int_find(const char *name, int size)
|
||||||
{
|
{
|
||||||
|
RGBA_Font_Int tmp_fi;
|
||||||
|
RGBA_Font_Source tmp_fn;
|
||||||
RGBA_Font_Int *fi;
|
RGBA_Font_Int *fi;
|
||||||
|
|
||||||
EINA_INLIST_FOREACH(fonts, fi)
|
tmp_fn.name = (char*) eina_stringshare_add(name);
|
||||||
|
tmp_fi.src = &tmp_fn;
|
||||||
|
tmp_fi.size = size;
|
||||||
|
|
||||||
|
fi = eina_hash_find(fonts, &tmp_fi);
|
||||||
|
if (fi)
|
||||||
{
|
{
|
||||||
if ((fi->size == size) && (!strcmp(name, fi->src->name)))
|
if (fi->references == 0)
|
||||||
{
|
{
|
||||||
if (fi->references == 0) evas_common_font_int_modify_cache_by(fi, -1);
|
evas_common_font_int_modify_cache_by(fi, -1);
|
||||||
fi->references++;
|
fonts_lru = eina_list_remove(fonts_lru, fi);
|
||||||
fonts = eina_inlist_promote(fonts, EINA_INLIST_GET(fi));
|
|
||||||
return fi;
|
|
||||||
}
|
}
|
||||||
|
fi->references++;
|
||||||
}
|
}
|
||||||
return NULL;
|
|
||||||
|
eina_stringshare_del(tmp_fn.name);
|
||||||
|
return fi;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ evas_common_font_init(void)
|
||||||
initialised--;
|
initialised--;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
evas_common_font_load_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
EAPI void
|
EAPI void
|
||||||
|
@ -30,6 +31,7 @@ evas_common_font_shutdown(void)
|
||||||
initialised--;
|
initialised--;
|
||||||
if (initialised != 0) return;
|
if (initialised != 0) return;
|
||||||
|
|
||||||
|
evas_common_font_load_shutdown();
|
||||||
evas_common_font_cache_set(0);
|
evas_common_font_cache_set(0);
|
||||||
evas_common_font_flush();
|
evas_common_font_flush();
|
||||||
|
|
||||||
|
|
|
@ -800,8 +800,6 @@ struct _RGBA_Font
|
||||||
|
|
||||||
struct _RGBA_Font_Int
|
struct _RGBA_Font_Int
|
||||||
{
|
{
|
||||||
EINA_INLIST;
|
|
||||||
|
|
||||||
RGBA_Font_Source *src;
|
RGBA_Font_Source *src;
|
||||||
|
|
||||||
int size;
|
int size;
|
||||||
|
@ -814,7 +812,7 @@ struct _RGBA_Font_Int
|
||||||
Eina_Hash *glyphs;
|
Eina_Hash *glyphs;
|
||||||
|
|
||||||
LK(ft_mutex);
|
LK(ft_mutex);
|
||||||
|
|
||||||
Eina_Hash *kerning;
|
Eina_Hash *kerning;
|
||||||
Eina_Hash *indexes;
|
Eina_Hash *indexes;
|
||||||
|
|
||||||
|
@ -822,12 +820,11 @@ struct _RGBA_Font_Int
|
||||||
Font_Hint_Flags hinting;
|
Font_Hint_Flags hinting;
|
||||||
|
|
||||||
int references;
|
int references;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _RGBA_Font_Source
|
struct _RGBA_Font_Source
|
||||||
{
|
{
|
||||||
EINA_INLIST;
|
|
||||||
|
|
||||||
const char *name;
|
const char *name;
|
||||||
const char *file;
|
const char *file;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue