atagen

monolith

public
Star0
Watch0

Commit e74c94ae1257

tarball

atagen <boss@atagen.co>2026-10-19 09:35 UTC

3 files changed+25-3
xdg: honour xdg-dialog hints so marked dialogs float

A toplevel that creates an xdg_dialog_v1 is a dialog even when it sets no xdg parent, which GTK dialogs without a transient-for do. `floats` and the rules `dialog` matcher only looked at the parent, so these tiled.
Modified file: crates/monolith/src/globals.rs+3 additions
@@ -28,6 +28,7 @@ use smithay::wayland::session_lock::SessionLockManagerState;
2828 use smithay::wayland::shell::wlr_layer::WlrLayerShellState;
2929 use smithay::wayland::shell::xdg::XdgShellState;
3030 use smithay::wayland::shell::xdg::decoration::XdgDecorationState;
31+use smithay::wayland::shell::xdg::dialog::XdgDialogState;
3132 use smithay::wayland::shm::ShmState;
3233 use smithay::wayland::single_pixel_buffer::SinglePixelBufferState;
3334 use smithay::wayland::viewporter::ViewporterState;
@@ -56,6 +57,7 @@ pub struct Protocols {
5657 pub output_power: OutputPowerState,
5758 pub toplevel_list: ForeignToplevelListState,
5859 pub xdg_foreign: XdgForeignState,
60+ _xdg_dialog: XdgDialogState,
5961 _image_capture_source: ImageCaptureSourceState,
6062 _idle_inhibit: IdleInhibitManagerState,
6163 _xdg_decoration: XdgDecorationState,
@@ -118,6 +120,7 @@ impl Protocols {
118120 output_power: OutputPowerState::new::<State>(dh),
119121 toplevel_list: ForeignToplevelListState::new::<State>(dh),
120122 xdg_foreign: XdgForeignState::new::<State>(dh),
123+ _xdg_dialog: XdgDialogState::new::<State>(dh),
121124 _image_capture_source: ImageCaptureSourceState::new(),
122125 _idle_inhibit: IdleInhibitManagerState::new::<State>(dh),
123126 _xdg_decoration: XdgDecorationState::new::<State>(dh),
Modified file: crates/monolith/src/rules.rs+1 additions−1 deletions
@@ -44,7 +44,7 @@ impl State {
4444 (Some(x), _) => {
4545 x.is_transient_for().is_some() || x.window_type() == Some(WmWindowType::Dialog)
4646 }
47- (_, Some(t)) => t.parent().is_some(),
47+ (_, Some(t)) => crate::xdg::is_dialog(t),
4848 _ => false,
4949 };
5050 let parent_app_id = self
Modified file: crates/monolith/src/xdg.rs+21 additions−2 deletions
@@ -12,16 +12,27 @@ use smithay::reexports::wayland_server::Resource;
1212 use smithay::utils::{Logical, Serial, Size};
1313 use smithay::wayland::shell::xdg::decoration::XdgDecorationHandler;
1414 use smithay::wayland::compositor::with_states;
15+use smithay::wayland::shell::xdg::dialog::{ToplevelDialogHint, XdgDialogHandler};
1516 use smithay::wayland::shell::xdg::{
1617 PopupSurface, PositionerState, SurfaceCachedState, ToplevelSurface, XdgShellHandler,
17- XdgShellState,
18+ XdgShellState, XdgToplevelSurfaceData,
1819 };
1920 use smithay::wayland::xdg_foreign::{XdgForeignHandler, XdgForeignState};
2021
2122 /// Dialogs, and windows that cap their size and so could never fill a tile.
2223 pub fn floats(toplevel: &ToplevelSurface) -> bool {
2324 let (_, max) = size_hints(toplevel);
24- toplevel.parent().is_some() || (max.w > 0 && max.h > 0)
25+ is_dialog(toplevel) || (max.w > 0 && max.h > 0)
26+}
27+
28+/// A toplevel with a parent, whether set through xdg_shell or xdg-foreign, or
29+/// one that marked itself a dialog.
30+pub fn is_dialog(toplevel: &ToplevelSurface) -> bool {
31+ let marked = with_states(toplevel.wl_surface(), |s| {
32+ let role = s.data_map.get::<XdgToplevelSurfaceData>();
33+ role.is_some_and(|r| r.lock().unwrap().dialog_hint != ToplevelDialogHint::Unknown)
34+ });
35+ toplevel.parent().is_some() || marked
2536 }
2637
2738 /// The size of a window that asks for exactly one.
@@ -127,6 +138,14 @@ impl XdgForeignHandler for State {
127138 }
128139 }
129140
141+impl XdgDialogHandler for State {
142+ fn dialog_hint_changed(&mut self, surface: ToplevelSurface, _hint: ToplevelDialogHint) {
143+ if let Some(id) = self.window_id(surface.wl_surface()) {
144+ self.rematch(id);
145+ }
146+ }
147+}
148+
130149 impl XdgDecorationHandler for State {
131150 fn new_decoration(&mut self, toplevel: ToplevelSurface) {
132151 server_side(&toplevel);