SchOS/.claude/memory/schunix-text-rendering.md
Nexoniarz a1c965da1e include: one folder per subsystem, and includes that name it
Sixty-five headers sat side by side in include/, so `#include "irq.h"`
said nothing about where irq.h was or what it belonged to, and errno.h,
stat.h and fcntl.h at the top shadowed the system's for any host tool
built with -I./include. They are in folders now -- arch/ core/ fs/
dev/ gfx/ net/ uapi/ (and ldls/ for the Linux driver layer) -- and
every include names the folder, as Linux's do: "core/proc.h".

Done mechanically and checked the hard way: the kernel built from this
tree disassembles identically to the one built from the tree before
it. The full image boots, and userland is the same set of programs.
Paths in docs and comments follow; docs/guides/layout.md has a table
of what is where, and tools/fix-includes.py rewrites old-style
includes from the folders in the tree, for code written before this.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
2026-10-23 15:05:12 +02:00

4.1 KiB

name description metadata
schunix-text-rendering The text layer that replaces the 8x16 character-cell grid — glyph metrics, surfaces, coverage blending, UTF-8.
node_type type originSessionId modified
memory project 9d8a2aef-bcf0-47a0-96c6-f706efab4a95 2026-08-08T22:45:34.934Z

Built 2026-08-09. include/gfx/text.h + kernel/dev/text.c. Nexoniarz: "using ANSI fonts in 2026 is a not funny joke."

The font was never the problem — the grid was. The console indexes font_data[256][16] and steps the cursor eight pixels. A grid cannot do variable-width glyphs, cannot position text off a multiple of eight, cannot anti-alias, and cannot represent a codepoint above 255. A nicer bitmap fixes none of that.

Three decisions that make the layer worth having:

  • Glyphs carry metrics (w, h, bearing_x/y, advance). Variable-width text is that; everything else follows. text_measure() exists because you cannot centre or lay out anything without it.
  • Draws into a struct surface, not the screen. {pixels, stride, width, height}. A compositor draws off-screen and presents; rendering that writes straight to the VGA aperture is useless to it. Works on the virtio-gpu back buffer, a window buffer, or the VGA aperture, none of which know about each other.
  • struct font_ops behind an interface, and glyphs carry 8-bit coverage, not bits. The bitmap font fills it with 0 and 255 only, which costs nothing — and a TrueType rasteriser then plugs in with no caller changing, because it already has somewhere to put anti-aliased edges. blend_px() reads the destination for the same reason.

Pen position is on the baseline, as every non-terminal text API does, so glyphs of different heights sit on a common line.

Verified with echo t > /proc/sysrq + tools/textshot.ps1: text at x=61 and x=63, a checkerboard background visible around the glyphs (no cell boxes), ┌─┐ █▓▒░ ° ± → • above U+00FF, and a string measured at 160 px then centred on it. The old console renders in the same screenshot with solid cell backgrounds — a useful side-by-side.

Traps hit

  • include/gfx/font.h defines font_data rather than declaring it, so including it from a second file duplicates the symbol under -fno-common. text.c declares extern unsigned char font_data[256][16]; instead; the header is generated data and is better left alone.
  • The built-in font is CP437, not Latin-1 (known trap in this codebase). to_cp437() maps the ~25 codepoints worth having — box drawing, blocks, arrows, degree. That table exists to be deleted when a real font lands; it is a bridge, not a design.
  • A missing glyph draws U+25A1 or ?, never nothing. Silently shorter text hides the fault.
  • utf8_next() always advances, even on malformed input — a decoder that does not is a hang, not a rendering artefact. Rejects surrogates and overlong encodings.

The typefaces, decided 2026-08-09

  • Roboto for the DE / WM / compositor. Apache-2.0 -- the same licence as Schunix, so it needs only a NOTICE entry.
  • JetBrains Mono for the terminal / TTY. SIL OFL 1.1, and it declares no Reserved Font Name (checked: the copyright line names none; the only "reserved font name" hit in OFL.txt is the licence defining the term). So a modified version may keep the name -- which matters, because pre-rasterising a font into bitmaps at build time is a modification and an RFN would have forbidden keeping the name on the result.

OFL keeps the font under OFL inside an Apache-2.0 system, does not reach anything else, and only forbids selling the font on its own. Both fetched and verified at source, not assumed. Full note in docs/licensing.md ("Fonts").

Neither is in the tree yet -- there is no rasteriser to use them, and shipping a TTF nothing can read would be dead weight.

Not done

The console still uses the old path; nothing is ported to this yet. Porting vga_putc onto it is the obvious follow-up and would delete the cell grid for good. A TTF rasteriser is a separate provider behind font_ops — the layer is what was blocking it, and it no longer is.