proka_kernel/output/
console.rs1extern 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
10const FONT_W: u64 = 8;
12const FONT_H: u64 = 16;
13
14lazy_static! {
16 pub static ref CONSOLE: Mutex<Console> = Mutex::new(Console::init());
17}
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21enum ParseState {
22 Normal, Escape, EscapeBracket, CollectingParams, }
27
28pub struct Console {
30 address: *mut u8,
31 width: u64,
32 height: u64,
33 pitch: u64,
34 position: (u64, u64), fg_color: Color,
36 bg_color: Color,
37 parse_state: ParseState,
38 ansi_params: Vec<u16>,
39 current_param: u16, }
41
42unsafe 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 core::ptr::copy(
74 base_ptr.add(scroll_bytes),
75 base_ptr,
76 total_bytes - scroll_bytes,
77 );
78
79 core::ptr::write_bytes(base_ptr.add(total_bytes - scroll_bytes), 0, scroll_bytes);
81 }
82 }
83
84 pub fn print_char(&mut self, c: u8) {
89 match self.parse_state {
91 ParseState::Normal => {
92 if c == 0x1B {
93 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 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 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 c == ('\n' as usize) {
149 self.position.0 = 0;
150 self.position.1 += FONT_H;
151 return;
152 }
153
154 if self.position.0 + FONT_W > self.width {
156 self.position.0 = 0;
157 self.position.1 += FONT_H;
158 }
159
160 let start_x = self.position.0;
162 let start_y = self.position.1;
163
164 for line in 0..FONT_H {
166 for i in 0..FONT_W {
167 let mask = 0x80 >> i;
168
169 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 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 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 _ => {} }
208 }
209
210 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 self.fg_color = color::WHITE;
222 self.bg_color = color::BLACK;
223 }
224 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 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 pub fn print_string(&mut self, s: &str) {
251 for c in s.bytes() {
252 self.print_char(c);
253 }
254 }
255
256 pub fn set_fg_color(&mut self, color: Color) {
259 self.fg_color = color
260 }
261
262 pub fn set_bg_color(&mut self, color: Color) {
264 self.bg_color = color
265 }
266
267 pub fn get_fg_color(&self) -> Color {
269 self.fg_color
270 }
271
272 pub fn get_bg_color(&self) -> Color {
274 self.bg_color
275 }
276
277 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
285impl 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}