summaryrefslogtreecommitdiffstats
path: root/rsnix/src/gop.rs
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-08-17 18:56:04 +0200
committersalmonik <salmonik@tuta.io>2026-08-17 18:56:04 +0200
commit5558e3509d020a0e021a77f0617044497b2f2aee (patch)
treecd7b6cee9f7c327f9fae7ad9ac27602d1a97b222 /rsnix/src/gop.rs
parentUSB2/1.1 (EHCI/UHCI) drivers + real-hardware bring-up hardening (diff)
downloadrs-dos-5558e3509d020a0e021a77f0617044497b2f2aee.tar.gz
rs-dos-5558e3509d020a0e021a77f0617044497b2f2aee.zip
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
Diffstat (limited to 'rsnix/src/gop.rs')
-rw-r--r--rsnix/src/gop.rs103
1 files changed, 101 insertions, 2 deletions
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 @@
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.
6use crate::font; 6use crate::font;
7use crate::uefi::{GopModeInfo, GraphicsOutputProtocol, EFI_SUCCESS, PIXEL_BGR, PIXEL_RGB}; 7use crate::uefi::{EFI_SUCCESS, GopModeInfo, GraphicsOutputProtocol, PIXEL_BGR, PIXEL_RGB};
8use core::fmt; 8use core::fmt;
9use core::ptr; 9use core::ptr;
10use core::sync::atomic::{AtomicBool, AtomicU32, AtomicU64, AtomicU8, Ordering}; 10use core::sync::atomic::{AtomicBool, AtomicU8, AtomicU32, AtomicU64, Ordering};
11 11
12/// Framebuffer state captured before ExitBootServices. 12/// Framebuffer state captured before ExitBootServices.
13pub struct Framebuffer { 13pub struct Framebuffer {
@@ -512,6 +512,105 @@ pub fn fill_rect(x: usize, y: usize, w: usize, h: usize, color: u32) {
512 push_rect(x, y, w, h); 512 push_rect(x, y, w, h);
513} 513}
514 514
515/// Fill a shadow rectangle WITHOUT pushing to the display. The window
516/// manager composites several surfaces into the shadow and pushes once at
517/// the end.
518pub(crate) fn shadow_fill_rect(x: usize, y: usize, w: usize, h: usize, color: u32) {
519 let Some(fb) = fb().as_ref() else {
520 return;
521 };
522 if unsafe { SHADOW.is_null() } || x >= fb.width || y >= fb.height {
523 return;
524 }
525 let w = w.min(fb.width - x);
526 let h = h.min(fb.height - y);
527 let row_words = fb.pitch / 4;
528 unsafe {
529 for dy in 0..h {
530 let off = (y + dy) * row_words + x;
531 for dx in 0..w {
532 *SHADOW.add(off + dx) = color;
533 }
534 }
535 }
536}
537
538/// Copy a row-major `u32` source buffer (stride in pixels) into the shadow
539/// WITHOUT pushing. Clipped to the framebuffer.
540pub(crate) fn shadow_blit(
541 x: usize,
542 y: usize,
543 w: usize,
544 h: usize,
545 src: *const u32,
546 src_stride: usize,
547) {
548 let Some(fb) = fb().as_ref() else {
549 return;
550 };
551 if unsafe { SHADOW.is_null() } || src.is_null() || x >= fb.width || y >= fb.height {
552 return;
553 }
554 let w = w.min(fb.width - x);
555 let h = h.min(fb.height - y);
556 let row_words = fb.pitch / 4;
557 unsafe {
558 for dy in 0..h {
559 let doff = (y + dy) * row_words + x;
560 let soff = dy * src_stride;
561 for dx in 0..w {
562 *SHADOW.add(doff + dx) = *src.add(soff + dx);
563 }
564 }
565 }
566}
567
568/// Render one 8×16 glyph into the shadow at a pixel origin WITHOUT pushing
569/// (for window chrome and console repaint). Same glyph lookup as
570/// `blit_glyph`, but caller-controlled push.
571pub(crate) fn shadow_glyph_px(x: usize, y: usize, ch: u16, fg: (u8, u8, u8), bg: (u8, u8, u8)) {
572 let Some(fb) = fb().as_ref() else {
573 return;
574 };
575 if unsafe { SHADOW.is_null() } {
576 return;
577 }
578 let glyph = font::GLYPH_8X16;
579 let data = if ch < 256 {
580 match glyph.get(ch as usize) {
581 Some(d) => *d,
582 None => return,
583 }
584 } else if let Some(bits) = unicode_glyph_bits(ch) {
585 bits
586 } else {
587 match glyph.get(32) {
588 Some(d) => *d,
589 None => return,
590 }
591 };
592 let fgc = rgb(fg.0, fg.1, fg.2);
593 let bgc = rgb(bg.0, bg.1, bg.2);
594 let row_words = fb.pitch / 4;
595 unsafe {
596 for cy in 0..16 {
597 let py = y + cy;
598 if py >= fb.height {
599 break;
600 }
601 let row_bits = data[cy];
602 for cx in 0..8 {
603 let px = x + cx;
604 if px >= fb.width {
605 break;
606 }
607 let on = row_bits & (0x80 >> cx) != 0;
608 *SHADOW.add(py * row_words + px) = if on { fgc } else { bgc };
609 }
610 }
611 }
612}
613
515/// Print crash/fault lines directly to the framebuffer, bypassing the vt 614/// Print crash/fault lines directly to the framebuffer, bypassing the vt
516/// entirely (the vt state may be corrupt when an exception fires). 615/// entirely (the vt state may be corrupt when an exception fires).
517/// 616///