diff --git a/src/draw/chrome.rs b/src/draw/chrome.rs index d6f70be..3e03891 100644 --- a/src/draw/chrome.rs +++ b/src/draw/chrome.rs @@ -84,15 +84,23 @@ pub( crate ) fn apply_input_region( wl_surface: &WlSurface, compositor: &CompositorState, input_region: Option<&[Rect]>, + scale: u32, ) { if let Some( regions ) = input_region { 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 { - 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() ) ); } diff --git a/src/draw/gles.rs b/src/draw/gles.rs index 0938993..a5bb6dc 100644 --- a/src/draw/gles.rs +++ b/src/draw/gles.rs @@ -141,7 +141,7 @@ pub( crate ) fn draw_surface_full_gpu( ss.content_dirty = false; 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 // `swap_buffers_with_damage`, so the compositor can attach it to this // frame and not the previous one. @@ -241,7 +241,7 @@ pub( crate ) fn draw_surface_partial_gpu( ss.content_dirty = false; 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 ); // Convert top-left dirty rects to EGL bottom-left coords for the // swap-with-damage call. diff --git a/src/draw/software.rs b/src/draw/software.rs index 6ea9ee7..893b7b8 100644 --- a/src/draw/software.rs +++ b/src/draw/software.rs @@ -167,7 +167,7 @@ pub( crate ) fn draw_surface_full( } } - 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 // against this exact frame; sets `frame_pending` so the run loop won't // try to draw this surface again until the callback fires. @@ -282,7 +282,7 @@ pub( crate ) fn draw_surface_partial( 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 ); wl_surface.commit(); ss.frame_pending = true; diff --git a/src/event_loop/overlays_reconcile.rs b/src/event_loop/overlays_reconcile.rs index 670c4c5..c5628da 100644 --- a/src/event_loop/overlays_reconcile.rs +++ b/src/event_loop/overlays_reconcile.rs @@ -99,7 +99,19 @@ pub( super ) fn reconcile_overlays( data: &mut AppData ) SurfaceKind::Window( ref w ) => Some( w.xdg_surface().clone() ), _ => 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 // `anchor_widget_id` → `Rect` without holding a borrow on `data.main` // across the overlay-mut loop below. @@ -122,7 +134,8 @@ pub( super ) fn reconcile_overlays( data: &mut AppData ) { 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(); ss.last_requested_size = spec.size; } @@ -272,7 +285,7 @@ pub( super ) fn reconcile_overlays( data: &mut AppData ) layer: spec.layer.to_wlr_layer(), exclusive_zone: spec.exclusive_zone, anchor: spec.anchor, - size: spec.size, + size: to_logical_size( spec.size ), keyboard_exclusive: spec.keyboard_exclusive, namespace: "ltk-overlay", }; @@ -282,6 +295,11 @@ pub( super ) fn reconcile_overlays( data: &mut AppData ) surface.materialize( cs, layer_shell, qh, output ); } let mut ss = SurfaceState::::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.layer_anchor = Some( spec.anchor ); overlays_m.insert( spec.id, ss );