2022-01-05 19:41:46 +00:00
|
|
|
//! This module contains the [`Space`] helper class as well has related
|
|
|
|
//! rendering helpers to add custom elements or different clients to a space.
|
|
|
|
|
2021-11-28 00:22:00 +00:00
|
|
|
use crate::{
|
2021-12-26 23:03:42 +00:00
|
|
|
backend::renderer::{utils::SurfaceState, Frame, ImportAll, Renderer, Transform},
|
|
|
|
desktop::{
|
|
|
|
layer::{layer_map_for_output, LayerSurface},
|
|
|
|
window::Window,
|
|
|
|
},
|
2021-11-28 00:22:00 +00:00
|
|
|
utils::{Logical, Point, Rectangle},
|
2021-11-30 15:53:42 +00:00
|
|
|
wayland::{
|
2021-12-17 11:59:11 +00:00
|
|
|
compositor::{
|
|
|
|
get_parent, is_sync_subsurface, with_surface_tree_downward, SubsurfaceCachedState,
|
|
|
|
TraversalAction,
|
|
|
|
},
|
2021-11-30 15:53:42 +00:00
|
|
|
output::Output,
|
2021-12-13 21:06:39 +00:00
|
|
|
shell::wlr_layer::Layer as WlrLayer,
|
2021-11-30 15:53:42 +00:00
|
|
|
},
|
2021-11-28 00:22:00 +00:00
|
|
|
};
|
|
|
|
use indexmap::{IndexMap, IndexSet};
|
2022-01-05 20:06:59 +00:00
|
|
|
use std::{cell::RefCell, collections::VecDeque, fmt};
|
2021-11-30 15:53:42 +00:00
|
|
|
use wayland_server::protocol::wl_surface::WlSurface;
|
2021-11-28 00:22:00 +00:00
|
|
|
|
2021-12-26 23:03:42 +00:00
|
|
|
mod element;
|
|
|
|
mod layer;
|
|
|
|
mod output;
|
|
|
|
mod window;
|
|
|
|
|
|
|
|
pub use self::element::*;
|
|
|
|
use self::layer::*;
|
|
|
|
use self::output::*;
|
|
|
|
use self::window::*;
|
|
|
|
|
2022-01-04 17:28:39 +00:00
|
|
|
crate::utils::ids::id_gen!(next_space_id, SPACE_ID, SPACE_IDS);
|
2021-11-28 00:22:00 +00:00
|
|
|
|
2022-01-05 19:40:52 +00:00
|
|
|
/// Represents two dimensional plane to map windows and outputs upon.
|
2021-11-28 00:22:00 +00:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct Space {
|
2021-12-14 13:12:08 +00:00
|
|
|
pub(super) id: usize,
|
2021-11-28 00:22:00 +00:00
|
|
|
// in z-order, back to front
|
|
|
|
windows: IndexSet<Window>,
|
|
|
|
outputs: Vec<Output>,
|
|
|
|
logger: ::slog::Logger,
|
|
|
|
}
|
|
|
|
|
2022-01-05 19:41:46 +00:00
|
|
|
/// Elements rendered by [`Space::render_output`] in addition to windows, layers and popups.
|
2022-01-05 12:03:41 +00:00
|
|
|
pub type DynamicRenderElements<R> =
|
|
|
|
Box<dyn RenderElement<R, <R as Renderer>::Frame, <R as Renderer>::Error, <R as Renderer>::TextureId>>;
|
|
|
|
|
2022-01-05 19:40:52 +00:00
|
|
|
impl PartialEq for Space {
|
|
|
|
fn eq(&self, other: &Space) -> bool {
|
|
|
|
self.id == other.id
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-28 00:22:00 +00:00
|
|
|
impl Drop for Space {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
SPACE_IDS.lock().unwrap().remove(&self.id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Space {
|
2022-01-05 19:41:46 +00:00
|
|
|
/// Create a new [`Space`]
|
2021-11-28 00:22:00 +00:00
|
|
|
pub fn new<L>(log: L) -> Space
|
|
|
|
where
|
2021-12-16 12:04:00 +00:00
|
|
|
L: Into<Option<slog::Logger>>,
|
2021-11-28 00:22:00 +00:00
|
|
|
{
|
|
|
|
Space {
|
|
|
|
id: next_space_id(),
|
|
|
|
windows: IndexSet::new(),
|
|
|
|
outputs: Vec::new(),
|
2021-12-16 12:04:00 +00:00
|
|
|
logger: crate::slog_or_fallback(log),
|
2021-11-28 00:22:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-05 19:41:46 +00:00
|
|
|
/// Map a [`Window`] and move it to top of the stack
|
2021-11-28 00:22:00 +00:00
|
|
|
///
|
|
|
|
/// This can safely be called on an already mapped window
|
2022-01-05 19:41:46 +00:00
|
|
|
/// 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.
|
2022-01-04 17:42:52 +00:00
|
|
|
pub fn map_window<P: Into<Point<i32, Logical>>>(&mut self, window: &Window, location: P, activate: bool) {
|
|
|
|
self.insert_window(window, activate);
|
2021-12-16 12:04:00 +00:00
|
|
|
window_state(self.id, window).location = location.into();
|
2021-12-02 11:45:58 +00:00
|
|
|
}
|
|
|
|
|
2022-01-05 19:41:46 +00:00
|
|
|
/// 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.
|
2022-01-04 17:42:52 +00:00
|
|
|
pub fn raise_window(&mut self, window: &Window, activate: bool) {
|
2021-12-13 21:06:13 +00:00
|
|
|
if self.windows.shift_remove(window) {
|
2022-01-04 17:42:52 +00:00
|
|
|
self.insert_window(window, activate);
|
2021-12-13 21:06:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-04 17:42:52 +00:00
|
|
|
fn insert_window(&mut self, window: &Window, activate: bool) {
|
2021-11-28 00:22:00 +00:00
|
|
|
self.windows.insert(window.clone());
|
|
|
|
|
2022-01-04 17:42:52 +00:00
|
|
|
if activate {
|
|
|
|
window.set_activated(true);
|
|
|
|
for w in self.windows.iter() {
|
|
|
|
if w != window {
|
|
|
|
w.set_activated(false);
|
|
|
|
}
|
2021-11-28 00:22:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-05 23:26:14 +00:00
|
|
|
/// Unmap a [`Window`] from this space.
|
2022-01-05 19:41:46 +00:00
|
|
|
///
|
|
|
|
/// This function does nothing for already unmapped windows
|
2021-11-28 00:22:00 +00:00
|
|
|
pub fn unmap_window(&mut self, window: &Window) {
|
|
|
|
if let Some(map) = window.user_data().get::<WindowUserdata>() {
|
|
|
|
map.borrow_mut().remove(&self.id);
|
|
|
|
}
|
|
|
|
self.windows.shift_remove(window);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Iterate window in z-order back to front
|
2021-12-13 21:06:13 +00:00
|
|
|
pub fn windows(&self) -> impl DoubleEndedIterator<Item = &Window> {
|
2021-11-28 00:22:00 +00:00
|
|
|
self.windows.iter()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get a reference to the window under a given point, if any
|
2021-12-16 12:04:00 +00:00
|
|
|
pub fn window_under<P: Into<Point<f64, Logical>>>(&self, point: P) -> Option<&Window> {
|
|
|
|
let point = point.into();
|
2021-12-06 19:19:03 +00:00
|
|
|
self.windows.iter().rev().find(|w| {
|
2021-12-01 17:46:23 +00:00
|
|
|
let bbox = window_rect(w, &self.id);
|
2021-11-28 00:22:00 +00:00
|
|
|
bbox.to_f64().contains(point)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-01-06 17:29:30 +00:00
|
|
|
/// Get a reference to the outputs under a given point
|
|
|
|
pub fn output_under<P: Into<Point<f64, Logical>>>(&self, point: P) -> impl Iterator<Item = &Output> {
|
2021-12-16 12:04:00 +00:00
|
|
|
let point = point.into();
|
2022-01-06 17:29:30 +00:00
|
|
|
self.outputs.iter().rev().filter(move |o| {
|
2021-12-13 21:06:13 +00:00
|
|
|
let bbox = self.output_geometry(o);
|
|
|
|
bbox.map(|bbox| bbox.to_f64().contains(point)).unwrap_or(false)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-01-05 19:41:46 +00:00
|
|
|
/// Returns the window matching a given surface, if any
|
2021-11-30 15:53:42 +00:00
|
|
|
pub fn window_for_surface(&self, surface: &WlSurface) -> Option<&Window> {
|
2021-11-28 00:22:00 +00:00
|
|
|
if !surface.as_ref().is_alive() {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
self.windows
|
|
|
|
.iter()
|
|
|
|
.find(|w| w.toplevel().get_surface().map(|x| x == surface).unwrap_or(false))
|
|
|
|
}
|
|
|
|
|
2022-01-05 19:41:46 +00:00
|
|
|
/// Returns the layer matching a given surface, if any
|
2021-12-13 21:06:39 +00:00
|
|
|
pub fn layer_for_surface(&self, surface: &WlSurface) -> Option<LayerSurface> {
|
|
|
|
if !surface.as_ref().is_alive() {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
self.outputs.iter().find_map(|o| {
|
|
|
|
let map = layer_map_for_output(o);
|
|
|
|
map.layer_for_surface(surface).cloned()
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-01-05 19:41:46 +00:00
|
|
|
/// Returns the geometry of a [`Window`] including its relative position inside the Space.
|
2021-11-28 00:22:00 +00:00
|
|
|
pub fn window_geometry(&self, w: &Window) -> Option<Rectangle<i32, Logical>> {
|
|
|
|
if !self.windows.contains(w) {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
2021-12-01 17:46:23 +00:00
|
|
|
Some(window_geo(w, &self.id))
|
|
|
|
}
|
|
|
|
|
2022-01-05 19:41:46 +00:00
|
|
|
/// Returns the bounding box of a [`Window`] including its relative position inside the Space.
|
2021-12-01 17:46:23 +00:00
|
|
|
pub fn window_bbox(&self, w: &Window) -> Option<Rectangle<i32, Logical>> {
|
|
|
|
if !self.windows.contains(w) {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
2021-11-28 00:22:00 +00:00
|
|
|
Some(window_rect(w, &self.id))
|
|
|
|
}
|
|
|
|
|
2022-01-05 19:41:46 +00:00
|
|
|
/// 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.
|
2021-12-16 12:04:00 +00:00
|
|
|
pub fn map_output<P: Into<Point<i32, Logical>>>(&mut self, output: &Output, scale: f64, location: P) {
|
2021-11-28 00:22:00 +00:00
|
|
|
let mut state = output_state(self.id, output);
|
|
|
|
*state = OutputState {
|
2021-12-16 12:04:00 +00:00
|
|
|
location: location.into(),
|
2021-11-28 00:22:00 +00:00
|
|
|
render_scale: scale,
|
|
|
|
..Default::default()
|
|
|
|
};
|
|
|
|
if !self.outputs.contains(output) {
|
|
|
|
self.outputs.push(output.clone());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-05 19:41:46 +00:00
|
|
|
/// Iterate over all mapped [`Output`]s of this space.
|
2021-11-28 00:22:00 +00:00
|
|
|
pub fn outputs(&self) -> impl Iterator<Item = &Output> {
|
|
|
|
self.outputs.iter()
|
|
|
|
}
|
|
|
|
|
2022-01-05 19:41:46 +00:00
|
|
|
/// Unmap an [`Output`] from this space.
|
|
|
|
///
|
|
|
|
/// Does nothing if the output was not previously mapped.
|
2021-11-28 00:22:00 +00:00
|
|
|
pub fn unmap_output(&mut self, output: &Output) {
|
2022-01-05 19:41:46 +00:00
|
|
|
if !self.outputs.contains(output) {
|
|
|
|
return;
|
|
|
|
}
|
2021-11-28 00:22:00 +00:00
|
|
|
if let Some(map) = output.user_data().get::<OutputUserdata>() {
|
|
|
|
map.borrow_mut().remove(&self.id);
|
|
|
|
}
|
|
|
|
self.outputs.retain(|o| o != output);
|
|
|
|
}
|
|
|
|
|
2022-01-05 19:41:46 +00:00
|
|
|
/// 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.
|
2021-11-28 00:22:00 +00:00
|
|
|
pub fn output_geometry(&self, o: &Output) -> Option<Rectangle<i32, Logical>> {
|
|
|
|
if !self.outputs.contains(o) {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
2021-12-28 12:08:18 +00:00
|
|
|
let transform: Transform = o.current_transform().into();
|
2021-11-28 00:22:00 +00:00
|
|
|
let state = output_state(self.id, o);
|
|
|
|
o.current_mode().map(|mode| {
|
|
|
|
Rectangle::from_loc_and_size(
|
|
|
|
state.location,
|
2021-12-28 12:08:18 +00:00
|
|
|
transform
|
|
|
|
.transform_size(mode.size)
|
|
|
|
.to_f64()
|
|
|
|
.to_logical(state.render_scale)
|
|
|
|
.to_i32_round(),
|
2021-11-28 00:22:00 +00:00
|
|
|
)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-01-05 19:41:46 +00:00
|
|
|
/// Returns the reder scale of a mapped output.
|
|
|
|
///
|
|
|
|
/// If the output was not previously mapped to the `Space`
|
|
|
|
/// this function returns `None`.
|
2021-11-28 00:22:00 +00:00
|
|
|
pub fn output_scale(&self, o: &Output) -> Option<f64> {
|
|
|
|
if !self.outputs.contains(o) {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
let state = output_state(self.id, o);
|
|
|
|
Some(state.render_scale)
|
|
|
|
}
|
|
|
|
|
2022-01-05 19:41:46 +00:00
|
|
|
/// Returns all [`Output`]s a [`Window`] overlaps with.
|
2021-12-02 11:45:21 +00:00
|
|
|
pub fn outputs_for_window(&self, w: &Window) -> Vec<Output> {
|
2021-11-28 00:22:00 +00:00
|
|
|
if !self.windows.contains(w) {
|
2021-12-02 11:45:21 +00:00
|
|
|
return Vec::new();
|
2021-11-28 00:22:00 +00:00
|
|
|
}
|
|
|
|
|
2021-12-02 11:45:21 +00:00
|
|
|
let w_geo = window_rect(w, &self.id);
|
|
|
|
let mut outputs = self
|
|
|
|
.outputs
|
|
|
|
.iter()
|
|
|
|
.cloned()
|
|
|
|
.filter(|o| {
|
|
|
|
let o_geo = self.output_geometry(o).unwrap();
|
|
|
|
w_geo.overlaps(o_geo)
|
|
|
|
})
|
|
|
|
.collect::<Vec<Output>>();
|
|
|
|
outputs.sort_by(|o1, o2| {
|
|
|
|
let overlap = |rect1: Rectangle<i32, Logical>, rect2: Rectangle<i32, Logical>| -> i32 {
|
|
|
|
// x overlap
|
|
|
|
std::cmp::max(0, std::cmp::min(rect1.loc.x + rect1.size.w, rect2.loc.x + rect2.size.w) - std::cmp::max(rect1.loc.x, rect2.loc.x))
|
|
|
|
// y overlap
|
|
|
|
* std::cmp::max(0, std::cmp::min(rect1.loc.y + rect1.size.h, rect2.loc.y + rect2.size.h) - std::cmp::max(rect1.loc.y, rect2.loc.y))
|
|
|
|
};
|
|
|
|
let o1_area = overlap(self.output_geometry(o1).unwrap(), w_geo);
|
|
|
|
let o2_area = overlap(self.output_geometry(o2).unwrap(), w_geo);
|
|
|
|
o1_area.cmp(&o2_area)
|
|
|
|
});
|
|
|
|
outputs
|
2021-11-28 00:22:00 +00:00
|
|
|
}
|
|
|
|
|
2022-01-05 19:41:46 +00:00
|
|
|
/// 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.
|
2021-11-30 15:53:42 +00:00
|
|
|
pub fn refresh(&mut self) {
|
2021-11-28 00:22:00 +00:00
|
|
|
self.windows.retain(|w| w.toplevel().alive());
|
2021-11-30 15:53:42 +00:00
|
|
|
|
|
|
|
for output in &mut self.outputs {
|
|
|
|
output_state(self.id, output)
|
|
|
|
.surfaces
|
|
|
|
.retain(|s| s.as_ref().is_alive());
|
|
|
|
}
|
|
|
|
|
|
|
|
for window in &self.windows {
|
|
|
|
let bbox = window_rect(window, &self.id);
|
|
|
|
let kind = window.toplevel();
|
|
|
|
|
|
|
|
for output in &self.outputs {
|
|
|
|
let output_geometry = self
|
|
|
|
.output_geometry(output)
|
|
|
|
.unwrap_or_else(|| Rectangle::from_loc_and_size((0, 0), (0, 0)));
|
|
|
|
let mut output_state = output_state(self.id, output);
|
|
|
|
|
|
|
|
// Check if the bounding box of the toplevel intersects with
|
|
|
|
// the output, if not no surface in the tree can intersect with
|
|
|
|
// the output.
|
|
|
|
if !output_geometry.overlaps(bbox) {
|
|
|
|
if let Some(surface) = kind.get_surface() {
|
|
|
|
with_surface_tree_downward(
|
|
|
|
surface,
|
|
|
|
(),
|
|
|
|
|_, _, _| TraversalAction::DoChildren(()),
|
|
|
|
|wl_surface, _, _| {
|
|
|
|
if output_state.surfaces.contains(wl_surface) {
|
|
|
|
slog::trace!(
|
|
|
|
self.logger,
|
|
|
|
"surface ({:?}) leaving output {:?}",
|
|
|
|
wl_surface,
|
|
|
|
output.name()
|
|
|
|
);
|
|
|
|
output.leave(wl_surface);
|
|
|
|
output_state.surfaces.retain(|s| s != wl_surface);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|_, _, _| true,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(surface) = kind.get_surface() {
|
|
|
|
with_surface_tree_downward(
|
|
|
|
surface,
|
|
|
|
window_loc(window, &self.id),
|
|
|
|
|_, states, location| {
|
|
|
|
let mut location = *location;
|
|
|
|
let data = states.data_map.get::<RefCell<SurfaceState>>();
|
|
|
|
|
|
|
|
if data.is_some() {
|
|
|
|
if states.role == Some("subsurface") {
|
|
|
|
let current = states.cached_state.current::<SubsurfaceCachedState>();
|
|
|
|
location += current.location;
|
|
|
|
}
|
|
|
|
|
|
|
|
TraversalAction::DoChildren(location)
|
|
|
|
} else {
|
|
|
|
// If the parent surface is unmapped, then the child surfaces are hidden as
|
|
|
|
// well, no need to consider them here.
|
|
|
|
TraversalAction::SkipChildren
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|wl_surface, states, &loc| {
|
|
|
|
let data = states.data_map.get::<RefCell<SurfaceState>>();
|
|
|
|
|
|
|
|
if let Some(size) = data.and_then(|d| d.borrow().size()) {
|
|
|
|
let surface_rectangle = Rectangle { loc, size };
|
|
|
|
|
|
|
|
if output_geometry.overlaps(surface_rectangle) {
|
|
|
|
// We found a matching output, check if we already sent enter
|
|
|
|
if !output_state.surfaces.contains(wl_surface) {
|
|
|
|
slog::trace!(
|
|
|
|
self.logger,
|
|
|
|
"surface ({:?}) entering output {:?}",
|
|
|
|
wl_surface,
|
|
|
|
output.name()
|
|
|
|
);
|
|
|
|
output.enter(wl_surface);
|
|
|
|
output_state.surfaces.push(wl_surface.clone());
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Surface does not match output, if we sent enter earlier
|
|
|
|
// we should now send leave
|
|
|
|
if output_state.surfaces.contains(wl_surface) {
|
|
|
|
slog::trace!(
|
|
|
|
self.logger,
|
|
|
|
"surface ({:?}) leaving output {:?}",
|
|
|
|
wl_surface,
|
|
|
|
output.name()
|
|
|
|
);
|
|
|
|
output.leave(wl_surface);
|
|
|
|
output_state.surfaces.retain(|s| s != wl_surface);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Maybe the the surface got unmapped, send leave on output
|
|
|
|
if output_state.surfaces.contains(wl_surface) {
|
|
|
|
slog::trace!(
|
|
|
|
self.logger,
|
|
|
|
"surface ({:?}) leaving output {:?}",
|
|
|
|
wl_surface,
|
|
|
|
output.name()
|
|
|
|
);
|
|
|
|
output.leave(wl_surface);
|
|
|
|
output_state.surfaces.retain(|s| s != wl_surface);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|_, _, _| true,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-11-28 00:22:00 +00:00
|
|
|
}
|
|
|
|
|
2022-01-05 19:41:46 +00:00
|
|
|
/// 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.
|
2021-12-20 12:40:08 +00:00
|
|
|
pub fn commit(&self, surface: &WlSurface) {
|
2021-12-17 11:59:11 +00:00
|
|
|
if is_sync_subsurface(surface) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
let mut root = surface.clone();
|
|
|
|
while let Some(parent) = get_parent(&root) {
|
|
|
|
root = parent;
|
|
|
|
}
|
|
|
|
if let Some(window) = self.windows().find(|w| w.toplevel().get_surface() == Some(&root)) {
|
|
|
|
window.refresh();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-05 19:41:46 +00:00
|
|
|
/// 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.
|
2021-11-28 00:22:00 +00:00
|
|
|
pub fn render_output<R>(
|
|
|
|
&mut self,
|
|
|
|
renderer: &mut R,
|
|
|
|
output: &Output,
|
|
|
|
age: usize,
|
|
|
|
clear_color: [f32; 4],
|
2022-01-05 12:03:41 +00:00
|
|
|
custom_elements: &[DynamicRenderElements<R>],
|
2021-12-29 14:56:00 +00:00
|
|
|
) -> Result<Option<Vec<Rectangle<i32, Logical>>>, RenderError<R>>
|
2021-11-28 00:22:00 +00:00
|
|
|
where
|
2021-12-20 18:13:55 +00:00
|
|
|
R: Renderer + ImportAll + 'static,
|
2021-11-28 00:22:00 +00:00
|
|
|
R::TextureId: 'static,
|
2021-12-20 18:13:55 +00:00
|
|
|
R::Error: 'static,
|
|
|
|
R::Frame: 'static,
|
2021-11-28 00:22:00 +00:00
|
|
|
{
|
2022-01-05 19:40:52 +00:00
|
|
|
if !self.outputs.contains(output) {
|
|
|
|
return Err(RenderError::UnmappedOutput);
|
|
|
|
}
|
|
|
|
|
2021-12-26 23:03:42 +00:00
|
|
|
type SpaceElem<R> =
|
|
|
|
dyn SpaceElement<R, <R as Renderer>::Frame, <R as Renderer>::Error, <R as Renderer>::TextureId>;
|
|
|
|
|
2021-11-28 00:22:00 +00:00
|
|
|
let mut state = output_state(self.id, output);
|
|
|
|
let output_size = output
|
|
|
|
.current_mode()
|
|
|
|
.ok_or(RenderError::OutputNoMode)?
|
|
|
|
.size
|
|
|
|
.to_f64()
|
|
|
|
.to_logical(state.render_scale)
|
|
|
|
.to_i32_round();
|
|
|
|
let output_geo = Rectangle::from_loc_and_size(state.location, output_size);
|
2021-12-13 21:06:39 +00:00
|
|
|
let layer_map = layer_map_for_output(output);
|
2021-11-28 00:22:00 +00:00
|
|
|
|
|
|
|
// This will hold all the damage we need for this rendering step
|
|
|
|
let mut damage = Vec::<Rectangle<i32, Logical>>::new();
|
|
|
|
// First add damage for windows gone
|
2021-12-20 18:13:55 +00:00
|
|
|
for old_toplevel in state
|
2021-11-28 00:22:00 +00:00
|
|
|
.last_state
|
|
|
|
.iter()
|
2021-12-26 23:03:42 +00:00
|
|
|
.filter_map(|(id, geo)| {
|
|
|
|
if !self
|
|
|
|
.windows
|
|
|
|
.iter()
|
|
|
|
.map(|w| w as &SpaceElem<R>)
|
|
|
|
.chain(layer_map.layers().map(|l| l as &SpaceElem<R>))
|
|
|
|
.chain(custom_elements.iter().map(|c| c as &SpaceElem<R>))
|
|
|
|
.any(|e| ToplevelId::from(e) == *id)
|
2021-12-13 21:06:39 +00:00
|
|
|
{
|
2021-12-26 23:03:42 +00:00
|
|
|
Some(*geo)
|
2021-11-28 00:22:00 +00:00
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.collect::<Vec<Rectangle<i32, Logical>>>()
|
|
|
|
{
|
2021-12-20 18:13:55 +00:00
|
|
|
slog::trace!(self.logger, "Removing toplevel at: {:?}", old_toplevel);
|
|
|
|
damage.push(old_toplevel);
|
2021-11-28 00:22:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// lets iterate front to back and figure out, what new windows or unmoved windows we have
|
2021-12-26 23:03:42 +00:00
|
|
|
for element in self
|
|
|
|
.windows
|
|
|
|
.iter()
|
|
|
|
.map(|w| w as &SpaceElem<R>)
|
|
|
|
.chain(layer_map.layers().map(|l| l as &SpaceElem<R>))
|
|
|
|
.chain(custom_elements.iter().map(|c| c as &SpaceElem<R>))
|
|
|
|
{
|
|
|
|
let geo = element.geometry(self.id);
|
|
|
|
let old_geo = state.last_state.get(&ToplevelId::from(element)).cloned();
|
2021-11-28 00:22:00 +00:00
|
|
|
|
|
|
|
// window was moved or resized
|
|
|
|
if old_geo.map(|old_geo| old_geo != geo).unwrap_or(false) {
|
|
|
|
// Add damage for the old position of the window
|
|
|
|
damage.push(old_geo.unwrap());
|
2021-11-28 21:30:17 +00:00
|
|
|
damage.push(geo);
|
|
|
|
} else {
|
|
|
|
// window stayed at its place
|
2021-12-26 23:03:42 +00:00
|
|
|
let loc = element.location(self.id);
|
|
|
|
damage.extend(element.accumulated_damage(Some((self, output))).into_iter().map(
|
|
|
|
|mut rect| {
|
|
|
|
rect.loc += loc;
|
|
|
|
rect
|
|
|
|
},
|
|
|
|
));
|
2021-12-20 18:13:55 +00:00
|
|
|
}
|
|
|
|
}
|
2021-11-28 00:22:00 +00:00
|
|
|
|
|
|
|
// That is all completely new damage, which we need to store for subsequent renders
|
|
|
|
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 {
|
2022-01-05 19:41:46 +00:00
|
|
|
// We do not need even older states anymore
|
2021-11-28 00:22:00 +00:00
|
|
|
state.old_damage.truncate(age);
|
|
|
|
damage.extend(state.old_damage.iter().flatten().copied());
|
|
|
|
} else {
|
|
|
|
// just damage everything, if we have no damage
|
|
|
|
damage = vec![output_geo];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Optimize the damage for rendering
|
2021-12-07 17:34:10 +00:00
|
|
|
damage.dedup();
|
2021-11-28 00:22:00 +00:00
|
|
|
damage.retain(|rect| rect.overlaps(output_geo));
|
|
|
|
damage.retain(|rect| rect.size.h > 0 && rect.size.w > 0);
|
2022-01-05 19:40:52 +00:00
|
|
|
// merge overlapping rectangles
|
2021-12-07 17:34:10 +00:00
|
|
|
damage = damage.into_iter().fold(Vec::new(), |mut new_damage, rect| {
|
|
|
|
if let Some(existing) = new_damage.iter_mut().find(|other| rect.overlaps(**other)) {
|
|
|
|
*existing = existing.merge(rect);
|
|
|
|
} else {
|
|
|
|
new_damage.push(rect);
|
|
|
|
}
|
|
|
|
new_damage
|
|
|
|
});
|
|
|
|
|
|
|
|
if damage.is_empty() {
|
2021-12-29 14:56:00 +00:00
|
|
|
return Ok(None);
|
2021-12-07 17:34:10 +00:00
|
|
|
}
|
2021-11-28 00:22:00 +00:00
|
|
|
|
|
|
|
let output_transform: Transform = output.current_transform().into();
|
2022-01-04 18:06:18 +00:00
|
|
|
let res = renderer.render(
|
2021-11-28 00:22:00 +00:00
|
|
|
output_transform
|
|
|
|
.transform_size(output_size)
|
|
|
|
.to_f64()
|
|
|
|
.to_physical(state.render_scale)
|
|
|
|
.to_i32_round(),
|
|
|
|
output_transform,
|
|
|
|
|renderer, frame| {
|
|
|
|
// First clear all damaged regions
|
2021-12-07 17:34:10 +00:00
|
|
|
slog::trace!(self.logger, "Clearing at {:#?}", damage);
|
|
|
|
frame.clear(
|
|
|
|
clear_color,
|
|
|
|
&damage
|
|
|
|
.iter()
|
|
|
|
.map(|geo| geo.to_f64().to_physical(state.render_scale).to_i32_round())
|
|
|
|
.collect::<Vec<_>>(),
|
|
|
|
)?;
|
2021-11-28 00:22:00 +00:00
|
|
|
|
2021-12-13 21:06:39 +00:00
|
|
|
// Then re-draw all windows & layers overlapping with a damage rect.
|
|
|
|
|
2021-12-26 23:03:42 +00:00
|
|
|
for element in layer_map
|
2021-12-13 21:06:39 +00:00
|
|
|
.layers_on(WlrLayer::Background)
|
|
|
|
.chain(layer_map.layers_on(WlrLayer::Bottom))
|
2021-12-26 23:03:42 +00:00
|
|
|
.map(|l| l as &SpaceElem<R>)
|
|
|
|
.chain(self.windows.iter().map(|w| w as &SpaceElem<R>))
|
|
|
|
.chain(
|
|
|
|
layer_map
|
|
|
|
.layers_on(WlrLayer::Top)
|
|
|
|
.chain(layer_map.layers_on(WlrLayer::Overlay))
|
|
|
|
.map(|l| l as &SpaceElem<R>),
|
|
|
|
)
|
|
|
|
.chain(custom_elements.iter().map(|c| c as &SpaceElem<R>))
|
2021-12-13 21:06:39 +00:00
|
|
|
{
|
2021-12-26 23:03:42 +00:00
|
|
|
let geo = element.geometry(self.id);
|
|
|
|
if damage.iter().any(|d| d.overlaps(geo)) {
|
|
|
|
let loc = element.location(self.id) - output_geo.loc;
|
|
|
|
let damage = damage
|
2021-12-13 21:06:39 +00:00
|
|
|
.iter()
|
2021-12-26 23:03:42 +00:00
|
|
|
.flat_map(|d| d.intersection(geo))
|
2021-12-14 13:38:33 +00:00
|
|
|
.map(|geo| Rectangle::from_loc_and_size(geo.loc - loc, geo.size))
|
2021-12-07 17:34:10 +00:00
|
|
|
.collect::<Vec<_>>();
|
|
|
|
slog::trace!(
|
|
|
|
self.logger,
|
2021-12-26 23:03:42 +00:00
|
|
|
"Rendering toplevel at {:?} with damage {:#?}",
|
|
|
|
geo,
|
|
|
|
damage
|
2021-12-07 17:34:10 +00:00
|
|
|
);
|
2021-12-26 23:03:42 +00:00
|
|
|
element.draw(
|
|
|
|
self.id,
|
2021-12-07 17:34:10 +00:00
|
|
|
renderer,
|
|
|
|
frame,
|
|
|
|
state.render_scale,
|
|
|
|
loc,
|
2021-12-26 23:03:42 +00:00
|
|
|
&damage,
|
2021-12-20 18:13:55 +00:00
|
|
|
&self.logger,
|
|
|
|
)?;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-28 00:22:00 +00:00
|
|
|
Result::<(), R::Error>::Ok(())
|
|
|
|
},
|
2022-01-04 18:06:18 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
if let Err(err) = res {
|
2021-11-28 00:22:00 +00:00
|
|
|
// if the rendering errors on us, we need to be prepared, that this whole buffer was partially updated and thus now unusable.
|
|
|
|
// thus clean our old states before returning
|
|
|
|
state.old_damage = VecDeque::new();
|
|
|
|
state.last_state = IndexMap::new();
|
|
|
|
return Err(RenderError::Rendering(err));
|
|
|
|
}
|
|
|
|
|
|
|
|
// If rendering was successful capture the state and add the damage
|
|
|
|
state.last_state = self
|
|
|
|
.windows
|
|
|
|
.iter()
|
2021-12-26 23:03:42 +00:00
|
|
|
.map(|w| w as &SpaceElem<R>)
|
|
|
|
.chain(layer_map.layers().map(|l| l as &SpaceElem<R>))
|
|
|
|
.chain(custom_elements.iter().map(|c| c as &SpaceElem<R>))
|
|
|
|
.map(|elem| {
|
|
|
|
let geo = elem.geometry(self.id);
|
|
|
|
(ToplevelId::from(elem), geo)
|
2021-11-28 00:22:00 +00:00
|
|
|
})
|
|
|
|
.collect();
|
2021-12-29 14:56:00 +00:00
|
|
|
state.old_damage.push_front(new_damage.clone());
|
2021-11-28 00:22:00 +00:00
|
|
|
|
2021-12-29 14:56:00 +00:00
|
|
|
Ok(Some(new_damage))
|
2021-11-28 00:22:00 +00:00
|
|
|
}
|
|
|
|
|
2022-01-05 19:41:46 +00:00
|
|
|
/// 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.
|
2021-11-28 00:22:00 +00:00
|
|
|
pub fn send_frames(&self, all: bool, time: u32) {
|
|
|
|
for window in self.windows.iter().filter(|w| {
|
|
|
|
all || {
|
|
|
|
let mut state = window_state(self.id, w);
|
|
|
|
std::mem::replace(&mut state.drawn, false)
|
|
|
|
}
|
|
|
|
}) {
|
|
|
|
window.send_frame(time);
|
|
|
|
}
|
2021-12-13 21:06:39 +00:00
|
|
|
|
|
|
|
for output in self.outputs.iter() {
|
|
|
|
let map = layer_map_for_output(output);
|
|
|
|
for layer in map.layers().filter(|l| {
|
|
|
|
all || {
|
|
|
|
let mut state = layer_state(self.id, l);
|
|
|
|
std::mem::replace(&mut state.drawn, false)
|
|
|
|
}
|
|
|
|
}) {
|
|
|
|
layer.send_frame(time);
|
|
|
|
}
|
|
|
|
}
|
2021-11-28 00:22:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-05 19:41:46 +00:00
|
|
|
/// Errors thrown by [`Space::render_output`]
|
2022-01-05 20:06:59 +00:00
|
|
|
#[derive(thiserror::Error)]
|
2021-11-28 00:22:00 +00:00
|
|
|
pub enum RenderError<R: Renderer> {
|
2022-01-05 19:41:46 +00:00
|
|
|
/// The provided [`Renderer`] did return an error during an operation
|
2021-11-28 00:22:00 +00:00
|
|
|
#[error(transparent)]
|
|
|
|
Rendering(R::Error),
|
2022-01-05 19:41:46 +00:00
|
|
|
/// The given [`Output`] has no set mode
|
2021-11-28 00:22:00 +00:00
|
|
|
#[error("Output has no active mode")]
|
|
|
|
OutputNoMode,
|
2022-01-05 19:41:46 +00:00
|
|
|
/// The given [`Output`] is not mapped to this [`Space`].
|
2022-01-05 19:40:52 +00:00
|
|
|
#[error("Output was not mapped to this space")]
|
|
|
|
UnmappedOutput,
|
2021-11-28 00:22:00 +00:00
|
|
|
}
|
2022-01-05 20:06:59 +00:00
|
|
|
|
|
|
|
impl<R: Renderer> fmt::Debug for RenderError<R> {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
match self {
|
|
|
|
RenderError::Rendering(err) => fmt::Debug::fmt(err, f),
|
|
|
|
RenderError::OutputNoMode => f.write_str("Output has no active move"),
|
|
|
|
RenderError::UnmappedOutput => f.write_str("Output was not mapped to this space"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|