smithay_client_toolkit/shell/xdg/
mod.rs1use std::os::unix::io::OwnedFd;
5use std::sync::{Arc, Mutex};
6
7use wayland_protocols::xdg::dialog::v1::client::xdg_wm_dialog_v1;
8
9use crate::reexports::client::globals::{BindError, GlobalList};
10use crate::reexports::client::Connection;
11use crate::reexports::client::{protocol::wl_surface, Dispatch, Proxy, QueueHandle};
12use crate::reexports::protocols::xdg::decoration::zv1::client::zxdg_decoration_manager_v1::ZxdgDecorationManagerV1;
13use crate::reexports::protocols::xdg::decoration::zv1::client::zxdg_toplevel_decoration_v1::Mode;
14use crate::reexports::protocols::xdg::decoration::zv1::client::{
15 zxdg_decoration_manager_v1, zxdg_toplevel_decoration_v1,
16};
17use crate::reexports::protocols::xdg::dialog::v1::client::xdg_dialog_v1;
18use crate::reexports::protocols::xdg::shell::client::{
19 xdg_positioner, xdg_surface, xdg_toplevel, xdg_wm_base,
20};
21
22use crate::compositor::Surface;
23use crate::dispatch2::Dispatch2;
24use crate::error::GlobalError;
25use crate::globals::{GlobalData, ProvidesBoundGlobal};
26use crate::registry::GlobalProxy;
27use crate::shell::xdg::dialog::{Dialog, DialogData, DialogHandler};
28
29use self::window::inner::WindowInner;
30use self::window::{Window, WindowData, WindowDecorations, WindowHandler};
31
32use super::WaylandSurface;
33
34pub mod dialog;
35pub mod fallback_frame;
36pub mod popup;
37pub mod window;
38
39#[derive(Debug)]
41pub struct XdgShell {
42 xdg_wm_dialog_v1: Option<xdg_wm_dialog_v1::XdgWmDialogV1>,
43 xdg_wm_base: xdg_wm_base::XdgWmBase,
44 xdg_decoration_manager: GlobalProxy<zxdg_decoration_manager_v1::ZxdgDecorationManagerV1>,
45}
46
47impl XdgShell {
48 pub const API_VERSION_MAX: u32 = 6;
53
54 pub fn bind<State>(globals: &GlobalList, qh: &QueueHandle<State>) -> Result<Self, BindError>
63 where
64 State: Dispatch<xdg_wm_base::XdgWmBase, GlobalData, State>
65 + Dispatch<xdg_wm_dialog_v1::XdgWmDialogV1, GlobalData, State>
66 + Dispatch<zxdg_decoration_manager_v1::ZxdgDecorationManagerV1, GlobalData, State>
67 + 'static,
68 {
69 let xdg_wm_base = globals.bind(qh, 1..=Self::API_VERSION_MAX, GlobalData)?;
70 let xdg_wm_dialog_v1 = globals.bind(qh, 1..=1, GlobalData).ok();
71 let xdg_decoration_manager = GlobalProxy::from(globals.bind(qh, 1..=1, GlobalData));
72 Ok(Self { xdg_wm_base, xdg_wm_dialog_v1, xdg_decoration_manager })
73 }
74
75 pub(crate) fn toplevel_decoration<State, D>(
76 decoration_manager: Option<&ZxdgDecorationManagerV1>,
77 xdg_toplevel: &xdg_toplevel::XdgToplevel,
78 decorations: WindowDecorations,
79 data: D,
80 qh: &QueueHandle<State>,
81 ) -> Option<zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1>
82 where
83 D: Send + Sync + 'static,
84 State: Dispatch<zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1, D> + 'static,
85 {
86 decoration_manager.and_then(|decoration_manager| {
88 match decorations {
89 WindowDecorations::ClientOnly | WindowDecorations::None => None,
91
92 _ => {
93 let toplevel_decoration =
95 decoration_manager.get_toplevel_decoration(xdg_toplevel, qh, data);
96
97 let mode = match decorations {
99 WindowDecorations::RequestServer => Some(Mode::ServerSide),
100 WindowDecorations::RequestClient => Some(Mode::ClientSide),
101 _ => None,
102 };
103
104 if let Some(mode) = mode {
105 toplevel_decoration.set_mode(mode);
106 }
107
108 Some(toplevel_decoration)
109 }
110 }
111 })
112 }
113
114 #[must_use = "Dropping all window handles will destroy the window"]
128 pub fn create_window<State>(
129 &self,
130 surface: impl Into<Surface>,
131 decorations: WindowDecorations,
132 qh: &QueueHandle<State>,
133 ) -> Window
134 where
135 State: Dispatch<xdg_surface::XdgSurface, WindowData>
136 + Dispatch<xdg_toplevel::XdgToplevel, WindowData>
137 + Dispatch<zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1, WindowData>
138 + WindowHandler
139 + 'static,
140 {
141 let decoration_manager = self.xdg_decoration_manager.get().ok();
142 let surface = surface.into();
143
144 let freeze = qh.freeze();
147
148 let inner = Arc::new_cyclic(|weak| {
149 let xdg_surface = self.xdg_wm_base.get_xdg_surface(
150 surface.wl_surface(),
151 qh,
152 WindowData(weak.clone()),
153 );
154 let xdg_surface = XdgShellSurface { surface, xdg_surface };
155 let xdg_toplevel = xdg_surface.xdg_surface().get_toplevel(qh, WindowData(weak.clone()));
156
157 let toplevel_decoration = Self::toplevel_decoration(
158 decoration_manager,
159 &xdg_toplevel,
160 decorations,
161 WindowData(weak.clone()),
162 qh,
163 );
164
165 WindowInner {
166 xdg_surface,
167 xdg_toplevel,
168 toplevel_decoration,
169 pending_configure: Mutex::new(Default::default()),
170 }
171 });
172
173 drop(freeze);
175
176 Window(inner)
177 }
178
179 #[must_use = "Dropping all dialog handles will destroy the dialog"]
180 pub fn create_dialog<State>(
181 &self,
182 surface: impl Into<Surface>,
183 decorations: WindowDecorations,
184 qh: &QueueHandle<State>,
185 parent: &xdg_toplevel::XdgToplevel,
186 ) -> Result<Dialog, GlobalError>
187 where
188 State: Dispatch<xdg_surface::XdgSurface, DialogData>
189 + Dispatch<xdg_toplevel::XdgToplevel, DialogData>
190 + Dispatch<xdg_dialog_v1::XdgDialogV1, DialogData>
191 + Dispatch<zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1, DialogData>
192 + DialogHandler
193 + 'static,
194 {
195 let decoration_manager = self.xdg_decoration_manager.get().ok();
196 Dialog::from_surface(surface, parent, qh, self, decoration_manager, decorations)
197 }
198
199 pub fn xdg_wm_base(&self) -> &xdg_wm_base::XdgWmBase {
200 &self.xdg_wm_base
201 }
202}
203
204#[derive(Debug)]
209pub struct XdgPositioner(xdg_positioner::XdgPositioner);
210
211impl XdgPositioner {
212 pub fn new(
213 wm_base: &impl ProvidesBoundGlobal<xdg_wm_base::XdgWmBase, { XdgShell::API_VERSION_MAX }>,
214 ) -> Result<Self, GlobalError> {
215 wm_base
216 .bound_global()
217 .map(|wm_base| {
218 wm_base
219 .send_constructor(
220 xdg_wm_base::Request::CreatePositioner {},
221 Arc::new(PositionerData),
222 )
223 .unwrap_or_else(|_| Proxy::inert(wm_base.backend().clone()))
224 })
225 .map(XdgPositioner)
226 }
227}
228
229impl std::ops::Deref for XdgPositioner {
230 type Target = xdg_positioner::XdgPositioner;
231
232 fn deref(&self) -> &Self::Target {
233 &self.0
234 }
235}
236
237impl Drop for XdgPositioner {
238 fn drop(&mut self) {
239 self.0.destroy()
240 }
241}
242
243struct PositionerData;
244
245impl wayland_client::backend::ObjectData for PositionerData {
246 fn event(
247 self: Arc<Self>,
248 _: &wayland_client::backend::Backend,
249 _: wayland_client::backend::protocol::Message<wayland_client::backend::ObjectId, OwnedFd>,
250 ) -> Option<Arc<dyn wayland_client::backend::ObjectData + 'static>> {
251 unreachable!("xdg_positioner has no events");
252 }
253 fn destroyed(&self, _: wayland_client::backend::ObjectId) {}
254}
255
256#[derive(Debug)]
258pub struct XdgShellSurface {
259 xdg_surface: xdg_surface::XdgSurface,
260 surface: Surface,
261}
262
263impl XdgShellSurface {
264 pub fn new<U, D>(
285 wm_base: &impl ProvidesBoundGlobal<xdg_wm_base::XdgWmBase, { XdgShell::API_VERSION_MAX }>,
286 qh: &QueueHandle<D>,
287 surface: impl Into<Surface>,
288 udata: U,
289 ) -> Result<XdgShellSurface, GlobalError>
290 where
291 D: Dispatch<xdg_surface::XdgSurface, U> + 'static,
292 U: Send + Sync + 'static,
293 {
294 let surface = surface.into();
295 let xdg_surface = wm_base.bound_global()?.get_xdg_surface(surface.wl_surface(), qh, udata);
296
297 Ok(XdgShellSurface { xdg_surface, surface })
298 }
299
300 pub fn xdg_surface(&self) -> &xdg_surface::XdgSurface {
301 &self.xdg_surface
302 }
303
304 pub fn wl_surface(&self) -> &wl_surface::WlSurface {
305 self.surface.wl_surface()
306 }
307}
308
309pub trait XdgSurface: WaylandSurface + Sized {
310 fn xdg_surface(&self) -> &xdg_surface::XdgSurface;
312
313 fn set_window_geometry(&self, x: u32, y: u32, width: u32, height: u32) {
314 self.xdg_surface().set_window_geometry(x as i32, y as i32, width as i32, height as i32);
315 }
316}
317
318impl WaylandSurface for XdgShellSurface {
319 fn wl_surface(&self) -> &wl_surface::WlSurface {
320 self.wl_surface()
321 }
322}
323
324impl XdgSurface for XdgShellSurface {
325 fn xdg_surface(&self) -> &xdg_surface::XdgSurface {
326 &self.xdg_surface
327 }
328}
329
330impl Drop for XdgShellSurface {
331 fn drop(&mut self) {
332 self.xdg_surface.destroy();
334 }
335}
336
337impl ProvidesBoundGlobal<xdg_wm_base::XdgWmBase, 5> for XdgShell {
339 fn bound_global(&self) -> Result<xdg_wm_base::XdgWmBase, GlobalError> {
340 <Self as ProvidesBoundGlobal<xdg_wm_base::XdgWmBase, 6>>::bound_global(self)
341 }
342}
343
344impl ProvidesBoundGlobal<xdg_wm_base::XdgWmBase, { XdgShell::API_VERSION_MAX }> for XdgShell {
345 fn bound_global(&self) -> Result<xdg_wm_base::XdgWmBase, GlobalError> {
346 Ok(self.xdg_wm_base.clone())
347 }
348}
349
350impl ProvidesBoundGlobal<xdg_wm_dialog_v1::XdgWmDialogV1, 1> for XdgShell {
352 fn bound_global(&self) -> Result<xdg_wm_dialog_v1::XdgWmDialogV1, GlobalError> {
353 self.xdg_wm_dialog_v1
354 .clone()
355 .ok_or(GlobalError::MissingGlobal("Dialog v1 is not available"))
356 }
357}
358
359impl<D> Dispatch2<xdg_wm_dialog_v1::XdgWmDialogV1, D> for GlobalData {
361 fn event(
362 &self,
363 _: &mut D,
364 _: &xdg_wm_dialog_v1::XdgWmDialogV1,
365 _: xdg_wm_dialog_v1::Event,
366 _: &Connection,
367 _: &QueueHandle<D>,
368 ) {
369 unreachable!("xdg_wm_dialog_v1 has no events")
370 }
371}
372
373impl<D> Dispatch2<xdg_wm_base::XdgWmBase, D> for GlobalData {
375 fn event(
376 &self,
377 _state: &mut D,
378 xdg_wm_base: &xdg_wm_base::XdgWmBase,
379 event: xdg_wm_base::Event,
380 _conn: &Connection,
381 _qh: &QueueHandle<D>,
382 ) {
383 match event {
384 xdg_wm_base::Event::Ping { serial } => {
385 xdg_wm_base.pong(serial);
386 }
387
388 _ => unreachable!(),
389 }
390 }
391}