diff options
Diffstat (limited to 'rsnix/src/gop.rs')
| -rw-r--r-- | rsnix/src/gop.rs | 87 |
1 files changed, 86 insertions, 1 deletions
diff --git a/rsnix/src/gop.rs b/rsnix/src/gop.rs index 9dedcb8..dad4505 100644 --- a/rsnix/src/gop.rs +++ b/rsnix/src/gop.rs | |||
| @@ -4,7 +4,7 @@ | |||
| 4 | /// module is the low-level rasteriser: glyph blits and pixel shifts on the | 4 | /// module is the low-level rasteriser: glyph blits and pixel shifts on the |
| 5 | /// linear framebuffer. | 5 | /// linear framebuffer. |
| 6 | use crate::font; | 6 | use crate::font; |
| 7 | use crate::uefi::{GraphicsOutputProtocol, PIXEL_BGR, PIXEL_RGB}; | 7 | use crate::uefi::{EFI_SUCCESS, GopModeInfo, GraphicsOutputProtocol, PIXEL_BGR, PIXEL_RGB}; |
| 8 | use alloc::boxed::Box; | 8 | use alloc::boxed::Box; |
| 9 | use alloc::vec; | 9 | use alloc::vec; |
| 10 | use alloc::vec::Vec; | 10 | use alloc::vec::Vec; |
| @@ -37,8 +37,93 @@ static mut FB: Option<Framebuffer> = None; | |||
| 37 | static mut SHADOW: *mut u32 = core::ptr::null_mut(); | 37 | static mut SHADOW: *mut u32 = core::ptr::null_mut(); |
| 38 | static mut SHADOW_WORDS: usize = 0; | 38 | static mut SHADOW_WORDS: usize = 0; |
| 39 | 39 | ||
| 40 | /// The GUI canvas. Every display surface assumes this resolution: | ||
| 41 | /// `vt` derives its text grid from it, the compositor's cursor clamps | ||
| 42 | /// to it, and the future window manager lays out windows on it. We | ||
| 43 | /// force it through GOP `SetMode` at boot — QEMU's std-vga offers it, | ||
| 44 | /// and real GPUs list it as a standard mode — falling back to the | ||
| 45 | /// smallest mode that fits, then to whatever the firmware chose. | ||
| 46 | const WANTED_W: usize = 800; | ||
| 47 | const WANTED_H: usize = 600; | ||
| 48 | |||
| 49 | /// Pick a GOP mode: prefer an exact 800×600 (BGR/RGB 32-bit), else the | ||
| 50 | /// smallest mode that fits the canvas, else leave the firmware's choice | ||
| 51 | /// alone. Call before reading the framebuffer — `SetMode` may move | ||
| 52 | /// both the mode info pointer and the framebuffer base. | ||
| 53 | fn select_gop_mode(gop: &mut GraphicsOutputProtocol) { | ||
| 54 | let mode = unsafe { &*gop.mode }; | ||
| 55 | let cur = mode.mode; | ||
| 56 | let info = unsafe { &*mode.info }; | ||
| 57 | let cur_w = info.horizontal_resolution as usize; | ||
| 58 | let cur_h = info.vertical_resolution as usize; | ||
| 59 | if cur_w == WANTED_W && cur_h == WANTED_H { | ||
| 60 | crate::serial_println!("gop: mode {} — already {}x{}", cur, cur_w, cur_h); | ||
| 61 | return; | ||
| 62 | } | ||
| 63 | |||
| 64 | let mut best: Option<(u32, usize, usize)> = None; // (mode#, w, h) | ||
| 65 | for i in 0..mode.max_mode { | ||
| 66 | let mut size: usize = 0; | ||
| 67 | let mut info_ptr: *mut GopModeInfo = core::ptr::null_mut(); | ||
| 68 | let st = unsafe { (gop.query_mode)(gop, i, &mut size, &mut info_ptr) }; | ||
| 69 | if st != EFI_SUCCESS || info_ptr.is_null() { | ||
| 70 | continue; | ||
| 71 | } | ||
| 72 | let mi = unsafe { &*info_ptr }; | ||
| 73 | if mi.pixel_format != PIXEL_BGR && mi.pixel_format != PIXEL_RGB { | ||
| 74 | continue; // 32-bit BGR/RGB only (the driver assumes 4 Bpp) | ||
| 75 | } | ||
| 76 | let w = mi.horizontal_resolution as usize; | ||
| 77 | let h = mi.vertical_resolution as usize; | ||
| 78 | if w == WANTED_W && h == WANTED_H { | ||
| 79 | best = Some((i, w, h)); | ||
| 80 | break; | ||
| 81 | } | ||
| 82 | if w >= WANTED_W && h >= WANTED_H { | ||
| 83 | match best { | ||
| 84 | Some((_, bw, bh)) if bw * bh <= w * h => {} | ||
| 85 | _ => best = Some((i, w, h)), | ||
| 86 | } | ||
| 87 | } | ||
| 88 | } | ||
| 89 | |||
| 90 | match best { | ||
| 91 | Some((n, w, h)) => { | ||
| 92 | let st = unsafe { (gop.set_mode)(gop, n) }; | ||
| 93 | if st != EFI_SUCCESS { | ||
| 94 | crate::serial_println!( | ||
| 95 | "gop: SetMode({}) failed 0x{:x} — keeping current {}x{}", | ||
| 96 | n, | ||
| 97 | st, | ||
| 98 | cur_w, | ||
| 99 | cur_h | ||
| 100 | ); | ||
| 101 | return; | ||
| 102 | } | ||
| 103 | crate::serial_println!( | ||
| 104 | "gop: mode {} selected — {}x{} (was {}x{})", | ||
| 105 | n, | ||
| 106 | w, | ||
| 107 | h, | ||
| 108 | cur_w, | ||
| 109 | cur_h | ||
| 110 | ); | ||
| 111 | } | ||
| 112 | None => crate::serial_println!( | ||
| 113 | "gop: no mode ≥ {}x{} — keeping current {}x{}", | ||
| 114 | WANTED_W, | ||
| 115 | WANTED_H, | ||
| 116 | cur_w, | ||
| 117 | cur_h | ||
| 118 | ), | ||
| 119 | } | ||
| 120 | } | ||
| 121 | |||
| 40 | /// Initialise the framebuffer from GOP. Call *before* ExitBootServices. | 122 | /// Initialise the framebuffer from GOP. Call *before* ExitBootServices. |
| 41 | pub fn init(gop: &mut GraphicsOutputProtocol) { | 123 | pub fn init(gop: &mut GraphicsOutputProtocol) { |
| 124 | // Force the 800×600 canvas first; SetMode may move the info | ||
| 125 | // pointer and framebuffer base, so everything is re-read after. | ||
| 126 | select_gop_mode(gop); | ||
| 42 | let mode = unsafe { &*gop.mode }; | 127 | let mode = unsafe { &*gop.mode }; |
| 43 | let info = unsafe { &*mode.info }; | 128 | let info = unsafe { &*mode.info }; |
| 44 | let base = mode.framebuffer_base as *mut u8; | 129 | let base = mode.framebuffer_base as *mut u8; |
