diff options
| author | salmonik <salmonik@tuta.io> | 2026-07-31 17:42:21 +0200 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-07-31 17:42:21 +0200 |
| commit | a2ffb8358cf4f4fc7230b5307c651d5badab0860 (patch) | |
| tree | 5412fa837a6832ca078443e7668a776ee760f75c /rsnix/src/vt.rs | |
| parent | vt: decode UTF-8, render box-drawing/arrow glyphs procedurally (diff) | |
| download | rs-dos-a2ffb8358cf4f4fc7230b5307c651d5badab0860.tar.gz rs-dos-a2ffb8358cf4f4fc7230b5307c651d5badab0860.zip | |
vt: don't strand stale inverse cursor cells on scroll/clear
The cursor was blitted as an inverse-video cell directly onto the
framebuffer, but that inversion was never recorded. RENDERED (the
diff grid) only stored the logical cell, so when scrolling shifted the
raw pixels up (gop::shift_up), the stale inverse block rode along
while the grids said 'blank' — and blank-vs-blank never re-blits.
Result: a cursor-shaped block at the end of every scrolled line,
surviving even 'clear'.
- flush() now mirrors the inverse colours into RENDERED when drawing
the cursor (and restores RENDERED when erasing it), and the diff
loop skips the visible cursor cell (owned by the cursor pass).
Any scroll/clear then sees CELLS != RENDERED at a stale-inverse
position and repaints it.
- memory.rs heap self-test: snapshot heap state before its own
allocations and assert the heap returns to that state, instead of
asserting it coalesces to a single empty block. The vt cell grids
(allocated earlier in boot) made the old 'free_blocks == 1' check
fail; the new check is correct regardless of prior live allocations.
Verified in QEMU: hexdump /dev/mem streams hundreds of lines, ^C,
screendump shows exactly one inverse cell (the live cursor); 'clear'
leaves exactly one (the fresh prompt cursor). heap self-test OK again.
Diffstat (limited to '')
| -rw-r--r-- | rsnix/src/vt.rs | 33 |
1 files changed, 26 insertions, 7 deletions
diff --git a/rsnix/src/vt.rs b/rsnix/src/vt.rs index 3b81d68..4db08fc 100644 --- a/rsnix/src/vt.rs +++ b/rsnix/src/vt.rs | |||
| @@ -485,20 +485,29 @@ pub fn clear_all() { | |||
| 485 | // --------------------------------------------------------------------------- | 485 | // --------------------------------------------------------------------------- |
| 486 | 486 | ||
| 487 | /// Blit every cell whose content differs from what the framebuffer shows, | 487 | /// Blit every cell whose content differs from what the framebuffer shows, |
| 488 | /// then draw the logical cursor (inverse video) on top. | 488 | /// then draw the logical cursor (inverse video) on top. The cursor's |
| 489 | /// inverse colours are mirrored into RENDERED so that pixel shifts | ||
| 490 | /// (scrolling) or full clears can never leave a stale inverse block that | ||
| 491 | /// the diff can't see (blank-vs-blank). | ||
| 489 | fn flush() { | 492 | fn flush() { |
| 490 | let rows = unsafe { ROWS }; | 493 | let rows = unsafe { ROWS }; |
| 491 | let cols = unsafe { COLS }; | 494 | let cols = unsafe { COLS }; |
| 492 | let n = rows * cols; | 495 | let n = rows * cols; |
| 496 | let cursor_visible = unsafe { CURSOR_VISIBLE }; | ||
| 497 | let (cr, cc) = unsafe { (CURSOR_R, CURSOR_C) }; | ||
| 493 | { | 498 | { |
| 494 | let cells = cells(); | 499 | let cells = cells(); |
| 495 | let rendered = rendered_cells(); | 500 | let rendered = rendered_cells(); |
| 496 | for i in 0..n { | 501 | for i in 0..n { |
| 502 | let row = i / cols; | ||
| 503 | let col = i % cols; | ||
| 504 | // The visible cursor cell is owned by the cursor pass below. | ||
| 505 | if cursor_visible && row == cr && col == cc { | ||
| 506 | continue; | ||
| 507 | } | ||
| 497 | let c = cells[i]; | 508 | let c = cells[i]; |
| 498 | let r = rendered[i]; | 509 | let r = rendered[i]; |
| 499 | if c != r { | 510 | if c != r { |
| 500 | let row = i / cols; | ||
| 501 | let col = i % cols; | ||
| 502 | let fg = PALETTE[c.fg as usize]; | 511 | let fg = PALETTE[c.fg as usize]; |
| 503 | let bg = PALETTE[c.bg as usize]; | 512 | let bg = PALETTE[c.bg as usize]; |
| 504 | gop::blit_glyph(col, row, c.ch, fg, bg); | 513 | gop::blit_glyph(col, row, c.ch, fg, bg); |
| @@ -510,20 +519,30 @@ fn flush() { | |||
| 510 | unsafe { | 519 | unsafe { |
| 511 | let (lr, lc) = LAST_CURSOR; | 520 | let (lr, lc) = LAST_CURSOR; |
| 512 | let moved = (lr, lc) != (CURSOR_R, CURSOR_C); | 521 | let moved = (lr, lc) != (CURSOR_R, CURSOR_C); |
| 513 | // Erase the previous cursor position (restore its normal colours). | 522 | // Erase the previous cursor position (restore its normal colours) |
| 523 | // and record it in RENDERED so the diff stays in sync. | ||
| 514 | if (moved || !CURSOR_VISIBLE) && lr < rows && lc < cols { | 524 | if (moved || !CURSOR_VISIBLE) && lr < rows && lc < cols { |
| 515 | let c = cells()[idx(lr, lc)]; | 525 | let i = idx(lr, lc); |
| 526 | let c = cells()[i]; | ||
| 516 | let fg = PALETTE[c.fg as usize]; | 527 | let fg = PALETTE[c.fg as usize]; |
| 517 | let bg = PALETTE[c.bg as usize]; | 528 | let bg = PALETTE[c.bg as usize]; |
| 518 | gop::blit_glyph(lc, lr, c.ch, fg, bg); | 529 | gop::blit_glyph(lc, lr, c.ch, fg, bg); |
| 530 | rendered_cells()[i] = c; | ||
| 519 | } | 531 | } |
| 520 | LAST_CURSOR = (CURSOR_R, CURSOR_C); | 532 | LAST_CURSOR = (CURSOR_R, CURSOR_C); |
| 521 | // Draw the cursor as an inverse-video cell. | 533 | // Draw the cursor as an inverse-video cell, mirroring the inverse |
| 534 | // colours into RENDERED (scrolling/clearing then repair it). | ||
| 522 | if CURSOR_VISIBLE && CURSOR_R < rows && CURSOR_C < cols { | 535 | if CURSOR_VISIBLE && CURSOR_R < rows && CURSOR_C < cols { |
| 523 | let c = cells()[idx(CURSOR_R, CURSOR_C)]; | 536 | let i = idx(CURSOR_R, CURSOR_C); |
| 537 | let c = cells()[i]; | ||
| 524 | let fg = PALETTE[c.fg as usize]; | 538 | let fg = PALETTE[c.fg as usize]; |
| 525 | let bg = PALETTE[c.bg as usize]; | 539 | let bg = PALETTE[c.bg as usize]; |
| 526 | gop::blit_glyph(CURSOR_C, CURSOR_R, c.ch, bg, fg); | 540 | gop::blit_glyph(CURSOR_C, CURSOR_R, c.ch, bg, fg); |
| 541 | rendered_cells()[i] = Cell { | ||
| 542 | ch: c.ch, | ||
| 543 | fg: c.bg, | ||
| 544 | bg: c.fg, | ||
| 545 | }; | ||
| 527 | } | 546 | } |
| 528 | } | 547 | } |
| 529 | } | 548 | } |
