diff --git a/src/usr/wm/wm_logic.cpp b/src/usr/wm/wm_logic.cpp index 49d8057..81d9f7f 100644 --- a/src/usr/wm/wm_logic.cpp +++ b/src/usr/wm/wm_logic.cpp @@ -343,8 +343,10 @@ static void get_window_opaque_cover_rects(const Window &w, DirtyRect *out_rects, return; *out_count = 0; if (w.transparent) { - out_rects[0] = window_client_bounds(w); - *out_count = 1; + // Transparent windows have no opaque coverage. Treating them as + // opaque causes the compositor to skip painting windows underneath + // (dock, menubar, and stacked windows behind them), which manifests + // as black or stale pixels showing through the transparent surface. return; } @@ -384,7 +386,7 @@ static void get_window_opaque_cover_rects(const Window &w, DirtyRect *out_rects, DirtyRect window_opaque_bounds(const Window &w) { if (w.transparent) - return window_client_bounds(w); + return {0, 0, 0, 0}; int side_inset = window_safe_side_inset(); int title_h = wm_title_bar_h(); int radius = gui_scaled_metric(12) - wm_frame_border(); @@ -401,6 +403,8 @@ DirtyRect window_opaque_bounds(const Window &w) DirtyRect window_occlusion_bounds(const Window &w) { + if (w.transparent) + return {0, 0, 0, 0}; return window_opaque_bounds(w); } @@ -2747,7 +2751,10 @@ void update_cursor_kind() n_k = g_input.drag_edges == RESIZE_NONE ? GUI_CURSOR_MOVE : ck_edges(g_input.drag_edges); else if (g_input.hover_resize_edges != RESIZE_NONE) n_k = ck_edges(g_input.hover_resize_edges); - else if (g_input.hover_frame_index >= 2) + else if (g_input.hover_frame_index >= 2 && g_input.hover_button < 0) + // Only show the move cursor when hovering the title bar itself; over + // a chrome button the user expects an arrow cursor so the click + // affordance is unambiguous. n_k = GUI_CURSOR_MOVE; if (!g_input.pointer_down) reset_window_snap_state(); diff --git a/src/usr/wm/wm_main.cpp b/src/usr/wm/wm_main.cpp index 340b8b2..9ee2e62 100644 --- a/src/usr/wm/wm_main.cpp +++ b/src/usr/wm/wm_main.cpp @@ -1716,6 +1716,11 @@ extern "C" int main(int argc, char **argv) inter, g_display_copy_path, manip}); if (g_dirty_frame_ready && action == wm::PresentPolicyDecision::Submit) { + // Publish all backbuffer stores before the display engine can latch + // the present buffer. Without this fence, weakly-ordered cores or + // write-combining stores can cause partially composited frames to + // appear as tearing or sparkle on the screen. + asm volatile("sfence" ::: "memory"); uint32_t sub = present_frame(&g_presentbuffer, g_dirty_rects, clamp_dirty_rect_count(g_dirty_count), frame_seq, g_frame_cursor_handle, g_frame_cursor_x, g_frame_cursor_y); if (sub) { @@ -1751,4 +1756,4 @@ extern "C" int main(int argc, char **argv) reap_exited_children(); } return 0; -} \ No newline at end of file +} diff --git a/src/usr/wm/wm_render.cpp b/src/usr/wm/wm_render.cpp index 2499a5c..d3fb921 100644 --- a/src/usr/wm/wm_render.cpp +++ b/src/usr/wm/wm_render.cpp @@ -1,5 +1,32 @@ #include "wm_core.h" +// Clipped wrapper around gui_fill_rounded_rect. When the requested rect is +// fully contained inside the dirty clip we draw the rounded shape, otherwise +// we fall back to a clipped axis-aligned fill so we never overdraw beyond the +// dirty region (which would corrupt neighbouring windows already composited +// into this frame). +static void gui_fill_rounded_rect_clipped(Surface *dst, int x, int y, int w, int h, int r, uint32_t color, + const DirtyRect &clip) +{ + int ix, iy, iw, ih; + if (!gui_intersect_rect(x, y, w, h, clip.x, clip.y, clip.w, clip.h, &ix, &iy, &iw, &ih)) + return; + if (rect_contains(clip, {x, y, w, h})) { + gui_fill_rounded_rect(dst, x, y, w, h, r, color); + } else { + gui_fill_rect(dst, ix, iy, iw, ih, color); + } +} + +static void gui_draw_rounded_rect_clipped(Surface *dst, int x, int y, int w, int h, int r, uint32_t color, + const DirtyRect &clip) +{ + int ix, iy, iw, ih; + if (!gui_intersect_rect(x, y, w, h, clip.x, clip.y, clip.w, clip.h, &ix, &iy, &iw, &ih)) + return; + gui_draw_rounded_rect(dst, x, y, w, h, r, color); +} + static Surface g_icon_close = {}; static Surface g_icon_minimize = {}; static Surface g_icon_maximize = {}; @@ -676,19 +703,20 @@ static void draw_window_decoration_frame(Surface *dst, const Window &w, const Di int shadow_offset = focused ? gui_scaled_metric(3) : gui_scaled_metric(2); uint32_t shadow_color = focused ? 0x20000000u : 0x12000000u; - gui_fill_rounded_rect(dst, sx, sy + shadow_offset, sw, sh, radius + gui_scaled_metric(1), shadow_color); + gui_fill_rounded_rect_clipped(dst, sx, sy + shadow_offset, sw, sh, radius + gui_scaled_metric(1), shadow_color, + clip); - gui_fill_rounded_rect(dst, sx, sy, sw, sh, radius, outline_color); + gui_fill_rounded_rect_clipped(dst, sx, sy, sw, sh, radius, outline_color, clip); if (sw > border * 2 && sh > border * 2) { - gui_fill_rounded_rect(dst, sx + border, sy + border, sw - border * 2, sh - border * 2, frame_radius, - frame_fill_color); + gui_fill_rounded_rect_clipped(dst, sx + border, sy + border, sw - border * 2, sh - border * 2, frame_radius, + frame_fill_color, clip); } if (sw > body_inset * 2 && sh > body_inset * 2) { - gui_fill_rounded_rect(dst, sx + body_inset, sy + body_inset, sw - body_inset * 2, sh - body_inset * 2, - body_radius, body_color); - gui_draw_rounded_rect(dst, sx + border, sy + border, sw - border * 2, sh - border * 2, frame_radius, - inner_stroke_color); + gui_fill_rounded_rect_clipped(dst, sx + body_inset, sy + body_inset, sw - body_inset * 2, sh - body_inset * 2, + body_radius, body_color, clip); + gui_draw_rounded_rect_clipped(dst, sx + border, sy + border, sw - border * 2, sh - border * 2, frame_radius, + inner_stroke_color, clip); } int title_fill_x = sx + border; @@ -1139,4 +1167,4 @@ void draw_window_client_clipped(Surface *dst, const Window &w, const DirtyRect & dst_ptr[x] = blend_coverage_rgb(dst_ptr[x], src_ptr[src_col], coverage); } } -} \ No newline at end of file +}