1use core::sync::atomic::{AtomicI64, Ordering};
2
3use embedded_graphics::{
4 draw_target::DrawTarget,
5 geometry::{self, OriginDimensions},
6 pixelcolor::Rgb888,
7};
8pub use kernel_user_link::graphics::FrameBufferInfo;
9
10use crate::{
11 memory_management::virtual_space::VirtualSpace,
12 multiboot2::{self, FramebufferColorInfo},
13 sync::{
14 once::OnceLock,
15 spin::mutex::{Mutex, MutexGuard},
16 },
17};
18
19use super::Pixel;
20
21static VGA_DISPLAY_CONTROLLER: OnceLock<VgaDisplayController> = OnceLock::new();
22
23pub fn init(framebuffer: Option<multiboot2::Framebuffer>) {
24 if VGA_DISPLAY_CONTROLLER.try_get().is_some() {
25 panic!("VGA display controller already initialized");
26 }
27
28 match framebuffer {
29 Some(framebuffer) => match framebuffer.color_info {
30 FramebufferColorInfo::Indexed { .. } => {}
31 FramebufferColorInfo::Rgb { .. } => {
32 VGA_DISPLAY_CONTROLLER.get_or_init(|| VgaDisplayController::new(framebuffer));
34 }
35 FramebufferColorInfo::EgaText => {}
36 },
37 None => panic!("No framebuffer provided"),
38 }
39}
40
41pub fn controller() -> Option<&'static VgaDisplayController> {
42 VGA_DISPLAY_CONTROLLER.try_get()
43}
44
45pub struct VgaDisplayController {
46 display: Mutex<VgaDisplay>,
47 framebuffer_info: FrameBufferInfo,
48 owner_process: AtomicI64,
49}
50
51#[allow(dead_code)]
52impl VgaDisplayController {
53 pub fn new(framebuffer: multiboot2::Framebuffer) -> Self {
54 let display = VgaDisplay::new(framebuffer);
55
56 Self {
57 framebuffer_info: display.fb_info,
58 display: Mutex::new(display),
59 owner_process: AtomicI64::new(-1),
60 }
61 }
62
63 pub fn lock_process(&self, pid: u64) -> Option<MutexGuard<'_, VgaDisplay>> {
64 if self.owner_process.load(Ordering::Relaxed) == pid as i64 {
65 Some(self.display.lock())
66 } else {
67 None
68 }
69 }
70
71 pub fn lock_kernel(&self) -> Option<MutexGuard<'_, VgaDisplay>> {
72 if self.owner_process.load(Ordering::Relaxed) == -1 {
73 Some(self.display.lock())
74 } else {
75 None
76 }
77 }
78
79 pub fn take_ownership(&self, pid: u64) -> bool {
80 assert!(pid < i64::MAX as u64);
81 self.owner_process
82 .compare_exchange(
83 -1,
84 pid as i64,
85 core::sync::atomic::Ordering::Relaxed,
86 Ordering::Relaxed,
87 )
88 .is_ok()
89 }
90
91 pub fn release(&self, pid: u64) -> bool {
92 assert!(pid < i64::MAX as u64);
93 self.owner_process
94 .compare_exchange(
95 pid as i64,
96 -1,
97 core::sync::atomic::Ordering::Relaxed,
98 Ordering::Relaxed,
99 )
100 .is_ok()
101 }
102
103 pub fn framebuffer_info(&self) -> &FrameBufferInfo {
104 &self.framebuffer_info
105 }
106}
107
108trait FrameBufferDraw {
110 fn read_pixel(&self, memory: &[u8], pos: (usize, usize)) -> Option<Pixel>;
111 fn write_pixel(&self, memory: &mut [u8], pos: (usize, usize), color: Pixel) -> Option<()>;
112}
113
114impl FrameBufferDraw for FrameBufferInfo {
115 fn read_pixel(&self, memory: &[u8], pos: (usize, usize)) -> Option<Pixel> {
116 let pixel_mem = self.pixel_mem(memory, pos)?;
117 Some(Pixel {
118 r: pixel_mem[self.field_pos.0 as usize],
119 g: pixel_mem[self.field_pos.1 as usize],
120 b: pixel_mem[self.field_pos.2 as usize],
121 })
122 }
123
124 fn write_pixel(&self, memory: &mut [u8], pos: (usize, usize), color: Pixel) -> Option<()> {
125 let pixel_mem = self.pixel_mem_mut(memory, pos)?;
126 let r = color.r & self.mask.0;
127 let g = color.g & self.mask.1;
128 let b = color.b & self.mask.2;
129 pixel_mem[self.field_pos.0 as usize] = r;
130 pixel_mem[self.field_pos.1 as usize] = g;
131 pixel_mem[self.field_pos.2 as usize] = b;
132
133 Some(())
134 }
135}
136
137pub struct VgaDisplay {
138 fb_info: FrameBufferInfo,
139 memory: VirtualSpace<[u8]>,
140}
141
142#[allow(dead_code)]
143impl VgaDisplay {
144 fn new(framebuffer: multiboot2::Framebuffer) -> Self {
145 let multiboot2::FramebufferColorInfo::Rgb {
146 red_field_position,
147 red_mask_size,
148 green_field_position,
149 green_mask_size,
150 blue_field_position,
151 blue_mask_size,
152 } = framebuffer.color_info
153 else {
154 panic!("Only RGB framebuffer is supported");
155 };
156 assert_eq!(framebuffer.bpp % 8, 0, "Only byte aligned bpp is supported");
157 assert!(
158 red_field_position.is_multiple_of(8)
159 && green_field_position.is_multiple_of(8)
160 && blue_field_position.is_multiple_of(8),
161 "Only byte aligned field position is supported"
162 );
163
164 let physical_addr = framebuffer.addr;
165 let memory_size = framebuffer.pitch * framebuffer.height;
166 let memory =
167 unsafe { VirtualSpace::new_slice(physical_addr, memory_size as usize).unwrap() };
168
169 let red_mask = (1 << red_mask_size) - 1;
170 let green_mask = (1 << green_mask_size) - 1;
171 let blue_mask = (1 << blue_mask_size) - 1;
172 Self {
173 fb_info: FrameBufferInfo {
174 pitch: framebuffer.pitch as usize,
175 height: framebuffer.height as usize,
176 width: framebuffer.width as usize,
177 field_pos: (
178 red_field_position / 8,
179 green_field_position / 8,
180 blue_field_position / 8,
181 ),
182 mask: (red_mask as u8, green_mask as u8, blue_mask as u8),
183 byte_per_pixel: framebuffer.bpp.div_ceil(8),
184 },
185 memory,
186 }
187 }
188
189 pub fn put_pixel(&mut self, x: usize, y: usize, color: Pixel) {
190 self.fb_info.write_pixel(&mut self.memory, (x, y), color);
191 }
192
193 pub fn clear(&mut self) {
194 self.memory.fill(0);
195 }
196
197 pub fn blit_inner_ranges(
198 &mut self,
199 src: (usize, usize),
200 dest: (usize, usize),
201 width: usize,
202 height: usize,
203 ) {
204 let (src_x, src_y) = src;
205 let (dest_x, dest_y) = dest;
206 assert!(src_x + width <= self.fb_info.width);
207 assert!(src_y + height <= self.fb_info.height);
208 assert!(dest_x + width <= self.fb_info.width);
209 assert!(dest_y + height <= self.fb_info.height);
210
211 assert!(
213 src_x + width <= dest_x
214 || dest_x + width <= src_x
215 || src_y + height <= dest_y
216 || dest_y + height <= src_y
217 );
218
219 let chunk_size = width * self.fb_info.byte_per_pixel as usize;
220
221 let is_src_after =
224 src_y * self.fb_info.pitch + src_x > dest_y * self.fb_info.pitch + dest_x;
225 let mut copy_handler_src_after;
226 let mut copy_handler_src_before;
227 let copy_handler: &mut dyn FnMut(usize, usize, &mut [u8]) = if is_src_after {
228 copy_handler_src_after = |src_i: usize, dest_i: usize, memory: &mut [u8]| {
229 let (before, after) = memory.split_at_mut(src_i);
230 before[dest_i..dest_i + chunk_size].copy_from_slice(&after[..chunk_size]);
231 };
232 &mut copy_handler_src_after
233 } else {
234 copy_handler_src_before = |src_i: usize, dest_i: usize, memory: &mut [u8]| {
235 let (before, after) = memory.split_at_mut(src_i);
236 before[dest_i..dest_i + chunk_size].copy_from_slice(&after[..chunk_size]);
237 };
238 &mut copy_handler_src_before
239 };
240
241 for y in 0..height {
242 let src_i = self.fb_info.get_arr_pos((src_x, src_y + y)).unwrap();
243 let dest_i = self.fb_info.get_arr_pos((dest_x, dest_y + y)).unwrap();
244
245 copy_handler(src_i, dest_i, &mut self.memory);
246 }
247 }
248
249 pub fn blit(
250 &mut self,
251 src_buffer: &[u8],
252 src_framebuffer_info: &FrameBufferInfo,
253 src: (usize, usize),
254 dest: (usize, usize),
255 width: usize,
256 height: usize,
257 ) {
258 if self.fb_info.byte_per_pixel == src_framebuffer_info.byte_per_pixel
259 && self.fb_info.field_pos == src_framebuffer_info.field_pos
260 && self.fb_info.mask == src_framebuffer_info.mask
261 {
262 unsafe { self.blit_fast(src_buffer, src_framebuffer_info, src, dest, width, height) }
264 } else {
265 self.blit_slow(src_buffer, src_framebuffer_info, src, dest, width, height)
266 }
267 }
268
269 unsafe fn blit_fast(
272 &mut self,
273 src_buffer: &[u8],
274 src_framebuffer_info: &FrameBufferInfo,
275 src: (usize, usize),
276 dest: (usize, usize),
277 width: usize,
278 height: usize,
279 ) {
280 let (src_x, src_y) = src;
281 let (dest_x, dest_y) = dest;
282 assert!(src_x + width <= src_framebuffer_info.width);
283 assert!(src_y + height <= src_framebuffer_info.height);
284 assert!(dest_x + width <= self.fb_info.width);
285 assert!(dest_y + height <= self.fb_info.height);
286
287 let chunk_size = width * self.fb_info.byte_per_pixel as usize;
289
290 for y in 0..height {
291 let src_i = src_framebuffer_info
292 .get_arr_pos((src_x, src_y + y))
293 .unwrap();
294 let dest_i = self.fb_info.get_arr_pos((dest_x, dest_y + y)).unwrap();
295
296 let src_line = &src_buffer[src_i..src_i + chunk_size];
297 let dest_line = &mut self.memory[dest_i..dest_i + chunk_size];
298 dest_line.copy_from_slice(src_line);
299 }
300 }
301
302 fn blit_slow(
303 &mut self,
304 src_buffer: &[u8],
305 src_framebuffer_info: &FrameBufferInfo,
306 src: (usize, usize),
307 dest: (usize, usize),
308 width: usize,
309 height: usize,
310 ) {
311 let (src_x, src_y) = src;
312 let (dest_x, dest_y) = dest;
313
314 assert!(dest_x + width <= self.fb_info.width);
315 assert!(dest_y + height <= self.fb_info.height);
316 assert!(src_x + width <= src_framebuffer_info.width);
317 assert!(src_y + height <= src_framebuffer_info.height);
318
319 for y in 0..height {
320 for x in 0..width {
321 let src_pixel = src_framebuffer_info
322 .read_pixel(src_buffer, (src_x + x, src_y + y))
323 .unwrap();
324 self.fb_info
325 .write_pixel(&mut self.memory, (dest_x + x, dest_y + y), src_pixel)
326 .unwrap();
327 }
328 }
329 }
330
331 pub fn clear_rect(
332 &mut self,
333 dest_x: usize,
334 dest_y: usize,
335 width: usize,
336 height: usize,
337 color: Pixel,
338 ) {
339 assert!(dest_x + width <= self.fb_info.width);
340 assert!(dest_y + height <= self.fb_info.height);
341
342 if height == 0 || width == 0 {
343 return;
344 }
345
346 let line_chunk_size = width * self.fb_info.byte_per_pixel as usize;
347 let first_line_start = self.fb_info.get_arr_pos((dest_x, dest_y)).unwrap();
348 let first_line_end = first_line_start + line_chunk_size;
349 let first_line = &mut self.memory[first_line_start..first_line_end];
350
351 for i in 0..width {
353 self.fb_info.write_pixel(first_line, (i, 0), color);
354 }
355
356 let second_line_start = self.fb_info.get_arr_pos((0, dest_y + 1)).unwrap();
359 let (before, after) = self.memory.split_at_mut(second_line_start);
360 let first_line = &before[first_line_start..first_line_end];
361
362 for y in 1..height {
363 let dest_i = self.fb_info.get_arr_pos((dest_x, y - 1)).unwrap();
364 let dest_line = &mut after[dest_i..dest_i + line_chunk_size];
365 dest_line.copy_from_slice(first_line);
366 }
367 }
368}
369
370impl DrawTarget for VgaDisplay {
371 type Color = Rgb888;
372
373 type Error = ();
374
375 fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
376 where
377 I: IntoIterator<Item = embedded_graphics::prelude::Pixel<Self::Color>>,
378 {
379 for embedded_graphics::prelude::Pixel(pos, color) in pixels {
380 self.put_pixel(pos.x as usize, pos.y as usize, color.into());
381 }
382 Ok(())
383 }
384
385 fn fill_solid(
386 &mut self,
387 area: &embedded_graphics::primitives::Rectangle,
388 color: Self::Color,
389 ) -> Result<(), Self::Error> {
390 if area.top_left.x < 0
391 || area.top_left.y < 0
392 || area.bottom_right().unwrap().x >= self.fb_info.width as i32
393 || area.bottom_right().unwrap().y >= self.fb_info.height as i32
394 {
395 return Err(());
396 }
397
398 let (x, y) = (area.top_left.x as usize, area.top_left.y as usize);
399 let (width, height) = (area.size.width as usize, area.size.height as usize);
400 self.clear_rect(x, y, width, height, color.into());
401
402 Ok(())
403 }
404
405 fn clear(&mut self, color: Self::Color) -> Result<(), Self::Error> {
406 self.clear_rect(0, 0, self.fb_info.width, self.fb_info.height, color.into());
407 Ok(())
408 }
409}
410
411impl OriginDimensions for VgaDisplay {
412 fn size(&self) -> geometry::Size {
413 geometry::Size::new(self.fb_info.width as u32, self.fb_info.height as u32)
414 }
415}