(Font) One copy of the language-to-font decision

The same switch existed five times - twice in ozone, once each in
materialui, gfx_widgets and file_path_special - mapping Arabic and
Persian to fallback-font.ttf, Chinese to chinese-fallback-font.ttf,
Korean to korean-fallback-font.ttf, and everything else to the
caller's own default. Character for character the same, differing only
in which variable held the assets directory.

font_driver_language_font_file() returns the file name, or NULL when
the language has no special requirement. The name rather than a full
path, because the callers do genuinely differ on the directory:
ozone and materialui build it from directory_assets, widgets keep
assets_pkg_dir already, file_path_special has its own. The default
branch stays with the caller too - bold.ttf, regular.ttf,
FILE_PATH_TTF_FONT and the xmb theme directory are all different.

Adding a language was five edits and is now one. That matters
immediately: Thai renders as tofu today because it is not in the
switch, and neither shipped fallback font contains Thai - checking the
cmap tables, fallback-font.ttf covers Arabic but not U+0E01. So Thai
needs a font as well as a case, and when one arrives there is now a
single place to put it.

This is also the piece the language switch was missing.
font_driver_reload_fonts() rebuilds each font from the path it was
created with, which re-reads the same face; making a language change
take effect without a restart needs the path re-resolved, and that
needs one function to ask. It exists now.

samples/gfx/font_lifecycle grows a msg_hash_get_uint() stub, since
font_driver.c now asks which language is selected. English there - the
tests are about the lifecycle, not the mapping. Clean under ASan,
UBSan, LeakSanitizer and TSan.
This commit is contained in:
G authored and libretroadmin committed 2026-08-08 20:31:32 +02:00
1 parent 588c1c8ecb
commit f7bb668669
7 files changed
+83 -95

No files matched your search

+14 -28
View File
@@ -381,35 +381,21 @@ size_t fill_pathname_application_special(char *s,
{
char tmp_dir[DIR_MAX_LENGTH];
switch (*msg_hash_get_uint(MSG_HASH_USER_LANGUAGE))
const char *lang_font = font_driver_language_font_file();
if (lang_font)
{
case RETRO_LANGUAGE_ARABIC:
case RETRO_LANGUAGE_PERSIAN:
fill_pathname_join_special(tmp_dir,
settings->paths.directory_assets, "pkg", sizeof(tmp_dir));
_len = fill_pathname_join_special(s, tmp_dir, "fallback-font.ttf", len);
break;
case RETRO_LANGUAGE_CHINESE_SIMPLIFIED:
case RETRO_LANGUAGE_CHINESE_TRADITIONAL:
fill_pathname_join_special(tmp_dir,
settings->paths.directory_assets, "pkg", sizeof(tmp_dir));
_len = fill_pathname_join_special(s, tmp_dir, "chinese-fallback-font.ttf", len);
break;
case RETRO_LANGUAGE_KOREAN:
fill_pathname_join_special(tmp_dir,
settings->paths.directory_assets, "pkg", sizeof(tmp_dir));
_len = fill_pathname_join_special(s, tmp_dir, "korean-fallback-font.ttf", len);
break;
default:
{
char tmp_dir2[DIR_MAX_LENGTH];
settings_t *settings = config_get_ptr();
const char *dir_assets = settings->paths.directory_assets;
fill_pathname_join_special(tmp_dir2, dir_assets, "xmb", sizeof(tmp_dir2));
fill_pathname_join_special(tmp_dir, tmp_dir2, xmb_theme_ident(), sizeof(tmp_dir));
_len = fill_pathname_join_special(s, tmp_dir, FILE_PATH_TTF_FONT, len);
}
break;
fill_pathname_join_special(tmp_dir,
settings->paths.directory_assets, "pkg", sizeof(tmp_dir));
_len = fill_pathname_join_special(s, tmp_dir, lang_font, len);
}
else
{
char tmp_dir2[DIR_MAX_LENGTH];
const char *dir_assets = settings->paths.directory_assets;
fill_pathname_join_special(tmp_dir2, dir_assets, "xmb", sizeof(tmp_dir2));
fill_pathname_join_special(tmp_dir, tmp_dir2, xmb_theme_ident(), sizeof(tmp_dir));
_len = fill_pathname_join_special(s, tmp_dir, FILE_PATH_TTF_FONT, len);
}
}
}
+20
View File
@@ -24,6 +24,7 @@
#include "../config.h"
#endif
#include "../msg_hash.h"
#include "font_driver.h"
#include "video_thread_wrapper.h"
@@ -68,6 +69,25 @@ static void font_driver_cache_metrics(font_data_t *font)
}
}
const char *font_driver_language_font_file(void)
{
switch (*msg_hash_get_uint(MSG_HASH_USER_LANGUAGE))
{
case RETRO_LANGUAGE_ARABIC:
case RETRO_LANGUAGE_PERSIAN:
return "fallback-font.ttf";
case RETRO_LANGUAGE_CHINESE_SIMPLIFIED:
case RETRO_LANGUAGE_CHINESE_TRADITIONAL:
return "chinese-fallback-font.ttf";
case RETRO_LANGUAGE_KOREAN:
return "korean-fallback-font.ttf";
default:
break;
}
return NULL;
}
unsigned font_driver_reload_fonts(void)
{
font_data_t *font;
+9
View File
@@ -150,6 +150,15 @@ void font_driver_free(font_data_t *font);
* left alone. Returns the number rebuilt. */
unsigned font_driver_reload_fonts(void);
/* The font file the current menu language needs, relative to the
* assets pkg directory, or NULL when it has no special requirement
* and the caller's own default should be used.
*
* One copy of this decision. It was four - ozone, materialui,
* gfx_widgets and file_path_special each carried the same switch -
* which is three places to forget when a language is added. */
const char *font_driver_language_font_file(void);
/* Returns a monotonic counter incremented on every font free;
* see font_driver.c for rationale. Used to validate externally
* cached per-font derived data */
+8 -15
View File
@@ -1015,22 +1015,15 @@ static void gfx_widgets_layout(
is_threaded, p_dispwidget->ozone_bold_font_path, BASE_FONT_SIZE);
/* Create msg_queue font */
switch (*msg_hash_get_uint(MSG_HASH_USER_LANGUAGE))
{
case RETRO_LANGUAGE_ARABIC:
case RETRO_LANGUAGE_PERSIAN:
fill_pathname_join_special(font_file, p_dispwidget->assets_pkg_dir, "fallback-font.ttf", sizeof(font_file));
break;
case RETRO_LANGUAGE_CHINESE_SIMPLIFIED:
case RETRO_LANGUAGE_CHINESE_TRADITIONAL:
fill_pathname_join_special(font_file, p_dispwidget->assets_pkg_dir, "chinese-fallback-font.ttf", sizeof(font_file));
break;
case RETRO_LANGUAGE_KOREAN:
fill_pathname_join_special(font_file, p_dispwidget->assets_pkg_dir, "korean-fallback-font.ttf", sizeof(font_file));
break;
default:
strlcpy(font_file, p_dispwidget->ozone_regular_font_path, sizeof(font_file));
break;
const char *lang_font = font_driver_language_font_file();
if (lang_font)
fill_pathname_join_special(font_file,
p_dispwidget->assets_pkg_dir, lang_font, sizeof(font_file));
else
strlcpy(font_file, p_dispwidget->ozone_regular_font_path,
sizeof(font_file));
}
gfx_widgets_font_init(p_disp, p_dispwidget,
&p_dispwidget->gfx_widget_fonts.msg_queue,
+9 -20
View File
@@ -9327,33 +9327,22 @@ static void materialui_init_font(gfx_display_t *p_disp,
font_data->font = NULL;
}
switch (*msg_hash_get_uint(MSG_HASH_USER_LANGUAGE))
{
case RETRO_LANGUAGE_ARABIC:
case RETRO_LANGUAGE_PERSIAN:
const char *lang_font = font_driver_language_font_file();
if (lang_font)
{
fill_pathname_join_special(tmp_dir,
settings->paths.directory_assets, "pkg", sizeof(tmp_dir));
fill_pathname_join_special(fontpath, tmp_dir, "fallback-font.ttf",
fill_pathname_join_special(fontpath, tmp_dir, lang_font,
sizeof(fontpath));
break;
case RETRO_LANGUAGE_CHINESE_SIMPLIFIED:
case RETRO_LANGUAGE_CHINESE_TRADITIONAL:
fill_pathname_join_special(tmp_dir,
settings->paths.directory_assets, "pkg", sizeof(tmp_dir));
fill_pathname_join_special(fontpath, tmp_dir, "chinese-fallback-font.ttf",
sizeof(fontpath));
break;
case RETRO_LANGUAGE_KOREAN:
fill_pathname_join_special(tmp_dir,
settings->paths.directory_assets, "pkg", sizeof(tmp_dir));
fill_pathname_join_special(fontpath, tmp_dir, "korean-fallback-font.ttf",
sizeof(fontpath));
break;
default:
}
else
{
fill_pathname_join_special(tmp_dir, dir_assets, "glui", sizeof(tmp_dir));
fill_pathname_join_special(fontpath, tmp_dir, FILE_PATH_TTF_FONT,
sizeof(fontpath));
break;
}
}
font_data->font = gfx_display_font_file(p_disp,
+14 -32
View File
@@ -10114,25 +10114,16 @@ static void ozone_set_layout(
ozone->pointer_active_delta = CURSOR_ACTIVE_DELTA * scale_factor;
/* Initialise fonts */
switch (*msg_hash_get_uint(MSG_HASH_USER_LANGUAGE))
{
case RETRO_LANGUAGE_ARABIC:
case RETRO_LANGUAGE_PERSIAN:
const char *lang_font = font_driver_language_font_file();
if (lang_font)
{
fill_pathname_join_special(tmp_dir, path_directory_assets, "pkg", sizeof(tmp_dir));
fill_pathname_join_special(font_path, tmp_dir, "fallback-font.ttf", sizeof(font_path));
break;
case RETRO_LANGUAGE_CHINESE_SIMPLIFIED:
case RETRO_LANGUAGE_CHINESE_TRADITIONAL:
fill_pathname_join_special(tmp_dir, path_directory_assets, "pkg", sizeof(tmp_dir));
fill_pathname_join_special(font_path, tmp_dir, "chinese-fallback-font.ttf", sizeof(font_path));
break;
case RETRO_LANGUAGE_KOREAN:
fill_pathname_join_special(tmp_dir, path_directory_assets, "pkg", sizeof(tmp_dir));
fill_pathname_join_special(font_path, tmp_dir, "korean-fallback-font.ttf", sizeof(font_path));
break;
default:
fill_pathname_join_special(font_path, tmp_dir, lang_font, sizeof(font_path));
}
else
fill_pathname_join_special(font_path, ozone->assets_path, "bold.ttf", sizeof(font_path));
break;
}
if (path_menu_font && *path_menu_font)
@@ -10143,25 +10134,16 @@ static void ozone_set_layout(
if (!(((ozone->flags & OZONE_FLAG_HAS_ALL_ASSETS) > 0) && font_inited))
ozone->flags &= ~OZONE_FLAG_HAS_ALL_ASSETS;
switch (*msg_hash_get_uint(MSG_HASH_USER_LANGUAGE))
{
case RETRO_LANGUAGE_ARABIC:
case RETRO_LANGUAGE_PERSIAN:
const char *lang_font = font_driver_language_font_file();
if (lang_font)
{
fill_pathname_join_special(tmp_dir, path_directory_assets, "pkg", sizeof(tmp_dir));
fill_pathname_join_special(font_path, tmp_dir, "fallback-font.ttf", sizeof(font_path));
break;
case RETRO_LANGUAGE_CHINESE_SIMPLIFIED:
case RETRO_LANGUAGE_CHINESE_TRADITIONAL:
fill_pathname_join_special(tmp_dir, path_directory_assets, "pkg", sizeof(tmp_dir));
fill_pathname_join_special(font_path, tmp_dir, "chinese-fallback-font.ttf", sizeof(font_path));
break;
case RETRO_LANGUAGE_KOREAN:
fill_pathname_join_special(tmp_dir, path_directory_assets, "pkg", sizeof(tmp_dir));
fill_pathname_join_special(font_path, tmp_dir, "korean-fallback-font.ttf", sizeof(font_path));
break;
default:
fill_pathname_join_special(font_path, tmp_dir, lang_font, sizeof(font_path));
}
else
fill_pathname_join_special(font_path, ozone->assets_path, "regular.ttf", sizeof(font_path));
break;
}
if (path_menu_font && *path_menu_font)
@@ -26,6 +26,15 @@ bool filestream_read_file(const char *path, void **buf, int64_t *len)
return *buf != NULL;
}
/* font_driver_language_font_file() asks which language is selected.
* English here: the tests are about the lifecycle, not the mapping. */
unsigned *msg_hash_get_uint(enum msg_hash_action type)
{
static unsigned lang = 0; /* RETRO_LANGUAGE_ENGLISH */
(void)type;
return ⟨
}
static video_driver_state_t vst;
video_driver_state_t *video_state_get_ptr(void) { return &vst; }