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

84 lines
4.1 KiB
Markdown

---
name: schunix-text-rendering
description: "The text layer that replaces the 8x16 character-cell grid — glyph metrics, surfaces, coverage blending, UTF-8."
metadata:
node_type: memory
type: project
originSessionId: 9d8a2aef-bcf0-47a0-96c6-f706efab4a95
modified: 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.