atagen

monolith

public
Star0
Watch0

Commit d3ea8aacd50c

tarball

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

2 files changed+19-0
xdg: advertise xdg-foreign so portal dialogs float over their app

A portal file chooser is a separate client that parents itself to the app through zxdg_imported_v2.set_parent_of, which the compositor never advertised. The window sat tiled with no parent. Smithay stores the imported parent as the toplevel's xdg parent, so `floats` and `parent_of` already treat it like an xdg_toplevel.set_parent. parent_changed now keeps the layout parent current when it is set after mapping.
Modified file: crates/monolith/src/globals.rs+3 additions
@@ -32,6 +32,7 @@ use smithay::wayland::shm::ShmState;
3232 use smithay::wayland::single_pixel_buffer::SinglePixelBufferState;
3333 use smithay::wayland::viewporter::ViewporterState;
3434 use smithay::wayland::xdg_activation::XdgActivationState;
35+use smithay::wayland::xdg_foreign::XdgForeignState;
3536 use smithay::wayland::xwayland_shell::XWaylandShellState;
3637
3738 pub struct Protocols {
@@ -54,6 +55,7 @@ pub struct Protocols {
5455 pub foreign_toplevels: ForeignToplevelState,
5556 pub output_power: OutputPowerState,
5657 pub toplevel_list: ForeignToplevelListState,
58+ pub xdg_foreign: XdgForeignState,
5759 _image_capture_source: ImageCaptureSourceState,
5860 _idle_inhibit: IdleInhibitManagerState,
5961 _xdg_decoration: XdgDecorationState,
@@ -115,6 +117,7 @@ impl Protocols {
115117 foreign_toplevels: ForeignToplevelState::new::<State>(dh),
116118 output_power: OutputPowerState::new::<State>(dh),
117119 toplevel_list: ForeignToplevelListState::new::<State>(dh),
120+ xdg_foreign: XdgForeignState::new::<State>(dh),
118121 _image_capture_source: ImageCaptureSourceState::new(),
119122 _idle_inhibit: IdleInhibitManagerState::new::<State>(dh),
120123 _xdg_decoration: XdgDecorationState::new::<State>(dh),
Modified file: crates/monolith/src/xdg.rs+16 additions
@@ -16,6 +16,7 @@ use smithay::wayland::shell::xdg::{
1616 PopupSurface, PositionerState, SurfaceCachedState, ToplevelSurface, XdgShellHandler,
1717 XdgShellState,
1818 };
19+use smithay::wayland::xdg_foreign::{XdgForeignHandler, XdgForeignState};
1920
2021 /// Dialogs, and windows that cap their size and so could never fill a tile.
2122 pub fn floats(toplevel: &ToplevelSurface) -> bool {
@@ -78,6 +79,15 @@ impl XdgShellHandler for State {
7879 }
7980 }
8081
82+ fn parent_changed(&mut self, surface: ToplevelSurface) {
83+ let Some(id) = self.window_id(surface.wl_surface()) else {
84+ return;
85+ };
86+ let parent = self.wm.get(id).and_then(|s| self.parent_of(&s.window));
87+ self.wm.layout.set_parent(id, parent);
88+ self.rematch(id);
89+ }
90+
8191 fn fullscreen_request(&mut self, surface: ToplevelSurface, output: Option<WlOutput>) {
8292 self.set_fullscreen(&surface, true, output);
8393 }
@@ -111,6 +121,12 @@ impl XdgShellHandler for State {
111121 }
112122 }
113123
124+impl XdgForeignHandler for State {
125+ fn xdg_foreign_state(&mut self) -> &mut XdgForeignState {
126+ &mut self.protocols.xdg_foreign
127+ }
128+}
129+
114130 impl XdgDecorationHandler for State {
115131 fn new_decoration(&mut self, toplevel: ToplevelSurface) {
116132 server_side(&toplevel);