From cb17c57452ccd2ccca77e99098402d4b9f61c0a5 Mon Sep 17 00:00:00 2001 From: salmonik Date: Thu, 20 Aug 2026 13:47:10 +0200 Subject: wm: F1 spawns a windowed shell — real shell, not a duplicate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Architecture: - vt: ACTIVE terminal redirect — when set, all gop_print!/put_char/write_str go there instead of HOME. Terminal::from_buffer() renders into a WM-owned pixel buffer. - tty: read_line_from_queue() — canonical-mode line input from the task's own message queue (TaskMsg::Key). Returns None on Close. - shell: run_windowed() — same shell as the console but reads from the message queue. Returns on ^D or window close. - wm: F1 calls SPAWN_TERMINAL_CB (set by rsdos). No shell logic in the WM. - rsdos: terminal_task callback + terminal_entry task — creates a 640x400 chrome window, wires up Terminal::from_buffer, sets ACTIVE, runs shell::run_windowed(), cleans up on exit. - shell: 'term' debug command that invokes the callback directly and logs each step to serial. --- rsnix/src/vt.rs | 96 ++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 81 insertions(+), 15 deletions(-) (limited to 'rsnix/src/vt.rs') diff --git a/rsnix/src/vt.rs b/rsnix/src/vt.rs index 12e2cdc..0d7cf9b 100644 --- a/rsnix/src/vt.rs +++ b/rsnix/src/vt.rs @@ -82,6 +82,7 @@ pub struct Terminal { last_sync_tick: u64, dirty_all: bool, buffer: Vec, + ext_buf: Option<*mut u32>, w: usize, h: usize, window_id: Option, @@ -131,20 +132,63 @@ impl Terminal { last_sync_tick: 0, dirty_all: true, buffer: vec![0; w * h], + ext_buf: None, w, h, window_id: None, } } + /// Build a terminal that renders into an externally-owned pixel buffer + /// (e.g. one allocated by the window manager). The caller must free + /// the buffer when the window is destroyed. + pub fn from_buffer(buf: *mut u32, w: usize, h: usize) -> Terminal { + let cols = w / 8; + let rows = h / 16; + Terminal { + cells: vec![BLANK; rows * cols], + rendered: vec![BLANK; rows * cols], + rows, + cols, + cursor_r: 1, + cursor_c: 0, + last_cursor: (1, 0), + cursor_visible: true, + sgr_fg: 7, + sgr_bg: 0, + parser: Parser::Ground, + private: false, + params: [0; 4], + nparams: 0, + cur: 0, + pending_scrolls: 0, + last_sync_tick: 0, + dirty_all: true, + buffer: Vec::new(), + ext_buf: Some(buf), + w, + h, + window_id: None, + } + } + + /// Raw pointer to the pixel buffer (owned or external). + fn buf_ptr(&mut self) -> *mut u32 { + if let Some(p) = self.ext_buf { + p + } else { + self.buffer.as_mut_ptr() + } + } + /// Attach this terminal to a WM window (so `flush` composites it). pub fn set_window(&mut self, id: usize) { self.window_id = Some(id); } /// Buffer pointer and pixel dimensions (for the WM to composite). - pub fn window_params(&self) -> (*const u32, usize, usize) { - (self.buffer.as_ptr(), self.w, self.h) + pub fn window_params(&mut self) -> (*const u32, usize, usize) { + (self.buf_ptr() as *const u32, self.w, self.h) } pub fn ready(&self) -> bool { @@ -191,7 +235,7 @@ impl Terminal { } let rows = self.rows; let cols = self.cols; - gop::buffer_shift_up(self.buffer.as_mut_ptr(), self.w, self.h, n * 16); + gop::buffer_shift_up(self.buf_ptr(), self.w, self.h, n * 16); // Mirror the pushed content in RENDERED (it describes the buffer). let total = rows * cols; self.rendered.copy_within(n * cols..total, 0); @@ -206,8 +250,8 @@ impl Terminal { /// Re-blit a region of this terminal's buffer into the shadow (used by /// the window manager to restore the console layer beneath a window). - pub fn repaint_region(&self, x: usize, y: usize, w: usize, h: usize) { - let src = unsafe { self.buffer.as_ptr().add(y * self.w + x) }; + pub fn repaint_region(&mut self, x: usize, y: usize, w: usize, h: usize) { + let src = unsafe { self.buf_ptr().add(y * self.w + x) }; gop::shadow_blit(x, y, w, h, src, self.w); } @@ -537,7 +581,7 @@ impl Terminal { let fg = PALETTE[c.fg as usize]; let bg = PALETTE[c.bg as usize]; gop::buffer_glyph_px( - self.buffer.as_mut_ptr(), + self.buf_ptr(), self.w, self.h, col * 8, @@ -560,7 +604,7 @@ impl Terminal { let fg = PALETTE[c.fg as usize]; let bg = PALETTE[c.bg as usize]; gop::buffer_glyph_px( - self.buffer.as_mut_ptr(), + self.buf_ptr(), self.w, self.h, lc * 8, @@ -578,7 +622,7 @@ impl Terminal { let fg = PALETTE[c.bg as usize]; let bg = PALETTE[c.fg as usize]; gop::buffer_glyph_px( - self.buffer.as_mut_ptr(), + self.buf_ptr(), self.w, self.h, cc * 8, @@ -602,7 +646,7 @@ impl Terminal { Some(id) => crate::wm::flush(id), None => { if let Some((x, y, w, h)) = dirty { - let src = unsafe { self.buffer.as_ptr().add(y * self.w + x) }; + let src = unsafe { self.buf_ptr().add(y * self.w + x) }; gop::shadow_blit(x, y, w, h, src, self.w); gop::push_rect(x, y, w, h); } @@ -616,6 +660,20 @@ impl Terminal { // --------------------------------------------------------------------------- static mut HOME: Option = None; +/// When set, all vt output goes here instead of HOME. Used by windowed +/// shells so that gop_print!/put_char/write_str write to the active +/// terminal's pixel buffer. +static mut ACTIVE: *mut Terminal = core::ptr::null_mut(); + +fn active() -> &'static mut Terminal { + unsafe { + if !ACTIVE.is_null() { + &mut *ACTIVE + } else { + home() + } + } +} fn home() -> &'static mut Terminal { unsafe { @@ -624,6 +682,14 @@ fn home() -> &'static mut Terminal { } } +/// Set the active terminal for output (pass null to restore the home +/// terminal). +pub fn set_active(term: *mut Terminal) { + unsafe { + ACTIVE = term; + } +} + /// Initialise the home terminal over a full-screen pixel buffer. Call once /// after `gop::init()`. pub fn init() { @@ -675,27 +741,27 @@ pub fn set_cursor_visible(v: bool) { } pub fn sync_scroll() { - home().sync_scroll(); + active().sync_scroll(); } pub fn repaint_region(x: usize, y: usize, w: usize, h: usize) { - home().repaint_region(x, y, w, h); + active().repaint_region(x, y, w, h); } pub fn write_bytes(b: &[u8]) { - home().write_bytes(b); + active().write_bytes(b); } pub fn write_str(s: &str) { - home().write_str(s); + active().write_str(s); } pub fn put_char(b: u8) { - home().put_char(b); + active().put_char(b); } pub fn clear_all() { - home().clear_all(); + active().clear_all(); } // --------------------------------------------------------------------------- -- cgit v1.2.3