summaryrefslogtreecommitdiffstats
path: root/rsnix/src/vt.rs
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-08-20 13:47:10 +0200
committersalmonik <salmonik@tuta.io>2026-08-20 13:47:10 +0200
commitcb17c57452ccd2ccca77e99098402d4b9f61c0a5 (patch)
tree6566e0ddaa764c6675e9a56f03772de345170992 /rsnix/src/vt.rs
parentwm: integrate home terminal as window 0 + fix console focus after close (diff)
downloadrs-dos-cb17c57452ccd2ccca77e99098402d4b9f61c0a5.tar.gz
rs-dos-cb17c57452ccd2ccca77e99098402d4b9f61c0a5.zip
wm: F1 spawns a windowed shell — real shell, not a duplicate
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.
Diffstat (limited to '')
-rw-r--r--rsnix/src/vt.rs96
1 files changed, 81 insertions, 15 deletions
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 {
82 last_sync_tick: u64, 82 last_sync_tick: u64,
83 dirty_all: bool, 83 dirty_all: bool,
84 buffer: Vec<u32>, 84 buffer: Vec<u32>,
85 ext_buf: Option<*mut u32>,
85 w: usize, 86 w: usize,
86 h: usize, 87 h: usize,
87 window_id: Option<usize>, 88 window_id: Option<usize>,
@@ -131,20 +132,63 @@ impl Terminal {
131 last_sync_tick: 0, 132 last_sync_tick: 0,
132 dirty_all: true, 133 dirty_all: true,
133 buffer: vec![0; w * h], 134 buffer: vec![0; w * h],
135 ext_buf: None,
134 w, 136 w,
135 h, 137 h,
136 window_id: None, 138 window_id: None,
137 } 139 }
138 } 140 }
139 141
142 /// Build a terminal that renders into an externally-owned pixel buffer
143 /// (e.g. one allocated by the window manager). The caller must free
144 /// the buffer when the window is destroyed.
145 pub fn from_buffer(buf: *mut u32, w: usize, h: usize) -> Terminal {
146 let cols = w / 8;
147 let rows = h / 16;
148 Terminal {
149 cells: vec![BLANK; rows * cols],
150 rendered: vec![BLANK; rows * cols],
151 rows,
152 cols,
153 cursor_r: 1,
154 cursor_c: 0,
155 last_cursor: (1, 0),
156 cursor_visible: true,
157 sgr_fg: 7,
158 sgr_bg: 0,
159 parser: Parser::Ground,
160 private: false,
161 params: [0; 4],
162 nparams: 0,
163 cur: 0,
164 pending_scrolls: 0,
165 last_sync_tick: 0,
166 dirty_all: true,
167 buffer: Vec::new(),
168 ext_buf: Some(buf),
169 w,
170 h,
171 window_id: None,
172 }
173 }
174
175 /// Raw pointer to the pixel buffer (owned or external).
176 fn buf_ptr(&mut self) -> *mut u32 {
177 if let Some(p) = self.ext_buf {
178 p
179 } else {
180 self.buffer.as_mut_ptr()
181 }
182 }
183
140 /// Attach this terminal to a WM window (so `flush` composites it). 184 /// Attach this terminal to a WM window (so `flush` composites it).
141 pub fn set_window(&mut self, id: usize) { 185 pub fn set_window(&mut self, id: usize) {
142 self.window_id = Some(id); 186 self.window_id = Some(id);
143 } 187 }
144 188
145 /// Buffer pointer and pixel dimensions (for the WM to composite). 189 /// Buffer pointer and pixel dimensions (for the WM to composite).
146 pub fn window_params(&self) -> (*const u32, usize, usize) { 190 pub fn window_params(&mut self) -> (*const u32, usize, usize) {
147 (self.buffer.as_ptr(), self.w, self.h) 191 (self.buf_ptr() as *const u32, self.w, self.h)
148 } 192 }
149 193
150 pub fn ready(&self) -> bool { 194 pub fn ready(&self) -> bool {
@@ -191,7 +235,7 @@ impl Terminal {
191 } 235 }
192 let rows = self.rows; 236 let rows = self.rows;
193 let cols = self.cols; 237 let cols = self.cols;
194 gop::buffer_shift_up(self.buffer.as_mut_ptr(), self.w, self.h, n * 16); 238 gop::buffer_shift_up(self.buf_ptr(), self.w, self.h, n * 16);
195 // Mirror the pushed content in RENDERED (it describes the buffer). 239 // Mirror the pushed content in RENDERED (it describes the buffer).
196 let total = rows * cols; 240 let total = rows * cols;
197 self.rendered.copy_within(n * cols..total, 0); 241 self.rendered.copy_within(n * cols..total, 0);
@@ -206,8 +250,8 @@ impl Terminal {
206 250
207 /// Re-blit a region of this terminal's buffer into the shadow (used by 251 /// Re-blit a region of this terminal's buffer into the shadow (used by
208 /// the window manager to restore the console layer beneath a window). 252 /// the window manager to restore the console layer beneath a window).
209 pub fn repaint_region(&self, x: usize, y: usize, w: usize, h: usize) { 253 pub fn repaint_region(&mut self, x: usize, y: usize, w: usize, h: usize) {
210 let src = unsafe { self.buffer.as_ptr().add(y * self.w + x) }; 254 let src = unsafe { self.buf_ptr().add(y * self.w + x) };
211 gop::shadow_blit(x, y, w, h, src, self.w); 255 gop::shadow_blit(x, y, w, h, src, self.w);
212 } 256 }
213 257
@@ -537,7 +581,7 @@ impl Terminal {
537 let fg = PALETTE[c.fg as usize]; 581 let fg = PALETTE[c.fg as usize];
538 let bg = PALETTE[c.bg as usize]; 582 let bg = PALETTE[c.bg as usize];
539 gop::buffer_glyph_px( 583 gop::buffer_glyph_px(
540 self.buffer.as_mut_ptr(), 584 self.buf_ptr(),
541 self.w, 585 self.w,
542 self.h, 586 self.h,
543 col * 8, 587 col * 8,
@@ -560,7 +604,7 @@ impl Terminal {
560 let fg = PALETTE[c.fg as usize]; 604 let fg = PALETTE[c.fg as usize];
561 let bg = PALETTE[c.bg as usize]; 605 let bg = PALETTE[c.bg as usize];
562 gop::buffer_glyph_px( 606 gop::buffer_glyph_px(
563 self.buffer.as_mut_ptr(), 607 self.buf_ptr(),
564 self.w, 608 self.w,
565 self.h, 609 self.h,
566 lc * 8, 610 lc * 8,
@@ -578,7 +622,7 @@ impl Terminal {
578 let fg = PALETTE[c.bg as usize]; 622 let fg = PALETTE[c.bg as usize];
579 let bg = PALETTE[c.fg as usize]; 623 let bg = PALETTE[c.fg as usize];
580 gop::buffer_glyph_px( 624 gop::buffer_glyph_px(
581 self.buffer.as_mut_ptr(), 625 self.buf_ptr(),
582 self.w, 626 self.w,
583 self.h, 627 self.h,
584 cc * 8, 628 cc * 8,
@@ -602,7 +646,7 @@ impl Terminal {
602 Some(id) => crate::wm::flush(id), 646 Some(id) => crate::wm::flush(id),
603 None => { 647 None => {
604 if let Some((x, y, w, h)) = dirty { 648 if let Some((x, y, w, h)) = dirty {
605 let src = unsafe { self.buffer.as_ptr().add(y * self.w + x) }; 649 let src = unsafe { self.buf_ptr().add(y * self.w + x) };
606 gop::shadow_blit(x, y, w, h, src, self.w); 650 gop::shadow_blit(x, y, w, h, src, self.w);
607 gop::push_rect(x, y, w, h); 651 gop::push_rect(x, y, w, h);
608 } 652 }
@@ -616,6 +660,20 @@ impl Terminal {
616// --------------------------------------------------------------------------- 660// ---------------------------------------------------------------------------
617 661
618static mut HOME: Option<Terminal> = None; 662static mut HOME: Option<Terminal> = None;
663/// When set, all vt output goes here instead of HOME. Used by windowed
664/// shells so that gop_print!/put_char/write_str write to the active
665/// terminal's pixel buffer.
666static mut ACTIVE: *mut Terminal = core::ptr::null_mut();
667
668fn active() -> &'static mut Terminal {
669 unsafe {
670 if !ACTIVE.is_null() {
671 &mut *ACTIVE
672 } else {
673 home()
674 }
675 }
676}
619 677
620fn home() -> &'static mut Terminal { 678fn home() -> &'static mut Terminal {
621 unsafe { 679 unsafe {
@@ -624,6 +682,14 @@ fn home() -> &'static mut Terminal {
624 } 682 }
625} 683}
626 684
685/// Set the active terminal for output (pass null to restore the home
686/// terminal).
687pub fn set_active(term: *mut Terminal) {
688 unsafe {
689 ACTIVE = term;
690 }
691}
692
627/// Initialise the home terminal over a full-screen pixel buffer. Call once 693/// Initialise the home terminal over a full-screen pixel buffer. Call once
628/// after `gop::init()`. 694/// after `gop::init()`.
629pub fn init() { 695pub fn init() {
@@ -675,27 +741,27 @@ pub fn set_cursor_visible(v: bool) {
675} 741}
676 742
677pub fn sync_scroll() { 743pub fn sync_scroll() {
678 home().sync_scroll(); 744 active().sync_scroll();
679} 745}
680 746
681pub fn repaint_region(x: usize, y: usize, w: usize, h: usize) { 747pub fn repaint_region(x: usize, y: usize, w: usize, h: usize) {
682 home().repaint_region(x, y, w, h); 748 active().repaint_region(x, y, w, h);
683} 749}
684 750
685pub fn write_bytes(b: &[u8]) { 751pub fn write_bytes(b: &[u8]) {
686 home().write_bytes(b); 752 active().write_bytes(b);
687} 753}
688 754
689pub fn write_str(s: &str) { 755pub fn write_str(s: &str) {
690 home().write_str(s); 756 active().write_str(s);
691} 757}
692 758
693pub fn put_char(b: u8) { 759pub fn put_char(b: u8) {
694 home().put_char(b); 760 active().put_char(b);
695} 761}
696 762
697pub fn clear_all() { 763pub fn clear_all() {
698 home().clear_all(); 764 active().clear_all();
699} 765}
700 766
701// --------------------------------------------------------------------------- 767// ---------------------------------------------------------------------------