From 76b6485a33094b40e8f833c32dc84457055062ec Mon Sep 17 00:00:00 2001 From: salmonik Date: Mon, 26 Oct 2026 20:13:07 +0100 Subject: Fonts: bundle Liberation Sans and register it with fontconfig - ship assets/LiberationSans-Regular.ttf (+ OFL license/README) - at startup, resolve the .so path via dladdr, add assets/...ttf to the current fontconfig config with FcConfigAppFontAddFile, and fall back to the system Liberation path if the asset is missing - font creation tries Liberation Sans, then DejaVu Sans/Noto/Tahoma/Verdana/ Arial, so the overlay still renders on installs without the bundled font --- src/menu.cpp | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 77 insertions(+), 10 deletions(-) (limited to 'src/menu.cpp') diff --git a/src/menu.cpp b/src/menu.cpp index 9537e05..1ef8638 100644 --- a/src/menu.cpp +++ b/src/menu.cpp @@ -4,7 +4,9 @@ #include #include #include +#include #include +#include // --------------------------------------------------------------------------- // ConMsg (tier0 console printer — prints to in-game console) @@ -217,6 +219,63 @@ static std::string config_path() { return dir + "/config"; } +// Register the bundled Liberation Sans with fontconfig so the VGUI overlay +// does not depend on the host/game install having it. The font file lives in +// assets/ next to the .so (or the system path as a fallback). +void register_bundled_font() { + static bool done = false; + if (done) + return; + done = true; + + std::string path; + Dl_info info; + if (dladdr((void *)®ister_bundled_font, &info) && info.dli_fname) { + char resolved[4096]; + const char *lib = realpath(info.dli_fname, resolved); + std::string p = lib ? resolved : info.dli_fname; + size_t slash = p.find_last_of('/'); + if (slash != std::string::npos) + p.resize(slash + 1); + else + p = "./"; + p += "assets/LiberationSans-Regular.ttf"; + if (access(p.c_str(), R_OK) == 0) + path = p; + } + if (path.empty()) { + const char *sys = "/usr/share/fonts/liberation/LiberationSans-Regular.ttf"; + if (access(sys, R_OK) == 0) + path = sys; + } + if (path.empty()) { + logf("font: bundled LiberationSans-Regular.ttf not found"); + return; + } + + void *fc = dlopen("libfontconfig.so.1", RTLD_LAZY | RTLD_GLOBAL); + if (!fc) { + logf("font: libfontconfig.so.1 not available"); + return; + } + using InitFn = int (*)(); + using GetCurrentFn = void *(*)(); + using AddFileFn = int (*)(void *, const char *); + auto init = (InitFn)dlsym(fc, "FcInit"); + auto get = (GetCurrentFn)dlsym(fc, "FcConfigGetCurrent"); + auto add = (AddFileFn)dlsym(fc, "FcConfigAppFontAddFile"); + if (!init || !get || !add) { + logf("font: fontconfig symbols missing"); + return; + } + init(); + void *cfg = get(); + if (cfg && add(cfg, path.c_str())) + logf("font: registered %s", path.c_str()); + else + logf("font: failed to register %s", path.c_str()); +} + static void config_save() { FILE *f = fopen(config_path().c_str(), "w"); if (!f) @@ -671,17 +730,24 @@ void outer_draw() { if (!g_surface || (!g_menu_open && !draw_test && !esp_on)) return; if (!g_font) { - g_font = surf_create_font(); - if (!g_font) { - logf("outer_draw: CreateFont failed"); - return; + static const char *families[] = { + "Liberation Sans", "DejaVu Sans", "Noto Sans", "Tahoma", + "Verdana", "Arial", nullptr}; + bool font_ok = false; + for (int fi = 0; families[fi] && !font_ok; fi++) { + g_font = surf_create_font(); + if (!g_font) + continue; + if (surf_set_font_glyph_set(g_font, families[fi], 16, 400, 0, 0, 0x400, + 0x20, 0xFF)) { + logf("outer_draw: font family %s", families[fi]); + font_ok = true; + } else { + g_font = 0; + } } - // FONTFLAG_CUSTOM only; Liberation Sans is an installed real TTF (Verdana - // substitutes to Noto). Raw glyphs, ASCII range pinned. - if (!surf_set_font_glyph_set(g_font, "Liberation Sans", 16, 400, 0, 0, - 0x400, 0x20, 0xFF)) { - logf("outer_draw: SetFontGlyphSet failed"); - g_font = 0; + if (!font_ok) { + logf("outer_draw: no usable font"); return; } } @@ -789,6 +855,7 @@ static ISurface *find_surface_interface() { void install_menu() { build_tree(); + register_bundled_font(); logf("config: %s", config_path().c_str()); config_load(); // resolve ISurface once (used by the DrawPrintText hook in hl2h.cpp and -- cgit v1.2.3