FABRIC.md item 4.4t: confine REPL text rendering to the CANVAS box
vt100's TTF-mode text grid now operates within the per-arch 640x480 box (computed in 4.4o, pixel-verified in 4.4p) instead of the full framebuffer: box-origin offset in px_of()/py_of(), box-derived cols/rows (53x20) set before the 4.4q scrollback allocation depends on them, mode-aware erase_display()/reverse-index fill, and a new box-scoped fb_scroll_rect() alongside the existing whole-framebuffer fb_scroll_rows() (bitmap/boot mode unaffected either way). Also clears the full framebuffer once at the bitmap-to-TTF switch so leftover boot debris doesn't sit frozen outside the box now that erase_display(2) is box-scoped afterward. Three-arch QEMU boot + pixel-scanned screendumps confirm zero non-background pixels land outside the box on amd64, aarch64, and riscv64. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
1f3ec3554e
commit
c92be2768f
@@ -305,3 +305,56 @@ void fb_scroll_rows(uint32_t char_rows, uint32_t bg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* fb_scroll_rect — box-confined scroll (FABRIC.md item 4.4t).
|
||||
*
|
||||
* Same non-volatile bulk-copy rationale as fb_scroll_rows() above, but
|
||||
* bounded to a caller-supplied rectangle instead of the whole framebuffer --
|
||||
* needed once REPL text is confined to the 4.4o CANVAS box, since a
|
||||
* whole-framebuffer scroll would drag pixels from outside the box (e.g. a
|
||||
* future 4.4r border) into the text area. Takes pixel_rows directly rather
|
||||
* than char_rows * a hardcoded cell height, since TTF mode's cell height
|
||||
* (24px) differs from bitmap mode's (16px * scale).
|
||||
*/
|
||||
void fb_scroll_rect(uint32_t x, uint32_t y, uint32_t w, uint32_t h,
|
||||
uint32_t pixel_rows, uint32_t bg)
|
||||
{
|
||||
uint32_t src_y, dst_y, px;
|
||||
uint32_t packed_bg;
|
||||
uint32_t *fb = (uint32_t *)(uintptr_t)g_fb.base;
|
||||
uint32_t x_end, y_end;
|
||||
|
||||
if (!g_fb.ready || pixel_rows == 0 || w == 0 || h == 0) return;
|
||||
|
||||
x_end = x + w;
|
||||
y_end = y + h;
|
||||
if (x_end > g_fb.width) x_end = g_fb.width;
|
||||
if (y_end > g_fb.height) y_end = g_fb.height;
|
||||
if (x >= x_end || y >= y_end) return;
|
||||
|
||||
if (pixel_rows >= (y_end - y)) {
|
||||
fb_fill_rect(x, y, x_end - x, y_end - y, bg);
|
||||
return;
|
||||
}
|
||||
|
||||
packed_bg = pack_pixel(bg);
|
||||
|
||||
/* Copy pixel rows upward, bounded to [x, x_end) */
|
||||
for (dst_y = y; dst_y + pixel_rows < y_end; dst_y++) {
|
||||
src_y = dst_y + pixel_rows;
|
||||
uint32_t *src = fb + src_y * g_fb.stride + x;
|
||||
uint32_t *dst = fb + dst_y * g_fb.stride + x;
|
||||
for (px = 0; px < (x_end - x); px++) {
|
||||
dst[px] = src[px];
|
||||
}
|
||||
}
|
||||
|
||||
/* Clear the newly exposed rows at the bottom of the rect */
|
||||
for (dst_y = y_end - pixel_rows; dst_y < y_end; dst_y++) {
|
||||
uint32_t *row = fb + dst_y * g_fb.stride + x;
|
||||
for (px = 0; px < (x_end - x); px++) {
|
||||
row[px] = packed_bg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+80
-15
@@ -80,6 +80,28 @@ static ttf_font_t g_ttf_font;
|
||||
static int g_ttf_ready = 0;
|
||||
static ttf_raster_cache_t g_ttf_cache;
|
||||
|
||||
/* -----------------------------------------------------------------------
|
||||
* FABRIC.md item 4.4t: confine REPL text (TTF mode only) to the 640x480
|
||||
* CANVAS box computed in 4.4o and pixel-verified in 4.4p. Per-arch raster
|
||||
* top-left origin, from those items' measurements. Boot/POST (bitmap mode)
|
||||
* is untouched -- g_origin_x/g_origin_y default to (0,0) and only change
|
||||
* once vt100_enable_ttf() runs.
|
||||
* --------------------------------------------------------------------- */
|
||||
#if defined(__x86_64__) || defined(__i386__)
|
||||
#define VT100_BOX_ORIGIN_X 320u
|
||||
#define VT100_BOX_ORIGIN_Y 104u
|
||||
#else
|
||||
#define VT100_BOX_ORIGIN_X 80u
|
||||
#define VT100_BOX_ORIGIN_Y 4u
|
||||
#endif
|
||||
#define VT100_BOX_W 640u
|
||||
#define VT100_BOX_H 480u
|
||||
#define VT100_BOX_COLS 53u /* 640 / VT100_TTF_CELL_W_PX, exact */
|
||||
#define VT100_BOX_ROWS 20u /* 480 / VT100_TTF_CELL_H_PX, exact */
|
||||
|
||||
static uint32_t g_origin_x = 0; /* box top-left, TTF mode only; 0 in bitmap mode */
|
||||
static uint32_t g_origin_y = 0;
|
||||
|
||||
/* -----------------------------------------------------------------------
|
||||
* FABRIC.md item 4.4q: REPL scrollback. Text-only (not pixel snapshots --
|
||||
* at 1000 lines x ~cols bytes this is tens of KB; a pixel-snapshot ring
|
||||
@@ -196,9 +218,11 @@ static void apply_sgr(void);
|
||||
static uint32_t cell_w(void) { return g_glyph_mode == VT_GLYPH_TTF ? VT100_TTF_CELL_W_PX : fb_cell_w(); }
|
||||
static uint32_t cell_h(void) { return g_glyph_mode == VT_GLYPH_TTF ? VT100_TTF_CELL_H_PX : fb_cell_h(); }
|
||||
|
||||
/* Pixel position of the top-left corner of character cell (cx, cy) */
|
||||
static uint32_t px_of(uint32_t col) { return col * cell_w(); }
|
||||
static uint32_t py_of(uint32_t row) { return row * cell_h(); }
|
||||
/* Pixel position of the top-left corner of character cell (cx, cy),
|
||||
* offset by the box origin (4.4t) -- (0,0) in bitmap mode, so boot/POST
|
||||
* is unaffected. */
|
||||
static uint32_t px_of(uint32_t col) { return g_origin_x + col * cell_w(); }
|
||||
static uint32_t py_of(uint32_t row) { return g_origin_y + row * cell_h(); }
|
||||
|
||||
/* Effective fg / bg accounting for reverse video */
|
||||
static uint32_t eff_fg(void) { return g_vt.reverse ? g_vt.bg : g_vt.fg; }
|
||||
@@ -362,14 +386,25 @@ void vt100_enable_ttf(void)
|
||||
|
||||
g_glyph_mode = VT_GLYPH_TTF;
|
||||
|
||||
/* Cell geometry just changed (font_8x16's fixed 8x16 vs. TTF's 12x24,
|
||||
* see this file's 4.4j block above) -- re-derive cols/rows and clear,
|
||||
* the same as vt100_init() itself does. This only ever runs once, at
|
||||
* the POST-to-REPL boundary (sk_repl()), so nothing of value is on
|
||||
* screen yet to preserve. */
|
||||
g_vt.cols = fb_width() / cell_w();
|
||||
g_vt.rows = fb_height() / cell_h();
|
||||
/* FABRIC.md item 4.4t: switch to the box-confined coordinate system
|
||||
* before anything below derives cols/rows/stride from it -- the
|
||||
* scrollback allocation block just below depends on g_vt.cols via
|
||||
* g_line_stride, so origin/cols/rows MUST be set first or every
|
||||
* shadow/ring index silently corrupts instead of crashing. */
|
||||
g_origin_x = VT100_BOX_ORIGIN_X;
|
||||
g_origin_y = VT100_BOX_ORIGIN_Y;
|
||||
g_vt.cols = VT100_BOX_COLS;
|
||||
g_vt.rows = VT100_BOX_ROWS;
|
||||
g_vt.cx = g_vt.cy = 0;
|
||||
|
||||
/* One-time full-screen clear at the bitmap-to-TTF transition: the boot/
|
||||
* POST scroll left the whole framebuffer covered in font_8x16 text, and
|
||||
* from here on erase_display(2) is box-scoped (see its mode-aware
|
||||
* branch below) -- without this, that debris would sit frozen outside
|
||||
* the box forever instead of leaving clean CANVAS around it. This is
|
||||
* the one and only whole-framebuffer wipe in TTF mode; every later
|
||||
* ESC[2J stays box-scoped. */
|
||||
fb_fill_rect(0, 0, fb_width(), fb_height(), g_vt.def_bg);
|
||||
erase_display(2);
|
||||
|
||||
/* FABRIC.md item 4.4q: allocate the scrollback shadow + ring buffers
|
||||
@@ -380,6 +415,17 @@ void vt100_enable_ttf(void)
|
||||
{
|
||||
uint32_t i;
|
||||
g_line_stride = g_vt.cols + 1u;
|
||||
/* 4.4t: cols is now the fixed box width (53) -- verify the stride
|
||||
* came out right rather than assuming the ordering above landed
|
||||
* correctly (advisor-flagged failure mode: silent corruption, not
|
||||
* a crash, if this ever drifts). */
|
||||
if (g_line_stride != VT100_BOX_COLS + 1u) {
|
||||
log_message(LOG_ERROR, "vt100: unexpected line_stride=%u (want %u), scrollback unavailable",
|
||||
g_line_stride, VT100_BOX_COLS + 1u);
|
||||
g_shadow = (void *)0;
|
||||
g_ring = (void *)0;
|
||||
return;
|
||||
}
|
||||
g_shadow = (char *)kmalloc((size_t)g_vt.rows * g_line_stride);
|
||||
g_ring = (char *)kmalloc((size_t)VT100_SCROLLBACK_MAX_LINES * g_line_stride);
|
||||
if (!g_shadow || !g_ring) {
|
||||
@@ -521,7 +567,15 @@ static void scroll_up(uint32_t lines)
|
||||
}
|
||||
}
|
||||
|
||||
fb_scroll_rows(lines, g_vt.def_bg);
|
||||
/* 4.4t: box-scoped scroll in TTF mode (fb_scroll_rows() is a
|
||||
* whole-framebuffer blit and would drag pixels from outside the box);
|
||||
* bitmap mode (boot/POST) keeps the original full-framebuffer path. */
|
||||
if (g_glyph_mode == VT_GLYPH_TTF) {
|
||||
fb_scroll_rect(g_origin_x, g_origin_y, VT100_BOX_W, VT100_BOX_H,
|
||||
lines * cell_h(), g_vt.def_bg);
|
||||
} else {
|
||||
fb_scroll_rows(lines, g_vt.def_bg);
|
||||
}
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------
|
||||
@@ -558,8 +612,14 @@ static void erase_display(int mode)
|
||||
}
|
||||
erase_line_range(g_vt.cy, 0, g_vt.cx + 1);
|
||||
} else {
|
||||
/* erase entire screen */
|
||||
fb_fill_rect(0, 0, fb_width(), fb_height(), g_vt.def_bg);
|
||||
/* erase entire screen -- 4.4t: box-scoped in TTF mode (a
|
||||
* full-framebuffer wipe here would blank CANVAS outside the box),
|
||||
* full-screen in bitmap mode as before (boot/POST unaffected). */
|
||||
if (g_glyph_mode == VT_GLYPH_TTF) {
|
||||
fb_fill_rect(g_origin_x, g_origin_y, VT100_BOX_W, VT100_BOX_H, g_vt.def_bg);
|
||||
} else {
|
||||
fb_fill_rect(0, 0, fb_width(), fb_height(), g_vt.def_bg);
|
||||
}
|
||||
g_vt.cx = g_vt.cy = 0;
|
||||
}
|
||||
}
|
||||
@@ -804,8 +864,13 @@ void vt100_putc(char raw)
|
||||
/* RI reverse index: scroll down (insert line at top) */
|
||||
if (g_vt.cy == 0) {
|
||||
/* Scroll display down: not trivially done without a
|
||||
* full back-buffer; fill top row with bg instead. */
|
||||
fb_fill_rect(0, 0, fb_width(), cell_h(), g_vt.def_bg);
|
||||
* full back-buffer; fill top row with bg instead.
|
||||
* 4.4t: box-scoped in TTF mode, full-width in bitmap mode. */
|
||||
if (g_glyph_mode == VT_GLYPH_TTF) {
|
||||
fb_fill_rect(g_origin_x, g_origin_y, VT100_BOX_W, cell_h(), g_vt.def_bg);
|
||||
} else {
|
||||
fb_fill_rect(0, 0, fb_width(), cell_h(), g_vt.def_bg);
|
||||
}
|
||||
} else {
|
||||
g_vt.cy--;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user