Render UTF-8 names correctly in the Linux Insert menu #1389

Merged
bronze-ff merged 2 commits from fix/linux-menu-utf8 into main 2026-11-10 04:42:52 +00:00
bronze-ff commented 2026-11-10 00:10:40 +00:00 (Migrated from github.com)

SDL2 provides device names in UTF-8. The Linux Insert menu's GDI shim measures and draws each byte separately, which distorts names such as Micrófono. The menu also clips long labels at arbitrary byte offsets.

  • Decode Unicode code points in the Linux text measurement and drawing paths, with replacement glyphs for invalid bytes.
  • Keep marquee slices and ellipses at UTF-8 character boundaries, using the same boundary check as the saved microphone name.
  • Cover measurement and drawing with the bundled Inter font, including supplementary and malformed sequences.

The renderer fixture fails on main (173 px instead of 156 px for Micrófono) and passes with this change. I also checked the marquee and ellipsis logic with accented and CJK text, ran the Linux updater fixture, and ran the audio input and menu hit tests on Windows.

SDL2 audio device name contract

SDL2 provides device names in UTF-8. The Linux Insert menu's GDI shim measures and draws each byte separately, which distorts names such as `Micrófono`. The menu also clips long labels at arbitrary byte offsets. - Decode Unicode code points in the Linux text measurement and drawing paths, with replacement glyphs for invalid bytes. - Keep marquee slices and ellipses at UTF-8 character boundaries, using the same boundary check as the saved microphone name. - Cover measurement and drawing with the bundled Inter font, including supplementary and malformed sequences. The renderer fixture fails on `main` (173 px instead of 156 px for `Micrófono`) and passes with this change. I also checked the marquee and ellipsis logic with accented and CJK text, ran the Linux updater fixture, and ran the audio input and menu hit tests on Windows. [SDL2 audio device name contract](https://wiki.libsdl.org/SDL2/SDL_GetAudioDeviceName)
NotRexed commented 2026-11-10 03:49:54 +00:00 (Migrated from github.com)

This landed in the v2.0.12 branch (merged there for testing; it reaches main with that release), so closing. Thanks!

This landed in the v2.0.12 branch (merged there for testing; it reaches `main` with that release), so closing. Thanks!
NotRexed commented 2026-11-10 03:54:46 +00:00 (Migrated from github.com)

Reopened: closing this was a mistake on my side. It was tested in the v2.0.12 branch, and it will be merged into main here, so it shows as merged. Sorry for the noise.

Reopened: closing this was a mistake on my side. It was tested in the v2.0.12 branch, and it will be merged into main here, so it shows as merged. Sorry for the noise.
hckrman101 (Migrated from github.com) approved these changes 2026-11-10 04:17:06 +00:00
hckrman101 (Migrated from github.com) left a comment

Looks good. The decoder in text_codepoint is correct: overlongs, surrogates, > U+10FFFF, and truncated or stray continuation bytes each give one U+FFFD per bad byte. Width and draw use the same decoder and the same kerning order, and the char-boundary helper keeps mic_key behaving as before. The fixture runs against the bundled Inter fonts via fonts/*.ttf beside the fake exe, so it does not depend on the system font, and it is registered in run_all.py with a Windows skip.

Siblings: #1371 and #1405 only touch the face-loading code at the top of gdi_posix.c (face_load, faces_load), and this PR only touches the text functions further down, so no textual conflict there. The only likely conflict is the adjacent entries in tests/run_all.py. Logically they are independent too, because the test puts the bundled fonts first and #1371/#1405 only change the fallback path.

Nits, none blocking:

  • Glyphs the chosen font lacks (CJK with Inter or DejaVu) now render as that font's .notdef box per code point instead of per-byte garbage. That is better than before, but real CJK device names still won't look right without a fallback font. Probably fine as a follow-up; the description only claims correct slicing for CJK, which holds.
  • The marquee still advances marquee_pos one byte per tick (see inline).

CI: the Windows and builder jobs are green; the Linux Python checks and appimage were still pending when I looked.

Looks good. The decoder in `text_codepoint` is correct: overlongs, surrogates, > U+10FFFF, and truncated or stray continuation bytes each give one U+FFFD per bad byte. Width and draw use the same decoder and the same kerning order, and the char-boundary helper keeps `mic_key` behaving as before. The fixture runs against the bundled Inter fonts via `fonts/*.ttf` beside the fake exe, so it does not depend on the system font, and it is registered in `run_all.py` with a Windows skip. Siblings: #1371 and #1405 only touch the face-loading code at the top of `gdi_posix.c` (`face_load`, `faces_load`), and this PR only touches the text functions further down, so no textual conflict there. The only likely conflict is the adjacent entries in `tests/run_all.py`. Logically they are independent too, because the test puts the bundled fonts first and #1371/#1405 only change the fallback path. Nits, none blocking: - Glyphs the chosen font lacks (CJK with Inter or DejaVu) now render as that font's .notdef box per code point instead of per-byte garbage. That is better than before, but real CJK device names still won't look right without a fallback font. Probably fine as a follow-up; the description only claims correct slicing for CJK, which holds. - The marquee still advances `marquee_pos` one byte per tick (see inline). CI: the Windows and builder jobs are green; the Linux Python checks and appimage were still pending when I looked.
hckrman101 (Migrated from github.com) commented 2026-11-10 04:17:06 +00:00

Nit: marquee_pos still ticks per byte, so inside a 2 to 4 byte character it snaps back to the same from and the text holds still for 1 to 3 ticks before moving on. Harmless, but it makes the slide uneven on accented or CJK names. Stepping marquee_pos to the next char boundary in the tick code would even it out.

Nit: `marquee_pos` still ticks per byte, so inside a 2 to 4 byte character it snaps back to the same `from` and the text holds still for 1 to 3 ticks before moving on. Harmless, but it makes the slide uneven on accented or CJK names. Stepping `marquee_pos` to the next char boundary in the tick code would even it out.
Commenting is not possible because the repository is archived.
No description provided.