From ef38c975b994bb131eb327454c836c0ea84efd20 Mon Sep 17 00:00:00 2001 From: MaxsTechReview Date: Wed, 7 Oct 2026 02:23:27 -0400 Subject: [PATCH] Draw Steam's CJK and Thai text with the device's fonts (#374) Steam ships no CJK font and the rootfs only has DejaVu, so a phone set to Chinese, Japanese or Korean got Steam in that language with every character drawn as a box. Bind Android's /system/fonts into the guest, index it again whenever it changes, and prefer the matching Noto CJK face for each language. --- .../launcher/runtime/LinuxRuntime.java | 12 +++++++++++ tools/linuxfs/desktop/droiddeck-desktop | 2 +- .../overlay/usr/local/bin/droiddeck-session | 20 +++++++++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/droiddeck/launcher/runtime/LinuxRuntime.java b/app/src/main/java/com/droiddeck/launcher/runtime/LinuxRuntime.java index 35a73971f..38a33980a 100644 --- a/app/src/main/java/com/droiddeck/launcher/runtime/LinuxRuntime.java +++ b/app/src/main/java/com/droiddeck/launcher/runtime/LinuxRuntime.java @@ -42,6 +42,8 @@ public final class LinuxRuntime { * session ended at once. A long host path behind it is proot's to shorten. */ public static final String GUEST_RUNTIME_DIR = "/run/droiddeck"; + private static final String SYSTEM_FONTS = "/system/fonts"; + private static final String GUEST_SYSTEM_FONTS = "/usr/local/share/fonts/android"; /** Where every Linux session's debug log lands: public, so a user can just hand the folder over. */ public static final String DEBUG_LOG_DIR = "DroidDeck"; @@ -207,6 +209,7 @@ public final class LinuxRuntime { File shm = new File(context.getCacheDir(), "shm"); shm.mkdirs(); bind(cmd, shm.getPath() + ":/dev/shm"); + bindSystemFonts(cmd, root); // Android denies apps these; glibc, Steam and libcap read them at startup. File fakeProc = new File(root, "etc/droiddeck/proc"); @@ -465,6 +468,15 @@ public final class LinuxRuntime { return byType; } + private static void bindSystemFonts(List cmd, File root) { + File fonts = new File(SYSTEM_FONTS); + File target = new File(root, GUEST_SYSTEM_FONTS.substring(1)); + if (!fonts.isDirectory() || !fonts.canRead() || !new File(root, "usr/local").isDirectory()) return; + if (target.isDirectory() || target.mkdirs()) { + bind(cmd, SYSTEM_FONTS + ":" + GUEST_SYSTEM_FONTS); + } + } + private static void bind(List cmd, String spec) { cmd.add("-b"); cmd.add(spec); diff --git a/tools/linuxfs/desktop/droiddeck-desktop b/tools/linuxfs/desktop/droiddeck-desktop index b69c2a993..395799784 100644 --- a/tools/linuxfs/desktop/droiddeck-desktop +++ b/tools/linuxfs/desktop/droiddeck-desktop @@ -92,7 +92,7 @@ find "$XDG_RUNTIME_DIR" -mindepth 1 -maxdepth 1 ! -name 'wayland-0*' ! -name 'ke # fontconfig's rules (sans-serif -> DejaVu Sans and the rest) live as links in /etc/fonts/conf.d, # and the rootfs has none: applications then find fonts but no default font. Firefox logs # "no fonts" and draws pages without text. -if [ -d /usr/share/fontconfig/conf.default ] && ! ls /etc/fonts/conf.d/*.conf >/dev/null 2>&1; then +if [ -d /usr/share/fontconfig/conf.default ] && ! ls /etc/fonts/conf.d/*.conf 2>/dev/null | grep -qv droiddeck; then for f in /usr/share/fontconfig/conf.default/*.conf; do ln -sf "$f" /etc/fonts/conf.d/; done echo "== fontconfig: linked $(ls /etc/fonts/conf.d/*.conf | wc -l) default rules" fi diff --git a/tools/linuxfs/overlay/usr/local/bin/droiddeck-session b/tools/linuxfs/overlay/usr/local/bin/droiddeck-session index 09b769b3b..5bdabd997 100755 --- a/tools/linuxfs/overlay/usr/local/bin/droiddeck-session +++ b/tools/linuxfs/overlay/usr/local/bin/droiddeck-session @@ -136,6 +136,26 @@ program_cores() { } if [ -z "${BL_INSIDE:-}" ]; then + device_fonts=/usr/local/share/fonts/android + if [ -d "$device_fonts" ] && [ -d /etc/fonts/conf.d ] && command -v fc-cache >/dev/null 2>&1; then + fonts_rules=$( + echo '' + for pair in "zh-cn=Noto Sans CJK SC" "zh-sg=Noto Sans CJK SC" "zh-tw=Noto Sans CJK TC" "zh-hk=Noto Sans CJK HK" \ + "zh-mo=Noto Sans CJK HK" "ja=Noto Sans CJK JP" "ko=Noto Sans CJK KR" "th=Noto Sans Thai"; do + printf '%s%s\n' \ + "${pair%%=*}" "${pair#*=}" + done + echo '' + ) + fonts_conf=/etc/fonts/conf.d/66-droiddeck-device-fonts.conf + [ "$fonts_rules" = "$(cat "$fonts_conf" 2>/dev/null)" ] || printf '%s\n' "$fonts_rules" > "$fonts_conf" + fonts_seen=$(ls -lL "$device_fonts" 2>/dev/null | md5sum) + fonts_stamp=/var/cache/fontconfig/.droiddeck-android-fonts + if [ "$fonts_seen" != "$(cat "$fonts_stamp" 2>/dev/null)" ]; then + fc-cache -f "${device_fonts%/*}" >/dev/null 2>&1 && printf '%s\n' "$fonts_seen" > "$fonts_stamp" + echo "== fontconfig: indexed $(ls "$device_fonts" | wc -l) device fonts, zh falls back to $(fc-match -f '%{family[0]}' ':lang=zh-cn' 2>/dev/null)" + fi + fi # The compositor may still be starting, and a socket left by an earlier session is not # a compositor: probe until a connection succeeds. for _ in $(seq 1 100); do