diff --git a/src/desktop/mod.rs b/src/desktop/mod.rs index fb33afc..da44617 100644 --- a/src/desktop/mod.rs +++ b/src/desktop/mod.rs @@ -1,7 +1,9 @@ // TODO: Remove - but for now, this makes sure these files are not completely highlighted with warnings #![allow(missing_docs, clippy::all)] +mod output; mod popup; mod space; +pub mod utils; mod window; pub use self::popup::*; diff --git a/src/desktop/output.rs b/src/desktop/output.rs new file mode 100644 index 0000000..de69c2b --- /dev/null +++ b/src/desktop/output.rs @@ -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, + pub render_scale: f64, + + // damage and last_state are in space coordinate space + pub old_damage: VecDeque>>, + pub last_state: IndexMap>, + + // surfaces for tracking enter and leave events + pub surfaces: Vec, +} + +pub(super) type OutputUserdata = RefCell>; +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::().unwrap().borrow_mut(), |m| { + m.entry(space).or_default() + }) +} diff --git a/src/desktop/space.rs b/src/desktop/space.rs index aa2253c..39a9eeb 100644 --- a/src/desktop/space.rs +++ b/src/desktop/space.rs @@ -1,6 +1,7 @@ use super::{draw_window, Window}; use crate::{ backend::renderer::{utils::SurfaceState, Frame, ImportAll, Renderer, Transform}, + desktop::output::*, utils::{Logical, Point, Rectangle}, wayland::{ 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, - render_scale: f64, - - // damage and last_state are in space coordinate space - old_damage: VecDeque>>, - last_state: IndexMap>, - - // surfaces for tracking enter and leave events - surfaces: Vec, -} - -type OutputUserdata = RefCell>; -fn output_state(space: usize, o: &Output) -> RefMut<'_, OutputState> { - let userdata = o.user_data(); - userdata.insert_if_missing(OutputUserdata::default); - RefMut::map(userdata.get::().unwrap().borrow_mut(), |m| { - m.entry(space).or_default() - }) -} - // TODO: Maybe replace UnmanagedResource if nothing else comes up? #[derive(Debug, thiserror::Error)] pub enum SpaceError { @@ -118,12 +97,17 @@ impl Space { /// /// This can safely be called on an already mapped window pub fn map_window(&mut self, window: &Window, location: Point) { - self.raise_window(window); + self.insert_window(window); window_state(self.id, window).location = location; } 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()); // TODO: should this be handled by us? @@ -144,7 +128,7 @@ impl Space { } /// Iterate window in z-order back to front - pub fn windows(&self) -> impl Iterator { + pub fn windows(&self) -> impl DoubleEndedIterator { 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) -> 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> { if !surface.as_ref().is_alive() { return None; @@ -405,7 +397,7 @@ impl Space { .last_state .iter() .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) } else { None @@ -413,14 +405,14 @@ impl Space { }) .collect::>>() { - slog::trace!(self.logger, "Removing window at: {:?}", old_window); + slog::trace!(self.logger, "Removing toplevel at: {:?}", old_window); damage.push(old_window); } // 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 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 if old_geo.map(|old_geo| old_geo != geo).unwrap_or(false) { @@ -540,7 +532,7 @@ impl Space { .iter() .map(|window| { let wgeo = window_rect_with_popups(window, &self.id); - (window.0.id, wgeo) + (ToplevelId::Xdg(window.0.id), wgeo) }) .collect(); state.old_damage.push_front(new_damage); diff --git a/src/desktop/utils.rs b/src/desktop/utils.rs new file mode 100644 index 0000000..8a08fdc --- /dev/null +++ b/src/desktop/utils.rs @@ -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> { + self.buffer_dimensions + .map(|dims| dims.to_logical(self.buffer_scale)) + } + + fn contains_point(&self, attrs: &SurfaceAttributes, point: Point) -> 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

(surface: &wl_surface::WlSurface, location: P) -> Rectangle +where + P: Into>, +{ + 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| { + let mut loc = *loc; + let data = states.data_map.get::>(); + + if let Some(size) = data.and_then(|d| d.borrow().size()) { + if states.role == Some("subsurface") { + let current = states.cached_state.current::(); + 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

( + surface: &wl_surface::WlSurface, + location: P, +) -> Vec> +where + P: Into>, +{ + 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::>() { + let data = data.borrow(); + if data.texture.is_none() { + if states.role == Some("subsurface") { + let current = states.cached_state.current::(); + location += current.location; + } + } + } + TraversalAction::DoChildren(location) + }, + |_surface, states, location| { + let mut location = *location; + if let Some(data) = states.data_map.get::>() { + let data = data.borrow(); + let attributes = states.cached_state.current::(); + + if data.texture.is_none() { + if states.role == Some("subsurface") { + let current = states.cached_state.current::(); + 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

( + surface: &wl_surface::WlSurface, + point: Point, + location: P, +) -> Option<(wl_surface::WlSurface, Point)> +where + P: Into>, +{ + let found = RefCell::new(None); + with_surface_tree_downward( + surface, + location.into(), + |wl_surface, states, location: &Point| { + let mut location = *location; + let data = states.data_map.get::>(); + + if states.role == Some("subsurface") { + let current = states.cached_state.current::(); + 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::() + .frame_callbacks + .drain(..) + { + callback.done(time); + } + }, + |_, _, &()| true, + ); +} diff --git a/src/desktop/window.rs b/src/desktop/window.rs index 6e8a5bc..61a8478 100644 --- a/src/desktop/window.rs +++ b/src/desktop/window.rs @@ -1,20 +1,13 @@ use crate::{ - backend::renderer::{ - utils::{draw_surface_tree, SurfaceState}, - Frame, ImportAll, Renderer, Texture, - }, - desktop::PopupManager, - utils::{Logical, Point, Rectangle, Size}, + backend::renderer::{utils::draw_surface_tree, Frame, ImportAll, Renderer, Texture}, + desktop::{utils::*, PopupManager}, + utils::{Logical, Point, Rectangle}, wayland::{ - compositor::{ - with_states, with_surface_tree_downward, with_surface_tree_upward, Damage, SubsurfaceCachedState, - SurfaceAttributes, TraversalAction, - }, + compositor::with_states, shell::xdg::{SurfaceCachedState, ToplevelSurface}, }, }; use std::{ - cell::RefCell, collections::HashSet, hash::{Hash, Hasher}, rc::Rc, @@ -25,7 +18,7 @@ use std::{ }; use wayland_commons::user_data::UserDataMap; 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); lazy_static::lazy_static! { @@ -101,44 +94,6 @@ impl Kind { } } -impl SurfaceState { - /// Returns the size of the surface. - pub fn size(&self) -> Option> { - self.buffer_dimensions - .map(|dims| dims.to_logical(self.buffer_scale)) - } - - fn contains_point(&self, attrs: &SurfaceAttributes, point: Point) -> 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)] pub(super) struct WindowInner { pub(super) id: usize, @@ -317,154 +272,6 @@ impl Window { } } -fn damage_from_surface_tree

(surface: &wl_surface::WlSurface, location: P) -> Vec> -where - P: Into>, -{ - 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::>() { - let data = data.borrow(); - if data.texture.is_none() { - if states.role == Some("subsurface") { - let current = states.cached_state.current::(); - location += current.location; - } - return TraversalAction::DoChildren(location); - } - } - TraversalAction::SkipChildren - }, - |_surface, states, location| { - let mut location = *location; - if let Some(data) = states.data_map.get::>() { - let data = data.borrow(); - let attributes = states.cached_state.current::(); - - if data.texture.is_none() { - if states.role == Some("subsurface") { - let current = states.cached_state.current::(); - 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

(surface: &wl_surface::WlSurface, location: P) -> Rectangle -where - P: Into>, -{ - 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| { - let mut loc = *loc; - let data = states.data_map.get::>(); - - if let Some(size) = data.and_then(|d| d.borrow().size()) { - if states.role == Some("subsurface") { - let current = states.cached_state.current::(); - 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

( - surface: &wl_surface::WlSurface, - point: Point, - location: P, -) -> Option<(wl_surface::WlSurface, Point)> -where - P: Into>, -{ - let found = RefCell::new(None); - with_surface_tree_downward( - surface, - location.into(), - |wl_surface, states, location: &Point| { - let mut location = *location; - let data = states.data_map.get::>(); - - if states.role == Some("subsurface") { - let current = states.cached_state.current::(); - 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::() - .frame_callbacks - .drain(..) - { - callback.done(time); - } - }, - |_, _, &()| true, - ); -} - pub fn draw_window( renderer: &mut R, frame: &mut F, diff --git a/src/wayland/output/mod.rs b/src/wayland/output/mod.rs index 236205a..fdfba5e 100644 --- a/src/wayland/output/mod.rs +++ b/src/wayland/output/mod.rs @@ -100,7 +100,7 @@ pub struct PhysicalProperties { } #[derive(Debug)] -struct Inner { +pub(crate) struct Inner { name: String, log: ::slog::Logger, instances: Vec, @@ -170,7 +170,7 @@ impl Inner { /// about any change in the properties of this output. #[derive(Debug, Clone)] pub struct Output { - inner: InnerType, + pub(crate) inner: InnerType, } impl Output { @@ -274,6 +274,11 @@ impl Output { 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 pub fn name(&self) -> String { self.inner.0.lock().unwrap().name.clone()