Implement basic support for selecting font variations in fontconfig

This commit is contained in:
Kovid Goyal 2024-04-25 15:13:48 +05:30
parent 6e3c9856d1
commit 56f8fb9f75
No known key found for this signature in database
GPG key ID: 06BC317B515ACE7C
10 changed files with 233 additions and 105 deletions

View file

@ -2653,7 +2653,7 @@ def apply_new_options(self, opts: Options) -> None:
# Update font data
set_scale(opts.box_drawing_scale)
from .fonts.render import set_font_family
set_font_family(opts, debug_font_matching=self.args.debug_font_fallback)
set_font_family(opts)
for os_window_id, tm in self.os_window_map.items():
if tm is not None:
os_window_font_size(os_window_id, opts.font_size, True)

View file

@ -1,6 +1,6 @@
import termios
from ctypes import Array, c_ubyte
from typing import Any, Callable, Dict, Iterator, List, Literal, NewType, Optional, Tuple, TypedDict, Union, overload
from typing import Any, Callable, Dict, Iterator, List, Literal, NewType, NotRequired, Optional, Tuple, TypedDict, Union, overload
from kitty.boss import Boss
from kitty.fonts import FontFeature, VariableData
@ -394,6 +394,10 @@ class FontConfigPattern(TypedDict):
color: bool
variable: bool
# The following two are used by C code to get a face from the pattern
named_style: NotRequired[int]
axes: NotRequired[List[float]]
def fc_list(spacing: int = -1, allow_bitmapped_fonts: bool = False, only_variable: bool = False) -> Tuple[FontConfigPattern, ...]:
pass
@ -421,6 +425,7 @@ class Face:
def __init__(self, descriptor: Optional[FontConfigPattern] = None, path: str = '', index: int = 0): ...
def get_variable_data(self) -> VariableData: ...
def identify_for_debug(self) -> str: ...
def display_name(self) -> str: ...
class CoreTextFont(TypedDict):
@ -447,6 +452,7 @@ class CTFace:
def __init__(self, descriptor: Optional[CoreTextFont] = None, path: str = ''): ...
def get_variable_data(self) -> VariableData: ...
def identify_for_debug(self) -> str: ...
def display_name(self) -> str: ...
def coretext_all_fonts() -> Tuple[CoreTextFont, ...]:
@ -903,8 +909,18 @@ def concat_cells(cell_width: int, cell_height: int, is_32_bit: bool, cells: Tupl
pass
def current_fonts() -> Dict[str, Any]:
pass
FontFace = Union[Face, CTFace]
class CurrentFonts(TypedDict):
medium: FontFace
bold: FontFace
italic: FontFace
bi: FontFace
symbol: Tuple[FontFace, ...]
fallback: Tuple[FontFace, ...]
def current_fonts() -> CurrentFonts: ...
def remove_window(os_window_id: int, tab_id: int, window_id: int) -> None:

View file

@ -395,25 +395,40 @@ end:
PyObject*
specialize_font_descriptor(PyObject *base_descriptor, FONTS_DATA_HANDLE fg) {
ensure_initialized();
PyObject *p = PyDict_GetItemString(base_descriptor, "path"), *ans = NULL;
PyObject *p = PyDict_GetItemString(base_descriptor, "path");
PyObject *idx = PyDict_GetItemString(base_descriptor, "index");
if (p == NULL) { PyErr_SetString(PyExc_ValueError, "Base descriptor has no path"); return NULL; }
if (idx == NULL) { PyErr_SetString(PyExc_ValueError, "Base descriptor has no index"); return NULL; }
unsigned long face_idx = PyLong_AsUnsignedLong(idx);
if (PyErr_Occurred()) return NULL;
FcPattern *pat = FcPatternCreate();
if (pat == NULL) return PyErr_NoMemory();
long face_idx = MAX(0, PyLong_AsLong(idx));
RAII_PyObject(ans, NULL);
AP(FcPatternAddString, FC_FILE, (const FcChar8*)PyUnicode_AsUTF8(p), "path");
AP(FcPatternAddInteger, FC_INDEX, face_idx, "index");
AP(FcPatternAddDouble, FC_SIZE, fg->font_sz_in_pts, "size");
AP(FcPatternAddDouble, FC_DPI, (fg->logical_dpi_x + fg->logical_dpi_y) / 2.0, "dpi");
ans = _fc_match(pat);
FcPatternDestroy(pat); pat = NULL;
if (face_idx > 0) {
// For some reason FcFontMatch sets the index to zero, so manually restore it.
PyDict_SetItemString(ans, "index", idx);
if (PyDict_SetItemString(ans, "index", idx) != 0) return NULL;
}
end:
if (pat != NULL) FcPatternDestroy(pat);
PyObject *named_style = PyDict_GetItemString(base_descriptor, "named_style");
if (named_style) {
if (PyDict_SetItemString(ans, "named_style", named_style) != 0) return NULL;
}
PyObject *axes = PyDict_GetItemString(base_descriptor, "axes");
if (axes) {
if (PyDict_SetItemString(ans, "axes", axes) != 0) return NULL;
}
Py_INCREF(ans);
return ans;
end:
if (pat) FcPatternDestroy(pat);
return NULL;
}
bool

View file

@ -1635,25 +1635,31 @@ concat_cells(PyObject UNUSED *self, PyObject *args) {
static PyObject*
current_fonts(PYNOARG) {
if (!num_font_groups) { PyErr_SetString(PyExc_RuntimeError, "must create font group first"); return NULL; }
PyObject *ans = PyDict_New();
RAII_PyObject(ans, PyDict_New());
if (!ans) return NULL;
FontGroup *fg = font_groups;
#define SET(key, val) {if (PyDict_SetItemString(ans, #key, fg->fonts[val].face) != 0) { goto error; }}
#define SET(key, val) {if (PyDict_SetItemString(ans, #key, fg->fonts[val].face) != 0) { return NULL; }}
SET(medium, fg->medium_font_idx);
if (fg->bold_font_idx > 0) SET(bold, fg->bold_font_idx);
if (fg->italic_font_idx > 0) SET(italic, fg->italic_font_idx);
if (fg->bi_font_idx > 0) SET(bi, fg->bi_font_idx);
PyObject *ff = PyTuple_New(fg->fallback_fonts_count);
if (!ff) goto error;
unsigned num_symbol_fonts = fg->first_fallback_font_idx - fg->first_symbol_font_idx;
RAII_PyObject(ss, PyTuple_New(num_symbol_fonts));
if (!ss) return NULL;
for (size_t i = 0; i < num_symbol_fonts; i++) {
Py_INCREF(fg->fonts[fg->first_symbol_font_idx + i].face);
PyTuple_SET_ITEM(ss, i, fg->fonts[fg->first_symbol_font_idx + i].face);
}
if (PyDict_SetItemString(ans, "symbol", ss) != 0) return NULL;
RAII_PyObject(ff, PyTuple_New(fg->fallback_fonts_count));
if (!ff) return NULL;
for (size_t i = 0; i < fg->fallback_fonts_count; i++) {
Py_INCREF(fg->fonts[fg->first_fallback_font_idx + i].face);
PyTuple_SET_ITEM(ff, i, fg->fonts[fg->first_fallback_font_idx + i].face);
}
PyDict_SetItemString(ans, "fallback", ff);
Py_CLEAR(ff);
if (PyDict_SetItemString(ans, "fallback", ff) != 0) return NULL;
Py_INCREF(ans);
return ans;
error:
Py_CLEAR(ans); return NULL;
#undef SET
}

View file

@ -1,5 +1,5 @@
from enum import Enum, IntEnum, auto
from typing import Dict, NamedTuple, Optional, Tuple, TypedDict, Union
from typing import Dict, NamedTuple, Tuple, TypedDict, Union
from kitty.typing import CoreTextFont, FontConfigPattern
@ -24,8 +24,8 @@ class VariableAxis(TypedDict):
class NamedStyle(TypedDict):
axis_values: Dict[str, float]
name: Optional[str]
psname: Optional[str]
name: str
psname: str # can be empty string when not present
class VariableData(TypedDict):

View file

@ -5,7 +5,7 @@
from typing import Dict, Generator, Iterable, List, Optional, Tuple
from kitty.fast_data_types import CTFace, coretext_all_fonts
from kitty.fonts import FontFeature, FontSpec, VariableData
from kitty.fonts import FontSpec, VariableData
from kitty.options.types import Options
from kitty.typing import CoreTextFont
from kitty.utils import log_error
@ -52,11 +52,6 @@ def list_fonts() -> Generator[ListedFont, None, None]:
'is_variable': fd['variable'], 'descriptor': fd}
def find_font_features(postscript_name: str) -> Tuple[FontFeature, ...]:
"""Not Implemented"""
return ()
def find_best_match(family: str, bold: bool = False, italic: bool = False, ignore_face: Optional[CoreTextFont] = None) -> CoreTextFont:
q = re.sub(r'\s+', ' ', family.lower())
font_map = all_fonts_map()
@ -124,8 +119,17 @@ def font_for_family(family: str) -> Tuple[CoreTextFont, bool, bool]:
return ans, ans['bold'], ans['italic']
cache_for_variable_data_by_path: Dict[str, VariableData] = {}
def get_variable_data_for_descriptor(f: ListedFont) -> VariableData:
return CTFace(descriptor=descriptor(f)).get_variable_data()
d = descriptor(f)
if not d['path']:
return CTFace(descriptor=d).get_variable_data()
ans = cache_for_variable_data_by_path.get(d['path'])
if ans is None:
ans = cache_for_variable_data_by_path[d['path']] = CTFace(descriptor=d).get_variable_data()
return ans
def descriptor(f: ListedFont) -> CoreTextFont:

View file

@ -3,7 +3,7 @@
import re
from functools import lru_cache
from typing import Dict, Generator, List, Optional, Tuple, cast
from typing import Callable, Dict, Generator, List, Literal, NamedTuple, Optional, Tuple, cast
from kitty.fast_data_types import (
FC_DUAL,
@ -15,15 +15,12 @@
FC_WIDTH_NORMAL,
Face,
fc_list,
fc_match_postscript_name,
parse_font_feature,
)
from kitty.fast_data_types import fc_match as fc_match_impl
from kitty.options.types import Options
from kitty.typing import FontConfigPattern
from kitty.utils import log_error
from . import FontFeature, FontSpec, ListedFont, VariableData
from . import FontSpec, ListedFont, VariableData
attr_map = {(False, False): 'font_family',
(True, False): 'bold_font',
@ -31,7 +28,8 @@
(True, True): 'bold_italic_font'}
FontMap = Dict[str, Dict[str, List[FontConfigPattern]]]
FontCollectionMapType = Literal['family_map', 'ps_map', 'full_map']
FontMap = Dict[FontCollectionMapType, Dict[str, List[FontConfigPattern]]]
def create_font_map(all_fonts: Tuple[FontConfigPattern, ...]) -> FontMap:
@ -84,47 +82,56 @@ def fc_match(family: str, bold: bool, italic: bool, spacing: int = FC_MONO) -> F
return fc_match_impl(family, bold, italic, spacing)
def find_font_features(postscript_name: str) -> Tuple[FontFeature, ...]:
pat = fc_match_postscript_name(postscript_name)
class Score(NamedTuple):
variable_score: int
style_score: int
monospace_score: int
width_score: int
if pat.get('postscript_name') != postscript_name or 'fontfeatures' not in pat:
return ()
Scorer = Callable[[FontConfigPattern], Score]
features = []
for feat in pat['fontfeatures']:
try:
parsed = parse_font_feature(feat)
except ValueError:
log_error(f'Ignoring invalid font feature: {feat}')
else:
features.append(FontFeature(feat, parsed))
def create_scorer(bold: bool = False, italic: bool = False, monospaced: bool = True, prefer_variable: bool = False) -> Scorer:
return tuple(features)
def score(candidate: FontConfigPattern) -> Score:
variable_score = 0 if prefer_variable and candidate['variable'] else 1
bold_score = abs((FC_WEIGHT_BOLD if bold else FC_WEIGHT_REGULAR) - candidate.get('weight', 0))
italic_score = abs((FC_SLANT_ITALIC if italic else FC_SLANT_ROMAN) - candidate.get('slant', 0))
monospace_match = 0
if monospaced:
monospace_match = 0 if candidate.get('spacing') == 'MONO' else 1
width_score = abs(candidate.get('width', FC_WIDTH_NORMAL) - FC_WIDTH_NORMAL)
return Score(variable_score, bold_score + italic_score, monospace_match, width_score)
return score
def find_best_match_in_candidates(
candidates: List[FontConfigPattern], scorer: Scorer, is_medium_face: bool
) -> Optional[FontConfigPattern]:
if not candidates:
return None
if len(candidates) == 1 and not is_medium_face and candidates[0].get('family') == candidates[0].get('full_name'):
# IBM Plex Mono does this, where the full name of the regular font
# face is the same as its family name
return None
candidates.sort(key=scorer)
return candidates[0]
def find_best_match(family: str, bold: bool = False, italic: bool = False, monospaced: bool = True) -> FontConfigPattern:
q = family_name_to_key(family)
font_map = all_fonts_map(monospaced)
def score(candidate: FontConfigPattern) -> Tuple[int, int, int]:
bold_score = abs((FC_WEIGHT_BOLD if bold else FC_WEIGHT_REGULAR) - candidate.get('weight', 0))
italic_score = abs((FC_SLANT_ITALIC if italic else FC_SLANT_ROMAN) - candidate.get('slant', 0))
monospace_match = 0 if candidate.get('spacing') == 'MONO' else 1
width_score = abs(candidate.get('width', FC_WIDTH_NORMAL) - FC_WIDTH_NORMAL)
return bold_score + italic_score, monospace_match, width_score
scorer = create_scorer(bold, italic, monospaced)
is_medium_face = not bold and not italic
# First look for an exact match
for selector in ('ps_map', 'full_map', 'family_map'):
candidates = font_map[selector].get(q)
if not candidates:
continue
if len(candidates) == 1 and (bold or italic) and candidates[0].get('family') == candidates[0].get('full_name'):
# IBM Plex Mono does this, where the full name of the regular font
# face is the same as its family name
continue
candidates.sort(key=score)
return candidates[0]
exact_match = (
find_best_match_in_candidates(font_map['ps_map'].get(q, []), scorer, is_medium_face) or
find_best_match_in_candidates(font_map['full_map'].get(q, []), scorer, is_medium_face) or
find_best_match_in_candidates(font_map['family_map'].get(q, []), scorer, is_medium_face)
)
if exact_match:
return exact_match
# Use fc-match to see if we can find a monospaced font that matches family
# When aliases are defined, spacing can cause the incorrect font to be
@ -135,6 +142,7 @@ def score(candidate: FontConfigPattern) -> Tuple[int, int, int]:
tries = (dual_possibility, mono_possibility) if any_possibility == dual_possibility else (mono_possibility, dual_possibility)
for possibility in tries:
for key, map_key in (('postscript_name', 'ps_map'), ('full_name', 'full_map'), ('family', 'family_map')):
map_key = cast(FontCollectionMapType, map_key)
val: Optional[str] = cast(Optional[str], possibility.get(key))
if val:
candidates = font_map[map_key].get(family_name_to_key(val))
@ -146,19 +154,83 @@ def score(candidate: FontConfigPattern) -> Tuple[int, int, int]:
family_name_candidates = font_map['family_map'].get(family_name_to_key(candidates[0]['family']))
if family_name_candidates and len(family_name_candidates) > 1:
candidates = family_name_candidates
return sorted(candidates, key=score)[0]
return sorted(candidates, key=scorer)[0]
# Use fc-match with a generic family
family = 'monospace' if monospaced else 'sans-serif'
return fc_match(family, bold, italic)
def get_fine_grained_font(
spec: FontSpec, bold: bool = False, italic: bool = False, medium_font_spec: FontSpec = FontSpec(),
resolved_medium_font: Optional[FontConfigPattern] = None, monospaced: bool = True
) -> FontConfigPattern:
font_map = all_fonts_map(monospaced)
scorer = create_scorer(bold, italic, monospaced, prefer_variable=bool(spec.axes) or bool(spec.style))
is_medium_face = not bold and not italic
if spec.postscript_name:
q = find_best_match_in_candidates(font_map['ps_map'].get(family_name_to_key(spec.postscript_name), []), scorer, is_medium_face)
if q:
return q
if spec.full_name:
q = find_best_match_in_candidates(font_map['full_map'].get(family_name_to_key(spec.full_name), []), scorer, is_medium_face)
if q:
return q
if spec.family:
candidates = font_map['family_map'].get(family_name_to_key(spec.family), [])
if spec.style:
qs = spec.style.lower()
candidates = [x for x in candidates if x['style'].lower() == qs]
q = find_best_match_in_candidates(candidates, scorer, is_medium_face)
if q:
return q
# Use fc-match with a generic family
family = 'monospace' if monospaced else 'sans-serif'
return fc_match(family, bold, italic)
def apply_variation_to_pattern(pat: FontConfigPattern, spec: FontSpec) -> FontConfigPattern:
if not pat['variable']:
return pat
vd = Face(descriptor=pat).get_variable_data()
if spec.style:
q = spec.style.lower()
for i, ns in enumerate(vd['named_styles']):
if ns.get('psname') and ns['psname'].lower() == q:
pat['named_style'] = i
break
else:
for i, ns in enumerate(vd['named_styles']):
if ns['name'].lower() == q:
pat['named_style'] = i
break
tag_map, name_map = {}, {}
axes = [ax['default'] for ax in vd['axes']]
for i, ax in enumerate(vd['axes']):
tag_map[ax['tag']] = i
if ax['strid']:
name_map[ax['strid'].lower()] = i
changed = False
for axspec in spec.axes:
qname = axspec[0]
axis = tag_map.get(qname)
if axis is None:
axis = name_map.get(qname.lower())
if axis is not None:
axes[axis] = axspec[1]
changed = True
if changed:
pat['axes'] = axes
return pat
def get_font_from_spec(
spec: FontSpec, bold: bool = False, italic: bool = False, medium_font_spec: FontSpec = FontSpec(),
resolved_medium_font: Optional[FontConfigPattern] = None
) -> FontConfigPattern:
if not spec.is_system:
raise NotImplementedError('TODO: Implement me')
return apply_variation_to_pattern(get_fine_grained_font(spec, bold, italic, medium_font_spec, resolved_medium_font), spec)
family = spec.system
if family == 'auto' and (bold or italic):
assert resolved_medium_font is not None
@ -191,8 +263,17 @@ def descriptor(f: ListedFont) -> FontConfigPattern:
return d
cache_for_variable_data_by_path: Dict[str, VariableData] = {}
def get_variable_data_for_descriptor(f: ListedFont) -> VariableData:
return Face(descriptor=descriptor(f)).get_variable_data()
d = descriptor(f)
if not d['path']:
return Face(descriptor=d).get_variable_data()
ans = cache_for_variable_data_by_path.get(d['path'])
if ans is None:
ans = cache_for_variable_data_by_path[d['path']] = Face(descriptor=d).get_variable_data()
return ans
def prune_family_group(g: List[ListedFont]) -> List[ListedFont]:

View file

@ -12,6 +12,7 @@
NUM_UNDERLINE_STYLES,
Screen,
create_test_font_group,
current_fonts,
get_fallback_font,
get_options,
set_font_data,
@ -29,11 +30,9 @@
from kitty.utils import log_error
if is_macos:
from .core_text import find_font_features
from .core_text import font_for_family as font_for_family_macos
from .core_text import get_font_files as get_font_files_coretext
else:
from .fontconfig import find_font_features
from .fontconfig import font_for_family as font_for_family_fontconfig
from .fontconfig import get_font_files as get_font_files_fontconfig
@ -165,27 +164,19 @@ def descriptor_for_idx(idx: int) -> Tuple[FontObject, bool, bool]:
return current_faces[idx]
def dump_faces(ftypes: List[str], indices: Dict[str, int]) -> None:
def face_str(f: Tuple[FontObject, bool, bool]) -> str:
fo = f[0]
if 'index' in fo:
return '{}:{}'.format(fo['path'], cast('FontConfigPattern', fo)['index'])
fo = cast('CoreTextFont', fo)
return fo['path']
log_error('Preloaded font faces:')
log_error('normal face:', face_str(current_faces[0]))
for ftype in ftypes:
if indices[ftype]:
log_error(ftype, 'face:', face_str(current_faces[indices[ftype]]))
si_faces = current_faces[max(indices.values())+1:]
if si_faces:
log_error('Symbol map faces:')
for face in si_faces:
log_error(face_str(face))
def dump_font_debug() -> None:
cf = current_fonts()
log_error('Text fonts:')
for key, text in {'medium': 'Normal', 'bold': 'Bold', 'italic': 'Italic', 'bi': 'Bold-Italic'}.items():
log_error(f' {text}:', cf[key].identify_for_debug()) # type: ignore
ss = cf['symbol']
if ss:
log_error('Symbol map fonts:')
for s in ss:
log_error(' ' + s.identify_for_debug())
def set_font_family(opts: Optional[Options] = None, override_font_size: Optional[float] = None, debug_font_matching: bool = False) -> None:
def set_font_family(opts: Optional[Options] = None, override_font_size: Optional[float] = None) -> None:
global current_faces
opts = opts or defaults
sz = override_font_size or opts.font_size
@ -201,16 +192,10 @@ def set_font_family(opts: Optional[Options] = None, override_font_size: Optional
sm = create_symbol_map(opts)
ns = create_narrow_symbols(opts)
num_symbol_fonts = len(current_faces) - before
font_features = {}
for face, _, _ in current_faces:
font_features[face['postscript_name']] = find_font_features(face['postscript_name'])
font_features.update(opts.font_features)
if debug_font_matching:
dump_faces(ftypes, indices)
set_font_data(
render_box_drawing, prerender_function, descriptor_for_idx,
indices['bold'], indices['italic'], indices['bi'], num_symbol_fonts,
sm, sz, font_features, ns
sm, sz, opts.font_features.copy(), ns
)

View file

@ -265,13 +265,32 @@ face_from_descriptor(PyObject *descriptor, FONTS_DATA_HANDLE fg) {
D(hinting, PyObject_IsTrue, true);
D(hint_style, PyLong_AsLong, true);
#undef D
Face *self = (Face *)Face_Type.tp_alloc(&Face_Type, 0);
if (self != NULL) {
int error = FT_New_Face(library, path, index, &(self->face));
if(error) { Py_CLEAR(self); return set_load_error(path, error); }
if (!init_ft_face(self, PyDict_GetItemString(descriptor, "path"), hinting, hint_style, fg)) { Py_CLEAR(self); return NULL; }
RAII_PyObject(retval, Face_Type.tp_alloc(&Face_Type, 0));
if (retval != NULL) {
Face *self = (Face *)retval;
int error;
if ((error = FT_New_Face(library, path, index, &(self->face)))) { self->face = NULL; set_load_error(path, error); }
if (!init_ft_face(self, PyDict_GetItemString(descriptor, "path"), hinting, hint_style, fg)) return NULL;
PyObject *ns = PyDict_GetItemString(descriptor, "named_style");
if (ns) {
unsigned long index = PyLong_AsUnsignedLong(ns);
if (PyErr_Occurred()) return NULL;
if ((error = FT_Set_Named_Instance(self->face, index + 1))) return set_load_error(path, error);
}
PyObject *axes = PyDict_GetItemString(descriptor, "axes");
if (axes) {
RAII_ALLOC(FT_Fixed, coords, malloc(sizeof(FT_Fixed) * PyList_GET_SIZE(axes)));
for (Py_ssize_t i = 0; i < PyList_GET_SIZE(axes); i++) {
PyObject *t = PyList_GET_ITEM(axes, i);
double val = PyFloat_AsDouble(t);
if (PyErr_Occurred()) return NULL;
coords[i] = (FT_Fixed)(val * 65536.0);
}
if ((error = FT_Set_Var_Design_Coordinates(self->face, PyList_GET_SIZE(axes), coords))) return set_load_error(path, error);
}
}
return (PyObject*)self;
Py_XINCREF(retval);
return retval;
}
static PyObject*
@ -768,7 +787,7 @@ convert_named_style_to_python(Face *face, const FT_Var_Named_Style *src, FT_Var_
if (!name) PyErr_Clear();
RAII_PyObject(psname, src->psid == 0xffff ? NULL : _get_best_name(face, src->psid));
if (!psname) PyErr_Clear();
return Py_BuildValue("{sO sO sO}", "axis_values", axis_values, "name", name ? name : Py_None, "psname", psname ? psname : Py_None);
return Py_BuildValue("{sO sO sO}", "axis_values", axis_values, "name", name ? name : PyUnicode_FromString(""), "psname", psname ? psname : PyUnicode_FromString(""));
}
static PyObject*

View file

@ -44,7 +44,7 @@
set_options,
)
from .fonts.box_drawing import set_scale
from .fonts.render import set_font_family
from .fonts.render import dump_font_debug, set_font_family
from .options.types import Options
from .options.utils import DELETE_ENV_VAR
from .os_window_size import edge_spacing, initial_window_size_func
@ -225,6 +225,8 @@ def _run_app(opts: Options, args: CLIOptions, bad_lines: Sequence[BadLine] = (),
wincls, wstate, load_all_shaders, disallow_override_title=bool(args.title), layer_shell_config=run_app.layer_shell_config)
boss = Boss(opts, args, cached_values, global_shortcuts, talk_fd)
boss.start(window_id, startup_sessions)
if args.debug_font_fallback:
dump_font_debug()
if bad_lines or boss.misc_config_errors:
boss.show_bad_config_lines(bad_lines, boss.misc_config_errors)
boss.misc_config_errors = []
@ -246,7 +248,7 @@ def __call__(self, opts: Options, args: CLIOptions, bad_lines: Sequence[BadLine]
set_scale(opts.box_drawing_scale)
set_options(opts, is_wayland(), args.debug_rendering, args.debug_font_fallback)
try:
set_font_family(opts, debug_font_matching=args.debug_font_fallback)
set_font_family(opts)
_run_app(opts, args, bad_lines, talk_fd)
finally:
set_options(None)