1use embedded_graphics::{
2 geometry::Point,
3 mono_font::{
4 ascii::{FONT_9X15, FONT_9X15_BOLD},
5 MonoTextStyle,
6 },
7 pixelcolor::{Rgb888, RgbColor},
8 text::{
9 renderer::{CharacterStyle, TextRenderer},
10 Baseline,
11 },
12};
13
14use crate::graphics::{self, vga, Pixel};
15
16use super::{VideoConsole, VideoConsoleAttribute};
17
18pub(super) struct VgaGraphics {
19 pos: Point,
20 text_style: MonoTextStyle<'static, Rgb888>,
21 vga: &'static vga::VgaDisplayController,
22}
23
24impl VgaGraphics {
25 pub fn new() -> Self {
26 Self {
27 pos: Point::new(0, 0),
28 text_style: MonoTextStyle::new(&FONT_9X15, Rgb888::WHITE),
29 vga: graphics::vga::controller().expect("We should have a VGA controller by now!"),
30 }
31 }
32
33 pub fn clear_current_text_line(&mut self, vga: &mut vga::VgaDisplay) {
34 let fb_info = self.vga.framebuffer_info();
35 vga.clear_rect(
36 0,
37 self.pos.y as usize,
38 fb_info.width,
39 self.text_style.line_height() as usize,
40 Pixel { r: 0, g: 0, b: 0 },
41 );
42 }
43
44 fn scroll(&mut self, lines: usize, vga: &mut vga::VgaDisplay) {
45 let fb_info = self.vga.framebuffer_info();
46 let height = fb_info.height;
47 let width = fb_info.width;
48 let mut i = 0;
49 while i < height - lines * 2 {
50 vga.blit_inner_ranges((0, i + lines), (0, i), width, lines);
51
52 i += lines;
53 }
54 self.pos.y -= lines as i32;
55 }
56
57 fn fix_after_advance(&mut self, vga: &mut vga::VgaDisplay) {
58 let fb_info = self.vga.framebuffer_info();
59 let width = fb_info.width as i32;
60 let height = fb_info.height as i32;
61 if self.pos.x + self.text_style.font.character_size.width as i32 >= width {
62 self.pos.x = 0;
63 self.pos.y += self.text_style.line_height() as i32;
64 }
65 if self.pos.y + self.text_style.line_height() as i32 >= height {
66 self.scroll(self.text_style.line_height() as usize, vga);
68
69 self.clear_current_text_line(vga);
71
72 self.fix_after_advance(vga);
74 }
75 }
76}
77
78impl VideoConsole for VgaGraphics {
79 fn init(&mut self) {
80 if let Some(mut vga) = self.vga.lock_kernel() {
81 vga.clear();
82 }
83 }
84
85 fn set_attrib(&mut self, attrib: VideoConsoleAttribute) {
86 let to_color = |color: u8| match color {
89 0 => Rgb888::new(0, 0, 0),
90 1 => Rgb888::new(197, 15, 31),
91 2 => Rgb888::new(19, 161, 14),
92 3 => Rgb888::new(193, 156, 0),
93 4 => Rgb888::new(0, 55, 218),
94 5 => Rgb888::new(136, 23, 152),
95 6 => Rgb888::new(58, 150, 221),
96 7 => Rgb888::new(204, 204, 204),
97
98 8 => Rgb888::new(118, 118, 118),
99 9 => Rgb888::new(231, 72, 86),
100 10 => Rgb888::new(22, 198, 12),
101 11 => Rgb888::new(249, 241, 165),
102 12 => Rgb888::new(59, 120, 255),
103 13 => Rgb888::new(180, 0, 158),
104 14 => Rgb888::new(97, 214, 214),
105 _ => Rgb888::new(242, 242, 242),
106 };
107
108 self.text_style
109 .set_background_color(Some(to_color(attrib.background as u8)));
110 self.text_style
111 .set_text_color(Some(to_color(attrib.foreground as u8)));
112
113 if attrib.bold {
114 self.text_style.font = &FONT_9X15_BOLD;
115 } else {
116 self.text_style.font = &FONT_9X15;
117 }
118 }
119
120 fn write_byte(&mut self, c: u8) {
121 let Some(mut vga) = self.vga.lock_kernel() else {
122 return;
124 };
125
126 if c == b'\n' {
127 self.pos = Point::new(0, self.pos.y + self.text_style.line_height() as i32);
128 } else if c == b'\r' {
129 self.pos.x = 0;
130 } else {
131 let mut dst = [0; 4];
132 let str = (c as char).encode_utf8(&mut dst);
133
134 let style = self.text_style;
135
136 self.pos = style
137 .draw_string(str, self.pos, Baseline::Bottom, &mut *vga)
138 .unwrap();
139 }
140 self.fix_after_advance(&mut vga);
141 }
142
143 fn backspace(&mut self) {
144 let Some(mut vga) = self.vga.lock_kernel() else {
145 return;
147 };
148
149 if self.pos.x < self.text_style.font.character_size.width as i32 {
150 if self.pos.y >= self.text_style.line_height() as i32 {
151 self.pos.y -= self.text_style.line_height() as i32;
152 self.pos.x = self.vga.framebuffer_info().width as i32
153 - self.text_style.font.character_size.width as i32;
154 }
155 } else {
156 self.pos.x -= self.text_style.font.character_size.width as i32;
157 }
158
159 vga.clear_rect(
160 self.pos.x as usize,
161 (self.pos.y as usize).saturating_sub(self.text_style.line_height() as usize),
162 self.text_style.font.character_size.width as usize,
163 self.text_style.line_height() as usize,
164 Pixel { r: 0, g: 0, b: 0 },
165 );
166 }
167}