From d9c47d52039be7a61d3e9d3652f73322fb6f47ba Mon Sep 17 00:00:00 2001 From: salmonik Date: Sat, 1 Aug 2026 21:30:48 +0200 Subject: rsnix v0.5.1: force the GUI canvas to 800x600 via GOP SetMode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The kernel used to inherit whatever GOP mode the firmware picked (QEMU/OVMF gave 1280x800). Now every surface derives from a fixed 800x600 canvas: - uefi: typed EfiGopQueryMode/SetMode/GetMode fn pointers in GraphicsOutputProtocol (were raw *mut c_void, never called). - gop::select_gop_mode: scan the mode list for an exact 800x600 (32-bit BGR/RGB only, the driver assumes 4 Bpp), SetMode it; fall back to the smallest mode that fits the canvas, then to the firmware's choice. SetMode may move both the mode info pointer and the framebuffer base, so both are re-read after the switch. Boot log: 'gop: mode 3 selected — 800x600 (was 1280x800)'. - tests: relative assertions are resolution-agnostic; test-gfx and test-mouse had a boot-log race (shell line prints before the cursor task line) — now wait for the cursor line instead of grepping once. All four suites pass at 800x600. 0 build warnings. --- rsnix/src/gop.rs | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 86 insertions(+), 1 deletion(-) (limited to 'rsnix/src/gop.rs') 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 @@ /// module is the low-level rasteriser: glyph blits and pixel shifts on the /// linear framebuffer. use crate::font; -use crate::uefi::{GraphicsOutputProtocol, PIXEL_BGR, PIXEL_RGB}; +use crate::uefi::{EFI_SUCCESS, GopModeInfo, GraphicsOutputProtocol, PIXEL_BGR, PIXEL_RGB}; use alloc::boxed::Box; use alloc::vec; use alloc::vec::Vec; @@ -37,8 +37,93 @@ static mut FB: Option = None; static mut SHADOW: *mut u32 = core::ptr::null_mut(); static mut SHADOW_WORDS: usize = 0; +/// The GUI canvas. Every display surface assumes this resolution: +/// `vt` derives its text grid from it, the compositor's cursor clamps +/// to it, and the future window manager lays out windows on it. We +/// force it through GOP `SetMode` at boot — QEMU's std-vga offers it, +/// and real GPUs list it as a standard mode — falling back to the +/// smallest mode that fits, then to whatever the firmware chose. +const WANTED_W: usize = 800; +const WANTED_H: usize = 600; + +/// Pick a GOP mode: prefer an exact 800×600 (BGR/RGB 32-bit), else the +/// smallest mode that fits the canvas, else leave the firmware's choice +/// alone. Call before reading the framebuffer — `SetMode` may move +/// both the mode info pointer and the framebuffer base. +fn select_gop_mode(gop: &mut GraphicsOutputProtocol) { + let mode = unsafe { &*gop.mode }; + let cur = mode.mode; + let info = unsafe { &*mode.info }; + let cur_w = info.horizontal_resolution as usize; + let cur_h = info.vertical_resolution as usize; + if cur_w == WANTED_W && cur_h == WANTED_H { + crate::serial_println!("gop: mode {} — already {}x{}", cur, cur_w, cur_h); + return; + } + + let mut best: Option<(u32, usize, usize)> = None; // (mode#, w, h) + for i in 0..mode.max_mode { + let mut size: usize = 0; + let mut info_ptr: *mut GopModeInfo = core::ptr::null_mut(); + let st = unsafe { (gop.query_mode)(gop, i, &mut size, &mut info_ptr) }; + if st != EFI_SUCCESS || info_ptr.is_null() { + continue; + } + let mi = unsafe { &*info_ptr }; + if mi.pixel_format != PIXEL_BGR && mi.pixel_format != PIXEL_RGB { + continue; // 32-bit BGR/RGB only (the driver assumes 4 Bpp) + } + let w = mi.horizontal_resolution as usize; + let h = mi.vertical_resolution as usize; + if w == WANTED_W && h == WANTED_H { + best = Some((i, w, h)); + break; + } + if w >= WANTED_W && h >= WANTED_H { + match best { + Some((_, bw, bh)) if bw * bh <= w * h => {} + _ => best = Some((i, w, h)), + } + } + } + + match best { + Some((n, w, h)) => { + let st = unsafe { (gop.set_mode)(gop, n) }; + if st != EFI_SUCCESS { + crate::serial_println!( + "gop: SetMode({}) failed 0x{:x} — keeping current {}x{}", + n, + st, + cur_w, + cur_h + ); + return; + } + crate::serial_println!( + "gop: mode {} selected — {}x{} (was {}x{})", + n, + w, + h, + cur_w, + cur_h + ); + } + None => crate::serial_println!( + "gop: no mode ≥ {}x{} — keeping current {}x{}", + WANTED_W, + WANTED_H, + cur_w, + cur_h + ), + } +} + /// Initialise the framebuffer from GOP. Call *before* ExitBootServices. pub fn init(gop: &mut GraphicsOutputProtocol) { + // Force the 800×600 canvas first; SetMode may move the info + // pointer and framebuffer base, so everything is re-read after. + select_gop_mode(gop); let mode = unsafe { &*gop.mode }; let info = unsafe { &*mode.info }; let base = mode.framebuffer_base as *mut u8; -- cgit v1.2.3