From df13e7bf4d56d0563628472f8716afb01a96cfac Mon Sep 17 00:00:00 2001 From: salmonik Date: Fri, 21 Aug 2026 04:04:20 +0200 Subject: 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). --- rsnix/src/wm.rs | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 57 insertions(+), 8 deletions(-) (limited to 'rsnix/src/wm.rs') 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) { return; }; let outer_h = win.h + if win.chrome { TITLE_BAR_H } else { 0 }; - composite_rect(win.x, win.y, win.w, outer_h); + let pad = if id != 0 { 1 } else { 0 }; + composite_rect( + win.x.saturating_sub(pad), win.y.saturating_sub(pad), + win.w + 2 * pad, outer_h + 2 * pad, + ); } pub fn destroy(id: usize) { @@ -323,12 +327,22 @@ pub fn focus(id: usize) { && let Some(pw) = get(p) { let _ = crate::sched::send(pw.owner, TaskMsg::Focus(false)); - composite_rect(pw.x, pw.y, pw.w, TITLE_BAR_H); + // Redraw old window: full content + border area. + let oh = pw.h + if pw.chrome { TITLE_BAR_H } else { 0 }; + let pad = if p != 0 { 1 } else { 0 }; + composite_rect( + pw.x.saturating_sub(pad), pw.y.saturating_sub(pad), + pw.w + 2 * pad, oh + 2 * pad, + ); } let _ = crate::sched::send(win.owner, TaskMsg::Focus(true)); - if win.chrome { - composite_rect(win.x, win.y, win.w, TITLE_BAR_H); - } + // Redraw new window: full content + border area. + let oh = win.h + if win.chrome { TITLE_BAR_H } else { 0 }; + let pad = if id != 0 { 1 } else { 0 }; + composite_rect( + win.x.saturating_sub(pad), win.y.saturating_sub(pad), + win.w + 2 * pad, oh + 2 * pad, + ); } pub fn focus_console() { @@ -338,7 +352,12 @@ pub fn focus_console() { && let Some(pw) = get(p) { let _ = crate::sched::send(pw.owner, TaskMsg::Focus(false)); - composite_rect(pw.x, pw.y, pw.w, TITLE_BAR_H); + let oh = pw.h + if pw.chrome { TITLE_BAR_H } else { 0 }; + let pad = if p != 0 { 1 } else { 0 }; + composite_rect( + pw.x.saturating_sub(pad), pw.y.saturating_sub(pad), + pw.w + 2 * pad, oh + 2 * pad, + ); } } @@ -585,8 +604,9 @@ fn move_window(id: usize, nx: usize, ny: usize) { WINDOWS[id].x = nx; WINDOWS[id].y = ny; } - composite_rect(ox, oy, w.w, w.h + TITLE_BAR_H); - composite_rect(nx, ny, w.w, w.h + TITLE_BAR_H); + let pad = 1; + composite_rect(ox - pad, oy - pad, w.w + 2 * pad, w.h + TITLE_BAR_H + 2 * pad); + composite_rect(nx - pad, ny - pad, w.w + 2 * pad, w.h + TITLE_BAR_H + 2 * pad); } pub fn owner(id: usize) -> Option { @@ -644,12 +664,41 @@ fn composite_rect(x: usize, y: usize, w: usize, h: usize) { gop::push_rect(x, y, w, h); } +/// Blit the portion of a window that intersects `(rx, ry, rw, rh)`. +/// 1px border drawn around every window except the root (id 0). +/// Blue when focused, grey when not. +fn draw_window_border(id: usize, rx: usize, ry: usize, rw: usize, rh: usize) { + if id == 0 { return; } + let win = unsafe { WINDOWS[id] }; + if !win.used || win.state != WinState::Visible { return; } + let outer_h = win.h + if win.chrome { TITLE_BAR_H } else { 0 }; + let bg = if focused() == Some(id) { TITLE_BG_FOCUS } else { TITLE_BG }; + let bc = gop::rgb(bg.0, bg.1, bg.2); + // Border sits 1px outside the window's outer rect. + let bx = win.x.saturating_sub(1); + let by = win.y.saturating_sub(1); + let bw = win.w + 2; + let bh = outer_h + 2; + let ix = bx.max(rx); + let iy = by.max(ry); + let ix2 = (bx + bw).min(rx + rw); + let iy2 = (by + bh).min(ry + rh); + if ix >= ix2 || iy >= iy2 { return; } + // Four edges. + gop::shadow_fill_rect(ix, iy, ix2 - ix, 1, bc); // top + gop::shadow_fill_rect(ix, iy2 - 1, ix2 - ix, 1, bc); // bottom + gop::shadow_fill_rect(ix, iy + 1, 1, iy2 - iy - 2, bc); // left + gop::shadow_fill_rect(ix2 - 1, iy + 1, 1, iy2 - iy - 2, bc); // right +} + /// Blit the portion of a window that intersects `(rx, ry, rw, rh)`. fn draw_window_intersect(id: usize, rx: usize, ry: usize, rw: usize, rh: usize) { let win = unsafe { WINDOWS[id] }; if !win.used || win.state != WinState::Visible || win.buf.is_null() { return; } + // Draw border first (under the window content). + draw_window_border(id, rx, ry, rw, rh); let outer_h = win.h + if win.chrome { TITLE_BAR_H } else { 0 }; let wx = win.x; let wy = win.y; -- cgit v1.2.3