diff options
| author | salmonik <salmonik@tuta.io> | 2026-08-17 19:08:47 +0200 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-08-17 19:08:47 +0200 |
| commit | f4fab5a2b06c37df08d2d315bcb44d15af1786a4 (patch) | |
| tree | 725fbdefb64fdd987ead8ea2240269c1ba29bcfc | |
| parent | wm: title-bar drag-to-move; stop delivering title-bar gestures to apps (diff) | |
| download | rs-dos-f4fab5a2b06c37df08d2d315bcb44d15af1786a4.tar.gz rs-dos-f4fab5a2b06c37df08d2d315bcb44d15af1786a4.zip | |
wm: keep windows in place when the console scrolls beneath them
The console's batched scroll (gop::shift_up_n) shifts the whole shared
shadow, which dragged any composited window pixels up with it. Add a
scroll hook so the window manager re-composites every visible window
after a console scroll, restoring them at their fixed positions.
| -rw-r--r-- | rsnix/src/gop.rs | 19 | ||||
| -rw-r--r-- | rsnix/src/vt.rs | 9 | ||||
| -rw-r--r-- | rsnix/src/wm.rs | 16 |
3 files changed, 37 insertions, 7 deletions
diff --git a/rsnix/src/gop.rs b/rsnix/src/gop.rs index deff226..cddad22 100644 --- a/rsnix/src/gop.rs +++ b/rsnix/src/gop.rs | |||
| @@ -104,6 +104,25 @@ pub fn qr_locked() -> bool { | |||
| 104 | QR_LOCKED.load(Ordering::Relaxed) | 104 | QR_LOCKED.load(Ordering::Relaxed) |
| 105 | } | 105 | } |
| 106 | 106 | ||
| 107 | /// Hook invoked after the console scrolls, so the window manager can | ||
| 108 | /// re-composite GUI windows (which sit above the console layer in the | ||
| 109 | /// shadow and must not scroll with it). | ||
| 110 | fn scroll_hook_noop() {} | ||
| 111 | |||
| 112 | static mut SCROLL_HOOK: fn() = scroll_hook_noop; | ||
| 113 | |||
| 114 | pub fn set_scroll_hook(f: fn()) { | ||
| 115 | unsafe { | ||
| 116 | SCROLL_HOOK = f; | ||
| 117 | } | ||
| 118 | } | ||
| 119 | |||
| 120 | pub(crate) fn call_scroll_hook() { | ||
| 121 | unsafe { | ||
| 122 | SCROLL_HOOK(); | ||
| 123 | } | ||
| 124 | } | ||
| 125 | |||
| 107 | #[inline] | 126 | #[inline] |
| 108 | fn stall_display() { | 127 | fn stall_display() { |
| 109 | let n = DISPLAY_STALL.load(Ordering::Relaxed); | 128 | let n = DISPLAY_STALL.load(Ordering::Relaxed); |
diff --git a/rsnix/src/vt.rs b/rsnix/src/vt.rs index c5fc5fd..4ef568d 100644 --- a/rsnix/src/vt.rs +++ b/rsnix/src/vt.rs | |||
| @@ -458,13 +458,7 @@ fn put_byte(b: u8) { | |||
| 458 | } | 458 | } |
| 459 | 459 | ||
| 460 | fn param(i: usize, default: usize) -> usize { | 460 | fn param(i: usize, default: usize) -> usize { |
| 461 | unsafe { | 461 | unsafe { if i < NPARAMS { PARAMS[i] } else { default } } |
| 462 | if i < NPARAMS { | ||
| 463 | PARAMS[i] | ||
| 464 | } else { | ||
| 465 | default | ||
| 466 | } | ||
| 467 | } | ||
| 468 | } | 462 | } |
| 469 | 463 | ||
| 470 | /// Place a printable glyph at the cursor and advance (wrapping/scrolling). | 464 | /// Place a printable glyph at the cursor and advance (wrapping/scrolling). |
| @@ -694,6 +688,7 @@ pub fn sync_scroll() { | |||
| 694 | PENDING_SCROLLS = 0; | 688 | PENDING_SCROLLS = 0; |
| 695 | LAST_SYNC_TICK = crate::interrupts::TICKS.load(core::sync::atomic::Ordering::Relaxed); | 689 | LAST_SYNC_TICK = crate::interrupts::TICKS.load(core::sync::atomic::Ordering::Relaxed); |
| 696 | flush(); | 690 | flush(); |
| 691 | gop::call_scroll_hook(); | ||
| 697 | } | 692 | } |
| 698 | } | 693 | } |
| 699 | 694 | ||
diff --git a/rsnix/src/wm.rs b/rsnix/src/wm.rs index be74f9a..1326696 100644 --- a/rsnix/src/wm.rs +++ b/rsnix/src/wm.rs | |||
| @@ -90,6 +90,8 @@ pub fn init() { | |||
| 90 | CASCADE_Y = 0; | 90 | CASCADE_Y = 0; |
| 91 | } | 91 | } |
| 92 | FOCUSED.store(CONSOLE, Ordering::Relaxed); | 92 | FOCUSED.store(CONSOLE, Ordering::Relaxed); |
| 93 | // When the console scrolls, re-composite windows so they stay put. | ||
| 94 | gop::set_scroll_hook(redraw_windows); | ||
| 93 | } | 95 | } |
| 94 | 96 | ||
| 95 | /// The focused window, or `None` if the console has focus. | 97 | /// The focused window, or `None` if the console has focus. |
| @@ -661,6 +663,20 @@ pub fn spawn_input_pump() -> Option<usize> { | |||
| 661 | crate::sched::spawn(pump_main, "wm-input") | 663 | crate::sched::spawn(pump_main, "wm-input") |
| 662 | } | 664 | } |
| 663 | 665 | ||
| 666 | /// Re-composite every visible window. Called after the console scrolls so | ||
| 667 | /// windows stay in place while the text layer beneath them shifts. | ||
| 668 | pub fn redraw_windows() { | ||
| 669 | unsafe { | ||
| 670 | for i in 0..ZLEN { | ||
| 671 | let id = ZORDER[i]; | ||
| 672 | let w = WINDOWS[id]; | ||
| 673 | if w.used && w.state == WinState::Visible { | ||
| 674 | composite_rect(w.x, w.y, w.w, w.h + TITLE_BAR_H); | ||
| 675 | } | ||
| 676 | } | ||
| 677 | } | ||
| 678 | } | ||
| 679 | |||
| 664 | fn pump_main() { | 680 | fn pump_main() { |
| 665 | loop { | 681 | loop { |
| 666 | if focused().is_some() { | 682 | if focused().is_some() { |
