From 8059bdc5db62d41641442ef289c218d76cc1fb27 Mon Sep 17 00:00:00 2001 From: Victor Brekenfeld Date: Wed, 5 Jan 2022 20:41:46 +0100 Subject: [PATCH] desktop: docs --- src/backend/renderer/mod.rs | 8 ++-- src/backend/renderer/utils.rs | 20 ++++++++ src/backend/winit/mod.rs | 14 ++++-- src/backend/x11/surface.rs | 2 +- src/desktop/layer.rs | 62 ++++++++++++++++++++++-- src/desktop/mod.rs | 53 ++++++++++++++++++++- src/desktop/popup.rs | 12 +++++ src/desktop/space/element.rs | 28 +++++++++++ src/desktop/space/mod.rs | 89 +++++++++++++++++++++++++++++++++-- src/desktop/utils.rs | 16 +++++++ src/desktop/window.rs | 36 ++++++++++++-- 11 files changed, 317 insertions(+), 23 deletions(-) diff --git a/src/backend/renderer/mod.rs b/src/backend/renderer/mod.rs index 95cdb2d..c84c233 100644 --- a/src/backend/renderer/mod.rs +++ b/src/backend/renderer/mod.rs @@ -257,7 +257,7 @@ pub trait Renderer { pub trait ImportShm: Renderer { /// Import a given shm-based buffer into the renderer (see [`buffer_type`]). /// - /// Returns a texture_id, which can be used with [`Frame::render_texture`] (or [`Frame::render_texture_at`]) + /// Returns a texture_id, which can be used with [`Frame::render_texture_from_to`] (or [`Frame::render_texture_at`]) /// or implementation-specific functions. /// /// If not otherwise defined by the implementation, this texture id is only valid for the renderer, that created it. @@ -324,7 +324,7 @@ pub trait ImportEgl: Renderer { /// Import a given wl_drm-based buffer into the renderer (see [`buffer_type`]). /// - /// Returns a texture_id, which can be used with [`Frame::render_texture`] (or [`Frame::render_texture_at`]) + /// Returns a texture_id, which can be used with [`Frame::render_texture_from_to`] (or [`Frame::render_texture_at`]) /// or implementation-specific functions. /// /// If not otherwise defined by the implementation, this texture id is only valid for the renderer, that created it. @@ -372,7 +372,7 @@ pub trait ImportDma: Renderer { /// Import a given raw dmabuf into the renderer. /// - /// Returns a texture_id, which can be used with [`Frame::render_texture`] (or [`Frame::render_texture_at`]) + /// Returns a texture_id, which can be used with [`Frame::render_texture_from_to`] (or [`Frame::render_texture_at`]) /// or implementation-specific functions. /// /// If not otherwise defined by the implementation, this texture id is only valid for the renderer, that created it. @@ -395,7 +395,7 @@ pub trait ImportDma: Renderer { pub trait ImportAll: Renderer { /// Import a given buffer into the renderer. /// - /// Returns a texture_id, which can be used with [`Frame::render_texture`] (or [`Frame::render_texture_at`]) + /// Returns a texture_id, which can be used with [`Frame::render_texture_from_to`] (or [`Frame::render_texture_at`]) /// or implementation-specific functions. /// /// If not otherwise defined by the implementation, this texture id is only valid for the renderer, that created it. diff --git a/src/backend/renderer/utils.rs b/src/backend/renderer/utils.rs index 31eb4bb..97efd61 100644 --- a/src/backend/renderer/utils.rs +++ b/src/backend/renderer/utils.rs @@ -1,3 +1,5 @@ +//! Utility module for helpers around drawing [`WlSurface`]s with [`Renderer`]s. + use crate::{ backend::renderer::{buffer_dimensions, Frame, ImportAll, Renderer, Texture}, utils::{Logical, Physical, Point, Rectangle, Size}, @@ -47,6 +49,15 @@ impl SurfaceState { } } +/// Handler to let smithay take over buffer management. +/// +/// Needs to be called first on the commit-callback of +/// [`crate::wayland::compositor::compositor_init`]. +/// +/// Consumes the buffer of [`SurfaceAttributes`], the buffer will +/// not be accessible anymore, but [`draw_surface_tree`] and other +/// `draw_*` helpers of the [desktop module](`crate::desktop`) will +/// become usable for surfaces handled this way. pub fn on_commit_buffer_handler(surface: &WlSurface) { if !is_sync_subsurface(surface) { with_surface_tree_upward( @@ -69,6 +80,15 @@ pub fn on_commit_buffer_handler(surface: &WlSurface) { } } +/// Draws a surface and its subsurfaces using a given [`Renderer`] and [`Frame`]. +/// +/// - `scale` needs to be equivalent to the fractional scale the rendered result should have. +/// - `location` is the position the surface should be drawn at. +/// - `damage` is the set of regions of the surface that should be drawn. +/// +/// Note: This element will render nothing, if you are not using +/// [`crate::backend::renderer::utils::on_commit_buffer_handler`] +/// to let smithay handle buffer management. pub fn draw_surface_tree( renderer: &mut R, frame: &mut F, diff --git a/src/backend/winit/mod.rs b/src/backend/winit/mod.rs index b6f2f58..d78560a 100644 --- a/src/backend/winit/mod.rs +++ b/src/backend/winit/mod.rs @@ -10,7 +10,8 @@ //! you want on the initialization of the backend. These functions will provide you //! with two objects: //! -//! - a [`WinitGraphicsBackend`], which can give you an implementation of a [`Renderer`] +//! - a [`WinitGraphicsBackend`], which can give you an implementation of a +//! [`Renderer`](crate::backend::renderer::Renderer) //! (or even [`Gles2Renderer`]) through its `renderer` method in addition to further //! functionality to access and manage the created winit-window. //! - a [`WinitEventLoop`], which dispatches some [`WinitEvent`] from the host graphics server. @@ -81,7 +82,7 @@ impl WindowSize { } } -/// Window with an active EGL Context created by `winit`. Implements the [`Renderer`] trait +/// Window with an active EGL Context created by `winit`. #[derive(Debug)] pub struct WinitGraphicsBackend { renderer: Gles2Renderer, @@ -112,7 +113,8 @@ pub struct WinitEventLoop { is_x11: bool, } -/// Create a new [`WinitGraphicsBackend`], which implements the [`Renderer`] trait and a corresponding +/// Create a new [`WinitGraphicsBackend`], which implements the +/// [`Renderer`](crate::backend::renderer::Renderer) trait and a corresponding /// [`WinitEventLoop`]. pub fn init(logger: L) -> Result<(WinitGraphicsBackend, WinitEventLoop), Error> where @@ -127,7 +129,8 @@ where ) } -/// Create a new [`WinitGraphicsBackend`], which implements the [`Renderer`] trait, from a given [`WindowBuilder`] +/// Create a new [`WinitGraphicsBackend`], which implements the +/// [`Renderer`](crate::backend::renderer::Renderer) trait, from a given [`WindowBuilder`] /// struct and a corresponding [`WinitEventLoop`]. pub fn init_from_builder( builder: WindowBuilder, @@ -148,7 +151,8 @@ where ) } -/// Create a new [`WinitGraphicsBackend`], which implements the [`Renderer`] trait, from a given [`WindowBuilder`] +/// Create a new [`WinitGraphicsBackend`], which implements the +/// [`Renderer`](crate::backend::renderer::Renderer) trait, from a given [`WindowBuilder`] /// struct, as well as given [`GlAttributes`] for further customization of the rendering pipeline and a /// corresponding [`WinitEventLoop`]. pub fn init_from_builder_with_gl_attr( diff --git a/src/backend/x11/surface.rs b/src/backend/x11/surface.rs index 1ad2e1f..5a3a2a8 100644 --- a/src/backend/x11/surface.rs +++ b/src/backend/x11/surface.rs @@ -72,7 +72,7 @@ impl X11Surface { /// /// You may bind this buffer to a renderer to render. /// This function will return the same buffer until [`submit`](Self::submit) is called - /// or [`reset_buffers`](Self::reset_buffer) is used to reset the buffers. + /// or [`reset_buffers`](Self::reset_buffers) is used to reset the buffers. pub fn buffer(&mut self) -> Result<(Dmabuf, u8), AllocateBuffersError> { if let Some(new_size) = self.resize.try_iter().last() { self.resize(new_size); diff --git a/src/desktop/layer.rs b/src/desktop/layer.rs index 4f72e0d..17d05f7 100644 --- a/src/desktop/layer.rs +++ b/src/desktop/layer.rs @@ -23,6 +23,7 @@ use std::{ crate::utils::ids::id_gen!(next_layer_id, LAYER_ID, LAYER_IDS); +/// Map of [`LayerSurface`]s on an [`Output`] #[derive(Debug)] pub struct LayerMap { layers: IndexSet, @@ -30,6 +31,15 @@ pub struct LayerMap { zone: Rectangle, } +/// Retrieve a [`LayerMap`] for a given [`Output`]. +/// +/// If none existed before a new empty [`LayerMap`] is attached +/// to the output and returned on subsequent calls. +/// +/// Note: This function internally uses a [`RefCell`] per +/// [`Output`] as exposed by its return type. Therefor +/// trying to hold on to multiple references of a [`LayerMap`] +/// of the same output using this function *will* result in a panic. pub fn layer_map_for_output(o: &Output) -> RefMut<'_, LayerMap> { let userdata = o.user_data(); let weak_output = Arc::downgrade(&o.inner); @@ -55,6 +65,7 @@ pub enum LayerError { } impl LayerMap { + /// Map a [`LayerSurface`] to this [`LayerMap`]. pub fn map_layer(&mut self, layer: &LayerSurface) -> Result<(), LayerError> { if !self.layers.contains(layer) { if layer @@ -73,6 +84,7 @@ impl LayerMap { Ok(()) } + /// Remove a [`LayerSurface`] from this [`LayerMap`]. pub fn unmap_layer(&mut self, layer: &LayerSurface) { if self.layers.shift_remove(layer) { let _ = layer.user_data().get::().take(); @@ -80,10 +92,15 @@ impl LayerMap { } } + /// Return the area of this output, that is not exclusive to any [`LayerSurface`]s. pub fn non_exclusive_zone(&self) -> Rectangle { self.zone } + /// Returns the geometry of a given mapped layer. + /// + /// If the layer was not previously mapped onto this layer map, + /// this function return `None`. pub fn layer_geometry(&self, layer: &LayerSurface) -> Option> { if !self.layers.contains(layer) { return None; @@ -94,6 +111,7 @@ impl LayerMap { Some(bbox) } + /// Returns a `LayerSurface` under a given point and on a given layer, if any. pub fn layer_under>>( &self, layer: WlrLayer, @@ -106,16 +124,19 @@ impl LayerMap { }) } + /// Iterator over all [`LayerSurface`]s currently mapped. pub fn layers(&self) -> impl DoubleEndedIterator { self.layers.iter() } + /// Iterator over all [`LayerSurface`]s currently mapped on a given layer. pub fn layers_on(&self, layer: WlrLayer) -> impl DoubleEndedIterator { self.layers .iter() .filter(move |l| l.layer().map(|l| l == layer).unwrap_or(false)) } + /// Returns the [`LayerSurface`] matching a given [`WlSurface`], if any. pub fn layer_for_surface(&self, surface: &WlSurface) -> Option<&LayerSurface> { if !surface.as_ref().is_alive() { return None; @@ -126,6 +147,9 @@ impl LayerMap { .find(|w| w.get_surface().map(|x| x == surface).unwrap_or(false)) } + /// Force re-arranging the layers, e.g. when the output size changes. + /// + /// Note: Mapping or unmapping a layer will automatically cause a re-arrangement. pub fn arrange(&mut self) { if let Some(output) = self.output() { let output_rect = Rectangle::from_loc_and_size( @@ -238,6 +262,10 @@ impl LayerMap { self.output.upgrade().map(|inner| Output { inner }) } + /// Cleanup some internally used resources. + /// + /// This function needs to be called periodically (though not necessarily frequently) + /// to be able cleanup internally used resources. pub fn cleanup(&mut self) { self.layers.retain(|layer| layer.alive()) } @@ -260,6 +288,7 @@ pub fn layer_state(layer: &LayerSurface) -> RefMut<'_, LayerState> { }) } +/// A [`LayerSurface`] represents a single layer surface as given by the wlr-layer-shell protocol. #[derive(Debug, Clone)] pub struct LayerSurface(pub(crate) Rc); @@ -292,6 +321,7 @@ impl Drop for LayerSurfaceInner { } impl LayerSurface { + /// Create a new [`LayerSurface`] from a given [`WlrLayerSurface`] and its namespace. pub fn new(surface: WlrLayerSurface, namespace: String) -> LayerSurface { LayerSurface(Rc::new(LayerSurfaceInner { id: next_layer_id(), @@ -301,18 +331,22 @@ impl LayerSurface { })) } + /// Checks if the surface is still alive pub fn alive(&self) -> bool { self.0.surface.alive() } + /// Returns the underlying [`WlrLayerSurface`] pub fn layer_surface(&self) -> &WlrLayerSurface { &self.0.surface } + /// Returns the underlying [`WlSurface`] pub fn get_surface(&self) -> Option<&WlSurface> { self.0.surface.get_surface() } + /// Returns the cached protocol state pub fn cached_state(&self) -> Option { self.0.surface.get_surface().map(|surface| { with_states(surface, |states| { @@ -322,6 +356,7 @@ impl LayerSurface { }) } + /// Returns true, if the surface has indicated, that it is able to process keyboard events. pub fn can_receive_keyboard_focus(&self) -> bool { self.0 .surface @@ -342,6 +377,7 @@ impl LayerSurface { .unwrap_or(false) } + /// Returns the layer this surface resides on, if any yet. pub fn layer(&self) -> Option { self.0.surface.get_surface().map(|surface| { with_states(surface, |states| { @@ -351,12 +387,12 @@ impl LayerSurface { }) } + /// Returns the namespace of this surface pub fn namespace(&self) -> &str { &self.0.namespace } - /// A bounding box over this window and its children. - // TODO: Cache and document when to trigger updates. If possible let space do it + /// Returns the bounding box over this layer surface and its subsurfaces. pub fn bbox(&self) -> Rectangle { if let Some(surface) = self.0.surface.get_surface() { bbox_from_surface_tree(surface, (0, 0)) @@ -365,6 +401,10 @@ impl LayerSurface { } } + /// Returns the bounding box over this layer, it subsurfaces as well as any popups. + /// + /// Note: You need to use a [`PopupManager`] to track popups, otherwise the bounding box + /// will not include the popups. pub fn bbox_with_popups(&self) -> Rectangle { let mut bounding_box = self.bbox(); if let Some(surface) = self.0.surface.get_surface() { @@ -383,6 +423,8 @@ impl LayerSurface { /// Finds the topmost surface under this point if any and returns it together with the location of this /// surface. + /// + /// - `point` needs to be relative to (0,0) of the layer surface. pub fn surface_under>>( &self, point: P, @@ -408,7 +450,11 @@ impl LayerSurface { } } - /// Damage of all the surfaces of this layer + /// Returns the damage of all the surfaces of this layer. + /// + /// If `for_values` is `Some(_)` it will only return the damage on the + /// first call for a given [`Space`] and [`Output`], if the buffer hasn't changed. + /// Subsequent calls will return an empty vector until the buffer is updated again. pub(super) fn accumulated_damage( &self, for_values: Option<(&Space, &Output)>, @@ -443,11 +489,21 @@ impl LayerSurface { } } + /// Returns a [`UserDataMap`] to allow associating arbitrary data with this surface. pub fn user_data(&self) -> &UserDataMap { &self.0.userdata } } +/// Renders a given [`LayerSurface`] using a provided renderer and frame. +/// +/// - `scale` needs to be equivalent to the fractional scale the rendered result should have. +/// - `location` is the position the layer surface should be drawn at. +/// - `damage` is the set of regions of the layer surface that should be drawn. +/// +/// Note: This function will render nothing, if you are not using +/// [`crate::backend::renderer::utils::on_commit_buffer_handler`] +/// to let smithay handle buffer management. pub fn draw_layer( renderer: &mut R, frame: &mut F, diff --git a/src/desktop/mod.rs b/src/desktop/mod.rs index 66e8d65..8e2346d 100644 --- a/src/desktop/mod.rs +++ b/src/desktop/mod.rs @@ -1,5 +1,54 @@ -// TODO: Remove - but for now, this makes sure these files are not completely highlighted with warnings -#![allow(missing_docs)] +//! Desktop management helpers +//! +//! This module contains helpers to organize and interact with desktop-style shells. +//! +//! It is therefor a lot more opinionate then for example the [xdg-shell handler](crate::wayland::shell::xdg::xdg_shell_init) +//! and tightly integrates with some protocols (e.g. xdg-shell). +//! +//! The usage of this module is therefor entirely optional and depending on your use-case you might also only want +//! to use a limited set of the helpers provided. +//! +//! ## Helpers +//! +//! ### [`Window`] +//! +//! A window represents what is by the user typically understood as a single application window. +//! +//! Currently it abstracts over xdg-shell toplevels and Xwayland surfaces (TODO). +//! It provides a bunch of methods to calculate and retrieve its size, manage itself, attach additional user_data +//! as well as a [drawing function](`draw_window`) to ease rendering it's related surfaces. +//! +//! Note that a [`Window`] on it's own has no position. For that it needs to be placed inside a [`Space`]. +//! +//! ### [`Space`] +//! +//! A space represents a two-dimensional plane of undefined dimensions. +//! [`Window`]s and [`Output`](crate::wayland::output::Output)s can be mapped onto it. +//! +//! Windows get a position and stacking order through mapping. Outputs become views of a part of the [`Space`] +//! and can be rendered via [`Space::render_output`]. Rendering results of spaces are automatically damage-tracked. +//! +//! ### Layer +//! +//! A [`LayerSurface`] represents a surface as provided by e.g. the layer-shell protocol. +//! It provides similar helper methods as a [`Window`] does to toplevel surfaces. +//! +//! Each [`Output`](crate::wayland::output::Output) can be associated a [`LayerMap`] by calling [`layer_map_for_output`], +//! which [`LayerSurface`]s can be mapped upon. Associated layer maps are automatically rendered by [`Space::render_output`], +//! but a [draw function](`draw_layer`) is also provided for manual layer-surface management. +//! +//! ### Popups +//! +//! Provides a [`PopupManager`], which can be used to automatically keep track of popups and their +//! relations to one-another. Popups are then automatically rendered with their matching toplevel surfaces, +//! when either [`draw_window`], [`draw_layer`] or [`Space::render_output`] is called. +//! +//! ## Remarks +//! +//! Note that the desktop abstractions are concerned with easing rendering different clients and therefor need to be able +//! to manage client buffers to do so. If you plan to use the provided drawing functions, you need to use +//! [`crate::backend::renderer::utils::on_commit_buffer_handler`]. + pub(crate) mod layer; mod popup; pub mod space; diff --git a/src/desktop/popup.rs b/src/desktop/popup.rs index 58aa164..d99f0eb 100644 --- a/src/desktop/popup.rs +++ b/src/desktop/popup.rs @@ -8,6 +8,7 @@ use crate::{ use std::sync::{Arc, Mutex}; use wayland_server::protocol::wl_surface::WlSurface; +/// Helper to track popups. #[derive(Debug)] pub struct PopupManager { unmapped_popups: Vec, @@ -16,6 +17,7 @@ pub struct PopupManager { } impl PopupManager { + /// Create a new [`PopupManager`]. pub fn new>>(logger: L) -> Self { PopupManager { unmapped_popups: Vec::new(), @@ -24,6 +26,7 @@ impl PopupManager { } } + /// Start tracking a new popup. pub fn track_popup(&mut self, kind: PopupKind) -> Result<(), DeadResource> { if kind.parent().is_some() { self.add_popup(kind) @@ -34,6 +37,7 @@ impl PopupManager { } } + /// Needs to be called for [`PopupManager`] to correctly update its internal state. pub fn commit(&mut self, surface: &WlSurface) { if get_role(surface) == Some(XDG_POPUP_ROLE) { if let Some(i) = self @@ -84,6 +88,7 @@ impl PopupManager { }) } + /// Finds the popup belonging to a given [`WlSurface`], if any. pub fn find_popup(&self, surface: &WlSurface) -> Option { self.unmapped_popups .iter() @@ -99,6 +104,7 @@ impl PopupManager { }) } + /// Returns the popups and their relative positions for a given toplevel surface, if any. pub fn popups_for_surface( surface: &WlSurface, ) -> Result)>, DeadResource> { @@ -112,6 +118,8 @@ impl PopupManager { }) } + /// Needs to be called periodically (but not necessarily frequently) + /// to cleanup internal resources. pub fn cleanup(&mut self) { // retain_mut is sadly still unstable self.popup_trees.iter_mut().for_each(|tree| tree.cleanup()); @@ -211,8 +219,10 @@ impl PopupNode { } } +/// Represents a popup surface #[derive(Debug, Clone)] pub enum PopupKind { + /// xdg-shell [`PopupSurface`] Xdg(PopupSurface), } @@ -223,6 +233,7 @@ impl PopupKind { } } + /// Retrieves the underlying [`WlSurface`] pub fn get_surface(&self) -> Option<&WlSurface> { match *self { PopupKind::Xdg(ref t) => t.get_surface(), @@ -235,6 +246,7 @@ impl PopupKind { } } + /// Returns the surface geometry as set by the client using `xdg_surface::set_window_geometry` pub fn geometry(&self) -> Rectangle { let wl_surface = match self.get_surface() { Some(s) => s, diff --git a/src/desktop/space/element.rs b/src/desktop/space/element.rs index 3fe3cfc..ac013c9 100644 --- a/src/desktop/space/element.rs +++ b/src/desktop/space/element.rs @@ -10,6 +10,7 @@ use std::{ }; use wayland_server::protocol::wl_surface::WlSurface; +/// Trait for custom elements to be rendered during [`Space::render_output`]. pub trait RenderElement where R: Renderer + ImportAll, @@ -18,16 +19,33 @@ where T: Texture + 'static, Self: Any + 'static, { + /// Returns an id unique to this element for the type of Self. fn id(&self) -> usize; #[doc(hidden)] fn type_of(&self) -> TypeId { std::any::Any::type_id(self) } + /// Returns the bounding box of this element including its position in the space. fn geometry(&self) -> Rectangle; + /// Returns the damage of the element since it's last update. + /// + /// If you receive `Some(_)` for `for_values` you may cache that you + /// send the damage for this `Space` and `Output` combination once + /// and return an empty vector for subsequent calls until the contents + /// of this element actually change again for optimization reasons. + /// + /// Returning `vec![Rectangle::from_loc_and_size((0, 0), (i32::MAX, i32::MAX))]` is always + /// correct, but very inefficient. fn accumulated_damage( &self, for_values: Option>, ) -> Vec>; + /// Draws the element using the provided `Frame` and `Renderer`. + /// + /// - `scale` provides the current fractional scale value to render as + /// - `damage` provides the regions you need to re-draw and *may* not + /// be equivalent to the damage returned by `accumulated_damage`. + /// Redrawing other parts of the element is not valid and may cause rendering artifacts. fn draw( &self, renderer: &mut R, @@ -98,9 +116,19 @@ where } } +/// Generic helper for drawing [`WlSurface`]s and their subsurfaces +/// as custom elements via [`RenderElement`]. +/// +/// For example useful for cursor or drag-and-drop surfaces. +/// +/// Note: This element will render nothing, if you are not using +/// [`crate::backend::renderer::utils::on_commit_buffer_handler`] +/// to let smithay handle buffer management. #[derive(Debug)] pub struct SurfaceTree { + /// Surface to be drawn pub surface: WlSurface, + /// Position to draw add pub position: Point, } diff --git a/src/desktop/space/mod.rs b/src/desktop/space/mod.rs index 0c65e30..a149e90 100644 --- a/src/desktop/space/mod.rs +++ b/src/desktop/space/mod.rs @@ -1,3 +1,6 @@ +//! This module contains the [`Space`] helper class as well has related +//! rendering helpers to add custom elements or different clients to a space. + use crate::{ backend::renderer::{utils::SurfaceState, Frame, ImportAll, Renderer, Transform}, desktop::{ @@ -40,6 +43,7 @@ pub struct Space { logger: ::slog::Logger, } +/// Elements rendered by [`Space::render_output`] in addition to windows, layers and popups. pub type DynamicRenderElements = Box::Frame, ::Error, ::TextureId>>; @@ -56,6 +60,7 @@ impl Drop for Space { } impl Space { + /// Create a new [`Space`] pub fn new(log: L) -> Space where L: Into>, @@ -68,14 +73,26 @@ impl Space { } } - /// Map window and moves it to top of the stack + /// Map a [`Window`] and move it to top of the stack /// /// This can safely be called on an already mapped window + /// to update its location inside the space. + /// + /// If activate is true it will set the new windows state + /// to be activate and removes that state from every + /// other mapped window. pub fn map_window>>(&mut self, window: &Window, location: P, activate: bool) { self.insert_window(window, activate); window_state(self.id, window).location = location.into(); } + /// Moves an already mapped [`Window`] to top of the stack + /// + /// This function does nothing for unmapped windows. + /// + /// If activate is true it will set the new windows state + /// to be activate and removes that state from every + /// other mapped window. pub fn raise_window(&mut self, window: &Window, activate: bool) { if self.windows.shift_remove(window) { self.insert_window(window, activate); @@ -95,7 +112,9 @@ impl Space { } } - /// Unmap a window from this space by its id + /// Unmap a [`Window`] from this space by. + /// + /// This function does nothing for already unmapped windows pub fn unmap_window(&mut self, window: &Window) { if let Some(map) = window.user_data().get::() { map.borrow_mut().remove(&self.id); @@ -126,6 +145,7 @@ impl Space { }) } + /// Returns the window matching a given surface, if any pub fn window_for_surface(&self, surface: &WlSurface) -> Option<&Window> { if !surface.as_ref().is_alive() { return None; @@ -136,6 +156,7 @@ impl Space { .find(|w| w.toplevel().get_surface().map(|x| x == surface).unwrap_or(false)) } + /// Returns the layer matching a given surface, if any pub fn layer_for_surface(&self, surface: &WlSurface) -> Option { if !surface.as_ref().is_alive() { return None; @@ -146,6 +167,7 @@ impl Space { }) } + /// Returns the geometry of a [`Window`] including its relative position inside the Space. pub fn window_geometry(&self, w: &Window) -> Option> { if !self.windows.contains(w) { return None; @@ -154,6 +176,7 @@ impl Space { Some(window_geo(w, &self.id)) } + /// Returns the bounding box of a [`Window`] including its relative position inside the Space. pub fn window_bbox(&self, w: &Window) -> Option> { if !self.windows.contains(w) { return None; @@ -162,6 +185,14 @@ impl Space { Some(window_rect(w, &self.id)) } + /// Maps an [`Output`] inside the space. + /// + /// Can be safely called on an already mapped + /// [`Output`] to update its scale or location. + /// + /// The scale is the what is rendered for the given output + /// and may be fractional. It is independent from the integer scale + /// reported to clients by the output. pub fn map_output>>(&mut self, output: &Output, scale: f64, location: P) { let mut state = output_state(self.id, output); *state = OutputState { @@ -174,17 +205,28 @@ impl Space { } } + /// Iterate over all mapped [`Output`]s of this space. pub fn outputs(&self) -> impl Iterator { self.outputs.iter() } + /// Unmap an [`Output`] from this space. + /// + /// Does nothing if the output was not previously mapped. pub fn unmap_output(&mut self, output: &Output) { + if !self.outputs.contains(output) { + return; + } if let Some(map) = output.user_data().get::() { map.borrow_mut().remove(&self.id); } self.outputs.retain(|o| o != output); } + /// Returns the geometry of the output including it's relative position inside the space. + /// + /// The size is matching the amount of logical pixels of the space visible on the output + /// given is current mode and render scale. pub fn output_geometry(&self, o: &Output) -> Option> { if !self.outputs.contains(o) { return None; @@ -204,6 +246,10 @@ impl Space { }) } + /// Returns the reder scale of a mapped output. + /// + /// If the output was not previously mapped to the `Space` + /// this function returns `None`. pub fn output_scale(&self, o: &Output) -> Option { if !self.outputs.contains(o) { return None; @@ -213,6 +259,7 @@ impl Space { Some(state.render_scale) } + /// Returns all [`Output`]s a [`Window`] overlaps with. pub fn outputs_for_window(&self, w: &Window) -> Vec { if !self.windows.contains(w) { return Vec::new(); @@ -242,6 +289,11 @@ impl Space { outputs } + /// Refresh some internal values and update client state + /// based on the position of windows and outputs. + /// + /// Needs to be called periodically, at best before every + /// wayland socket flush. pub fn refresh(&mut self) { self.windows.retain(|w| w.toplevel().alive()); @@ -362,8 +414,8 @@ impl Space { } } - /// Automatically calls `Window::refresh` for the window that belongs to the given surface, - /// if managed by this space. + /// Should be called on commit to let the space automatically call [`Window::refresh`] + /// for the window that belongs to the given surface, if managed by this space. pub fn commit(&self, surface: &WlSurface) { if is_sync_subsurface(surface) { return; @@ -377,6 +429,24 @@ impl Space { } } + /// Render a given [`Output`] using a given [`Renderer`]. + /// + /// [`Space`] will render all mapped [`Window`]s, mapped [`LayerSurface`](super::LayerSurface)s + /// of the given [`Output`] and their popups (if tracked by a [`PopupManager`](super::PopupManager)). + /// `clear_color` will be used to fill all unoccupied regions. + /// + /// Rendering using this function will automatically apply damage-tracking. + /// To facilitate this you need to provide age values of the buffers bound to + /// the given `renderer`. If you stop using `Space` temporarily for rendering + /// or apply additional rendering operations, you need to reset the age values + /// accordingly as `Space` will be unable to track your custom rendering operations + /// to avoid rendering artifacts. + /// + /// To add aditional elements without breaking damage-tracking implement the `RenderElement` + /// trait and use `custom_elements` to provide them to this function. `custom_elements are rendered + /// after every other element. + /// + /// Returns a list of updated regions (or `None` if that list would be empty) in case of success. pub fn render_output( &mut self, renderer: &mut R, @@ -467,7 +537,7 @@ impl Space { let new_damage = damage.clone(); // We now add old damage states, if we have an age value if age > 0 && state.old_damage.len() >= age { - // We do not need older states anymore + // We do not need even older states anymore state.old_damage.truncate(age); damage.extend(state.old_damage.iter().flatten().copied()); } else { @@ -582,6 +652,11 @@ impl Space { Ok(Some(new_damage)) } + /// Sends the frame callback to mapped [`Window`]s and [`LayerSurface`]s. + /// + /// If `all` is set this will be send to `all` mapped surfaces. + /// Otherwise only windows and layers previously drawn during the + /// previous frame will be send frame events. pub fn send_frames(&self, all: bool, time: u32) { for window in self.windows.iter().filter(|w| { all || { @@ -606,12 +681,16 @@ impl Space { } } +/// Errors thrown by [`Space::render_output`] #[derive(Debug, thiserror::Error)] pub enum RenderError { + /// The provided [`Renderer`] did return an error during an operation #[error(transparent)] Rendering(R::Error), + /// The given [`Output`] has no set mode #[error("Output has no active mode")] OutputNoMode, + /// The given [`Output`] is not mapped to this [`Space`]. #[error("Output was not mapped to this space")] UnmappedOutput, } diff --git a/src/desktop/utils.rs b/src/desktop/utils.rs index 0031c76..380ae4e 100644 --- a/src/desktop/utils.rs +++ b/src/desktop/utils.rs @@ -1,3 +1,5 @@ +//! Helper functions to ease dealing with surface trees + use crate::{ backend::renderer::utils::SurfaceState, desktop::Space, @@ -53,6 +55,9 @@ impl SurfaceState { } } +/// Returns the bounding box of a given surface and all its subsurfaces. +/// +/// - `location` can be set to offset the returned bounding box. pub fn bbox_from_surface_tree

(surface: &wl_surface::WlSurface, location: P) -> Rectangle where P: Into>, @@ -88,6 +93,12 @@ where bounding_box } +/// Returns the damage rectangles of the current buffer for a given surface and its subsurfaces. +/// +/// - `location` can be set to offset the returned bounding box. +/// - if a `key` is set the damage is only returned on the first call with the given key values. +/// Subsequent calls will return an empty vector until the buffer is updated again and new +/// damage values may be retrieved. pub fn damage_from_surface_tree

( surface: &wl_surface::WlSurface, location: P, @@ -155,6 +166,10 @@ where damage } +/// Returns the (sub-)surface under a given position given a surface, if any. +/// +/// - `point` has to be the position to query, relative to (0, 0) of the given surface + `location`. +/// - `location` can be used to offset the returned point. pub fn under_from_surface_tree

( surface: &wl_surface::WlSurface, point: Point, @@ -197,6 +212,7 @@ where found.into_inner() } +/// Sends frame callbacks for a surface and its subsurfaces with the given `time`. pub fn send_frames_surface_tree(surface: &wl_surface::WlSurface, time: u32) { with_surface_tree_downward( surface, diff --git a/src/desktop/window.rs b/src/desktop/window.rs index 9f43e59..a45d65d 100644 --- a/src/desktop/window.rs +++ b/src/desktop/window.rs @@ -19,14 +19,17 @@ use wayland_server::protocol::wl_surface; crate::utils::ids::id_gen!(next_window_id, WINDOW_ID, WINDOW_IDS); +/// Abstraction around different toplevel kinds #[derive(Debug, Clone, PartialEq)] pub enum Kind { + /// xdg-shell [`ToplevelSurface`] Xdg(ToplevelSurface), + /// XWayland surface (TODO) #[cfg(feature = "xwayland")] X11(X11Surface), } -// Big TODO +/// TODO #[derive(Debug, Clone)] pub struct X11Surface { surface: wl_surface::WlSurface, @@ -39,10 +42,12 @@ impl std::cmp::PartialEq for X11Surface { } impl X11Surface { + /// Checks if the surface is still alive. pub fn alive(&self) -> bool { self.surface.as_ref().is_alive() } + /// Returns the underlying [`WlSurface`](wl_surface::WlSurface), if still any. pub fn get_surface(&self) -> Option<&wl_surface::WlSurface> { if self.alive() { Some(&self.surface) @@ -53,6 +58,7 @@ impl X11Surface { } impl Kind { + /// Checks if the surface is still alive. pub fn alive(&self) -> bool { match *self { Kind::Xdg(ref t) => t.alive(), @@ -61,6 +67,7 @@ impl Kind { } } + /// Returns the underlying [`WlSurface`](wl_surface::WlSurface), if still any. pub fn get_surface(&self) -> Option<&wl_surface::WlSurface> { match *self { Kind::Xdg(ref t) => t.get_surface(), @@ -84,6 +91,7 @@ impl Drop for WindowInner { } } +/// Represents a single application window #[derive(Debug, Clone)] pub struct Window(pub(super) Rc); @@ -102,6 +110,7 @@ impl Hash for Window { } impl Window { + /// Construct a new [`Window`] from a given compatible toplevel surface pub fn new(toplevel: Kind) -> Window { let id = next_window_id(); @@ -123,7 +132,7 @@ impl Window { .unwrap_or_else(|| self.bbox()) } - /// A bounding box over this window and its children. + /// Returns a bounding box over this window and its children. pub fn bbox(&self) -> Rectangle { if self.0.toplevel.get_surface().is_some() { self.0.bbox.get() @@ -132,6 +141,10 @@ impl Window { } } + /// Returns a bounding box over this window and children including popups. + /// + /// Note: You need to use a [`PopupManager`] to track popups, otherwise the bounding box + /// will not include the popups. pub fn bbox_with_popups(&self) -> Rectangle { let mut bounding_box = self.bbox(); if let Some(surface) = self.0.toplevel.get_surface() { @@ -204,6 +217,8 @@ impl Window { /// Finds the topmost surface under this point if any and returns it together with the location of this /// surface. + /// + /// - `point` should be relative to (0,0) of the window. pub fn surface_under>>( &self, point: P, @@ -230,7 +245,11 @@ impl Window { } } - /// Damage of all the surfaces of this window + /// Damage of all the surfaces of this window. + /// + /// If `for_values` is `Some(_)` it will only return the damage on the + /// first call for a given [`Space`] and [`Output`], if the buffer hasn't changed. + /// Subsequent calls will return an empty vector until the buffer is updated again. pub fn accumulated_damage(&self, for_values: Option<(&Space, &Output)>) -> Vec> { let mut damage = Vec::new(); if let Some(surface) = self.0.toplevel.get_surface() { @@ -255,15 +274,26 @@ impl Window { damage } + /// Returns the underlying toplevel pub fn toplevel(&self) -> &Kind { &self.0.toplevel } + /// Returns a [`UserDataMap`] to allow associating arbitrary data with this window. pub fn user_data(&self) -> &UserDataMap { &self.0.user_data } } +/// Renders a given [`Window`] using a provided renderer and frame. +/// +/// - `scale` needs to be equivalent to the fractional scale the rendered result should have. +/// - `location` is the position the window should be drawn at. +/// - `damage` is the set of regions of the window that should be drawn. +/// +/// Note: This function will render nothing, if you are not using +/// [`crate::backend::renderer::utils::on_commit_buffer_handler`] +/// to let smithay handle buffer management. pub fn draw_window( renderer: &mut R, frame: &mut F,