diff options
| author | salmonik <salmonik@tuta.io> | 2026-08-21 04:04:20 +0200 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-08-21 04:04:20 +0200 |
| commit | df13e7bf4d56d0563628472f8716afb01a96cfac (patch) | |
| tree | 2d4ce322416cfeb8d589d1ca50c31ff53be0dc2f | |
| parent | wm: flush terminal window content on gaining focus (diff) | |
| download | rs-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.rs | 3 | ||||
| -rw-r--r-- | rsnix/src/wm.rs | 65 |
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 | ||
| 235 | pub fn destroy(id: usize) { | 239 | pub 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 | ||
| 334 | pub fn focus_console() { | 348 | pub 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 | ||
| 592 | pub fn owner(id: usize) -> Option<usize> { | 612 | pub 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. | ||
| 670 | fn 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)`. | ||
| 648 | fn draw_window_intersect(id: usize, rx: usize, ry: usize, rw: usize, rh: usize) { | 695 | fn 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; |
