desktop: Various cleanups

This commit is contained in:
Victor Brekenfeld 2021-12-13 22:06:13 +01:00
parent 31b308836f
commit cea88fdde0
6 changed files with 282 additions and 230 deletions

View File

@ -1,7 +1,9 @@
// TODO: Remove - but for now, this makes sure these files are not completely highlighted with warnings // TODO: Remove - but for now, this makes sure these files are not completely highlighted with warnings
#![allow(missing_docs, clippy::all)] #![allow(missing_docs, clippy::all)]
mod output;
mod popup; mod popup;
mod space; mod space;
pub mod utils;
mod window; mod window;
pub use self::popup::*; pub use self::popup::*;

47
src/desktop/output.rs Normal file
View File

@ -0,0 +1,47 @@
use crate::{
utils::{Logical, Point, Rectangle},
wayland::output::Output,
};
use indexmap::IndexMap;
use wayland_server::protocol::wl_surface::WlSurface;
use std::{
cell::{RefCell, RefMut},
collections::{HashMap, VecDeque},
};
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub(super) enum ToplevelId {
Xdg(usize),
}
impl ToplevelId {
pub fn is_xdg(&self) -> bool {
match self {
ToplevelId::Xdg(_) => true,
_ => false,
}
}
}
#[derive(Clone, Default)]
pub(super) struct OutputState {
pub location: Point<i32, Logical>,
pub render_scale: f64,
// damage and last_state are in space coordinate space
pub old_damage: VecDeque<Vec<Rectangle<i32, Logical>>>,
pub last_state: IndexMap<ToplevelId, Rectangle<i32, Logical>>,
// surfaces for tracking enter and leave events
pub surfaces: Vec<WlSurface>,
}
pub(super) type OutputUserdata = RefCell<HashMap<usize, OutputState>>;
pub(super) fn output_state(space: usize, o: &Output) -> RefMut<'_, OutputState> {
let userdata = o.user_data();
userdata.insert_if_missing(OutputUserdata::default);
RefMut::map(userdata.get::<OutputUserdata>().unwrap().borrow_mut(), |m| {
m.entry(space).or_default()
})
}

View File

@ -1,6 +1,7 @@
use super::{draw_window, Window}; use super::{draw_window, Window};
use crate::{ use crate::{
backend::renderer::{utils::SurfaceState, Frame, ImportAll, Renderer, Transform}, backend::renderer::{utils::SurfaceState, Frame, ImportAll, Renderer, Transform},
desktop::output::*,
utils::{Logical, Point, Rectangle}, utils::{Logical, Point, Rectangle},
wayland::{ wayland::{
compositor::{with_surface_tree_downward, SubsurfaceCachedState, TraversalAction}, compositor::{with_surface_tree_downward, SubsurfaceCachedState, TraversalAction},
@ -55,28 +56,6 @@ fn window_state(space: usize, w: &Window) -> RefMut<'_, WindowState> {
}) })
} }
#[derive(Clone, Default)]
struct OutputState {
location: Point<i32, Logical>,
render_scale: f64,
// damage and last_state are in space coordinate space
old_damage: VecDeque<Vec<Rectangle<i32, Logical>>>,
last_state: IndexMap<usize, Rectangle<i32, Logical>>,
// surfaces for tracking enter and leave events
surfaces: Vec<WlSurface>,
}
type OutputUserdata = RefCell<HashMap<usize, OutputState>>;
fn output_state(space: usize, o: &Output) -> RefMut<'_, OutputState> {
let userdata = o.user_data();
userdata.insert_if_missing(OutputUserdata::default);
RefMut::map(userdata.get::<OutputUserdata>().unwrap().borrow_mut(), |m| {
m.entry(space).or_default()
})
}
// TODO: Maybe replace UnmanagedResource if nothing else comes up? // TODO: Maybe replace UnmanagedResource if nothing else comes up?
#[derive(Debug, thiserror::Error)] #[derive(Debug, thiserror::Error)]
pub enum SpaceError { pub enum SpaceError {
@ -118,12 +97,17 @@ impl Space {
/// ///
/// This can safely be called on an already mapped window /// This can safely be called on an already mapped window
pub fn map_window(&mut self, window: &Window, location: Point<i32, Logical>) { pub fn map_window(&mut self, window: &Window, location: Point<i32, Logical>) {
self.raise_window(window); self.insert_window(window);
window_state(self.id, window).location = location; window_state(self.id, window).location = location;
} }
pub fn raise_window(&mut self, window: &Window) { pub fn raise_window(&mut self, window: &Window) {
self.windows.shift_remove(window); if self.windows.shift_remove(window) {
self.insert_window(window);
}
}
fn insert_window(&mut self, window: &Window) {
self.windows.insert(window.clone()); self.windows.insert(window.clone());
// TODO: should this be handled by us? // TODO: should this be handled by us?
@ -144,7 +128,7 @@ impl Space {
} }
/// Iterate window in z-order back to front /// Iterate window in z-order back to front
pub fn windows(&self) -> impl Iterator<Item = &Window> { pub fn windows(&self) -> impl DoubleEndedIterator<Item = &Window> {
self.windows.iter() self.windows.iter()
} }
@ -156,6 +140,14 @@ impl Space {
}) })
} }
/// Get a reference to the output under a given point, if any
pub fn output_under(&self, point: Point<f64, Logical>) -> Option<&Output> {
self.outputs.iter().rev().find(|o| {
let bbox = self.output_geometry(o);
bbox.map(|bbox| bbox.to_f64().contains(point)).unwrap_or(false)
})
}
pub fn window_for_surface(&self, surface: &WlSurface) -> Option<&Window> { pub fn window_for_surface(&self, surface: &WlSurface) -> Option<&Window> {
if !surface.as_ref().is_alive() { if !surface.as_ref().is_alive() {
return None; return None;
@ -405,7 +397,7 @@ impl Space {
.last_state .last_state
.iter() .iter()
.filter_map(|(id, w)| { .filter_map(|(id, w)| {
if !self.windows.iter().any(|w| w.0.id == *id) { if !self.windows.iter().any(|w| ToplevelId::Xdg(w.0.id) == *id) {
Some(*w) Some(*w)
} else { } else {
None None
@ -413,14 +405,14 @@ impl Space {
}) })
.collect::<Vec<Rectangle<i32, Logical>>>() .collect::<Vec<Rectangle<i32, Logical>>>()
{ {
slog::trace!(self.logger, "Removing window at: {:?}", old_window); slog::trace!(self.logger, "Removing toplevel at: {:?}", old_window);
damage.push(old_window); damage.push(old_window);
} }
// lets iterate front to back and figure out, what new windows or unmoved windows we have // lets iterate front to back and figure out, what new windows or unmoved windows we have
for window in self.windows.iter().rev() { for window in self.windows.iter() {
let geo = window_rect_with_popups(window, &self.id); let geo = window_rect_with_popups(window, &self.id);
let old_geo = state.last_state.get(&window.0.id).cloned(); let old_geo = state.last_state.get(&ToplevelId::Xdg(window.0.id)).cloned();
// window was moved or resized // window was moved or resized
if old_geo.map(|old_geo| old_geo != geo).unwrap_or(false) { if old_geo.map(|old_geo| old_geo != geo).unwrap_or(false) {
@ -540,7 +532,7 @@ impl Space {
.iter() .iter()
.map(|window| { .map(|window| {
let wgeo = window_rect_with_popups(window, &self.id); let wgeo = window_rect_with_popups(window, &self.id);
(window.0.id, wgeo) (ToplevelId::Xdg(window.0.id), wgeo)
}) })
.collect(); .collect();
state.old_damage.push_front(new_damage); state.old_damage.push_front(new_damage);

199
src/desktop/utils.rs Normal file
View File

@ -0,0 +1,199 @@
use crate::{
backend::renderer::utils::SurfaceState,
utils::{Logical, Point, Rectangle, Size},
wayland::compositor::{
with_surface_tree_downward, with_surface_tree_upward, Damage, SubsurfaceCachedState,
SurfaceAttributes, TraversalAction,
},
};
use wayland_server::protocol::wl_surface;
use std::cell::RefCell;
impl SurfaceState {
/// Returns the size of the surface.
pub fn size(&self) -> Option<Size<i32, Logical>> {
self.buffer_dimensions
.map(|dims| dims.to_logical(self.buffer_scale))
}
fn contains_point(&self, attrs: &SurfaceAttributes, point: Point<f64, Logical>) -> bool {
let size = match self.size() {
None => return false, // If the surface has no size, it can't have an input region.
Some(size) => size,
};
let rect = Rectangle {
loc: (0, 0).into(),
size,
}
.to_f64();
// The input region is always within the surface itself, so if the surface itself doesn't contain the
// point we can return false.
if !rect.contains(point) {
return false;
}
// If there's no input region, we're done.
if attrs.input_region.is_none() {
return true;
}
attrs
.input_region
.as_ref()
.unwrap()
.contains(point.to_i32_floor())
}
}
pub fn bbox_from_surface_tree<P>(surface: &wl_surface::WlSurface, location: P) -> Rectangle<i32, Logical>
where
P: Into<Point<i32, Logical>>,
{
let location = location.into();
let mut bounding_box = Rectangle::from_loc_and_size(location, (0, 0));
with_surface_tree_downward(
surface,
location,
|_, states, loc: &Point<i32, Logical>| {
let mut loc = *loc;
let data = states.data_map.get::<RefCell<SurfaceState>>();
if let Some(size) = data.and_then(|d| d.borrow().size()) {
if states.role == Some("subsurface") {
let current = states.cached_state.current::<SubsurfaceCachedState>();
loc += current.location;
}
// Update the bounding box.
bounding_box = bounding_box.merge(Rectangle::from_loc_and_size(loc, size));
TraversalAction::DoChildren(loc)
} else {
// If the parent surface is unmapped, then the child surfaces are hidden as
// well, no need to consider them here.
TraversalAction::SkipChildren
}
},
|_, _, _| {},
|_, _, _| true,
);
bounding_box
}
pub fn damage_from_surface_tree<P>(
surface: &wl_surface::WlSurface,
location: P,
) -> Vec<Rectangle<i32, Logical>>
where
P: Into<Point<i32, Logical>>,
{
let mut damage = Vec::new();
with_surface_tree_upward(
surface,
location.into(),
|_surface, states, location| {
let mut location = *location;
if let Some(data) = states.data_map.get::<RefCell<SurfaceState>>() {
let data = data.borrow();
if data.texture.is_none() {
if states.role == Some("subsurface") {
let current = states.cached_state.current::<SubsurfaceCachedState>();
location += current.location;
}
}
}
TraversalAction::DoChildren(location)
},
|_surface, states, location| {
let mut location = *location;
if let Some(data) = states.data_map.get::<RefCell<SurfaceState>>() {
let data = data.borrow();
let attributes = states.cached_state.current::<SurfaceAttributes>();
if data.texture.is_none() {
if states.role == Some("subsurface") {
let current = states.cached_state.current::<SubsurfaceCachedState>();
location += current.location;
}
damage.extend(attributes.damage.iter().map(|dmg| {
let mut rect = match dmg {
Damage::Buffer(rect) => rect.to_logical(attributes.buffer_scale),
Damage::Surface(rect) => *rect,
};
rect.loc += location;
rect
}));
}
}
},
|_, _, _| true,
);
damage
}
pub fn under_from_surface_tree<P>(
surface: &wl_surface::WlSurface,
point: Point<f64, Logical>,
location: P,
) -> Option<(wl_surface::WlSurface, Point<i32, Logical>)>
where
P: Into<Point<i32, Logical>>,
{
let found = RefCell::new(None);
with_surface_tree_downward(
surface,
location.into(),
|wl_surface, states, location: &Point<i32, Logical>| {
let mut location = *location;
let data = states.data_map.get::<RefCell<SurfaceState>>();
if states.role == Some("subsurface") {
let current = states.cached_state.current::<SubsurfaceCachedState>();
location += current.location;
}
let contains_the_point = data
.map(|data| {
data.borrow()
.contains_point(&*states.cached_state.current(), point - location.to_f64())
})
.unwrap_or(false);
if contains_the_point {
*found.borrow_mut() = Some((wl_surface.clone(), location));
}
TraversalAction::DoChildren(location)
},
|_, _, _| {},
|_, _, _| {
// only continue if the point is not found
found.borrow().is_none()
},
);
found.into_inner()
}
pub fn send_frames_surface_tree(surface: &wl_surface::WlSurface, time: u32) {
with_surface_tree_downward(
surface,
(),
|_, _, &()| TraversalAction::DoChildren(()),
|_surf, states, &()| {
// the surface may not have any user_data if it is a subsurface and has not
// yet been commited
for callback in states
.cached_state
.current::<SurfaceAttributes>()
.frame_callbacks
.drain(..)
{
callback.done(time);
}
},
|_, _, &()| true,
);
}

View File

@ -1,20 +1,13 @@
use crate::{ use crate::{
backend::renderer::{ backend::renderer::{utils::draw_surface_tree, Frame, ImportAll, Renderer, Texture},
utils::{draw_surface_tree, SurfaceState}, desktop::{utils::*, PopupManager},
Frame, ImportAll, Renderer, Texture, utils::{Logical, Point, Rectangle},
},
desktop::PopupManager,
utils::{Logical, Point, Rectangle, Size},
wayland::{ wayland::{
compositor::{ compositor::with_states,
with_states, with_surface_tree_downward, with_surface_tree_upward, Damage, SubsurfaceCachedState,
SurfaceAttributes, TraversalAction,
},
shell::xdg::{SurfaceCachedState, ToplevelSurface}, shell::xdg::{SurfaceCachedState, ToplevelSurface},
}, },
}; };
use std::{ use std::{
cell::RefCell,
collections::HashSet, collections::HashSet,
hash::{Hash, Hasher}, hash::{Hash, Hasher},
rc::Rc, rc::Rc,
@ -25,7 +18,7 @@ use std::{
}; };
use wayland_commons::user_data::UserDataMap; use wayland_commons::user_data::UserDataMap;
use wayland_protocols::xdg_shell::server::xdg_toplevel; use wayland_protocols::xdg_shell::server::xdg_toplevel;
use wayland_server::protocol::{wl_buffer, wl_surface}; use wayland_server::protocol::wl_surface;
static WINDOW_ID: AtomicUsize = AtomicUsize::new(0); static WINDOW_ID: AtomicUsize = AtomicUsize::new(0);
lazy_static::lazy_static! { lazy_static::lazy_static! {
@ -101,44 +94,6 @@ impl Kind {
} }
} }
impl SurfaceState {
/// Returns the size of the surface.
pub fn size(&self) -> Option<Size<i32, Logical>> {
self.buffer_dimensions
.map(|dims| dims.to_logical(self.buffer_scale))
}
fn contains_point(&self, attrs: &SurfaceAttributes, point: Point<f64, Logical>) -> bool {
let size = match self.size() {
None => return false, // If the surface has no size, it can't have an input region.
Some(size) => size,
};
let rect = Rectangle {
loc: (0, 0).into(),
size,
}
.to_f64();
// The input region is always within the surface itself, so if the surface itself doesn't contain the
// point we can return false.
if !rect.contains(point) {
return false;
}
// If there's no input region, we're done.
if attrs.input_region.is_none() {
return true;
}
attrs
.input_region
.as_ref()
.unwrap()
.contains(point.to_i32_floor())
}
}
#[derive(Debug)] #[derive(Debug)]
pub(super) struct WindowInner { pub(super) struct WindowInner {
pub(super) id: usize, pub(super) id: usize,
@ -317,154 +272,6 @@ impl Window {
} }
} }
fn damage_from_surface_tree<P>(surface: &wl_surface::WlSurface, location: P) -> Vec<Rectangle<i32, Logical>>
where
P: Into<Point<i32, Logical>>,
{
let mut damage = Vec::new();
with_surface_tree_upward(
surface,
location.into(),
|_surface, states, location| {
let mut location = *location;
if let Some(data) = states.data_map.get::<RefCell<SurfaceState>>() {
let data = data.borrow();
if data.texture.is_none() {
if states.role == Some("subsurface") {
let current = states.cached_state.current::<SubsurfaceCachedState>();
location += current.location;
}
return TraversalAction::DoChildren(location);
}
}
TraversalAction::SkipChildren
},
|_surface, states, location| {
let mut location = *location;
if let Some(data) = states.data_map.get::<RefCell<SurfaceState>>() {
let data = data.borrow();
let attributes = states.cached_state.current::<SurfaceAttributes>();
if data.texture.is_none() {
if states.role == Some("subsurface") {
let current = states.cached_state.current::<SubsurfaceCachedState>();
location += current.location;
}
damage.extend(attributes.damage.iter().map(|dmg| {
let mut rect = match dmg {
Damage::Buffer(rect) => rect.to_logical(attributes.buffer_scale),
Damage::Surface(rect) => *rect,
};
rect.loc += location;
rect
}));
}
}
},
|_, _, _| true,
);
damage
}
fn bbox_from_surface_tree<P>(surface: &wl_surface::WlSurface, location: P) -> Rectangle<i32, Logical>
where
P: Into<Point<i32, Logical>>,
{
let location = location.into();
let mut bounding_box = Rectangle::from_loc_and_size(location, (0, 0));
with_surface_tree_downward(
surface,
location,
|_, states, loc: &Point<i32, Logical>| {
let mut loc = *loc;
let data = states.data_map.get::<RefCell<SurfaceState>>();
if let Some(size) = data.and_then(|d| d.borrow().size()) {
if states.role == Some("subsurface") {
let current = states.cached_state.current::<SubsurfaceCachedState>();
loc += current.location;
}
// Update the bounding box.
bounding_box = bounding_box.merge(Rectangle::from_loc_and_size(loc, size));
TraversalAction::DoChildren(loc)
} else {
// If the parent surface is unmapped, then the child surfaces are hidden as
// well, no need to consider them here.
TraversalAction::SkipChildren
}
},
|_, _, _| {},
|_, _, _| true,
);
bounding_box
}
pub fn under_from_surface_tree<P>(
surface: &wl_surface::WlSurface,
point: Point<f64, Logical>,
location: P,
) -> Option<(wl_surface::WlSurface, Point<i32, Logical>)>
where
P: Into<Point<i32, Logical>>,
{
let found = RefCell::new(None);
with_surface_tree_downward(
surface,
location.into(),
|wl_surface, states, location: &Point<i32, Logical>| {
let mut location = *location;
let data = states.data_map.get::<RefCell<SurfaceState>>();
if states.role == Some("subsurface") {
let current = states.cached_state.current::<SubsurfaceCachedState>();
location += current.location;
}
let contains_the_point = data
.map(|data| {
data.borrow()
.contains_point(&*states.cached_state.current(), point - location.to_f64())
})
.unwrap_or(false);
if contains_the_point {
*found.borrow_mut() = Some((wl_surface.clone(), location));
}
TraversalAction::DoChildren(location)
},
|_, _, _| {},
|_, _, _| {
// only continue if the point is not found
found.borrow().is_none()
},
);
found.into_inner()
}
fn send_frames_surface_tree(surface: &wl_surface::WlSurface, time: u32) {
with_surface_tree_downward(
surface,
(),
|_, _, &()| TraversalAction::DoChildren(()),
|_surf, states, &()| {
// the surface may not have any user_data if it is a subsurface and has not
// yet been commited
for callback in states
.cached_state
.current::<SurfaceAttributes>()
.frame_callbacks
.drain(..)
{
callback.done(time);
}
},
|_, _, &()| true,
);
}
pub fn draw_window<R, E, F, T>( pub fn draw_window<R, E, F, T>(
renderer: &mut R, renderer: &mut R,
frame: &mut F, frame: &mut F,

View File

@ -100,7 +100,7 @@ pub struct PhysicalProperties {
} }
#[derive(Debug)] #[derive(Debug)]
struct Inner { pub(crate) struct Inner {
name: String, name: String,
log: ::slog::Logger, log: ::slog::Logger,
instances: Vec<WlOutput>, instances: Vec<WlOutput>,
@ -170,7 +170,7 @@ impl Inner {
/// about any change in the properties of this output. /// about any change in the properties of this output.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Output { pub struct Output {
inner: InnerType, pub(crate) inner: InnerType,
} }
impl Output { impl Output {
@ -274,6 +274,11 @@ impl Output {
self.inner.0.lock().unwrap().transform self.inner.0.lock().unwrap().transform
} }
/// Returns the currenly advertised scale of the output
pub fn current_scale(&self) -> i32 {
self.inner.0.lock().unwrap().scale
}
/// Returns the name of the output /// Returns the name of the output
pub fn name(&self) -> String { pub fn name(&self) -> String {
self.inner.0.lock().unwrap().name.clone() self.inner.0.lock().unwrap().name.clone()