Skip to main content

proka_kernel/output/
console.rs

1extern crate alloc;
2use crate::graphics::{color, Color};
3use crate::output::font8x16::FONT8X16;
4use crate::FRAMEBUFFER_REQUEST;
5use alloc::{vec, vec::Vec};
6use core::fmt::{self, Write};
7use lazy_static::lazy_static;
8use spin::Mutex;
9
10// Constants
11const FONT_W: u64 = 8;
12const FONT_H: u64 = 16;
13
14// Some statics which is global
15lazy_static! {
16    pub static ref CONSOLE: Mutex<Console> = Mutex::new(Console::init());
17}
18
19/// The ANSI parse status
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21enum ParseState {
22    Normal,           // Normal mode
23    Escape,           // Has read ESC char
24    EscapeBracket,    // Already read ESC[
25    CollectingParams, // Params for collecting
26}
27
28/// The object of console.
29pub struct Console {
30    address: *mut u8,
31    width: u64,
32    height: u64,
33    pitch: u64,
34    position: (u64, u64), // (x, y)
35    fg_color: Color,
36    bg_color: Color,
37    parse_state: ParseState,
38    ansi_params: Vec<u16>,
39    current_param: u16, // Current ANSI param
40}
41
42// We have to do it, so that it can be contained by Mutex.
43unsafe impl Send for Console {}
44unsafe impl Sync for Console {}
45
46impl Console {
47    pub fn init() -> Self {
48        let framebuffer_response = FRAMEBUFFER_REQUEST.get_response().unwrap();
49        let framebuffer = framebuffer_response.framebuffers().next().unwrap();
50        Self {
51            address: framebuffer.addr(),
52            width: framebuffer.width(),
53            height: framebuffer.height(),
54            pitch: framebuffer.pitch(),
55            position: (0, 0),
56            fg_color: color::WHITE,
57            bg_color: color::BLACK,
58            parse_state: ParseState::Normal,
59            ansi_params: Vec::new(),
60            current_param: 0,
61        }
62    }
63
64    #[inline(always)]
65    fn scroll_up(&mut self) {
66        unsafe {
67            let base_ptr = self.address as *mut u8;
68            let scroll_bytes = (FONT_H as usize) * self.pitch as usize;
69            let total_bytes = (self.height as usize) * self.pitch as usize;
70
71            // Use memmove
72            // Move all buffer upper scroll_bytes
73            core::ptr::copy(
74                base_ptr.add(scroll_bytes),
75                base_ptr,
76                total_bytes - scroll_bytes,
77            );
78
79            // Clear last scroll_bytes area
80            core::ptr::write_bytes(base_ptr.add(total_bytes - scroll_bytes), 0, scroll_bytes);
81        }
82    }
83
84    /// Print a char to framebuffer console.
85    ///
86    /// Note that the character must discovorable in ASCII, otherwise we don't know what
87    /// unexpected thing is being happened.
88    pub fn print_char(&mut self, c: u8) {
89        // Parse ANSI symbol
90        match self.parse_state {
91            ParseState::Normal => {
92                if c == 0x1B {
93                    // ESC char
94                    self.parse_state = ParseState::Escape;
95                } else {
96                    self.print_normal_char(c as usize);
97                }
98            }
99            ParseState::Escape => {
100                if c == b'[' {
101                    self.parse_state = ParseState::EscapeBracket;
102                    self.ansi_params.clear();
103                    self.current_param = 0;
104                } else {
105                    // Invalid symbol, fall back
106                    self.parse_state = ParseState::Normal;
107                    self.print_normal_char(0x1B as usize);
108                    self.print_normal_char(c as usize);
109                }
110            }
111            ParseState::EscapeBracket => {
112                if c.is_ascii_digit() {
113                    self.current_param = self.current_param * 10 + (c - b'0') as u16;
114                    self.parse_state = ParseState::CollectingParams;
115                } else if c == b';' {
116                    self.ansi_params.push(self.current_param);
117                    self.current_param = 0;
118                } else {
119                    self.ansi_params.push(self.current_param);
120                    self.parse_ansi_command(c);
121                    self.parse_state = ParseState::Normal;
122                }
123            }
124            ParseState::CollectingParams => {
125                if c.is_ascii_digit() {
126                    self.current_param = self.current_param * 10 + (c - b'0') as u16;
127                } else if c == b';' {
128                    self.ansi_params.push(self.current_param);
129                    self.current_param = 0;
130                    self.parse_state = ParseState::EscapeBracket;
131                } else {
132                    self.ansi_params.push(self.current_param);
133                    self.parse_ansi_command(c);
134                    self.parse_state = ParseState::Normal;
135                }
136            }
137        }
138    }
139
140    /// Print a normal char to console.
141    fn print_normal_char(&mut self, c: usize) {
142        if (self.height - self.position.1) < FONT_H {
143            self.scroll_up();
144            self.position.1 = self.height - FONT_H;
145        }
146
147        // If character is "\n", just switch to next line.
148        if c == ('\n' as usize) {
149            self.position.0 = 0;
150            self.position.1 += FONT_H;
151            return;
152        }
153
154        // If over than self.width, switch to next line.
155        if self.position.0 + FONT_W > self.width {
156            self.position.0 = 0;
157            self.position.1 += FONT_H;
158        }
159
160        // Compute the current position
161        let start_x = self.position.0;
162        let start_y = self.position.1;
163
164        // Write pixel
165        for line in 0..FONT_H {
166            for i in 0..FONT_W {
167                let mask = 0x80 >> i;
168
169                // Write white pixel
170                // Compute current pixel offset
171                let x = start_x + i;
172                let y = start_y + line;
173                if x < self.width && y < self.height {
174                    let pixel_offset = y * self.pitch + x * 4;
175                    if FONT8X16[c][line as usize] & mask != 0 {
176                        // Write
177                        unsafe {
178                            self.address
179                                .add(pixel_offset as usize)
180                                .cast::<u32>()
181                                .write(self.fg_color.to_u32(true));
182                        }
183                    } else {
184                        unsafe {
185                            self.address
186                                .add(pixel_offset as usize)
187                                .cast::<u32>()
188                                .write(self.bg_color.to_u32(true));
189                        }
190                    }
191                }
192            }
193        }
194
195        self.position.0 += FONT_W;
196    }
197
198    /// Handle ANSI commands
199    fn parse_ansi_command(&mut self, cmd: u8) {
200        match cmd {
201            b'm' => self.handle_sgr(),
202            b'A' => self.handle_cursor_up(),
203            b'B' => self.handle_cursor_down(),
204            b'C' => self.handle_cursor_right(),
205            b'D' => self.handle_cursor_left(),
206            _ => {} // Ignore non-impl command
207        }
208    }
209
210    // SGR command handler
211    fn handle_sgr(&mut self) {
212        let params = if self.ansi_params.is_empty() {
213            vec![0]
214        } else {
215            self.ansi_params.clone()
216        };
217        for param in params {
218            match param {
219                0 => {
220                    // Reset
221                    self.fg_color = color::WHITE;
222                    self.bg_color = color::BLACK;
223                }
224                // Normal foreground color
225                30 => self.fg_color = color::BLACK,
226                31 => self.fg_color = color::RED,
227                32 => self.fg_color = color::GREEN,
228                33 => self.fg_color = color::YELLOW,
229                34 => self.fg_color = color::BLUE,
230                35 => self.fg_color = color::MAGENTA,
231                36 => self.fg_color = color::CYAN,
232                37 => self.fg_color = color::WHITE,
233                // Normal background color
234                40 => self.bg_color = color::BLACK,
235                41 => self.bg_color = color::RED,
236                42 => self.bg_color = color::GREEN,
237                43 => self.bg_color = color::YELLOW,
238                44 => self.bg_color = color::BLUE,
239                45 => self.bg_color = color::MAGENTA,
240                46 => self.bg_color = color::CYAN,
241                47 => self.bg_color = color::WHITE,
242                _ => {}
243            }
244        }
245        self.ansi_params.clear();
246        self.current_param = 0;
247    }
248
249    /// Print a string to console.
250    pub fn print_string(&mut self, s: &str) {
251        for c in s.bytes() {
252            self.print_char(c);
253        }
254    }
255
256    /* Settings methods */
257    /// Set up the color of foreground character.
258    pub fn set_fg_color(&mut self, color: Color) {
259        self.fg_color = color
260    }
261
262    /// Set up the color of background character.
263    pub fn set_bg_color(&mut self, color: Color) {
264        self.bg_color = color
265    }
266
267    /// Get the color of foreground character.
268    pub fn get_fg_color(&self) -> Color {
269        self.fg_color
270    }
271
272    /// Get the color of background character.
273    pub fn get_bg_color(&self) -> Color {
274        self.bg_color
275    }
276
277    /* Cursor handler */
278    // Todo: Implement cursor
279    pub fn handle_cursor_up(&mut self) {}
280    pub fn handle_cursor_down(&mut self) {}
281    pub fn handle_cursor_left(&mut self) {}
282    pub fn handle_cursor_right(&mut self) {}
283}
284
285// Implement the [`Write`] trait to support formatting
286impl Write for Console {
287    fn write_str(&mut self, s: &str) -> fmt::Result {
288        self.print_string(s);
289        Ok(())
290    }
291}
292
293#[macro_export]
294macro_rules! console_print {
295    ($($arg:tt)*) => ($crate::output::console::_print(format_args!($($arg)*)));
296}
297
298#[macro_export]
299macro_rules! console_println {
300    () => ($crate::print!("\n"));
301    ($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*)));
302}
303
304#[doc(hidden)]
305pub fn _print(args: fmt::Arguments) {
306    use core::fmt::Write;
307    CONSOLE
308        .lock()
309        .write_fmt(args)
310        .expect("Failed to write to console");
311}