|
|
|
@@ -19,7 +19,7 @@ from typing import NamedTuple
|
|
|
|
|
from importlib.resources import as_file, files
|
|
|
|
|
from openpilot.common.swaglog import cloudlog
|
|
|
|
|
from openpilot.common.hardware import HARDWARE, PC
|
|
|
|
|
from openpilot.system.ui.lib.multilang import TRANSLATIONS_DIR, UNIFONT_LANGUAGES, multilang
|
|
|
|
|
from openpilot.system.ui.lib.multilang import FONT_FALLBACK_LANGUAGES, TRANSLATIONS_DIR, multilang
|
|
|
|
|
from openpilot.common.realtime import Ratekeeper
|
|
|
|
|
|
|
|
|
|
_DEFAULT_FPS = int(os.getenv("FPS", {'tizi': 20}.get(HARDWARE.get_device_type(), 60)))
|
|
|
|
@@ -93,6 +93,13 @@ FONT_SCALE = 1.242 if BIG_UI else 1.16
|
|
|
|
|
ASSETS_DIR = files("openpilot.selfdrive").joinpath("assets")
|
|
|
|
|
FONT_DIR = ASSETS_DIR.joinpath("fonts")
|
|
|
|
|
EXTRA_FONT_CHARS = "–‑✓×°§•X⚙✕◀▶✔⌫⇧␣○●↳çêüñ–‑✓×°§•€£¥"
|
|
|
|
|
NOTO_FONTS = {
|
|
|
|
|
"ja": "NotoSansCJKjp-Regular.otf",
|
|
|
|
|
"ko": "NotoSansCJKkr-Regular.otf",
|
|
|
|
|
"th": "NotoSansThai-Regular.ttf",
|
|
|
|
|
"zh-CHS": "NotoSansCJKsc-Regular.otf",
|
|
|
|
|
"zh-CHT": "NotoSansCJKtc-Regular.otf",
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class FontWeight(StrEnum):
|
|
|
|
@@ -109,9 +116,9 @@ class FontWeight(StrEnum):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def font_fallback(font: rl.Font) -> rl.Font:
|
|
|
|
|
"""Fall back to unifont for languages that require it."""
|
|
|
|
|
if multilang.requires_unifont():
|
|
|
|
|
return gui_app.font(FontWeight.UNIFONT)
|
|
|
|
|
"""Use a Noto fallback for languages not covered by Inter."""
|
|
|
|
|
if multilang.requires_font_fallback():
|
|
|
|
|
return gui_app.fallback_font()
|
|
|
|
|
return font
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -199,6 +206,7 @@ class GuiApplication:
|
|
|
|
|
self._set_log_callback()
|
|
|
|
|
|
|
|
|
|
self._fonts: dict[FontWeight, rl.Font] = {}
|
|
|
|
|
self._fallback_fonts: dict[str, rl.Font] = {}
|
|
|
|
|
self._width = width if width is not None else GuiApplication._default_width()
|
|
|
|
|
self._height = height if height is not None else GuiApplication._default_height()
|
|
|
|
|
|
|
|
|
@@ -554,6 +562,9 @@ class GuiApplication:
|
|
|
|
|
for font in self._fonts.values():
|
|
|
|
|
rl.unload_font(font)
|
|
|
|
|
self._fonts = {}
|
|
|
|
|
for font in self._fallback_fonts.values():
|
|
|
|
|
rl.unload_font(font)
|
|
|
|
|
self._fallback_fonts = {}
|
|
|
|
|
|
|
|
|
|
if self._render_texture is not None:
|
|
|
|
|
rl.unload_render_texture(self._render_texture)
|
|
|
|
@@ -676,6 +687,21 @@ class GuiApplication:
|
|
|
|
|
def font(self, font_weight: FontWeight = FontWeight.NORMAL) -> rl.Font:
|
|
|
|
|
return self._fonts[font_weight]
|
|
|
|
|
|
|
|
|
|
def fallback_font(self) -> rl.Font:
|
|
|
|
|
language = multilang.language
|
|
|
|
|
if language not in self._fallback_fonts:
|
|
|
|
|
chars = set(map(chr, range(32, 127))) | set(EXTRA_FONT_CHARS)
|
|
|
|
|
chars.update(TRANSLATIONS_DIR.joinpath(f"app_{language}.po").read_text(encoding="utf-8"))
|
|
|
|
|
codepoints = sorted(map(ord, chars))
|
|
|
|
|
codepoint_buffer = rl.ffi.new("int[]", codepoints)
|
|
|
|
|
with as_file(FONT_DIR) as fspath:
|
|
|
|
|
font = rl.load_font_ex((fspath / NOTO_FONTS[language]).as_posix(), 48,
|
|
|
|
|
rl.ffi.cast("int *", codepoint_buffer), len(codepoints))
|
|
|
|
|
rl.gen_texture_mipmaps(font.texture)
|
|
|
|
|
rl.set_texture_filter(font.texture, rl.TextureFilter.TEXTURE_FILTER_TRILINEAR)
|
|
|
|
|
self._fallback_fonts[language] = font
|
|
|
|
|
return self._fallback_fonts[language]
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def width(self):
|
|
|
|
|
return self._width
|
|
|
|
@@ -689,8 +715,8 @@ class GuiApplication:
|
|
|
|
|
unifont_chars = set(base_chars)
|
|
|
|
|
for language, code in multilang.languages.items():
|
|
|
|
|
unifont_chars.update(language)
|
|
|
|
|
chars = set(TRANSLATIONS_DIR.joinpath(f"app_{code}.po").read_text(encoding="utf-8"))
|
|
|
|
|
(unifont_chars if code in UNIFONT_LANGUAGES else base_chars).update(chars)
|
|
|
|
|
if code not in FONT_FALLBACK_LANGUAGES:
|
|
|
|
|
base_chars.update(TRANSLATIONS_DIR.joinpath(f"app_{code}.po").read_text(encoding="utf-8"))
|
|
|
|
|
|
|
|
|
|
for font_weight_file in FontWeight:
|
|
|
|
|
with as_file(FONT_DIR) as fspath:
|
|
|
|
@@ -703,6 +729,8 @@ class GuiApplication:
|
|
|
|
|
rl.gen_texture_mipmaps(font.texture)
|
|
|
|
|
rl.set_texture_filter(font.texture, rl.TextureFilter.TEXTURE_FILTER_TRILINEAR)
|
|
|
|
|
self._fonts[font_weight_file] = font
|
|
|
|
|
if multilang.requires_font_fallback():
|
|
|
|
|
self.fallback_font()
|
|
|
|
|
rl.gui_set_font(self._fonts[FontWeight.NORMAL])
|
|
|
|
|
|
|
|
|
|
def _set_styles(self):
|
|
|
|
|