1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321
//! XDG shell windows.
use std::{
num::NonZeroU32,
sync::{Arc, Weak},
};
use crate::reexports::client::{
protocol::{wl_output, wl_seat, wl_surface},
Connection, Proxy, QueueHandle,
};
use crate::reexports::csd_frame::{WindowManagerCapabilities, WindowState};
use crate::reexports::protocols::{
xdg::decoration::zv1::client::zxdg_toplevel_decoration_v1::{self, Mode},
xdg::shell::client::{xdg_surface, xdg_toplevel},
};
use crate::shell::WaylandSurface;
use self::inner::WindowInner;
use super::XdgSurface;
pub(super) mod inner;
/// Handler for toplevel operations on a [`Window`].
pub trait WindowHandler: Sized {
/// Request to close a window.
///
/// This request does not destroy the window. You must drop all [`Window`] handles to destroy the window.
/// This request may be sent either by the compositor or by some other mechanism (such as client side decorations).
fn request_close(&mut self, conn: &Connection, qh: &QueueHandle<Self>, window: &Window);
/// Apply a suggested surface change.
///
/// When this function is called, the compositor is requesting the window's size or state to change.
///
/// Internally this function is called when the underlying `xdg_surface` is configured. Any extension
/// protocols that interface with xdg-shell are able to be notified that the surface's configure sequence
/// is complete by using this function.
///
/// # Double buffering
///
/// Configure events in Wayland are considered to be double buffered and the state of the window does not
/// change until committed.
fn configure(
&mut self,
conn: &Connection,
qh: &QueueHandle<Self>,
window: &Window,
configure: WindowConfigure,
serial: u32,
);
}
/// Decoration mode of a window.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DecorationMode {
/// The window should draw client side decorations.
Client,
/// The server will draw window decorations.
Server,
}
/// A window configure.
///
/// A configure describes a compositor request to resize the window or change it's state.
#[non_exhaustive]
#[derive(Debug, Clone)]
pub struct WindowConfigure {
/// The compositor suggested new size of the window in window geometry coordinates.
///
/// If this value is [`None`], you may set the size of the window as you wish.
pub new_size: (Option<NonZeroU32>, Option<NonZeroU32>),
/// Compositor suggested maximum bounds for a window.
///
/// This may be used to ensure a window is not created in a way where it will not fit.
///
/// If xdg-shell is version 3 or lower, this will always be [`None`].
pub suggested_bounds: Option<(u32, u32)>,
/// The compositor set decoration mode of the window.
///
/// This will always be [`DecorationMode::Client`] if server side decorations are not enabled or
/// supported.
pub decoration_mode: DecorationMode,
/// The current state of the window.
///
/// For more see [`WindowState`] documentation on the flag values.
pub state: WindowState,
/// The capabilities supported by the compositor.
///
/// For more see [`WindowManagerCapabilities`] documentation on the flag values.
pub capabilities: WindowManagerCapabilities,
}
impl WindowConfigure {
/// Is [`WindowState::MAXIMIZED`] state is set.
#[inline]
pub fn is_maximized(&self) -> bool {
self.state.contains(WindowState::MAXIMIZED)
}
/// Is [`WindowState::FULLSCREEN`] state is set.
#[inline]
pub fn is_fullscreen(&self) -> bool {
self.state.contains(WindowState::FULLSCREEN)
}
/// Is [`WindowState::RESIZING`] state is set.
#[inline]
pub fn is_resizing(&self) -> bool {
self.state.contains(WindowState::RESIZING)
}
/// Is [`WindowState::TILED`] state is set.
#[inline]
pub fn is_tiled(&self) -> bool {
self.state.contains(WindowState::TILED)
}
/// Is [`WindowState::ACTIVATED`] state is set.
#[inline]
pub fn is_activated(&self) -> bool {
self.state.contains(WindowState::ACTIVATED)
}
/// Is [`WindowState::TILED_LEFT`] state is set.
#[inline]
pub fn is_tiled_left(&self) -> bool {
self.state.contains(WindowState::TILED_LEFT)
}
/// Is [`WindowState::TILED_RIGHT`] state is set.
#[inline]
pub fn is_tiled_right(&self) -> bool {
self.state.contains(WindowState::TILED_RIGHT)
}
/// Is [`WindowState::TILED_TOP`] state is set.
#[inline]
pub fn is_tiled_top(&self) -> bool {
self.state.contains(WindowState::TILED_TOP)
}
/// Is [`WindowState::TILED_BOTTOM`] state is set.
#[inline]
pub fn is_tiled_bottom(&self) -> bool {
self.state.contains(WindowState::TILED_BOTTOM)
}
}
/// Decorations a window is created with.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WindowDecorations {
/// The window should use the decoration mode the server asks for.
///
/// The server may ask the client to render with or without client side decorations. If server side
/// decorations are not available, client side decorations are drawn instead.
ServerDefault,
/// The window should request server side decorations.
///
/// The server may ignore this request and ask the client to render with client side decorations. If
/// server side decorations are not available, client side decorations are drawn instead.
RequestServer,
/// The window should request client side decorations.
///
/// The server may ignore this request and render server side decorations. If server side decorations are
/// not available, client side decorations are drawn.
RequestClient,
/// The window should always draw it's own client side decorations.
ClientOnly,
/// The window should use server side decorations or draw any client side decorations.
None,
}
#[derive(Debug, Clone)]
pub struct Window(pub(super) Arc<WindowInner>);
impl Window {
pub fn from_xdg_toplevel(toplevel: &xdg_toplevel::XdgToplevel) -> Option<Window> {
toplevel.data::<WindowData>().and_then(|data| data.0.upgrade()).map(Window)
}
pub fn from_xdg_surface(surface: &xdg_surface::XdgSurface) -> Option<Window> {
surface.data::<WindowData>().and_then(|data| data.0.upgrade()).map(Window)
}
pub fn from_toplevel_decoration(
decoration: &zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1,
) -> Option<Window> {
decoration.data::<WindowData>().and_then(|data| data.0.upgrade()).map(Window)
}
pub fn show_window_menu(&self, seat: &wl_seat::WlSeat, serial: u32, position: (i32, i32)) {
self.xdg_toplevel().show_window_menu(seat, serial, position.0, position.1);
}
pub fn set_title(&self, title: impl Into<String>) {
self.xdg_toplevel().set_title(title.into());
}
pub fn set_app_id(&self, app_id: impl Into<String>) {
self.xdg_toplevel().set_app_id(app_id.into());
}
pub fn set_parent(&self, parent: Option<&Window>) {
self.xdg_toplevel().set_parent(parent.map(Window::xdg_toplevel));
}
pub fn set_maximized(&self) {
self.xdg_toplevel().set_maximized()
}
pub fn unset_maximized(&self) {
self.xdg_toplevel().unset_maximized()
}
pub fn set_minimized(&self) {
self.xdg_toplevel().set_minimized()
}
pub fn set_fullscreen(&self, output: Option<&wl_output::WlOutput>) {
self.xdg_toplevel().set_fullscreen(output)
}
pub fn unset_fullscreen(&self) {
self.xdg_toplevel().unset_fullscreen()
}
/// Requests the window should use the specified decoration mode.
///
/// A mode of [`None`] indicates that the window does not care what type of decorations are used.
///
/// The compositor will respond with a [`configure`](WindowHandler::configure). The configure will
/// indicate whether the window's decoration mode has changed.
///
/// # Configure loops
///
/// You should avoid sending multiple decoration mode requests to ensure you do not enter a configure loop.
pub fn request_decoration_mode(&self, mode: Option<DecorationMode>) {
if let Some(toplevel_decoration) = &self.0.toplevel_decoration {
match mode {
Some(DecorationMode::Client) => toplevel_decoration.set_mode(Mode::ClientSide),
Some(DecorationMode::Server) => toplevel_decoration.set_mode(Mode::ServerSide),
None => toplevel_decoration.unset_mode(),
}
}
}
pub fn move_(&self, seat: &wl_seat::WlSeat, serial: u32) {
self.xdg_toplevel()._move(seat, serial)
}
pub fn resize(&self, seat: &wl_seat::WlSeat, serial: u32, edges: xdg_toplevel::ResizeEdge) {
self.xdg_toplevel().resize(seat, serial, edges)
}
// Double buffered window state
pub fn set_min_size(&self, min_size: Option<(u32, u32)>) {
let min_size = min_size.unwrap_or_default();
self.xdg_toplevel().set_min_size(min_size.0 as i32, min_size.1 as i32);
}
/// # Protocol errors
///
/// The maximum size of the window may not be smaller than the minimum size.
pub fn set_max_size(&self, max_size: Option<(u32, u32)>) {
let max_size = max_size.unwrap_or_default();
self.xdg_toplevel().set_max_size(max_size.0 as i32, max_size.1 as i32);
}
// Other
/// Returns the underlying xdg toplevel wrapped by this window.
pub fn xdg_toplevel(&self) -> &xdg_toplevel::XdgToplevel {
&self.0.xdg_toplevel
}
}
impl WaylandSurface for Window {
fn wl_surface(&self) -> &wl_surface::WlSurface {
self.0.xdg_surface.wl_surface()
}
}
impl XdgSurface for Window {
fn xdg_surface(&self) -> &xdg_surface::XdgSurface {
self.0.xdg_surface.xdg_surface()
}
}
impl PartialEq for Window {
fn eq(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.0, &other.0)
}
}
#[derive(Debug, Clone)]
pub struct WindowData(pub(crate) Weak<WindowInner>);
#[macro_export]
macro_rules! delegate_xdg_window {
($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => {
$crate::reexports::client::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
$crate::reexports::protocols::xdg::shell::client::xdg_surface::XdgSurface: $crate::shell::xdg::window::WindowData
] => $crate::shell::xdg::XdgShell);
$crate::reexports::client::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
$crate::reexports::protocols::xdg::shell::client::xdg_toplevel::XdgToplevel: $crate::shell::xdg::window::WindowData
] => $crate::shell::xdg::XdgShell);
};
}