smithay/anvil/src/window_map.rs

258 lines
8.1 KiB
Rust
Raw Normal View History

use std::cell::RefCell;
2018-10-02 21:37:24 +00:00
use smithay::{
2019-02-22 21:50:46 +00:00
reexports::wayland_server::protocol::wl_surface,
2018-10-02 21:37:24 +00:00
utils::Rectangle,
wayland::{
compositor::{roles::Role, CompositorToken, SubsurfaceRole, TraversalAction},
2018-10-02 21:37:24 +00:00
shell::{
legacy::{ShellSurface, ShellSurfaceRole},
xdg::{ToplevelSurface, XdgSurfaceRole},
},
},
};
2017-09-22 12:56:59 +00:00
use crate::shell::SurfaceData;
2019-04-23 21:52:14 +00:00
pub enum Kind<R> {
Xdg(ToplevelSurface<R>),
Wl(ShellSurface<R>),
}
2019-04-23 21:52:14 +00:00
impl<R> Kind<R>
where
2019-04-23 21:52:14 +00:00
R: Role<SubsurfaceRole> + Role<XdgSurfaceRole> + Role<ShellSurfaceRole> + 'static,
{
pub fn alive(&self) -> bool {
match *self {
Kind::Xdg(ref t) => t.alive(),
Kind::Wl(ref t) => t.alive(),
}
}
2019-02-22 21:50:46 +00:00
pub fn get_surface(&self) -> Option<&wl_surface::WlSurface> {
match *self {
Kind::Xdg(ref t) => t.get_surface(),
Kind::Wl(ref t) => t.get_surface(),
}
}
2020-02-01 15:25:50 +00:00
/// Do this handle and the other one actually refer to the same toplevel surface?
pub fn equals(&self, other: &Self) -> bool {
match (self, other) {
(Kind::Xdg(a), Kind::Xdg(b)) => a.equals(b),
(Kind::Wl(a), Kind::Wl(b)) => a.equals(b),
_ => false,
}
}
}
2019-04-23 21:52:14 +00:00
struct Window<R> {
2017-09-22 12:56:59 +00:00
location: (i32, i32),
/// A bounding box over this window and its children.
///
/// Used for the fast path of the check in `matching`, and as the fall-back for the window
/// geometry if that's not set explicitly.
bbox: Rectangle,
2019-04-23 21:52:14 +00:00
toplevel: Kind<R>,
2017-09-22 12:56:59 +00:00
}
2019-04-23 21:52:14 +00:00
impl<R> Window<R>
2017-09-22 12:56:59 +00:00
where
2019-04-23 21:52:14 +00:00
R: Role<SubsurfaceRole> + Role<XdgSurfaceRole> + Role<ShellSurfaceRole> + 'static,
2017-09-22 12:56:59 +00:00
{
2020-01-22 03:58:41 +00:00
/// Finds the topmost surface under this point if any and returns it together with the location of this
/// surface.
fn matching(
2018-04-22 09:58:39 +00:00
&self,
point: (f64, f64),
ctoken: CompositorToken<R>,
) -> Option<(wl_surface::WlSurface, (f64, f64))> {
if !self.bbox.contains((point.0 as i32, point.1 as i32)) {
2017-09-22 12:56:59 +00:00
return None;
}
// need to check more carefully
let found = RefCell::new(None);
2017-09-22 12:56:59 +00:00
if let Some(wl_surface) = self.toplevel.get_surface() {
ctoken.with_surface_tree_downward(
2017-09-22 12:56:59 +00:00
wl_surface,
self.location,
2018-01-07 21:30:38 +00:00
|wl_surface, attributes, role, &(mut x, mut y)| {
let data = attributes.user_data.get::<SurfaceData>();
if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) {
x += subdata.location.0;
y += subdata.location.1;
2017-09-22 12:56:59 +00:00
}
let surface_local_point = (point.0 - x as f64, point.1 - y as f64);
if data
.map(|data| data.contains_point(surface_local_point))
.unwrap_or(false)
{
*found.borrow_mut() = Some((wl_surface.clone(), (x as f64, y as f64)));
}
TraversalAction::DoChildren((x, y))
2017-09-22 12:56:59 +00:00
},
|_, _, _, _| {},
|_, _, _, _| {
// only continue if the point is not found
found.borrow().is_none()
},
2017-09-22 12:56:59 +00:00
);
}
found.into_inner()
2017-09-22 12:56:59 +00:00
}
fn self_update(&mut self, ctoken: CompositorToken<R>) {
2017-09-22 12:56:59 +00:00
let (base_x, base_y) = self.location;
let (mut min_x, mut min_y, mut max_x, mut max_y) = (base_x, base_y, base_x, base_y);
if let Some(wl_surface) = self.toplevel.get_surface() {
ctoken.with_surface_tree_downward(
2017-09-22 12:56:59 +00:00
wl_surface,
(base_x, base_y),
|_, attributes, role, &(mut x, mut y)| {
let data = attributes.user_data.get::<SurfaceData>();
if let Some((w, h)) = data.and_then(SurfaceData::size) {
2017-09-22 12:56:59 +00:00
if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) {
2018-04-22 09:58:39 +00:00
x += subdata.location.0;
y += subdata.location.1;
2017-09-22 12:56:59 +00:00
}
// Update the bounding box.
min_x = min_x.min(x);
min_y = min_y.min(y);
max_x = max_x.max(x + w);
max_y = max_y.max(y + h);
2017-09-22 12:56:59 +00:00
TraversalAction::DoChildren((x, y))
} else {
// If the parent surface is unmapped, then the child surfaces are hidden as
// well, no need to consider them here.
2017-09-22 12:56:59 +00:00
TraversalAction::SkipChildren
}
},
|_, _, _, _| {},
|_, _, _, _| true,
2017-09-22 12:56:59 +00:00
);
}
self.bbox = Rectangle {
2017-09-22 12:56:59 +00:00
x: min_x,
y: min_y,
width: max_x - min_x,
height: max_y - min_y,
};
}
/// Returns the geometry of this window.
pub fn geometry(&self, ctoken: CompositorToken<R>) -> Rectangle {
// It's the set geometry with the full bounding box as the fallback.
ctoken
.with_surface_data(self.toplevel.get_surface().unwrap(), |attributes| {
attributes.user_data.get::<SurfaceData>().unwrap().geometry
})
.unwrap_or(self.bbox)
}
2017-09-22 12:56:59 +00:00
}
pub struct WindowMap<R> {
ctoken: CompositorToken<R>,
2019-04-23 21:52:14 +00:00
windows: Vec<Window<R>>,
2017-09-22 12:56:59 +00:00
}
impl<R> WindowMap<R>
2017-09-22 12:56:59 +00:00
where
2019-04-23 21:52:14 +00:00
R: Role<SubsurfaceRole> + Role<XdgSurfaceRole> + Role<ShellSurfaceRole> + 'static,
2017-09-22 12:56:59 +00:00
{
pub fn new(ctoken: CompositorToken<R>) -> Self {
2017-09-22 12:56:59 +00:00
WindowMap {
2018-06-27 12:04:29 +00:00
ctoken,
2017-09-22 12:56:59 +00:00
windows: Vec::new(),
}
}
2019-04-23 21:52:14 +00:00
pub fn insert(&mut self, toplevel: Kind<R>, location: (i32, i32)) {
2017-09-22 12:56:59 +00:00
let mut window = Window {
2018-06-27 12:04:29 +00:00
location,
bbox: Rectangle::default(),
2018-06-27 12:04:29 +00:00
toplevel,
2017-09-22 12:56:59 +00:00
};
window.self_update(self.ctoken);
2017-09-22 12:56:59 +00:00
self.windows.insert(0, window);
}
2019-02-22 21:50:46 +00:00
pub fn get_surface_under(&self, point: (f64, f64)) -> Option<(wl_surface::WlSurface, (f64, f64))> {
2017-09-22 12:56:59 +00:00
for w in &self.windows {
if let Some(surface) = w.matching(point, self.ctoken) {
2017-09-22 12:56:59 +00:00
return Some(surface);
}
}
None
}
2018-01-07 21:30:38 +00:00
pub fn get_surface_and_bring_to_top(
2018-04-22 09:58:39 +00:00
&mut self,
point: (f64, f64),
2019-02-22 21:50:46 +00:00
) -> Option<(wl_surface::WlSurface, (f64, f64))> {
2017-09-22 12:56:59 +00:00
let mut found = None;
for (i, w) in self.windows.iter().enumerate() {
if let Some(surface) = w.matching(point, self.ctoken) {
2017-09-22 12:56:59 +00:00
found = Some((i, surface));
break;
}
}
if let Some((i, surface)) = found {
let winner = self.windows.remove(i);
self.windows.insert(0, winner);
Some(surface)
} else {
None
}
}
pub fn with_windows_from_bottom_to_top<Func>(&self, mut f: Func)
where
2019-04-23 21:52:14 +00:00
Func: FnMut(&Kind<R>, (i32, i32)),
2017-09-22 12:56:59 +00:00
{
for w in self.windows.iter().rev() {
f(&w.toplevel, w.location)
}
}
pub fn refresh(&mut self) {
self.windows.retain(|w| w.toplevel.alive());
2018-06-27 12:04:29 +00:00
for w in &mut self.windows {
w.self_update(self.ctoken);
2017-09-22 12:56:59 +00:00
}
}
pub fn clear(&mut self) {
self.windows.clear();
}
/// Returns the location of the toplevel, if it exists.
pub fn location(&self, toplevel: &Kind<R>) -> Option<(i32, i32)> {
self.windows
.iter()
.find(|w| w.toplevel.equals(toplevel))
.map(|w| w.location)
}
/// Sets the location of the toplevel, if it exists.
pub fn set_location(&mut self, toplevel: &Kind<R>, location: (i32, i32)) {
if let Some(w) = self.windows.iter_mut().find(|w| w.toplevel.equals(toplevel)) {
w.location = location;
w.self_update(self.ctoken);
}
}
/// Returns the geometry of the toplevel, if it exists.
pub fn geometry(&self, toplevel: &Kind<R>) -> Option<Rectangle> {
self.windows
.iter()
.find(|w| w.toplevel.equals(toplevel))
.map(|w| w.geometry(self.ctoken))
}
2017-09-22 12:56:59 +00:00
}