ltk: convert physical sizes to logical for overlays and input regions on HiDPI
Overlay `set_size` and input regions were handed physical (layout-space) pixels straight to layer-shell and `wl_region`, both of which expect logical coordinates. They only coincide at scale 1, so on a scale-2 output every overlay requested a surface twice its intended size and the input region covered the wrong area. `apply_input_region` now takes the surface scale and divides each `Rect` down to logical before adding it to the region; the four draw paths (software and gles, full and partial) forward their scale. `reconcile_overlays` converts `OverlaySpec::size` to logical for both `set_size` and the initial `OverlayConfig` (0 = fill survives the divide), and seeds the new surface's `scale_factor` from the parent so the first configure allocates a HiDPI buffer instead of rendering at scale 1 until `scale_factor_changed` lands a frame or two later.
This commit is contained in:
@@ -84,15 +84,23 @@ pub( crate ) fn apply_input_region(
|
|||||||
wl_surface: &WlSurface,
|
wl_surface: &WlSurface,
|
||||||
compositor: &CompositorState,
|
compositor: &CompositorState,
|
||||||
input_region: Option<&[Rect]>,
|
input_region: Option<&[Rect]>,
|
||||||
|
scale: u32,
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if let Some( regions ) = input_region
|
if let Some( regions ) = input_region
|
||||||
{
|
{
|
||||||
if let Ok( region ) = Region::new( compositor )
|
if let Ok( region ) = Region::new( compositor )
|
||||||
{
|
{
|
||||||
|
// `Rect`s are physical (layout space); `wl_region` is logical.
|
||||||
|
let s = scale.max( 1 ) as f32;
|
||||||
for r in regions
|
for r in regions
|
||||||
{
|
{
|
||||||
region.add( r.x as i32, r.y as i32, r.width as i32, r.height as i32 );
|
region.add(
|
||||||
|
( r.x / s ).round() as i32,
|
||||||
|
( r.y / s ).round() as i32,
|
||||||
|
( r.width / s ).round() as i32,
|
||||||
|
( r.height / s ).round() as i32,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
wl_surface.set_input_region( Some( region.wl_region() ) );
|
wl_surface.set_input_region( Some( region.wl_region() ) );
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ pub( crate ) fn draw_surface_full_gpu<Msg: Clone>(
|
|||||||
ss.content_dirty = false;
|
ss.content_dirty = false;
|
||||||
|
|
||||||
let wl_surface = ss.surface.wl_surface();
|
let wl_surface = ss.surface.wl_surface();
|
||||||
apply_input_region( wl_surface, compositor, input_region );
|
apply_input_region( wl_surface, compositor, input_region, scale );
|
||||||
// Frame callback request must precede the implicit commit done by
|
// Frame callback request must precede the implicit commit done by
|
||||||
// `swap_buffers_with_damage`, so the compositor can attach it to this
|
// `swap_buffers_with_damage`, so the compositor can attach it to this
|
||||||
// frame and not the previous one.
|
// frame and not the previous one.
|
||||||
@@ -241,7 +241,7 @@ pub( crate ) fn draw_surface_partial_gpu<Msg: Clone>(
|
|||||||
ss.content_dirty = false;
|
ss.content_dirty = false;
|
||||||
|
|
||||||
let wl_surface = ss.surface.wl_surface();
|
let wl_surface = ss.surface.wl_surface();
|
||||||
apply_input_region( wl_surface, compositor, input_region );
|
apply_input_region( wl_surface, compositor, input_region, scale );
|
||||||
request_frame( wl_surface );
|
request_frame( wl_surface );
|
||||||
// Convert top-left dirty rects to EGL bottom-left coords for the
|
// Convert top-left dirty rects to EGL bottom-left coords for the
|
||||||
// swap-with-damage call.
|
// swap-with-damage call.
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ pub( crate ) fn draw_surface_full<Msg: Clone>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
apply_input_region( wl_surface, compositor, input_region );
|
apply_input_region( wl_surface, compositor, input_region, scale );
|
||||||
// Request a frame callback BEFORE commit so the compositor schedules it
|
// Request a frame callback BEFORE commit so the compositor schedules it
|
||||||
// against this exact frame; sets `frame_pending` so the run loop won't
|
// against this exact frame; sets `frame_pending` so the run loop won't
|
||||||
// try to draw this surface again until the callback fires.
|
// try to draw this surface again until the callback fires.
|
||||||
@@ -282,7 +282,7 @@ pub( crate ) fn draw_surface_partial<Msg: Clone>(
|
|||||||
wl_surface.damage_buffer( r.x as i32, r.y as i32, r.width as i32, r.height as i32 );
|
wl_surface.damage_buffer( r.x as i32, r.y as i32, r.width as i32, r.height as i32 );
|
||||||
}
|
}
|
||||||
|
|
||||||
apply_input_region( wl_surface, compositor, input_region );
|
apply_input_region( wl_surface, compositor, input_region, scale );
|
||||||
request_frame( wl_surface );
|
request_frame( wl_surface );
|
||||||
wl_surface.commit();
|
wl_surface.commit();
|
||||||
ss.frame_pending = true;
|
ss.frame_pending = true;
|
||||||
|
|||||||
@@ -99,7 +99,19 @@ pub( super ) fn reconcile_overlays<A: App>( data: &mut AppData<A> )
|
|||||||
SurfaceKind::Window( ref w ) => Some( w.xdg_surface().clone() ),
|
SurfaceKind::Window( ref w ) => Some( w.xdg_surface().clone() ),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
let parent_scale = data.main.scale_factor.max( 1 ) as f32;
|
let parent_scale_i = data.main.scale_factor.max( 1 );
|
||||||
|
let parent_scale = parent_scale_i as f32;
|
||||||
|
// `OverlaySpec::size` is physical pixels (the app's layout space); the
|
||||||
|
// layer-shell `set_size` is logical. They only coincide at scale 1, so
|
||||||
|
// convert here — without it a scale-2 overlay requests a surface twice
|
||||||
|
// its intended size. `0 = fill` survives the divide.
|
||||||
|
let to_logical_size = move | ( w, h ): ( u32, u32 ) | -> ( u32, u32 )
|
||||||
|
{
|
||||||
|
(
|
||||||
|
( w as f32 / parent_scale ).round() as u32,
|
||||||
|
( h as f32 / parent_scale ).round() as u32,
|
||||||
|
)
|
||||||
|
};
|
||||||
// Snapshot the previous-frame anchor lookup table so we can resolve
|
// Snapshot the previous-frame anchor lookup table so we can resolve
|
||||||
// `anchor_widget_id` → `Rect` without holding a borrow on `data.main`
|
// `anchor_widget_id` → `Rect` without holding a borrow on `data.main`
|
||||||
// across the overlay-mut loop below.
|
// across the overlay-mut loop below.
|
||||||
@@ -122,7 +134,8 @@ pub( super ) fn reconcile_overlays<A: App>( data: &mut AppData<A> )
|
|||||||
{
|
{
|
||||||
if let SurfaceKind::Layer( ref layer_surface ) = ss.surface
|
if let SurfaceKind::Layer( ref layer_surface ) = ss.surface
|
||||||
{
|
{
|
||||||
layer_surface.set_size( spec.size.0, spec.size.1 );
|
let ( lw, lh ) = to_logical_size( spec.size );
|
||||||
|
layer_surface.set_size( lw, lh );
|
||||||
layer_surface.commit();
|
layer_surface.commit();
|
||||||
ss.last_requested_size = spec.size;
|
ss.last_requested_size = spec.size;
|
||||||
}
|
}
|
||||||
@@ -272,7 +285,7 @@ pub( super ) fn reconcile_overlays<A: App>( data: &mut AppData<A> )
|
|||||||
layer: spec.layer.to_wlr_layer(),
|
layer: spec.layer.to_wlr_layer(),
|
||||||
exclusive_zone: spec.exclusive_zone,
|
exclusive_zone: spec.exclusive_zone,
|
||||||
anchor: spec.anchor,
|
anchor: spec.anchor,
|
||||||
size: spec.size,
|
size: to_logical_size( spec.size ),
|
||||||
keyboard_exclusive: spec.keyboard_exclusive,
|
keyboard_exclusive: spec.keyboard_exclusive,
|
||||||
namespace: "ltk-overlay",
|
namespace: "ltk-overlay",
|
||||||
};
|
};
|
||||||
@@ -282,6 +295,11 @@ pub( super ) fn reconcile_overlays<A: App>( data: &mut AppData<A> )
|
|||||||
surface.materialize( cs, layer_shell, qh, output );
|
surface.materialize( cs, layer_shell, qh, output );
|
||||||
}
|
}
|
||||||
let mut ss = SurfaceState::<A::Message>::new( surface, 0.0, String::new() );
|
let mut ss = SurfaceState::<A::Message>::new( surface, 0.0, String::new() );
|
||||||
|
// Inherit the parent's scale so the first configure allocates a
|
||||||
|
// HiDPI buffer and lays out at the right size from frame one,
|
||||||
|
// instead of rendering at scale 1 until `scale_factor_changed`
|
||||||
|
// lands a frame or two later.
|
||||||
|
ss.scale_factor = parent_scale_i;
|
||||||
ss.last_requested_size = spec.size;
|
ss.last_requested_size = spec.size;
|
||||||
ss.layer_anchor = Some( spec.anchor );
|
ss.layer_anchor = Some( spec.anchor );
|
||||||
overlays_m.insert( spec.id, ss );
|
overlays_m.insert( spec.id, ss );
|
||||||
|
|||||||
Reference in New Issue
Block a user