smithay_client_toolkit/shell/xdg/
dialog.rs1use crate::reexports::client::{protocol::wl_compositor::WlCompositor, Proxy, QueueHandle};
2use crate::reexports::client::{protocol::wl_surface, Connection, Dispatch};
3use crate::reexports::protocols::xdg::decoration::zv1::client::zxdg_decoration_manager_v1::ZxdgDecorationManagerV1;
4use crate::shell::xdg::window::inner::{
5 determine_decoration_mode, determine_window_state, determine_wm_capabilities, WindowInner,
6};
7use crate::shell::xdg::window::{DecorationMode, WindowConfigure};
8use crate::shell::xdg::Dispatch2;
9use crate::shell::xdg::WindowDecorations;
10use crate::shell::WaylandSurface;
11use crate::{
12 compositor::{Surface, SurfaceData},
13 globals::ProvidesBoundGlobal,
14};
15use crate::{error::GlobalError, shell::xdg::XdgShellSurface};
16use std::num::NonZeroU32;
17use std::sync::{Arc, Mutex, Weak};
18use wayland_protocols::xdg::{
19 decoration::zv1::client::zxdg_toplevel_decoration_v1,
20 dialog::v1::client::xdg_dialog_v1::XdgDialogV1, shell::client::xdg_wm_base,
21};
22use wayland_protocols::xdg::{dialog::v1::client::xdg_dialog_v1, shell::client::xdg_surface};
23use wayland_protocols::xdg::{dialog::v1::client::xdg_wm_dialog_v1, shell::client::xdg_toplevel};
24
25pub trait DialogHandler: Sized {
27 fn request_close(&mut self, conn: &Connection, qh: &QueueHandle<Self>, window: &Dialog);
32
33 fn configure(
46 &mut self,
47 conn: &Connection,
48 qh: &QueueHandle<Self>,
49 window: &Dialog,
50 configure: WindowConfigure,
51 serial: u32,
52 );
53}
54
55#[derive(Debug, Clone)]
56pub struct Dialog {
57 inner: Arc<DialogInner>,
58}
59
60#[derive(Debug)]
61pub struct DialogData(pub(crate) Weak<DialogInner>);
62
63#[derive(Debug)]
64pub(crate) struct DialogInner {
65 pub xdg_dialog: XdgDialogV1,
66 pub window: WindowInner,
67}
68
69impl Dialog {
70 pub fn new<D, GLOBAL>(
71 parent: &xdg_toplevel::XdgToplevel,
72 qh: &QueueHandle<D>,
73 compositor: &impl ProvidesBoundGlobal<WlCompositor, 6>,
75 wm: &GLOBAL,
76 decoration_manager: Option<&ZxdgDecorationManagerV1>,
77 decorations: WindowDecorations,
78 ) -> Result<Self, GlobalError>
79 where
80 D: Dispatch<wl_surface::WlSurface, SurfaceData<()>>
81 + Dispatch<xdg_surface::XdgSurface, DialogData>
82 + Dispatch<xdg_dialog_v1::XdgDialogV1, DialogData>
83 + Dispatch<xdg_toplevel::XdgToplevel, DialogData>
84 + Dispatch<zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1, DialogData>
85 + 'static,
86 GLOBAL: ProvidesBoundGlobal<xdg_wm_dialog_v1::XdgWmDialogV1, 1>
87 + ProvidesBoundGlobal<xdg_wm_base::XdgWmBase, 5>,
88 {
89 let surface = Surface::new(compositor, qh)?;
90 let dialog = Self::from_surface(surface, parent, qh, wm, decoration_manager, decorations)?;
91 dialog.wl_surface().commit();
92 Ok(dialog)
93 }
94
95 pub fn from_surface<D, GLOBAL>(
96 surface: impl Into<Surface>,
97 parent: &xdg_toplevel::XdgToplevel,
98 qh: &QueueHandle<D>,
99 wm_base: &GLOBAL,
100 decoration_manager: Option<&ZxdgDecorationManagerV1>,
101 decorations: WindowDecorations,
102 ) -> Result<Self, GlobalError>
103 where
104 D: Dispatch<xdg_surface::XdgSurface, DialogData>
105 + Dispatch<xdg_dialog_v1::XdgDialogV1, DialogData>
106 + Dispatch<xdg_toplevel::XdgToplevel, DialogData>
107 + Dispatch<zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1, DialogData>
108 + 'static,
109 GLOBAL: ProvidesBoundGlobal<xdg_wm_dialog_v1::XdgWmDialogV1, 1>
110 + ProvidesBoundGlobal<xdg_wm_base::XdgWmBase, 5>,
111 {
112 let surface = surface.into();
113 let wm_dialog: xdg_wm_dialog_v1::XdgWmDialogV1 = wm_base.bound_global()?;
114 let wm_base: xdg_wm_base::XdgWmBase = wm_base.bound_global()?;
115
116 let freeze = qh.freeze();
119
120 let inner = Arc::new_cyclic(|weak| {
121 let xdg_surface =
122 wm_base.get_xdg_surface(surface.wl_surface(), qh, DialogData(weak.clone()));
123 let surface = XdgShellSurface { surface, xdg_surface };
124 let xdg_toplevel = surface.xdg_surface.get_toplevel(qh, DialogData(weak.clone()));
125 xdg_toplevel.set_parent(Some(parent));
126 let xdg_dialog = wm_dialog.get_xdg_dialog(&xdg_toplevel, qh, DialogData(weak.clone()));
127
128 let toplevel_decoration = crate::shell::xdg::XdgShell::toplevel_decoration(
129 decoration_manager,
130 &xdg_toplevel,
131 decorations,
132 DialogData(weak.clone()),
133 qh,
134 );
135
136 DialogInner {
137 xdg_dialog,
138 window: WindowInner {
139 xdg_surface: surface,
140 xdg_toplevel,
141 toplevel_decoration,
142 pending_configure: Mutex::new(Default::default()),
143 },
144 }
145 });
146 drop(freeze);
147 let dialog = Dialog { inner };
148 Ok(dialog)
149 }
150
151 pub fn from_xdg_toplevel(toplevel: &xdg_toplevel::XdgToplevel) -> Option<Dialog> {
152 toplevel.data::<DialogData>().and_then(|data| data.dialog())
153 }
154
155 pub fn from_xdg_surface(surface: &xdg_surface::XdgSurface) -> Option<Dialog> {
156 surface.data::<DialogData>().and_then(|data| data.dialog())
157 }
158
159 pub fn from_toplevel_decoration(
160 decoration: &zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1,
161 ) -> Option<Dialog> {
162 decoration.data::<DialogData>().and_then(|data| data.dialog())
163 }
164
165 pub fn xdg_dialog(&self) -> &XdgDialogV1 {
166 &self.inner.xdg_dialog
167 }
168
169 pub fn xdg_shell_surface(&self) -> &XdgShellSurface {
170 &self.inner.window.xdg_surface
171 }
172
173 pub fn xdg_toplevel(&self) -> &xdg_toplevel::XdgToplevel {
174 &self.inner.window.xdg_toplevel
175 }
176
177 pub fn xdg_surface(&self) -> &xdg_surface::XdgSurface {
178 self.inner.window.xdg_surface.xdg_surface()
179 }
180
181 pub fn wl_surface(&self) -> &wl_surface::WlSurface {
182 self.inner.window.xdg_surface.wl_surface()
183 }
184
185 pub fn set_modal(&self, modal: bool) {
186 if modal {
187 self.inner.xdg_dialog.set_modal();
188 } else {
189 self.inner.xdg_dialog.unset_modal();
190 }
191 }
192
193 pub fn request_decoration_mode(&self, mode: Option<DecorationMode>) {
206 self.inner.window.request_decoration_mode(mode)
207 }
208}
209
210impl WaylandSurface for Dialog {
211 fn wl_surface(&self) -> &wl_surface::WlSurface {
212 self.wl_surface()
213 }
214}
215
216impl PartialEq for Dialog {
217 fn eq(&self, other: &Self) -> bool {
218 Arc::ptr_eq(&self.inner, &other.inner)
219 }
220}
221
222impl DialogData {
223 pub fn dialog(&self) -> Option<Dialog> {
227 let inner = self.0.upgrade()?;
228 Some(Dialog { inner })
229 }
230}
231
232impl Drop for DialogInner {
233 fn drop(&mut self) {
234 self.xdg_dialog.destroy();
235 }
236}
237
238impl<D: DialogHandler> Dispatch2<xdg_surface::XdgSurface, D> for DialogData {
239 fn event(
240 &self,
241 data: &mut D,
242 xdg_surface: &xdg_surface::XdgSurface,
243 event: <xdg_surface::XdgSurface as wayland_client::Proxy>::Event,
244 conn: &Connection,
245 qhandle: &QueueHandle<D>,
246 ) {
247 if let Some(dialog) = Dialog::from_xdg_surface(xdg_surface) {
248 match event {
249 xdg_surface::Event::Configure { serial } => {
250 xdg_surface.ack_configure(serial);
251
252 let configure = dialog.inner.window.pending_configure.lock().unwrap().clone();
253 DialogHandler::configure(data, conn, qhandle, &dialog, configure, serial)
254 }
255 _ => unreachable!(),
256 }
257 }
258 }
259}
260
261impl<D> Dispatch2<XdgDialogV1, D> for DialogData {
262 fn event(
263 &self,
264 _state: &mut D,
265 _proxy: &XdgDialogV1,
266 _event: <XdgDialogV1 as wayland_client::Proxy>::Event,
267 _conn: &Connection,
268 _qhandle: &QueueHandle<D>,
269 ) {
270 }
271}
272
273impl<D: DialogHandler> Dispatch2<xdg_toplevel::XdgToplevel, D> for DialogData {
274 fn event(
275 &self,
276 data: &mut D,
277 toplevel: &xdg_toplevel::XdgToplevel,
278 event: <xdg_toplevel::XdgToplevel as wayland_client::Proxy>::Event,
279 conn: &Connection,
280 qhandle: &QueueHandle<D>,
281 ) {
282 let Some(dialog) = Dialog::from_xdg_toplevel(toplevel) else {
283 return;
284 };
285
286 match event {
287 xdg_toplevel::Event::Configure { width, height, states } => {
288 let new_state = determine_window_state(&states);
289
290 let width = u32::try_from(width).ok().and_then(NonZeroU32::new);
293 let height = u32::try_from(height).ok().and_then(NonZeroU32::new);
294
295 let pending_configure = &mut dialog.inner.window.pending_configure.lock().unwrap();
296 pending_configure.new_size = (width, height);
297 pending_configure.state = new_state;
298 }
299 xdg_toplevel::Event::Close => {
300 data.request_close(conn, qhandle, &dialog);
301 }
302
303 xdg_toplevel::Event::ConfigureBounds { width, height } => {
304 let pending_configure = &mut dialog.inner.window.pending_configure.lock().unwrap();
305 if width == 0 && height == 0 {
306 pending_configure.suggested_bounds = None;
307 } else {
308 pending_configure.suggested_bounds = Some((width as u32, height as u32));
309 }
310 }
311 xdg_toplevel::Event::WmCapabilities { capabilities } => {
312 let pending_configure = &mut dialog.inner.window.pending_configure.lock().unwrap();
313 pending_configure.capabilities = determine_wm_capabilities(&capabilities)
314 }
315 _ => unreachable!(),
316 }
317 }
318}
319
320impl<D> Dispatch2<zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1, D> for DialogData
321where
322 D: DialogHandler,
323{
324 fn event(
325 &self,
326 _: &mut D,
327 decoration: &zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1,
328 event: zxdg_toplevel_decoration_v1::Event,
329 _: &Connection,
330 _: &QueueHandle<D>,
331 ) {
332 if let Some(dialog) = Dialog::from_toplevel_decoration(decoration) {
333 match event {
334 zxdg_toplevel_decoration_v1::Event::Configure { mode } => match mode {
335 wayland_client::WEnum::Value(mode) => {
336 let mode = determine_decoration_mode(mode);
337 dialog.inner.window.pending_configure.lock().unwrap().decoration_mode =
338 mode;
339 }
340
341 wayland_client::WEnum::Unknown(unknown) => {
342 log::error!(target: "sctk", "unknown decoration mode 0x{:x}", unknown);
343 }
344 },
345
346 _ => unreachable!(),
347 }
348 }
349 }
350}