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:
2026-05-10 23:16:17 +02:00
parent f3621b72c9
commit 703a1ed228
3 changed files with 17 additions and 13 deletions

View File

@@ -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:?}" ) } )
};
// Pinning min == max is the standard idiom on xdg-shell for "I
// want this exact size". Skipped when going fullscreen — Mutter
// rejects the fullscreen request if a min_size constrains it.
// Skipped when going fullscreen: Mutter rejects the fullscreen
// request if a min_size constrains it.
let apply_size_hint = |window: &smithay_client_toolkit::shell::xdg::window::Window|
{
if app.start_fullscreen() { return; }
@@ -175,14 +174,8 @@ pub( crate ) fn try_run<A: App>( app: A ) -> Result<(), RunError>
{
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_max_size( Some( ( w, h ) ) );
}
None =>
{
@@ -262,7 +255,8 @@ pub( crate ) fn try_run<A: App>( app: A ) -> Result<(), RunError>
let titlebar_height = if force_window.is_some() { 36.0 } else { 0.0 };
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
{
@@ -304,6 +298,7 @@ pub( crate ) fn try_run<A: App>( app: A ) -> Result<(), RunError>
last_input_serial: 0,
exit_requested: false,
pending_fullscreen,
pending_size_hint_unpin,
main: SurfaceState::<A::Message>::new( surface_kind, titlebar_height, titlebar_title ),
overlays: std::collections::HashMap::new(),
pointer_focus: SurfaceFocus::Main,