smithay_client_toolkit/shell/xdg/
mod.rs1use std::os::unix::io::OwnedFd;
5use std::sync::{Arc, Mutex};
6
7use crate::reexports::client::globals::{BindError, GlobalList};
8use crate::reexports::client::Connection;
9use crate::reexports::client::{protocol::wl_surface, Dispatch, Proxy, QueueHandle};
10use crate::reexports::csd_frame::{WindowManagerCapabilities, WindowState};
11use crate::reexports::protocols::xdg::decoration::zv1::client::zxdg_toplevel_decoration_v1::Mode;
12use crate::reexports::protocols::xdg::decoration::zv1::client::{
13 zxdg_decoration_manager_v1, zxdg_toplevel_decoration_v1,
14};
15use crate::reexports::protocols::xdg::shell::client::{
16 xdg_positioner, xdg_surface, xdg_toplevel, xdg_wm_base,
17};
18
19use crate::compositor::Surface;
20use crate::dispatch2::Dispatch2;
21use crate::error::GlobalError;
22use crate::globals::{GlobalData, ProvidesBoundGlobal};
23use crate::registry::GlobalProxy;
24
25use self::window::inner::WindowInner;
26use self::window::{
27 DecorationMode, Window, WindowConfigure, WindowData, WindowDecorations, WindowHandler,
28};
29
30use super::WaylandSurface;
31
32pub mod fallback_frame;
33pub mod popup;
34pub mod window;
35
36#[derive(Debug)]
38pub struct XdgShell {
39 xdg_wm_base: xdg_wm_base::XdgWmBase,
40 xdg_decoration_manager: GlobalProxy<zxdg_decoration_manager_v1::ZxdgDecorationManagerV1>,
41}
42
43impl XdgShell {
44 pub const API_VERSION_MAX: u32 = 6;
49
50 pub fn bind<State>(globals: &GlobalList, qh: &QueueHandle<State>) -> Result<Self, BindError>
59 where
60 State: Dispatch<xdg_wm_base::XdgWmBase, GlobalData, State>
61 + Dispatch<zxdg_decoration_manager_v1::ZxdgDecorationManagerV1, GlobalData, State>
62 + 'static,
63 {
64 let xdg_wm_base = globals.bind(qh, 1..=Self::API_VERSION_MAX, GlobalData)?;
65 let xdg_decoration_manager = GlobalProxy::from(globals.bind(qh, 1..=1, GlobalData));
66 Ok(Self { xdg_wm_base, xdg_decoration_manager })
67 }
68
69 #[must_use = "Dropping all window handles will destroy the window"]
83 pub fn create_window<State>(
84 &self,
85 surface: impl Into<Surface>,
86 decorations: WindowDecorations,
87 qh: &QueueHandle<State>,
88 ) -> Window
89 where
90 State: Dispatch<xdg_surface::XdgSurface, WindowData>
91 + Dispatch<xdg_toplevel::XdgToplevel, WindowData>
92 + Dispatch<zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1, WindowData>
93 + WindowHandler
94 + 'static,
95 {
96 let decoration_manager = self.xdg_decoration_manager.get().ok();
97 let surface = surface.into();
98
99 let freeze = qh.freeze();
102
103 let inner = Arc::new_cyclic(|weak| {
104 let xdg_surface = self.xdg_wm_base.get_xdg_surface(
105 surface.wl_surface(),
106 qh,
107 WindowData(weak.clone()),
108 );
109 let xdg_surface = XdgShellSurface { surface, xdg_surface };
110 let xdg_toplevel = xdg_surface.xdg_surface().get_toplevel(qh, WindowData(weak.clone()));
111
112 let toplevel_decoration = decoration_manager.and_then(|decoration_manager| {
114 match decorations {
115 WindowDecorations::ClientOnly | WindowDecorations::None => None,
117
118 _ => {
119 let toplevel_decoration = decoration_manager.get_toplevel_decoration(
121 &xdg_toplevel,
122 qh,
123 WindowData(weak.clone()),
124 );
125
126 let mode = match decorations {
128 WindowDecorations::RequestServer => Some(Mode::ServerSide),
129 WindowDecorations::RequestClient => Some(Mode::ClientSide),
130 _ => None,
131 };
132
133 if let Some(mode) = mode {
134 toplevel_decoration.set_mode(mode);
135 }
136
137 Some(toplevel_decoration)
138 }
139 }
140 });
141
142 WindowInner {
143 xdg_surface,
144 xdg_toplevel,
145 toplevel_decoration,
146 pending_configure: Mutex::new(WindowConfigure {
147 new_size: (None, None),
148 suggested_bounds: None,
149 decoration_mode: DecorationMode::Client,
151 state: WindowState::empty(),
152 capabilities: WindowManagerCapabilities::all(),
154 }),
155 }
156 });
157
158 drop(freeze);
160
161 Window(inner)
162 }
163
164 pub fn xdg_wm_base(&self) -> &xdg_wm_base::XdgWmBase {
165 &self.xdg_wm_base
166 }
167}
168
169#[derive(Debug)]
174pub struct XdgPositioner(xdg_positioner::XdgPositioner);
175
176impl XdgPositioner {
177 pub fn new(
178 wm_base: &impl ProvidesBoundGlobal<xdg_wm_base::XdgWmBase, { XdgShell::API_VERSION_MAX }>,
179 ) -> Result<Self, GlobalError> {
180 wm_base
181 .bound_global()
182 .map(|wm_base| {
183 wm_base
184 .send_constructor(
185 xdg_wm_base::Request::CreatePositioner {},
186 Arc::new(PositionerData),
187 )
188 .unwrap_or_else(|_| Proxy::inert(wm_base.backend().clone()))
189 })
190 .map(XdgPositioner)
191 }
192}
193
194impl std::ops::Deref for XdgPositioner {
195 type Target = xdg_positioner::XdgPositioner;
196
197 fn deref(&self) -> &Self::Target {
198 &self.0
199 }
200}
201
202impl Drop for XdgPositioner {
203 fn drop(&mut self) {
204 self.0.destroy()
205 }
206}
207
208struct PositionerData;
209
210impl wayland_client::backend::ObjectData for PositionerData {
211 fn event(
212 self: Arc<Self>,
213 _: &wayland_client::backend::Backend,
214 _: wayland_client::backend::protocol::Message<wayland_client::backend::ObjectId, OwnedFd>,
215 ) -> Option<Arc<dyn wayland_client::backend::ObjectData + 'static>> {
216 unreachable!("xdg_positioner has no events");
217 }
218 fn destroyed(&self, _: wayland_client::backend::ObjectId) {}
219}
220
221#[derive(Debug)]
223pub struct XdgShellSurface {
224 xdg_surface: xdg_surface::XdgSurface,
225 surface: Surface,
226}
227
228impl XdgShellSurface {
229 pub fn new<U, D>(
250 wm_base: &impl ProvidesBoundGlobal<xdg_wm_base::XdgWmBase, { XdgShell::API_VERSION_MAX }>,
251 qh: &QueueHandle<D>,
252 surface: impl Into<Surface>,
253 udata: U,
254 ) -> Result<XdgShellSurface, GlobalError>
255 where
256 D: Dispatch<xdg_surface::XdgSurface, U> + 'static,
257 U: Send + Sync + 'static,
258 {
259 let surface = surface.into();
260 let xdg_surface = wm_base.bound_global()?.get_xdg_surface(surface.wl_surface(), qh, udata);
261
262 Ok(XdgShellSurface { xdg_surface, surface })
263 }
264
265 pub fn xdg_surface(&self) -> &xdg_surface::XdgSurface {
266 &self.xdg_surface
267 }
268
269 pub fn wl_surface(&self) -> &wl_surface::WlSurface {
270 self.surface.wl_surface()
271 }
272}
273
274pub trait XdgSurface: WaylandSurface + Sized {
275 fn xdg_surface(&self) -> &xdg_surface::XdgSurface;
277
278 fn set_window_geometry(&self, x: u32, y: u32, width: u32, height: u32) {
279 self.xdg_surface().set_window_geometry(x as i32, y as i32, width as i32, height as i32);
280 }
281}
282
283impl WaylandSurface for XdgShellSurface {
284 fn wl_surface(&self) -> &wl_surface::WlSurface {
285 self.wl_surface()
286 }
287}
288
289impl XdgSurface for XdgShellSurface {
290 fn xdg_surface(&self) -> &xdg_surface::XdgSurface {
291 &self.xdg_surface
292 }
293}
294
295impl Drop for XdgShellSurface {
296 fn drop(&mut self) {
297 self.xdg_surface.destroy();
299 }
300}
301
302impl ProvidesBoundGlobal<xdg_wm_base::XdgWmBase, 5> for XdgShell {
304 fn bound_global(&self) -> Result<xdg_wm_base::XdgWmBase, GlobalError> {
305 <Self as ProvidesBoundGlobal<xdg_wm_base::XdgWmBase, 6>>::bound_global(self)
306 }
307}
308
309impl ProvidesBoundGlobal<xdg_wm_base::XdgWmBase, { XdgShell::API_VERSION_MAX }> for XdgShell {
310 fn bound_global(&self) -> Result<xdg_wm_base::XdgWmBase, GlobalError> {
311 Ok(self.xdg_wm_base.clone())
312 }
313}
314
315impl<D> Dispatch2<xdg_wm_base::XdgWmBase, D> for GlobalData {
316 fn event(
317 &self,
318 _state: &mut D,
319 xdg_wm_base: &xdg_wm_base::XdgWmBase,
320 event: xdg_wm_base::Event,
321 _conn: &Connection,
322 _qh: &QueueHandle<D>,
323 ) {
324 match event {
325 xdg_wm_base::Event::Ping { serial } => {
326 xdg_wm_base.pong(serial);
327 }
328
329 _ => unreachable!(),
330 }
331 }
332}