Bound zxdg_decoration_manager_v1 for compositors that support server-side decorations. Fixed a resize deadlock. Fixed scroll box list rendering.

This commit is contained in:
2026-03-23 10:36:33 -04:00
parent ad5c233b15
commit d961dd7491
2 changed files with 35 additions and 1 deletions

View File

@@ -577,7 +577,13 @@ fn layout_element(
text_system,
perf_stats,
layout_cache,
clip_rect,
// Do NOT propagate clip_rect into children: the scroll-box PushClip already clips
// them visually, and propagating the clip causes the layout cache to bake in
// culling results for a specific scroll position. When the scroll changes, the
// same cache entry would replay with stale "empty" children that were culled at
// the previous offset. Culling applies only at the direct-child level of the
// scroll box (which passes Some(viewport_rect) to layout_container_children).
None,
);
if pushed_clip {

View File

@@ -40,6 +40,9 @@ use wayland_client::{
use wayland_protocols::wp::cursor_shape::v1::client::{
wp_cursor_shape_device_v1, wp_cursor_shape_manager_v1,
};
use wayland_protocols::xdg::decoration::zv1::client::{
zxdg_decoration_manager_v1, zxdg_toplevel_decoration_v1,
};
use wayland_protocols::wp::primary_selection::zv1::client::{
zwp_primary_selection_device_manager_v1, zwp_primary_selection_device_v1,
zwp_primary_selection_offer_v1, zwp_primary_selection_source_v1,
@@ -156,6 +159,8 @@ struct State {
clipboard_device: Option<wl_data_device::WlDataDevice>,
cursor_shape_manager: Option<wp_cursor_shape_manager_v1::WpCursorShapeManagerV1>,
cursor_shape_device: Option<wp_cursor_shape_device_v1::WpCursorShapeDeviceV1>,
/// Server-side decoration handle. Kept alive so the compositor maintains decorations.
_decoration: Option<zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1>,
primary_selection_manager:
Option<zwp_primary_selection_device_manager_v1::ZwpPrimarySelectionDeviceManagerV1>,
primary_selection_device: Option<zwp_primary_selection_device_v1::ZwpPrimarySelectionDeviceV1>,
@@ -318,6 +323,8 @@ impl WaylandWindow {
manager.get_device(&seat, &qh, ())
},
);
let decoration_manager: Option<zxdg_decoration_manager_v1::ZxdgDecorationManagerV1> =
globals.bind(&qh, 1..=1, ()).ok();
let surface = compositor.create_surface(&qh, ());
let xdg_surface = wm_base.get_xdg_surface(&surface, &qh, ());
let toplevel = xdg_surface.get_toplevel(&qh, ());
@@ -325,6 +332,11 @@ impl WaylandWindow {
if let Some(app_id) = spec.app_id.as_ref() {
toplevel.set_app_id(app_id.clone());
}
let decoration = decoration_manager.as_ref().map(|dm| {
let deco = dm.get_toplevel_decoration(&toplevel, &qh, ());
deco.set_mode(zxdg_toplevel_decoration_v1::Mode::ServerSide);
deco
});
apply_size_constraints(&toplevel, &spec);
if spec.maximized {
@@ -395,6 +407,7 @@ impl WaylandWindow {
last_selection_serial: None,
last_pointer_enter_serial: None,
cursor_icon: CursorIcon::Default,
_decoration: decoration,
},
})
}
@@ -1355,6 +1368,19 @@ fn spawn_window_worker(
);
state_ref.pending_viewport = Some(current_viewport);
state_ref.pending_viewport_since.get_or_insert_with(Instant::now);
// Deadlock prevention: if the app responded to the
// configure we sent (scene matches in_flight) but the
// viewport has since moved on, clear the in_flight
// marker so we can immediately send the next configure.
// Without this, viewport_request_in_flight stays set
// to the old size forever and the app is never told
// about the new viewport.
if state_ref.viewport_request_in_flight
== Some(scene.logical_size)
{
state_ref.viewport_request_in_flight = None;
maybe_request_pending_viewport(&mut state_ref);
}
state_ref.window.request_redraw();
} else if !state_ref.window.presentation_ready() {
// Correct scene is ready but the compositor
@@ -1582,6 +1608,8 @@ delegate_noop!(
State: ignore
zwp_primary_selection_device_manager_v1::ZwpPrimarySelectionDeviceManagerV1
);
delegate_noop!(State: ignore zxdg_decoration_manager_v1::ZxdgDecorationManagerV1);
delegate_noop!(State: ignore zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1);
impl Dispatch<wl_seat::WlSeat, ()> for State {
fn event(
state: &mut Self,