diff options
Diffstat (limited to 'rsnix/src/vt.rs')
| -rw-r--r-- | rsnix/src/vt.rs | 51 |
1 files changed, 49 insertions, 2 deletions
diff --git a/rsnix/src/vt.rs b/rsnix/src/vt.rs index 4ffb44d..9444063 100644 --- a/rsnix/src/vt.rs +++ b/rsnix/src/vt.rs | |||
| @@ -121,9 +121,11 @@ pub fn init() { | |||
| 121 | COLS = cols; | 121 | COLS = cols; |
| 122 | CELLS = Box::into_raw(Box::new(vec![BLANK; rows * cols])); | 122 | CELLS = Box::into_raw(Box::new(vec![BLANK; rows * cols])); |
| 123 | RENDERED = Box::into_raw(Box::new(vec![BLANK; rows * cols])); | 123 | RENDERED = Box::into_raw(Box::new(vec![BLANK; rows * cols])); |
| 124 | CURSOR_R = 0; | 124 | // Home below row 0: row 0 is the boot-marker status line, so the |
| 125 | // terminal (shell + klog screen echo) must never write to it. | ||
| 126 | CURSOR_R = 1; | ||
| 125 | CURSOR_C = 0; | 127 | CURSOR_C = 0; |
| 126 | LAST_CURSOR = (0, 0); | 128 | LAST_CURSOR = (1, 0); |
| 127 | CURSOR_VISIBLE = true; | 129 | CURSOR_VISIBLE = true; |
| 128 | SGR_FG = 7; | 130 | SGR_FG = 7; |
| 129 | SGR_BG = 0; | 131 | SGR_BG = 0; |
| @@ -133,6 +135,13 @@ pub fn init() { | |||
| 133 | } | 135 | } |
| 134 | } | 136 | } |
| 135 | 137 | ||
| 138 | /// True once the terminal is initialised (grids live). The klog screen | ||
| 139 | /// echo checks this so serial lines printed before vt::init (very early | ||
| 140 | /// boot) are skipped instead of hitting a null grid. | ||
| 141 | pub fn ready() -> bool { | ||
| 142 | unsafe { ROWS != 0 && !CELLS.is_null() } | ||
| 143 | } | ||
| 144 | |||
| 136 | /// (rows, cols) — matches the editor's expected (rows, cols) ordering. | 145 | /// (rows, cols) — matches the editor's expected (rows, cols) ordering. |
| 137 | pub fn size() -> (usize, usize) { | 146 | pub fn size() -> (usize, usize) { |
| 138 | unsafe { (ROWS, COLS) } | 147 | unsafe { (ROWS, COLS) } |
| @@ -667,10 +676,41 @@ pub fn sync_scroll() { | |||
| 667 | // public writing API | 676 | // public writing API |
| 668 | // --------------------------------------------------------------------------- | 677 | // --------------------------------------------------------------------------- |
| 669 | 678 | ||
| 679 | /// Disable interrupts unless they are already off (nested-safe). Returns | ||
| 680 | /// whether they were on, so `irq_restore` re-enables exactly when needed. | ||
| 681 | /// The terminal write path uses this so a klog screen-echo (which can run | ||
| 682 | /// in ISR context) can never tear a shell write, and a task-context write | ||
| 683 | /// can never be split by a tick. | ||
| 684 | fn irq_off() -> bool { | ||
| 685 | let rflags: u64; | ||
| 686 | unsafe { | ||
| 687 | core::arch::asm!( | ||
| 688 | "pushfq; pop {}", | ||
| 689 | out(reg) rflags, | ||
| 690 | options(nostack, preserves_flags) | ||
| 691 | ); | ||
| 692 | if rflags & (1 << 9) != 0 { | ||
| 693 | core::arch::asm!("cli", options(nostack, preserves_flags)); | ||
| 694 | true | ||
| 695 | } else { | ||
| 696 | false | ||
| 697 | } | ||
| 698 | } | ||
| 699 | } | ||
| 700 | |||
| 701 | fn irq_restore(was_on: bool) { | ||
| 702 | if was_on { | ||
| 703 | unsafe { | ||
| 704 | core::arch::asm!("sti", options(nostack, preserves_flags)); | ||
| 705 | } | ||
| 706 | } | ||
| 707 | } | ||
| 708 | |||
| 670 | /// Write raw bytes (ANSI-capable). Used by full-screen apps. UTF-8 | 709 | /// Write raw bytes (ANSI-capable). Used by full-screen apps. UTF-8 |
| 671 | /// sequences are decoded so Unicode glyphs (box-drawing, arrows) render; | 710 | /// sequences are decoded so Unicode glyphs (box-drawing, arrows) render; |
| 672 | /// undecodable bytes become '?'. | 711 | /// undecodable bytes become '?'. |
| 673 | pub fn write_bytes(b: &[u8]) { | 712 | pub fn write_bytes(b: &[u8]) { |
| 713 | let on = irq_off(); | ||
| 674 | let mut i = 0; | 714 | let mut i = 0; |
| 675 | while i < b.len() { | 715 | while i < b.len() { |
| 676 | let byte = b[i]; | 716 | let byte = b[i]; |
| @@ -701,20 +741,27 @@ pub fn write_bytes(b: &[u8]) { | |||
| 701 | } | 741 | } |
| 702 | } | 742 | } |
| 703 | flush(); | 743 | flush(); |
| 744 | irq_restore(on); | ||
| 704 | } | 745 | } |
| 705 | 746 | ||
| 706 | /// Write a UTF-8 string (plain text; Unicode glyphs render, others '?'). | 747 | /// Write a UTF-8 string (plain text; Unicode glyphs render, others '?'). |
| 748 | /// Atomic with respect to interrupts: a klog screen-echo in ISR context | ||
| 749 | /// cannot interleave with a shell write, and a tick cannot split a write. | ||
| 707 | pub fn write_str(s: &str) { | 750 | pub fn write_str(s: &str) { |
| 751 | let on = irq_off(); | ||
| 708 | for c in s.chars() { | 752 | for c in s.chars() { |
| 709 | write_char(c); | 753 | write_char(c); |
| 710 | } | 754 | } |
| 711 | flush(); | 755 | flush(); |
| 756 | irq_restore(on); | ||
| 712 | } | 757 | } |
| 713 | 758 | ||
| 714 | /// Write a single ASCII byte / char. | 759 | /// Write a single ASCII byte / char. |
| 715 | pub fn put_char(b: u8) { | 760 | pub fn put_char(b: u8) { |
| 761 | let on = irq_off(); | ||
| 716 | put_byte(b); | 762 | put_byte(b); |
| 717 | flush(); | 763 | flush(); |
| 764 | irq_restore(on); | ||
| 718 | } | 765 | } |
| 719 | 766 | ||
| 720 | /// Clear the whole screen, reset colours, home the cursor. | 767 | /// Clear the whole screen, reset colours, home the cursor. |
