summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-08-21 04:04:20 +0200
committersalmonik <salmonik@tuta.io>2026-08-21 04:04:20 +0200
commitdf13e7bf4d56d0563628472f8716afb01a96cfac (patch)
tree2d4ce322416cfeb8d589d1ca50c31ff53be0dc2f
parentwm: flush terminal window content on gaining focus (diff)
downloadrs-dos-df13e7bf4d56d0563628472f8716afb01a96cfac.tar.gz
rs-dos-df13e7bf4d56d0563628472f8716afb01a96cfac.zip
wm: 1px focus-colored border + flush full window on focus change
- draw_window_border(): 1px border around every window except the root. Blue (TITLE_BG_FOCUS) when focused, grey (TITLE_BG) when not. - focus() / focus_console(): composite the full window area + border pad on both the old and new focused windows (was title-bar-only before). - flush(): include border pad in the damage rectangle. - move_window(): redraw border area at old and new positions. - Remove redundant term.flush() from tty Focus handler (focus() covers it).
-rw-r--r--rsnix/src/tty.rs3
-rw-r--r--rsnix/src/wm.rs65
2 files changed, 57 insertions, 11 deletions
diff --git a/rsnix/src/tty.rs b/rsnix/src/tty.rs
index ad33cb3..c44dca8 100644
--- a/rsnix/src/tty.rs
+++ b/rsnix/src/tty.rs
@@ -201,9 +201,6 @@ pub fn read_line_from_queue(term: &mut crate::vt::Terminal) -> Option<LineInput>
201 crate::sched::TaskMsg::Focus(gained) => { 201 crate::sched::TaskMsg::Focus(gained) => {
202 if gained { 202 if gained {
203 crate::vt::set_active(term as *mut crate::vt::Terminal); 203 crate::vt::set_active(term as *mut crate::vt::Terminal);
204 // Re-composite the full window so content is visible
205 // immediately (focus() only redraws the title bar).
206 term.flush();
207 } else { 204 } else {
208 crate::vt::set_active(core::ptr::null_mut()); 205 crate::vt::set_active(core::ptr::null_mut());
209 } 206 }
diff --git a/rsnix/src/wm.rs b/rsnix/src/wm.rs
index 71c310f..e5ae6ba 100644
--- a/rsnix/src/wm.rs
+++ b/rsnix/src/wm.rs
@@ -229,7 +229,11 @@ pub fn flush(id: usize) {
229 return; 229 return;
230 }; 230 };
231 let outer_h = win.h + if win.chrome { TITLE_BAR_H } else { 0 }; 231 let outer_h = win.h + if win.chrome { TITLE_BAR_H } else { 0 };
232 composite_rect(win.x, win.y, win.w, outer_h); 232 let pad = if id != 0 { 1 } else { 0 };
233 composite_rect(
234 win.x.saturating_sub(pad), win.y.saturating_sub(pad),
235 win.w + 2 * pad, outer_h + 2 * pad,
236 );
233} 237}
234 238
235pub fn destroy(id: usize) { 239pub fn destroy(id: usize) {
@@ -323,12 +327,22 @@ pub fn focus(id: usize) {
323 && let Some(pw) = get(p) 327 && let Some(pw) = get(p)
324 { 328 {
325 let _ = crate::sched::send(pw.owner, TaskMsg::Focus(false)); 329 let _ = crate::sched::send(pw.owner, TaskMsg::Focus(false));
326 composite_rect(pw.x, pw.y, pw.w, TITLE_BAR_H); 330 // Redraw old window: full content + border area.
331 let oh = pw.h + if pw.chrome { TITLE_BAR_H } else { 0 };
332 let pad = if p != 0 { 1 } else { 0 };
333 composite_rect(
334 pw.x.saturating_sub(pad), pw.y.saturating_sub(pad),
335 pw.w + 2 * pad, oh + 2 * pad,
336 );
327 } 337 }
328 let _ = crate::sched::send(win.owner, TaskMsg::Focus(true)); 338 let _ = crate::sched::send(win.owner, TaskMsg::Focus(true));
329 if win.chrome { 339 // Redraw new window: full content + border area.
330 composite_rect(win.x, win.y, win.w, TITLE_BAR_H); 340 let oh = win.h + if win.chrome { TITLE_BAR_H } else { 0 };
331 } 341 let pad = if id != 0 { 1 } else { 0 };
342 composite_rect(
343 win.x.saturating_sub(pad), win.y.saturating_sub(pad),
344 win.w + 2 * pad, oh + 2 * pad,
345 );
332} 346}
333 347
334pub fn focus_console() { 348pub fn focus_console() {
@@ -338,7 +352,12 @@ pub fn focus_console() {
338 && let Some(pw) = get(p) 352 && let Some(pw) = get(p)
339 { 353 {
340 let _ = crate::sched::send(pw.owner, TaskMsg::Focus(false)); 354 let _ = crate::sched::send(pw.owner, TaskMsg::Focus(false));
341 composite_rect(pw.x, pw.y, pw.w, TITLE_BAR_H); 355 let oh = pw.h + if pw.chrome { TITLE_BAR_H } else { 0 };
356 let pad = if p != 0 { 1 } else { 0 };
357 composite_rect(
358 pw.x.saturating_sub(pad), pw.y.saturating_sub(pad),
359 pw.w + 2 * pad, oh + 2 * pad,
360 );
342 } 361 }
343} 362}
344 363
@@ -585,8 +604,9 @@ fn move_window(id: usize, nx: usize, ny: usize) {
585 WINDOWS[id].x = nx; 604 WINDOWS[id].x = nx;
586 WINDOWS[id].y = ny; 605 WINDOWS[id].y = ny;
587 } 606 }
588 composite_rect(ox, oy, w.w, w.h + TITLE_BAR_H); 607 let pad = 1;
589 composite_rect(nx, ny, w.w, w.h + TITLE_BAR_H); 608 composite_rect(ox - pad, oy - pad, w.w + 2 * pad, w.h + TITLE_BAR_H + 2 * pad);
609 composite_rect(nx - pad, ny - pad, w.w + 2 * pad, w.h + TITLE_BAR_H + 2 * pad);
590} 610}
591 611
592pub fn owner(id: usize) -> Option<usize> { 612pub fn owner(id: usize) -> Option<usize> {
@@ -645,11 +665,40 @@ fn composite_rect(x: usize, y: usize, w: usize, h: usize) {
645} 665}
646 666
647/// Blit the portion of a window that intersects `(rx, ry, rw, rh)`. 667/// Blit the portion of a window that intersects `(rx, ry, rw, rh)`.
668/// 1px border drawn around every window except the root (id 0).
669/// Blue when focused, grey when not.
670fn draw_window_border(id: usize, rx: usize, ry: usize, rw: usize, rh: usize) {
671 if id == 0 { return; }
672 let win = unsafe { WINDOWS[id] };
673 if !win.used || win.state != WinState::Visible { return; }
674 let outer_h = win.h + if win.chrome { TITLE_BAR_H } else { 0 };
675 let bg = if focused() == Some(id) { TITLE_BG_FOCUS } else { TITLE_BG };
676 let bc = gop::rgb(bg.0, bg.1, bg.2);
677 // Border sits 1px outside the window's outer rect.
678 let bx = win.x.saturating_sub(1);
679 let by = win.y.saturating_sub(1);
680 let bw = win.w + 2;
681 let bh = outer_h + 2;
682 let ix = bx.max(rx);
683 let iy = by.max(ry);
684 let ix2 = (bx + bw).min(rx + rw);
685 let iy2 = (by + bh).min(ry + rh);
686 if ix >= ix2 || iy >= iy2 { return; }
687 // Four edges.
688 gop::shadow_fill_rect(ix, iy, ix2 - ix, 1, bc); // top
689 gop::shadow_fill_rect(ix, iy2 - 1, ix2 - ix, 1, bc); // bottom
690 gop::shadow_fill_rect(ix, iy + 1, 1, iy2 - iy - 2, bc); // left
691 gop::shadow_fill_rect(ix2 - 1, iy + 1, 1, iy2 - iy - 2, bc); // right
692}
693
694/// Blit the portion of a window that intersects `(rx, ry, rw, rh)`.
648fn draw_window_intersect(id: usize, rx: usize, ry: usize, rw: usize, rh: usize) { 695fn draw_window_intersect(id: usize, rx: usize, ry: usize, rw: usize, rh: usize) {
649 let win = unsafe { WINDOWS[id] }; 696 let win = unsafe { WINDOWS[id] };
650 if !win.used || win.state != WinState::Visible || win.buf.is_null() { 697 if !win.used || win.state != WinState::Visible || win.buf.is_null() {
651 return; 698 return;
652 } 699 }
700 // Draw border first (under the window content).
701 draw_window_border(id, rx, ry, rw, rh);
653 let outer_h = win.h + if win.chrome { TITLE_BAR_H } else { 0 }; 702 let outer_h = win.h + if win.chrome { TITLE_BAR_H } else { 0 };
654 let wx = win.x; 703 let wx = win.x;
655 let wy = win.y; 704 let wy = win.y;