Skip to content

Commit e8e411d

Browse files
ryanbreenclaude
andcommitted
fix: pass z-order from BWM to kernel for correct GPU compositing order
Windows were drawn in slot order rather than z-order, causing window decorations to appear behind other windows' content. BWM now passes z_order (vec index) via set_window_position op=17, and the kernel sorts WindowCompositeInfo by z_order before GPU draws back-to-front. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 820086b commit e8e411d

3 files changed

Lines changed: 33 additions & 7 deletions

File tree

kernel/src/syscall/graphics.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,8 @@ struct WindowBuffer {
188188
/// Window position (set by compositor)
189189
x: i32,
190190
y: i32,
191+
/// Z-order (0 = bottom, higher = closer to viewer). Set by compositor.
192+
z_order: u32,
191193
/// VirGL TEXTURE_2D resource ID (0 = not initialized)
192194
virgl_resource_id: u32,
193195
/// Whether VirGL texture has been created + backed + primed
@@ -279,6 +281,7 @@ impl WindowRegistry {
279281
title_len: 0,
280282
x: 0,
281283
y: 0,
284+
z_order: 0,
282285
virgl_resource_id: 0,
283286
virgl_initialized: false,
284287
generation: 0,
@@ -918,16 +921,18 @@ fn handle_virgl_op(cmd: &FbDrawCmd) -> SyscallResult {
918921
handle_composite_windows(desc_ptr)
919922
}
920923
17 => {
921-
// SetWindowPosition: set window position for compositor
922-
// p1=buffer_id, p2=x (i16 low) | y (i16 high)
924+
// SetWindowPosition: set window position + z-order for compositor
925+
// p1=buffer_id, p2=x (i16 low) | y (i16 high), p3=z_order
923926
let buffer_id = cmd.p1 as u32;
924927
let x = (cmd.p2 & 0xFFFF) as i16 as i32;
925928
let y = ((cmd.p2 >> 16) & 0xFFFF) as i16 as i32;
929+
let z_order = cmd.p3 as u32;
926930
let mut reg = WINDOW_REGISTRY.lock();
927931
match reg.find_mut(buffer_id) {
928932
Some(buf) => {
929933
buf.x = x;
930934
buf.y = y;
935+
buf.z_order = z_order;
931936
SyscallResult::Ok(0)
932937
}
933938
None => SyscallResult::Err(super::ErrorCode::InvalidArgument as u64),
@@ -1456,6 +1461,7 @@ fn handle_composite_windows(desc_ptr: u64) -> SyscallResult {
14561461
height: buf.height,
14571462
x: buf.x,
14581463
y: buf.y,
1464+
z_order: buf.z_order,
14591465
dirty,
14601466
page_phys_addrs: buf.page_phys_addrs.clone(),
14611467
size: buf.size,
@@ -1472,6 +1478,8 @@ fn handle_composite_windows(desc_ptr: u64) -> SyscallResult {
14721478
win_idx += 1;
14731479
}
14741480
}
1481+
// Sort by z_order so GPU draws back-to-front (lower z = drawn first).
1482+
result.sort_unstable_by_key(|w| w.z_order);
14751483
result
14761484
};
14771485

@@ -1536,6 +1544,7 @@ pub struct WindowCompositeInfo {
15361544
pub height: u32,
15371545
pub x: i32,
15381546
pub y: i32,
1547+
pub z_order: u32,
15391548
pub dirty: bool,
15401549
pub page_phys_addrs: alloc::vec::Vec<u64>,
15411550
pub size: usize,

libs/libbreenix/src/graphics.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -567,13 +567,13 @@ pub fn mark_window_dirty(buffer_id: u32) -> Result<(), Error> {
567567
///
568568
/// Tells the kernel where to place this window during compositing.
569569
/// If position is never set, windows are auto-positioned.
570-
pub fn set_window_position(buffer_id: u32, x: i32, y: i32) -> Result<(), Error> {
570+
pub fn set_window_position(buffer_id: u32, x: i32, y: i32, z_order: u32) -> Result<(), Error> {
571571
let packed_xy = ((x as u16 as u32) | ((y as u16 as u32) << 16)) as i32;
572572
let cmd = FbDrawCmd {
573573
op: draw_op::SET_WINDOW_POSITION,
574574
p1: buffer_id as i32,
575575
p2: packed_xy,
576-
p3: 0,
576+
p3: z_order as i32,
577577
p4: 0,
578578
color: 0,
579579
};

userspace/programs/src/bwm.rs

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -604,10 +604,13 @@ fn discover_windows(windows: &mut Vec<Window>, screen_w: usize, screen_h: usize,
604604
core::str::from_utf8(&title[..title_len]).unwrap_or("?"),
605605
info.buffer_id, info.width, info.height, cascade_x, cascade_y);
606606

607-
// Tell kernel where the client content goes on screen (for GPU compositing)
607+
// Tell kernel where the client content goes on screen (for GPU compositing).
608+
// z_order = index in windows vec (0 = bottom). New windows are pushed to
609+
// the end, so they get the highest z_order.
608610
let content_x = cascade_x + BORDER_WIDTH as i32;
609611
let content_y = cascade_y + TITLE_BAR_HEIGHT as i32 + BORDER_WIDTH as i32;
610-
let _ = graphics::set_window_position(info.buffer_id, content_x, content_y);
612+
let z_order = windows.len() as u32; // will be at this index after push
613+
let _ = graphics::set_window_position(info.buffer_id, content_x, content_y, z_order);
611614

612615
let order = *next_order;
613616
*next_order += 1;
@@ -624,6 +627,17 @@ fn discover_windows(windows: &mut Vec<Window>, screen_w: usize, screen_h: usize,
624627
removed || added
625628
}
626629

630+
/// Update kernel z-order for all windows. Called after any z-order change
631+
/// (raise-to-front, new window, etc.) so the GPU compositor draws quads
632+
/// in correct back-to-front order.
633+
fn update_kernel_z_order(windows: &[Window]) {
634+
for (i, win) in windows.iter().enumerate() {
635+
if win.window_id != 0 {
636+
let _ = graphics::set_window_position(win.window_id, win.content_x(), win.content_y(), i as u32);
637+
}
638+
}
639+
}
640+
627641
/// Redraw all windows in z-order (index 0 = bottom), plus taskbar and app bar.
628642
/// Window frames and decorations go into the compositor buffer; GPU compositing
629643
/// handles client content via per-window textured quads.
@@ -882,6 +896,7 @@ fn main() {
882896
// New windows are pushed to end of Vec (top of z-order).
883897
// Always focus the topmost visible window so appbar selection
884898
// matches the visually foregrounded window.
899+
update_kernel_z_order(&windows);
885900
focused_win = next_visible_window(&windows, 0);
886901
compose_full_redraw(composite_buf, &mut fb, &mut shadow_fb, &bg_cache, &windows, focused_win, &clock_text);
887902
full_redraw = true;
@@ -961,7 +976,7 @@ fn main() {
961976
if windows[win_idx].window_id != 0 {
962977
let cx = windows[win_idx].content_x();
963978
let cy = windows[win_idx].content_y();
964-
let _ = graphics::set_window_position(windows[win_idx].window_id, cx, cy);
979+
let _ = graphics::set_window_position(windows[win_idx].window_id, cx, cy, win_idx as u32);
965980
}
966981
// Dirty region = union of old and new bounds
967982
let (nx0, ny0, nx1, ny1) = windows[win_idx].bounds();
@@ -1028,6 +1043,7 @@ fn main() {
10281043
if idx < windows.len() - 1 {
10291044
let win = windows.remove(idx);
10301045
windows.push(win);
1046+
update_kernel_z_order(&windows);
10311047
}
10321048
let top = windows.len() - 1;
10331049
if top != focused_win {
@@ -1060,6 +1076,7 @@ fn main() {
10601076
if ci < windows.len() - 1 {
10611077
let win = windows.remove(ci);
10621078
windows.push(win);
1079+
update_kernel_z_order(&windows);
10631080
}
10641081
let top = windows.len() - 1;
10651082

0 commit comments

Comments
 (0)