FABRIC.md: item 4.4i -- glyph-draw call-site audit, not cleanly separable

Investigation only, no code change. draw_cursor_glyph() itself is
single-call-site as claimed, but found two things the item's original
framing missed:

1. A second, independent fb_draw_glyph() call site in
   erase_line_range() (partial-line/partial-screen erase), inconsistent
   with full-screen erase which already uses a pixel fb_fill_rect().
2. fb_cell_w()/fb_cell_h() are hardcoded 8*scale/16*scale literals
   matching font_8x16.c specifically, not derived from any generic
   font-metric abstraction; UNDERLINE_ROW hardcodes "row 14 of 16" of
   that same fixed grid.

So 4.4j has three things to retarget/generalize, not one. Font data
verified directly from JetBrainsMono-Regular.ttf's hmtx table (parsed
by hand, no fontTools available): all 95 printable ASCII glyphs share
one advance width (600/1000 em units) -- genuinely monospace for the
glyphs in use, not just by filename.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Robert Allan James
2026-08-11 19:39:54 -04:00
co-authored by Claude Sonnet 5
parent ae7c9429b4
commit 64c20c78eb
+41 -1
View File
@@ -5252,7 +5252,7 @@ document and committing that amendment as its own item.*
> [evidence/amd64/qemu-screenshot-20260811-173006-4.4h-colored-prompt.png](evidence/amd64/qemu-screenshot-20260811-173006-4.4h-colored-prompt.png)
> — `[Hera]` renders orange, `ok>` renders cyan, confirmed by direct visual inspection.
- [ ] **4.4i — Verify the VT100 CSI/SGR parser's glyph-draw call site is cleanly separable.**
- [x] **4.4i — Verify the VT100 CSI/SGR parser's glyph-draw call site is cleanly separable.**
Verification only, no code change. `vt100.c`'s CSI/SGR state machine (escape parsing, cursor
tracking, 16/256-color SGR) calls `draw_cursor_glyph()` from a single site in `put_char()`
confirm nothing else in the parser depends on `font_8x16.c`-specific assumptions.
@@ -5266,6 +5266,46 @@ document and committing that amendment as its own item.*
compatibility question is answered from the actual font data, in writing here.
*Refs:* §27.8.
> **Done 2026-08-11 — NOT cleanly separable as originally framed; two real gaps found and
> recorded here for 4.4j to address, not silently worked around.**
>
> **1. `draw_cursor_glyph()` itself is single-call-site, as claimed** — called only from
> `put_char()` (`vt100.c:442`), which is itself called from a single site in the main
> character-dispatch loop (`vt100.c:486`). Retargeting that one call is clean.
>
> **2. But there's a second, independent `fb_draw_glyph()` call site the item's framing
> missed:** `erase_line_range()` (`vt100.c:235-241`), used by both `erase_display()` and
> `erase_line()` for partial-line/partial-screen erase, draws a blank space glyph
> (`fb_draw_glyph(px, py, 0x20, fg, bg)`) per cell rather than filling a pixel rectangle.
> Full-screen erase (`erase_display(2)`, `vt100.c:261`) uses `fb_fill_rect()` instead — pixel-
> based, no font dependency. So today's erase behavior is already inconsistent between modes,
> and 4.4j must retarget *this* call site too, not just `draw_cursor_glyph()`'s.
>
> **3. The cell-grid model itself is hardcoded to `font_8x16`'s fixed metrics, not derived
> from a generic "current font" abstraction:** `fb_cell_w()`/`fb_cell_h()`
> (`framebuffer.c:129-130`) return `8u * g_fb.scale` / `16u * g_fb.scale` literally — not
> read from font data, just baked-in constants matching `font_8x16.c`'s glyph size. `vt100.c`
> builds `g_vt.cols`/`g_vt.rows` and all cursor pixel math (`px_of()`/`py_of()`) on top of
> these. Additionally, `UNDERLINE_ROW` (`vt100.c:49`, value 14) is explicitly commented
> "row 14 of 16" — hardcodes a position within `font_8x16`'s 16-scanline glyph grid. Neither
> of these is a call-site problem exactly (nothing here calls `font_8x16.c` functions
> directly), but both are real assumptions 4.4j needs to replace, not just the two draw
> calls.
>
> **4. Font data confirmed via `hmtx` table (not assumed from the filename):**
> `JetBrainsMono-Regular.ttf` — read directly with a minimal `sfnt`/`cmap`-format-4/`hmtx`
> parser (no `fontTools` available in this environment). `unitsPerEm=1000`,
> `numberOfHMetrics=1717`. All 95 printable ASCII glyphs (`0x20``0x7E`) resolve via `cmap`
> and share **exactly one** advance width: `600` font units, no exceptions. Genuinely
> monospace for the glyph set actually in use, confirmed from the font's own data — at any
> single chosen point size, `TTF-TEXT` advances will tile uniformly, compatible with the
> existing fixed-cell cursor model in principle (once that model no longer hardcodes
> `font_8x16`'s specific 8×16 dimensions per point 3 above).
>
> **Net effect on 4.4j's scope:** three things to retarget/generalize, not one —
> `draw_cursor_glyph()`'s call, `erase_line_range()`'s call, and the `fb_cell_w()`/
> `fb_cell_h()`/`UNDERLINE_ROW` metrics that currently assume `font_8x16` specifically.
- [ ] **4.4j — Retarget the glyph-draw call site from `font_8x16.c` to `TTF-TEXT`.** Depends
on 4.4i. **Boundary, corrected 2026-08-11:** `font_8x16.c`/VT100 keeps rendering everything
through and including **POST** (the parity/dictionary-hash self-test) — not just the