event_loop: honour App::window_size_hint() on the first configure
Previously only `set_min_size()` was called with the requested size, on the assumption that the compositor would adopt it as the initial dimension. In practice that doesn't hold: xdg-shell has no "preferred initial size" primitive and compositors are free to pick any size within `[min, max]` on the first configure. The window ended up opening at the 800x600 fallback instead of the size the application had asked for. The fix pins `min == max` before the first commit, which forces the compositor to honour the requested size in its first configure, and then releases `max_size` from the configure handler via the `pending_size_hint_unpin` latch so the surface remains user-resizable afterwards. The 800x600 fallback now only applies when the application does not provide a hint.
This commit is contained in:
@@ -675,6 +675,9 @@ pub struct AppData<A: App>
|
|||||||
pub exit_requested: bool,
|
pub exit_requested: bool,
|
||||||
/// First-configure latch for `App::start_fullscreen`.
|
/// First-configure latch for `App::start_fullscreen`.
|
||||||
pub pending_fullscreen: bool,
|
pub pending_fullscreen: bool,
|
||||||
|
/// First-configure latch: clears `max_size` after the compositor
|
||||||
|
/// has honoured the pinned initial size.
|
||||||
|
pub pending_size_hint_unpin: bool,
|
||||||
/// Main application surface.
|
/// Main application surface.
|
||||||
pub main: SurfaceState<A::Message>,
|
pub main: SurfaceState<A::Message>,
|
||||||
/// Auxiliary layer-shell surfaces keyed by their stable [`OverlayId`].
|
/// Auxiliary layer-shell surfaces keyed by their stable [`OverlayId`].
|
||||||
|
|||||||
@@ -207,8 +207,14 @@ impl<A: App> WindowHandler for AppData<A>
|
|||||||
window.set_fullscreen( None );
|
window.set_fullscreen( None );
|
||||||
self.pending_fullscreen = false;
|
self.pending_fullscreen = false;
|
||||||
}
|
}
|
||||||
let w = configure.new_size.0.map( |v| v.get() ).unwrap_or( 800 );
|
if self.pending_size_hint_unpin
|
||||||
let h = configure.new_size.1.map( |v| v.get() ).unwrap_or( 600 );
|
{
|
||||||
|
window.set_max_size( None );
|
||||||
|
self.pending_size_hint_unpin = false;
|
||||||
|
}
|
||||||
|
let ( hint_w, hint_h ) = self.app.window_size_hint().unwrap_or( ( 800, 600 ) );
|
||||||
|
let w = configure.new_size.0.map( |v| v.get() ).unwrap_or( hint_w );
|
||||||
|
let h = configure.new_size.1.map( |v| v.get() ).unwrap_or( hint_h );
|
||||||
self.on_configure( w, h );
|
self.on_configure( w, h );
|
||||||
self.app.on_resize( w, h );
|
self.app.on_resize( w, h );
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -165,9 +165,8 @@ pub( crate ) fn try_run<A: App>( app: A ) -> Result<(), RunError>
|
|||||||
.map_err( |e| RunError::MissingProtocol { name: "xdg_wm_base", detail: format!( "{e:?}" ) } )
|
.map_err( |e| RunError::MissingProtocol { name: "xdg_wm_base", detail: format!( "{e:?}" ) } )
|
||||||
};
|
};
|
||||||
|
|
||||||
// Pinning min == max is the standard idiom on xdg-shell for "I
|
// Skipped when going fullscreen: Mutter rejects the fullscreen
|
||||||
// want this exact size". Skipped when going fullscreen — Mutter
|
// request if a min_size constrains it.
|
||||||
// rejects the fullscreen request if a min_size constrains it.
|
|
||||||
let apply_size_hint = |window: &smithay_client_toolkit::shell::xdg::window::Window|
|
let apply_size_hint = |window: &smithay_client_toolkit::shell::xdg::window::Window|
|
||||||
{
|
{
|
||||||
if app.start_fullscreen() { return; }
|
if app.start_fullscreen() { return; }
|
||||||
@@ -175,14 +174,8 @@ pub( crate ) fn try_run<A: App>( app: A ) -> Result<(), RunError>
|
|||||||
{
|
{
|
||||||
Some( ( w, h ) ) =>
|
Some( ( w, h ) ) =>
|
||||||
{
|
{
|
||||||
// `set_min_size` doubles as the suggested initial size:
|
|
||||||
// most compositors send the first configure with this
|
|
||||||
// value, after which the surface remains user-resizable
|
|
||||||
// (the runtime adopts whatever size the compositor
|
|
||||||
// supplies on subsequent configures). `set_max_size` is
|
|
||||||
// deliberately *not* called — pinning min == max would
|
|
||||||
// lock the toplevel and refuse user-initiated resize.
|
|
||||||
window.set_min_size( Some( ( w, h ) ) );
|
window.set_min_size( Some( ( w, h ) ) );
|
||||||
|
window.set_max_size( Some( ( w, h ) ) );
|
||||||
}
|
}
|
||||||
None =>
|
None =>
|
||||||
{
|
{
|
||||||
@@ -263,6 +256,7 @@ pub( crate ) fn try_run<A: App>( app: A ) -> Result<(), RunError>
|
|||||||
let titlebar_title = force_window.map( |( t, _ )| t ).unwrap_or_default();
|
let titlebar_title = force_window.map( |( t, _ )| t ).unwrap_or_default();
|
||||||
|
|
||||||
let pending_fullscreen = app.start_fullscreen();
|
let pending_fullscreen = app.start_fullscreen();
|
||||||
|
let pending_size_hint_unpin = !app.start_fullscreen() && app.window_size_hint().is_some();
|
||||||
|
|
||||||
let mut data = AppData
|
let mut data = AppData
|
||||||
{
|
{
|
||||||
@@ -304,6 +298,7 @@ pub( crate ) fn try_run<A: App>( app: A ) -> Result<(), RunError>
|
|||||||
last_input_serial: 0,
|
last_input_serial: 0,
|
||||||
exit_requested: false,
|
exit_requested: false,
|
||||||
pending_fullscreen,
|
pending_fullscreen,
|
||||||
|
pending_size_hint_unpin,
|
||||||
main: SurfaceState::<A::Message>::new( surface_kind, titlebar_height, titlebar_title ),
|
main: SurfaceState::<A::Message>::new( surface_kind, titlebar_height, titlebar_title ),
|
||||||
overlays: std::collections::HashMap::new(),
|
overlays: std::collections::HashMap::new(),
|
||||||
pointer_focus: SurfaceFocus::Main,
|
pointer_focus: SurfaceFocus::Main,
|
||||||
|
|||||||
Reference in New Issue
Block a user