"Ą Ę Ś Ł Ż" drew as "?????" and the obvious diagnosis -- the font has no glyphs for them -- was wrong twice over. `struct vtcell.ch` was **one byte** holding a CP437 index, and the console mapped UTF-8 down to it on the way in. U+0104 does not fit in a byte. No font could have fixed that; the cell had to get wider first. So: `ch` is a uint32 codepoint, the mapping on the way in is gone, and the cell draws through kernel/dev/ttf.c against JetBrains Mono linked into the image. Cell size comes from the font's measured advance instead of a hardcoded 8x16. The grid costs 864 KB of BSS instead of 432 KB, which is the honest price of holding a codepoint per cell for six terminals at 240x75. The rasteriser needed three fixes before it could be trusted with this. **21 KB of locals on a 4 KB kernel stack.** `ttf_render` held an outline (512 points as three arrays) and a raster (1024 edges) as automatics. KSTACKSIZE is 4096. They walked off the bottom of the stack into whatever was underneath, which surfaced as user processes faulting on addresses they never touched and then a panic inside freevm() on an overwritten page directory. Nothing pointed anywhere near a font. **And then again, recursively.** load_outline() held a 4736-byte outline as a local *and recursed* for composite glyphs. Composite glyphs are exactly Ą, Ę, Ś, Ł, Ż -- so the second crash arrived the moment the first fix let Unicode reach the screen, looking like a fresh bug. Now a static outline per recursion level with a depth cap of 4 (the font's deepest real nesting is 2), and the leaf parse split into load_simple() so its 512-byte flag array is not multiplied by the depth. Worst chain measured from the object file: ~2.3 KB. Neither showed in tools/ttftest, which has an 8 MB stack. That is the standing hazard of one source file built for host and kernel: the host is most forgiving about exactly the resource the kernel is strictest about. **Component transforms were being skipped**, on the stated theory that a wrong transform is worse than none. It is not. `‘` in this font is the comma glyph with a scale of -1 -- a 180-degree rotation -- and the offset that ships with it only lands correctly after the rotation. Without it the mark went twelve pixels into a ten-pixel cell and was clipped away entirely, so the character read as missing from the font rather than as misplaced. F2Dot14 2x2 now, with SCALED_COMPONENT_OFFSET honoured. The glyph cache was ASCII-only, so every accented letter re-rasterised on every repaint: a screenful of Polish did hundreds of bezier flattenings per scroll where the same screenful of English did none. 95 reserved ASCII slots plus 224 shared slots keyed by codepoint, 64 KB pool. Verified 13680 cached renders byte-identical to direct ones across collisions and a pool flush. tools/ttftest speaks UTF-8 now. Iterating bytes meant it could only ever ask for ASCII, and ASCII is entirely simple glyphs -- it could not reach the code path that mattered. Also: resolvconf's success line moves behind /proc/bootmode, same rule as vgpud. A quiet boot opened with "resolvconf: nameserver 10.0.2.3" where the one line was supposed to be "Welcome to SchOS!". Failures stay loud. Verified on screen, not just on the host: Ą Ę Ś Ł Ż ć ń ó ź, Ω λ π Œ Ð Æ ß, ∞ ‘x’ → ≤ all render, panic=0 across four boots. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
94 lines
4.2 KiB
C
94 lines
4.2 KiB
C
#pragma once
|
|
|
|
// Drawing, and the text console under it, on whatever display is bound.
|
|
//
|
|
// This was kernel/dev/vga.c, and every consumer called it by that name:
|
|
// panic.c drew the crash screen with vga_fill_rect, the console printed with
|
|
// vga_putc, /dev/fb0 handed out vga_fb_phys(). None of that code has anything
|
|
// to do with a VGA -- it is bitmap drawing over a linear framebuffer, and the
|
|
// only VGA-specific part was where the framebuffer came from.
|
|
//
|
|
// So the driver pushes its scanout in here (gfx_attach_scanout) and everything
|
|
// above draws through these calls. A second display driver becomes a second
|
|
// caller of one function rather than an edit to the crash screen. This is the
|
|
// same arrangement as include/netdev.h, for the same reason.
|
|
//
|
|
// Not the same thing as the display class in include/display.h. That is the
|
|
// compositor's interface -- acquire a back buffer, present a rectangle, wait
|
|
// for vblank -- and it is about *frames*. This is the kernel's own drawing
|
|
// surface, used by the console and the crash screen, which do not double
|
|
// buffer and must keep working when nothing else in the system does.
|
|
|
|
// A linear framebuffer, exactly as the hardware presents it.
|
|
struct fb_info {
|
|
volatile uchar *ptr; // kernel-mapped base, or 0 if there is none
|
|
uint64 phys; // physical base, for mmap and for page tables
|
|
uint64 size; // bytes, stride * height rather than w * h * bpp
|
|
uint32 stride; // bytes per row, which may exceed width * bpp/8
|
|
uint32 bpp;
|
|
uint32 width, height;
|
|
};
|
|
|
|
// A driver publishing the framebuffer it owns. Called once, early: the console
|
|
// draws long before any bus is enumerated, so whoever owns the display the
|
|
// loader left running has to say so before then.
|
|
void gfx_attach_scanout(const struct fb_info *);
|
|
|
|
// The scanout as attached, for the few callers that need the memory itself --
|
|
// /dev/fb0 maps it, and fork copies the mapping. Returns 0 if none.
|
|
const struct fb_info *gfx_scanout(void);
|
|
|
|
int gfx_ready(void);
|
|
void gfx_dims(int *w, int *h);
|
|
uint32 gfx_width(void);
|
|
uint32 gfx_height(void);
|
|
uint64 gfx_fb_phys(void);
|
|
uint64 gfx_fb_size(void);
|
|
uint32 gfx_fb_stride(void);
|
|
uint32 gfx_fb_bpp(void);
|
|
volatile uchar *gfx_fb_ptr(void);
|
|
|
|
// Pixels and shapes.
|
|
void gfx_draw_pixel(int x, int y, uint32 color);
|
|
void gfx_clear(uint32 color);
|
|
void gfx_fill_rect(int x, int y, int w, int h, uint32 color);
|
|
void gfx_fill_circle(int cx, int cy, int r, uint32 color);
|
|
void gfx_draw_logo(int x, int y, uint32 color, uint32 bg);
|
|
void gfx_logo_centered(int cy, uint32 color, uint32 bg);
|
|
|
|
// The built-in 8x16 font: glyphs at a position, and text that leaves the
|
|
// cursor alone. The crash screen lays itself out rather than printing, which
|
|
// is what needs the second one.
|
|
void gfx_draw_char(int x, int y, uchar c, uint32 color);
|
|
void gfx_text_at(int x, int y, const char *s, uint32 color);
|
|
int gfx_char_w(void);
|
|
int gfx_char_h(void);
|
|
|
|
// The text console: a cursor, scrolling, and the erase operations an ANSI
|
|
// escape parser needs. See kernel/core/console.c.
|
|
void gfx_putc(int c, uint32 color);
|
|
void gfx_puts(int x, int y, char *s, uint32 color);
|
|
void gfx_set_bg(uint32 color);
|
|
void gfx_screen_reset(void);
|
|
int gfx_cols(void);
|
|
int gfx_rows(void);
|
|
|
|
// For the text layer in kernel/dev/vt.c: one cell with its own colours, and
|
|
// a screen scroll that leaves the cursor alone.
|
|
// One terminal cell. `cp` is a Unicode codepoint, not a byte -- see the note on
|
|
// struct vtcell in vt.h for why that distinction is the whole feature.
|
|
void gfx_draw_cell(int col, int row, uint32 cp, uint32 fg, uint32 bg);
|
|
|
|
// Bring the console typeface up, once, early. Falls back to the built-in
|
|
// bitmap and says nothing if the font will not open -- see the note in gfx.c
|
|
// on why it cannot print for itself.
|
|
void gfx_font_init(void);
|
|
int gfx_font_ready(void);
|
|
void gfx_cell_size(int *w, int *h);
|
|
// A filled rectangle in pixels, with the per-pixel checks hoisted out.
|
|
void gfx_fill(int x, int y, int w, int h, uint32 color);
|
|
void gfx_scroll_up(uint32 bg);
|
|
void gfx_cursor_get(int *col, int *row);
|
|
void gfx_cursor_set(int col, int row);
|
|
void gfx_erase_display(int mode);
|
|
void gfx_erase_line(int mode);
|