diff options
| author | salmonik <salmonik@tuta.io> | 2026-08-17 18:56:04 +0200 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-08-17 18:56:04 +0200 |
| commit | 5558e3509d020a0e021a77f0617044497b2f2aee (patch) | |
| tree | cd7b6cee9f7c327f9fae7ad9ac27602d1a97b222 /rsnix/src/gop.rs | |
| parent | USB2/1.1 (EHCI/UHCI) drivers + real-hardware bring-up hardening (diff) | |
| download | rs-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 '')
| -rw-r--r-- | rsnix/src/gop.rs | 103 |
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. |
| 6 | use crate::font; | 6 | use crate::font; |
| 7 | use crate::uefi::{GopModeInfo, GraphicsOutputProtocol, EFI_SUCCESS, PIXEL_BGR, PIXEL_RGB}; | 7 | use crate::uefi::{EFI_SUCCESS, GopModeInfo, GraphicsOutputProtocol, PIXEL_BGR, PIXEL_RGB}; |
| 8 | use core::fmt; | 8 | use core::fmt; |
| 9 | use core::ptr; | 9 | use core::ptr; |
| 10 | use core::sync::atomic::{AtomicBool, AtomicU32, AtomicU64, AtomicU8, Ordering}; | 10 | use core::sync::atomic::{AtomicBool, AtomicU8, AtomicU32, AtomicU64, Ordering}; |
| 11 | 11 | ||
| 12 | /// Framebuffer state captured before ExitBootServices. | 12 | /// Framebuffer state captured before ExitBootServices. |
| 13 | pub struct Framebuffer { | 13 | pub 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. | ||
| 518 | pub(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. | ||
| 540 | pub(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. | ||
| 571 | pub(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 | /// |
