1#![allow(dead_code)]
15
16use crate::{
17 hardware::virtio::gpu,
18 memory::{self, phys_to_virt},
19};
20use core::sync::atomic::{AtomicBool, Ordering};
21use spin::Mutex;
22
23const MAX_WIDTH: u32 = 3840;
25const MAX_HEIGHT: u32 = 2160;
26
27#[derive(Clone, Copy, PartialEq, Eq, Debug)]
29pub enum FramebufferSource {
30 Limine,
31 VirtioGpu,
32 None,
33}
34
35#[derive(Clone, Copy, Debug)]
37pub struct PixelFormat {
38 pub red_mask: u32,
39 pub red_shift: u8,
40 pub green_mask: u32,
41 pub green_shift: u8,
42 pub blue_mask: u32,
43 pub blue_shift: u8,
44 pub bits_per_pixel: u8,
45}
46
47impl Default for PixelFormat {
48 fn default() -> Self {
50 Self {
51 red_mask: 0x00FF0000,
52 red_shift: 16,
53 green_mask: 0x0000FF00,
54 green_shift: 8,
55 blue_mask: 0x000000FF,
56 blue_shift: 0,
57 bits_per_pixel: 32,
58 }
59 }
60}
61
62#[derive(Clone, Copy, Debug)]
64pub struct FramebufferInfo {
65 pub base: u64,
66 pub base_virt: usize,
67 pub width: u32,
68 pub height: u32,
69 pub stride: u32,
70 pub format: PixelFormat,
71 pub source: FramebufferSource,
72}
73
74unsafe impl Send for FramebufferInfo {}
75unsafe impl Sync for FramebufferInfo {}
76
77pub struct Framebuffer {
79 info: FramebufferInfo,
80 double_buffer: Option<*mut u8>,
81 use_double_buffer: bool,
82 dirty: DirtyRect,
83}
84
85unsafe impl Send for Framebuffer {}
86unsafe impl Sync for Framebuffer {}
87
88static FRAMEBUFFER: Mutex<Option<Framebuffer>> = Mutex::new(None);
89static FRAMEBUFFER_INITIALIZED: AtomicBool = AtomicBool::new(false);
90
91#[derive(Clone, Copy)]
92struct DirtyRect {
93 valid: bool,
94 x0: u32,
95 y0: u32,
96 x1: u32,
97 y1: u32,
98}
99
100impl DirtyRect {
101 const fn empty() -> Self {
103 Self {
104 valid: false,
105 x0: 0,
106 y0: 0,
107 x1: 0,
108 y1: 0,
109 }
110 }
111
112 fn include(&mut self, x: u32, y: u32, width: u32, height: u32) {
114 if width == 0 || height == 0 {
115 return;
116 }
117 let x1 = x.saturating_add(width);
118 let y1 = y.saturating_add(height);
119 if !self.valid {
120 self.valid = true;
121 self.x0 = x;
122 self.y0 = y;
123 self.x1 = x1;
124 self.y1 = y1;
125 } else {
126 self.x0 = self.x0.min(x);
127 self.y0 = self.y0.min(y);
128 self.x1 = self.x1.max(x1);
129 self.y1 = self.y1.max(y1);
130 }
131 }
132
133 fn take(&mut self) -> Option<(u32, u32, u32, u32)> {
135 if !self.valid {
136 return None;
137 }
138 let x = self.x0;
139 let y = self.y0;
140 let width = self.x1.saturating_sub(self.x0);
141 let height = self.y1.saturating_sub(self.y0);
142 *self = Self::empty();
143 Some((x, y, width, height))
144 }
145}
146
147impl Framebuffer {
148 pub fn init_limine(
150 addr: u64,
151 width: u32,
152 height: u32,
153 stride: u32,
154 format: PixelFormat,
155 ) -> Result<(), &'static str> {
156 if addr == 0 || width == 0 || height == 0 {
157 return Err("Invalid framebuffer parameters");
158 }
159
160 let base_virt = addr as usize;
161
162 let info = FramebufferInfo {
163 base: addr,
164 base_virt,
165 width,
166 height,
167 stride,
168 format,
169 source: FramebufferSource::Limine,
170 };
171
172 let fb = Framebuffer {
173 info,
174 double_buffer: None,
175 use_double_buffer: false,
176 dirty: DirtyRect::empty(),
177 };
178
179 *FRAMEBUFFER.lock() = Some(fb);
180 FRAMEBUFFER_INITIALIZED.store(true, Ordering::SeqCst);
181
182 log::info!(
183 "[FB] Limine framebuffer: {}x{} @ {}bpp, stride={}",
184 width,
185 height,
186 format.bits_per_pixel,
187 stride
188 );
189
190 Ok(())
191 }
192
193 pub fn init_virtio_gpu() -> Result<(), &'static str> {
195 let gpu_info = gpu::get_framebuffer_info().ok_or("VirtIO GPU not initialized")?;
196
197 let format = PixelFormat {
198 red_mask: 0x00FF0000,
199 red_shift: 16,
200 green_mask: 0x0000FF00,
201 green_shift: 8,
202 blue_mask: 0x000000FF,
203 blue_shift: 0,
204 bits_per_pixel: 32,
205 };
206
207 let info = FramebufferInfo {
208 base: gpu_info.framebuffer_phys,
209 base_virt: gpu_info.framebuffer_virt as usize,
210 width: gpu_info.width,
211 height: gpu_info.height,
212 stride: gpu_info.stride,
213 format,
214 source: FramebufferSource::VirtioGpu,
215 };
216
217 let db_size = (info.stride as usize) * (info.height as usize);
219 if db_size == 0 {
220 return Err("Invalid VirtIO framebuffer size");
221 }
222 let db_pages = (db_size + 4095) / 4096;
223 let db_order = db_pages.next_power_of_two().trailing_zeros() as u8;
224 let db_frame = crate::sync::with_irqs_disabled(|token| {
225 memory::allocate_frames(token, db_order)
226 })
227 .map_err(|_| "Failed to allocate double buffer")?;
228 let db_virt = phys_to_virt(db_frame.start_address.as_u64()) as *mut u8;
229 unsafe {
230 core::ptr::write_bytes(db_virt, 0, db_size);
233 }
234
235 let fb = Framebuffer {
236 info,
237 double_buffer: Some(db_virt),
238 use_double_buffer: true,
239 dirty: DirtyRect::empty(),
240 };
241
242 *FRAMEBUFFER.lock() = Some(fb);
243 FRAMEBUFFER_INITIALIZED.store(true, Ordering::SeqCst);
244
245 log::info!(
246 "[FB] VirtIO GPU framebuffer: {}x{} @ {}bpp, stride={}",
247 info.width,
248 info.height,
249 info.format.bits_per_pixel,
250 info.stride
251 );
252
253 Ok(())
254 }
255
256 pub fn info() -> Option<FramebufferInfo> {
258 FRAMEBUFFER.lock().as_ref().map(|fb| fb.info)
259 }
260
261 pub fn width() -> u32 {
263 FRAMEBUFFER
264 .lock()
265 .as_ref()
266 .map(|fb| fb.info.width)
267 .unwrap_or(0)
268 }
269
270 pub fn height() -> u32 {
272 FRAMEBUFFER
273 .lock()
274 .as_ref()
275 .map(|fb| fb.info.height)
276 .unwrap_or(0)
277 }
278
279 pub fn stride() -> u32 {
281 FRAMEBUFFER
282 .lock()
283 .as_ref()
284 .map(|fb| fb.info.stride)
285 .unwrap_or(0)
286 }
287
288 pub fn is_available() -> bool {
290 FRAMEBUFFER_INITIALIZED.load(Ordering::Relaxed)
291 }
292
293 pub fn source() -> FramebufferSource {
295 FRAMEBUFFER
296 .lock()
297 .as_ref()
298 .map(|fb| fb.info.source)
299 .unwrap_or(FramebufferSource::None)
300 }
301
302 pub fn set_pixel(x: u32, y: u32, r: u8, g: u8, b: u8) {
304 let mut flush_region = None;
305 {
306 let mut guard = FRAMEBUFFER.lock();
307 let fb = match guard.as_mut() {
308 Some(f) => f,
309 None => return,
310 };
311
312 if x >= fb.info.width || y >= fb.info.height {
313 return;
314 }
315
316 let pixel = ((r as u32) << fb.info.format.red_shift)
317 | ((g as u32) << fb.info.format.green_shift)
318 | ((b as u32) << fb.info.format.blue_shift);
319
320 let offset = if fb.use_double_buffer {
321 fb.double_buffer.unwrap_or(fb.info.base_virt as *mut u8)
322 } else {
323 fb.info.base_virt as *mut u8
324 };
325
326 unsafe {
327 let pixel_ptr = offset.add((y * fb.info.stride + x * 4) as usize);
328 core::ptr::write(pixel_ptr as *mut u32, pixel);
329 }
330
331 fb.dirty.include(x, y, 1, 1);
332 if fb.info.source == FramebufferSource::VirtioGpu && !fb.use_double_buffer {
333 flush_region = Some((x, y, 1, 1));
334 }
335 }
336
337 if let Some((fx, fy, fw, fh)) = flush_region {
338 if let Some(gpu) = gpu::get_gpu() {
339 gpu.flush(fx, fy, fw, fh);
340 gpu.flush_now();
341 }
342 }
343 }
344
345 pub fn fill_rect(x: u32, y: u32, width: u32, height: u32, r: u8, g: u8, b: u8) {
347 if width == 0 || height == 0 {
348 return;
349 }
350
351 let mut flush_region = None;
352 {
353 let mut guard = FRAMEBUFFER.lock();
354 let fb = match guard.as_mut() {
355 Some(f) => f,
356 None => return,
357 };
358
359 if x >= fb.info.width || y >= fb.info.height {
360 return;
361 }
362
363 let max_w = fb.info.width - x;
364 let max_h = fb.info.height - y;
365 let width = width.min(max_w);
366 let height = height.min(max_h);
367 if width == 0 || height == 0 {
368 return;
369 }
370
371 let pixel = ((r as u32) << fb.info.format.red_shift)
372 | ((g as u32) << fb.info.format.green_shift)
373 | ((b as u32) << fb.info.format.blue_shift);
374
375 let offset = if fb.use_double_buffer {
376 fb.double_buffer.unwrap_or(fb.info.base_virt as *mut u8)
377 } else {
378 fb.info.base_virt as *mut u8
379 };
380
381 let stride = fb.info.stride as usize;
382 for dy in 0..height as usize {
383 let row_ptr =
384 unsafe { offset.add((y as usize + dy) * stride + x as usize * 4) as *mut u32 };
385 for dx in 0..width as usize {
386 unsafe {
387 core::ptr::write(row_ptr.add(dx), pixel);
388 }
389 }
390 }
391
392 fb.dirty.include(x, y, width, height);
393 if fb.info.source == FramebufferSource::VirtioGpu && !fb.use_double_buffer {
394 flush_region = Some((x, y, width, height));
395 }
396 }
397
398 if let Some((fx, fy, fw, fh)) = flush_region {
399 if let Some(gpu) = gpu::get_gpu() {
400 gpu.flush(fx, fy, fw, fh);
401 gpu.flush_now();
402 }
403 }
404 }
405
406 pub fn draw_hline(x: u32, y: u32, length: u32, r: u8, g: u8, b: u8) {
408 Self::fill_rect(x, y, length, 1, r, g, b);
409 }
410
411 pub fn draw_vline(x: u32, y: u32, length: u32, r: u8, g: u8, b: u8) {
413 Self::fill_rect(x, y, 1, length, r, g, b);
414 }
415
416 pub fn clear() {
418 let info = Self::info();
419 if let Some(info) = info {
420 Self::fill_rect(0, 0, info.width, info.height, 0, 0, 0);
421 }
422 }
423
424 pub fn swap_buffers() {
426 let mut virtio_present = None;
427 {
428 let mut guard = FRAMEBUFFER.lock();
429 let fb = match guard.as_mut() {
430 Some(f) => f,
431 None => return,
432 };
433
434 if !fb.use_double_buffer || fb.double_buffer.is_none() {
435 return;
436 }
437
438 let db = fb.double_buffer.unwrap();
439 let dirty = match fb.dirty.take() {
440 Some(d) => d,
441 None => return,
442 };
443 let (x, y, width, height) = dirty;
444 if width == 0 || height == 0 {
445 return;
446 }
447
448 if fb.info.source == FramebufferSource::VirtioGpu {
449 virtio_present = Some((db as *const u8, fb.info.stride, x, y, width, height));
450 } else {
451 let dst = fb.info.base_virt as *mut u8;
452 let row_bytes = width as usize * 4;
453 let stride = fb.info.stride as usize;
454 for row in 0..height as usize {
455 let row_y = y as usize + row;
456 let src_off = row_y * stride + x as usize * 4;
457 let dst_off = src_off;
458 unsafe {
459 core::ptr::copy_nonoverlapping(
460 db.add(src_off),
461 dst.add(dst_off),
462 row_bytes,
463 );
464 }
465 }
466 }
467 }
468
469 if let Some((src, src_stride, px, py, pw, ph)) = virtio_present {
470 if let Some(gpu) = gpu::get_gpu() {
471 let _ = gpu.present_from_linear(src, src_stride, px, py, pw, ph);
472 }
473 }
474 }
475
476 pub fn set_double_buffering(enable: bool) {
478 let mut fb = FRAMEBUFFER.lock();
479 if let Some(ref mut f) = fb.as_mut() {
480 f.use_double_buffer = enable && f.double_buffer.is_some();
481 }
482 }
483}
484
485#[derive(Clone, Copy)]
487pub struct RgbColor {
488 pub r: u8,
489 pub g: u8,
490 pub b: u8,
491}
492
493impl RgbColor {
494 pub const BLACK: Self = Self { r: 0, g: 0, b: 0 };
495 pub const WHITE: Self = Self {
496 r: 255,
497 g: 255,
498 b: 255,
499 };
500 pub const RED: Self = Self { r: 255, g: 0, b: 0 };
501 pub const GREEN: Self = Self { r: 0, g: 255, b: 0 };
502 pub const BLUE: Self = Self { r: 0, g: 0, b: 255 };
503 pub const CYAN: Self = Self {
504 r: 0,
505 g: 255,
506 b: 255,
507 };
508 pub const MAGENTA: Self = Self {
509 r: 255,
510 g: 0,
511 b: 255,
512 };
513 pub const YELLOW: Self = Self {
514 r: 255,
515 g: 255,
516 b: 0,
517 };
518}
519
520pub fn init() {
522 log::info!("[FB] Initializing framebuffer subsystem...");
523
524 if gpu::is_available() {
526 if let Err(e) = Framebuffer::init_virtio_gpu() {
527 log::warn!("[FB] VirtIO GPU init failed: {}", e);
528 } else {
529 log::info!("[FB] Using VirtIO GPU framebuffer");
530 return;
531 }
532 }
533
534 if Framebuffer::is_available() {
537 log::info!("[FB] Using Limine framebuffer");
538 } else {
539 log::warn!("[FB] No framebuffer available");
540 }
541}