Linux: Load libfontconfig at runtime to allow the binaries to work for running kittens on servers without FontConfig

This commit is contained in:
Kovid Goyal 2022-05-13 19:56:19 +05:30
parent aaffec1cbc
commit d3656bf7e9
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
3 changed files with 122 additions and 2 deletions

View File

@ -61,6 +61,9 @@ Detailed list of changes
- Fix deleting images by row not calculating image bounds correctly (:iss:`5081`)
- Linux: Load libfontconfig at runtime to allow the binaries to work for
running kittens on servers without FontConfig
0.25.0 [2022-04-11]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -10,6 +10,7 @@
#include "lineops.h"
#include "fonts.h"
#include <fontconfig/fontconfig.h>
#include <dlfcn.h>
#include "emoji.h"
#include "freetype_render_ui_text.h"
#ifndef FC_COLOR
@ -18,10 +19,111 @@
static bool initialized = false;
static void* libfontconfig_handle = NULL;
#define FcInit dynamically_loaded_fc_symbol.Init
#define FcFini dynamically_loaded_fc_symbol.Fini
#define FcCharSetAddChar dynamically_loaded_fc_symbol.CharSetAddChar
#define FcPatternDestroy dynamically_loaded_fc_symbol.PatternDestroy
#define FcObjectSetDestroy dynamically_loaded_fc_symbol.ObjectSetDestroy
#define FcPatternAddDouble dynamically_loaded_fc_symbol.PatternAddDouble
#define FcPatternAddString dynamically_loaded_fc_symbol.PatternAddString
#define FcFontMatch dynamically_loaded_fc_symbol.FontMatch
#define FcCharSetCreate dynamically_loaded_fc_symbol.CharSetCreate
#define FcPatternGetString dynamically_loaded_fc_symbol.PatternGetString
#define FcFontSetDestroy dynamically_loaded_fc_symbol.FontSetDestroy
#define FcPatternGetInteger dynamically_loaded_fc_symbol.PatternGetInteger
#define FcPatternAddBool dynamically_loaded_fc_symbol.PatternAddBool
#define FcFontList dynamically_loaded_fc_symbol.FontList
#define FcObjectSetBuild dynamically_loaded_fc_symbol.ObjectSetBuild
#define FcCharSetDestroy dynamically_loaded_fc_symbol.CharSetDestroy
#define FcConfigSubstitute dynamically_loaded_fc_symbol.ConfigSubstitute
#define FcDefaultSubstitute dynamically_loaded_fc_symbol.DefaultSubstitute
#define FcPatternAddInteger dynamically_loaded_fc_symbol.PatternAddInteger
#define FcPatternCreate dynamically_loaded_fc_symbol.PatternCreate
#define FcPatternGetBool dynamically_loaded_fc_symbol.PatternGetBool
#define FcPatternAddCharSet dynamically_loaded_fc_symbol.PatternAddCharSet
static struct {
FcBool(*Init)(void);
void(*Fini)(void);
FcBool (*CharSetAddChar) (FcCharSet *fcs, FcChar32 ucs4);
void (*PatternDestroy) (FcPattern *p);
void (*ObjectSetDestroy) (FcObjectSet *os);
FcBool (*PatternAddDouble) (FcPattern *p, const char *object, double d);
FcBool (*PatternAddString) (FcPattern *p, const char *object, const FcChar8 *s);
FcPattern * (*FontMatch) (FcConfig *config, FcPattern *p, FcResult *result);
FcCharSet* (*CharSetCreate) (void);
FcResult (*PatternGetString) (const FcPattern *p, const char *object, int n, FcChar8 ** s);
void (*FontSetDestroy) (FcFontSet *s);
FcResult (*PatternGetInteger) (const FcPattern *p, const char *object, int n, int *i);
FcBool (*PatternAddBool) (FcPattern *p, const char *object, FcBool b);
FcFontSet * (*FontList) (FcConfig *config, FcPattern *p, FcObjectSet *os);
FcObjectSet * (*ObjectSetBuild) (const char *first, ...);
void (*CharSetDestroy) (FcCharSet *fcs);
FcBool (*ConfigSubstitute) (FcConfig *config, FcPattern *p, FcMatchKind kind);
void (*DefaultSubstitute) (FcPattern *pattern);
FcBool (*PatternAddInteger) (FcPattern *p, const char *object, int i);
FcPattern * (*PatternCreate) (void);
FcResult (*PatternGetBool) (const FcPattern *p, const char *object, int n, FcBool *b);
FcBool (*PatternAddCharSet) (FcPattern *p, const char *object, const FcCharSet *c);
} dynamically_loaded_fc_symbol = {0};
#define LOAD_FUNC(name) {\
*(void **) (&dynamically_loaded_fc_symbol.name) = dlsym(libfontconfig_handle, "Fc" #name); \
if (!dynamically_loaded_fc_symbol.name) { \
const char* error = dlerror(); \
fatal("Failed to load the function Fc" #name " with error: %s", error ? error : ""); \
} \
}
static void
load_fontconfig_lib(void) {
const char* libnames[] = {
#if defined(_KITTY_FONTCONFIG_LIBRARY)
_KITTY_FONTCONFIG_LIBRARY,
#else
"libfontconfig.so",
// some installs are missing the .so symlink, so try the full name
"libfontconfig.so.1",
#endif
NULL
};
for (int i = 0; libnames[i]; i++) {
libfontconfig_handle = dlopen(libnames[i], RTLD_LAZY);
if (libfontconfig_handle) break;
}
if (libfontconfig_handle == NULL) { fatal("Failed to find and load fontconfig"); }
dlerror(); /* Clear any existing error */
LOAD_FUNC(Init);
LOAD_FUNC(Fini);
LOAD_FUNC(CharSetAddChar);
LOAD_FUNC(PatternDestroy);
LOAD_FUNC(ObjectSetDestroy);
LOAD_FUNC(PatternAddDouble);
LOAD_FUNC(PatternAddString);
LOAD_FUNC(FontMatch);
LOAD_FUNC(CharSetCreate);
LOAD_FUNC(PatternGetString);
LOAD_FUNC(FontSetDestroy);
LOAD_FUNC(PatternGetInteger);
LOAD_FUNC(PatternAddBool);
LOAD_FUNC(FontList);
LOAD_FUNC(ObjectSetBuild);
LOAD_FUNC(CharSetDestroy);
LOAD_FUNC(ConfigSubstitute);
LOAD_FUNC(DefaultSubstitute);
LOAD_FUNC(PatternAddInteger);
LOAD_FUNC(PatternCreate);
LOAD_FUNC(PatternGetBool);
LOAD_FUNC(PatternAddCharSet);
}
#undef LOAD_FUNC
static void
ensure_initialized(void) {
if (!initialized) {
load_fontconfig_lib();
if (!FcInit()) fatal("Failed to initialize fontconfig library");
initialized = true;
}
@ -31,6 +133,8 @@ static void
finalize(void) {
if (initialized) {
FcFini();
dlclose(libfontconfig_handle);
libfontconfig_handle = NULL;
initialized = false;
}
}

View File

@ -77,6 +77,7 @@ class Options(argparse.Namespace):
egl_library: Optional[str] = os.getenv('KITTY_EGL_LIBRARY')
startup_notification_library: Optional[str] = os.getenv('KITTY_STARTUP_NOTIFICATION_LIBRARY')
canberra_library: Optional[str] = os.getenv('KITTY_CANBERRA_LIBRARY')
fontconfig_library: Optional[str] = os.getenv('KITTY_FONTCONFIG_LIBRARY')
def emphasis(text: str) -> str:
@ -296,6 +297,7 @@ def init_env(
egl_library: Optional[str] = None,
startup_notification_library: Optional[str] = None,
canberra_library: Optional[str] = None,
fontconfig_library: Optional[str] = None,
extra_logging: Iterable[str] = (),
extra_include_dirs: Iterable[str] = (),
ignore_compiler_warnings: bool = False,
@ -381,6 +383,10 @@ def init_env(
assert('"' not in canberra_library)
desktop_libs += [f'_KITTY_CANBERRA_LIBRARY="{canberra_library}"']
if fontconfig_library is not None:
assert('"' not in fontconfig_library)
desktop_libs += [f'_KITTY_FONTCONFIG_LIBRARY="{fontconfig_library}"']
if desktop_libs != []:
library_paths['kitty/desktop.c'] = desktop_libs
@ -432,7 +438,7 @@ def kitty_env() -> Env:
cppflags.append('-DGL_SILENCE_DEPRECATION')
else:
cflags.extend(pkg_config('fontconfig', '--cflags-only-I'))
platform_libs = pkg_config('fontconfig', '--libs')
platform_libs = []
cflags.extend(pkg_config('harfbuzz', '--cflags-only-I'))
platform_libs.extend(pkg_config('harfbuzz', '--libs'))
pylib = get_python_flags(cflags)
@ -810,7 +816,7 @@ def init_env_from_args(args: Options, native_optimizations: bool = False) -> Non
global env
env = init_env(
args.debug, args.sanitize, native_optimizations, args.link_time_optimization, args.profile,
args.egl_library, args.startup_notification_library, args.canberra_library,
args.egl_library, args.startup_notification_library, args.canberra_library, args.fontconfig_library,
args.extra_logging, args.extra_include_dirs, args.ignore_compiler_warnings,
args.build_universal_binary, args.extra_library_dirs
)
@ -1470,6 +1476,13 @@ def option_parser() -> argparse.ArgumentParser: # {{{
help='The filename argument passed to dlopen for libcanberra.'
' This can be used to change the name of the loaded library or specify an absolute path.'
)
p.add_argument(
'--fontconfig-library',
type=str,
default=Options.fontconfig_library,
help='The filename argument passed to dlopen for libfontconfig.'
' This can be used to change the name of the loaded library or specify an absolute path.'
)
p.add_argument(
'--disable-link-time-optimization',
dest='link_time_optimization',