From 5558e3509d020a0e021a77f0617044497b2f2aee Mon Sep 17 00:00:00 2001 From: salmonik Date: Mon, 17 Aug 2026 18:56:04 +0200 Subject: Windowed GUI: wm module, input routing, and the paint app - Add the wm window-manager facility per ADR 0004: overlapping pixel-surface windows, damage-rectangle compositing, WM-drawn title bar, focus/minimize/close, and F1-F4 keybindings - Add gop shadow compositing primitives and vt::repaint_region so the console stays layer 0 beneath windows - Route mouse (absolute coords, press/release edges) and keyboard (focus-aware input pump) to the focused window - Add the rspaint drawing-program crate (pen, 8 colors, clear) and shell draw/windows/restore commands - Extend TaskMsg with AppMouse/Focus/Close and document the design in CONTEXT.md + ADR 0004 --- rsnix/src/gop.rs | 103 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 101 insertions(+), 2 deletions(-) (limited to 'rsnix/src/gop.rs') diff --git a/rsnix/src/gop.rs b/rsnix/src/gop.rs index 676814e..deff226 100644 --- a/rsnix/src/gop.rs +++ b/rsnix/src/gop.rs @@ -4,10 +4,10 @@ /// module is the low-level rasteriser: glyph blits and pixel shifts on the /// linear framebuffer. use crate::font; -use crate::uefi::{GopModeInfo, GraphicsOutputProtocol, EFI_SUCCESS, PIXEL_BGR, PIXEL_RGB}; +use crate::uefi::{EFI_SUCCESS, GopModeInfo, GraphicsOutputProtocol, PIXEL_BGR, PIXEL_RGB}; use core::fmt; use core::ptr; -use core::sync::atomic::{AtomicBool, AtomicU32, AtomicU64, AtomicU8, Ordering}; +use core::sync::atomic::{AtomicBool, AtomicU8, AtomicU32, AtomicU64, Ordering}; /// Framebuffer state captured before ExitBootServices. pub struct Framebuffer { @@ -512,6 +512,105 @@ pub fn fill_rect(x: usize, y: usize, w: usize, h: usize, color: u32) { push_rect(x, y, w, h); } +/// Fill a shadow rectangle WITHOUT pushing to the display. The window +/// manager composites several surfaces into the shadow and pushes once at +/// the end. +pub(crate) fn shadow_fill_rect(x: usize, y: usize, w: usize, h: usize, color: u32) { + let Some(fb) = fb().as_ref() else { + return; + }; + if unsafe { SHADOW.is_null() } || x >= fb.width || y >= fb.height { + return; + } + let w = w.min(fb.width - x); + let h = h.min(fb.height - y); + let row_words = fb.pitch / 4; + unsafe { + for dy in 0..h { + let off = (y + dy) * row_words + x; + for dx in 0..w { + *SHADOW.add(off + dx) = color; + } + } + } +} + +/// Copy a row-major `u32` source buffer (stride in pixels) into the shadow +/// WITHOUT pushing. Clipped to the framebuffer. +pub(crate) fn shadow_blit( + x: usize, + y: usize, + w: usize, + h: usize, + src: *const u32, + src_stride: usize, +) { + let Some(fb) = fb().as_ref() else { + return; + }; + if unsafe { SHADOW.is_null() } || src.is_null() || x >= fb.width || y >= fb.height { + return; + } + let w = w.min(fb.width - x); + let h = h.min(fb.height - y); + let row_words = fb.pitch / 4; + unsafe { + for dy in 0..h { + let doff = (y + dy) * row_words + x; + let soff = dy * src_stride; + for dx in 0..w { + *SHADOW.add(doff + dx) = *src.add(soff + dx); + } + } + } +} + +/// Render one 8×16 glyph into the shadow at a pixel origin WITHOUT pushing +/// (for window chrome and console repaint). Same glyph lookup as +/// `blit_glyph`, but caller-controlled push. +pub(crate) fn shadow_glyph_px(x: usize, y: usize, ch: u16, fg: (u8, u8, u8), bg: (u8, u8, u8)) { + let Some(fb) = fb().as_ref() else { + return; + }; + if unsafe { SHADOW.is_null() } { + return; + } + let glyph = font::GLYPH_8X16; + let data = if ch < 256 { + match glyph.get(ch as usize) { + Some(d) => *d, + None => return, + } + } else if let Some(bits) = unicode_glyph_bits(ch) { + bits + } else { + match glyph.get(32) { + Some(d) => *d, + None => return, + } + }; + let fgc = rgb(fg.0, fg.1, fg.2); + let bgc = rgb(bg.0, bg.1, bg.2); + let row_words = fb.pitch / 4; + unsafe { + for cy in 0..16 { + let py = y + cy; + if py >= fb.height { + break; + } + let row_bits = data[cy]; + for cx in 0..8 { + let px = x + cx; + if px >= fb.width { + break; + } + let on = row_bits & (0x80 >> cx) != 0; + *SHADOW.add(py * row_words + px) = if on { fgc } else { bgc }; + } + } + } +} + /// Print crash/fault lines directly to the framebuffer, bypassing the vt /// entirely (the vt state may be corrupt when an exception fires). /// -- cgit v1.2.3