FABRIC-2.md §I.9 follow-on: blinking | cursor instead of static block
Captain Bob asked for the framebuffer cursor to render as a blinking vertical bar rather than the previous static solid-block glyph. vt100_draw_cursor() (hal/vt100.c) now fills a thin bar (cell_w()/8, min 1px, full cell height) at the cursor's left edge instead of the whole cell -- an I-beam shape. vt100_erase_cursor() is unchanged (clearing the whole cell already safely covers the narrower bar). Blinking is new in repl.c: sk_console_readline()'s idle branch toggles the cursor on/off every SK_CURSOR_BLINK_INTERVAL (50 ticks, 500ms at 100Hz) via alternating console_fb_draw_cursor()/console_fb_erase_cursor() calls, independent of the heartbeat/idle-beat mechanism the §I.9 fix just touched (deliberately not reused, to avoid recoupling to that path). Runs regardless of n, so it blinks whether sitting at a bare prompt or paused mid-edit. Every deterministic draw site (initial prompt, prompt reanchor, backspace, character echo) now goes through a new helper, sk_cursor_show(), which resets the blink cycle to "on" and redraws -- typing always shows a solid cursor, never mid-blink. Verified via the mandatory foreground 3-arch QEMU acceptance boot: amd64 (logs/20260905-021054, extensive live interactive typing including multi-line : / ; word definitions and error cases, prompts stayed correctly attached throughout), aarch64 (logs/20260905-021551), riscv64 (logs/20260905-022324) -- all three reached (zuse) ok> and shut down cleanly via BYE. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
8edb95b65d
commit
70dc8beba4
+14
-10
@@ -354,20 +354,24 @@ static void draw_cursor_glyph(uint8_t ch)
|
||||
}
|
||||
}
|
||||
|
||||
/* Solid block cursor at the current position (g_vt.cx, g_vt.cy). Not
|
||||
* blinking -- a static indicator, simplest thing that actually shows
|
||||
* where typed input lands, callable as often as the caller likes since
|
||||
* it is idempotent (always draws the same thing at the current position).
|
||||
* A character typed at this position naturally overwrites it --
|
||||
* draw_cursor_glyph() always fills the whole cell with bg before drawing
|
||||
* a glyph -- but *leaving* this position without drawing a character
|
||||
* (Enter/newline) does not, which is what vt100_erase_cursor() below is
|
||||
* for. No-op outside TTF mode or graphics mode (4.4ab's
|
||||
/* Thin vertical-bar ("I-beam") cursor at the current position (g_vt.cx,
|
||||
* g_vt.cy) -- 2026-09-05, replaces the earlier solid block glyph per
|
||||
* request. Idempotent (always draws the same bar at the current
|
||||
* position), so callable as often as the caller likes; blinking (repl.c,
|
||||
* sk_console_readline()'s idle loop) is layered on top by alternating
|
||||
* calls to this and vt100_erase_cursor() on a timer, not by anything in
|
||||
* here. A character typed at this position naturally overwrites the bar
|
||||
* -- draw_cursor_glyph() always fills the whole cell with bg before
|
||||
* drawing a glyph -- but *leaving* this position without drawing a
|
||||
* character (Enter/newline) does not, which is what vt100_erase_cursor()
|
||||
* below is for. No-op outside TTF mode or graphics mode (4.4ab's
|
||||
* g_terminal_visible), matching every other draw call in this file. */
|
||||
void vt100_draw_cursor(void)
|
||||
{
|
||||
if (!g_terminal_visible || g_glyph_mode != VT_GLYPH_TTF) return;
|
||||
fb_fill_rect(px_of(g_vt.cx), py_of(g_vt.cy), cell_w(), cell_h(), eff_fg());
|
||||
uint32_t bar_w = cell_w() / 8u;
|
||||
if (bar_w < 1u) bar_w = 1u;
|
||||
fb_fill_rect(px_of(g_vt.cx), py_of(g_vt.cy), bar_w, cell_h(), eff_fg());
|
||||
}
|
||||
|
||||
/* Erases whatever vt100_draw_cursor() last drew at the current position,
|
||||
|
||||
+39
-4
@@ -144,6 +144,28 @@ blkio_dev_t *sk_repl_get_attached_blk_dev(void) {
|
||||
|
||||
static uint64_t g_last_beat_tick; /* zero-initialized (BSS) */
|
||||
|
||||
/* Cursor blink, 2026-09-05: the framebuffer cursor (now a thin vertical
|
||||
* bar, vt100.c's vt100_draw_cursor()) blinks on/off every
|
||||
* SK_CURSOR_BLINK_INTERVAL ticks while sk_console_readline()'s idle loop
|
||||
* is spinning -- i.e. whenever nothing has been typed for that long,
|
||||
* whether sitting at a bare prompt or paused mid-edit. g_cursor_visible
|
||||
* tracks which half of the blink cycle is current; sk_cursor_show() below
|
||||
* is the single place that resets the cycle back to "on" and redraws --
|
||||
* every deterministic draw site (fresh prompt, echoed character,
|
||||
* backspace) calls it instead of vt100_draw_cursor() directly, so typing
|
||||
* always shows a solid cursor rather than possibly landing mid-blink. */
|
||||
#define SK_CURSOR_BLINK_INTERVAL 50u /* ticks between blink toggles (500 ms at 100 Hz) */
|
||||
|
||||
static uint64_t g_cursor_blink_tick; /* zero-initialized (BSS) */
|
||||
static int g_cursor_visible = 1;
|
||||
|
||||
static void sk_cursor_show(void)
|
||||
{
|
||||
g_cursor_visible = 1;
|
||||
g_cursor_blink_tick = heartbeat_ticks();
|
||||
console_fb_draw_cursor();
|
||||
}
|
||||
|
||||
/* Reentrancy guards for the MSG-TICK pump inside sk_repl_idle().
|
||||
*
|
||||
* sk_repl_idle() runs vm_interpret(mama, ...) (below) to VM-EXEC MSG-TICK
|
||||
@@ -591,7 +613,7 @@ int sk_console_readline(char* buf, int size, VM* active_vm, int reanchor_prompt)
|
||||
uint64_t prompt_tx_mark = console_tx_count();
|
||||
|
||||
buf[0] = '\0';
|
||||
console_fb_draw_cursor(); /* show the cursor at the bare prompt, before any input */
|
||||
sk_cursor_show(); /* show the cursor at the bare prompt, before any input */
|
||||
|
||||
for (;;) {
|
||||
int c;
|
||||
@@ -637,6 +659,19 @@ int sk_console_readline(char* buf, int size, VM* active_vm, int reanchor_prompt)
|
||||
sk_repl_idle(active_vm);
|
||||
}
|
||||
|
||||
/* Blink the cursor while idle (no key ready this iteration),
|
||||
* regardless of n -- a real terminal blinks whether sitting at
|
||||
* a bare prompt or paused mid-edit. sk_cursor_show() (called
|
||||
* from every deterministic draw site below and at entry) resets
|
||||
* this cycle to "on" on every real keystroke, so typing never
|
||||
* looks like it landed mid-blink. */
|
||||
if (now - g_cursor_blink_tick >= SK_CURSOR_BLINK_INTERVAL) {
|
||||
g_cursor_blink_tick = now;
|
||||
g_cursor_visible = !g_cursor_visible;
|
||||
if (g_cursor_visible) console_fb_draw_cursor();
|
||||
else console_fb_erase_cursor();
|
||||
}
|
||||
|
||||
/*
|
||||
* Re-anchor the prompt (FABRIC-0.md 4.4a unified prompt: print
|
||||
* only "ok> " here -- console_putc() auto-prefixes the current
|
||||
@@ -659,7 +694,7 @@ int sk_console_readline(char* buf, int size, VM* active_vm, int reanchor_prompt)
|
||||
console_tx_count() != prompt_tx_mark)
|
||||
{
|
||||
sk_print_prompt();
|
||||
console_fb_draw_cursor();
|
||||
sk_cursor_show();
|
||||
prompt_tx_mark = console_tx_count();
|
||||
}
|
||||
|
||||
@@ -689,7 +724,7 @@ int sk_console_readline(char* buf, int size, VM* active_vm, int reanchor_prompt)
|
||||
console_putc('\b');
|
||||
console_putc(' ');
|
||||
console_putc('\b');
|
||||
console_fb_draw_cursor();
|
||||
sk_cursor_show();
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -699,7 +734,7 @@ int sk_console_readline(char* buf, int size, VM* active_vm, int reanchor_prompt)
|
||||
buf[n++] = (char)c;
|
||||
buf[n] = '\0';
|
||||
console_putc((char)c); /* echo */
|
||||
console_fb_draw_cursor();
|
||||
sk_cursor_show();
|
||||
}
|
||||
|
||||
buf[n] = '\0';
|
||||
|
||||
Reference in New Issue
Block a user