2019-04-10 13:22:06 +00:00
|
|
|
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, SurfaceAttributes, TraversalAction},
|
|
|
|
shell::{
|
|
|
|
legacy::{ShellSurface, ShellSurfaceRole},
|
|
|
|
xdg::{ToplevelSurface, XdgSurfaceRole},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|
2017-09-22 12:56:59 +00:00
|
|
|
|
2019-04-23 21:52:14 +00:00
|
|
|
pub enum Kind<R> {
|
|
|
|
Xdg(ToplevelSurface<R>),
|
|
|
|
Wl(ShellSurface<R>),
|
2018-04-23 09:40:41 +00:00
|
|
|
}
|
|
|
|
|
2019-04-23 21:52:14 +00:00
|
|
|
impl<R> Kind<R>
|
2018-04-23 09:40:41 +00:00
|
|
|
where
|
2019-04-23 21:52:14 +00:00
|
|
|
R: Role<SubsurfaceRole> + Role<XdgSurfaceRole> + Role<ShellSurfaceRole> + 'static,
|
2018-04-23 09:40:41 +00:00
|
|
|
{
|
|
|
|
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> {
|
2018-04-23 09:40:41 +00:00
|
|
|
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,
|
|
|
|
}
|
|
|
|
}
|
2018-04-23 09:40:41 +00:00
|
|
|
}
|
|
|
|
|
2019-04-23 21:52:14 +00:00
|
|
|
struct Window<R> {
|
2017-09-22 12:56:59 +00:00
|
|
|
location: (i32, i32),
|
2020-02-03 09:10:36 +00:00
|
|
|
/// A bounding box over this window and its children.
|
2020-01-22 03:57:45 +00:00
|
|
|
///
|
2020-02-03 09:10:36 +00:00
|
|
|
/// 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.
|
2020-01-22 04:00:37 +00:00
|
|
|
///
|
|
|
|
/// You need to provide a `contains_point` function which checks if the point (in surface-local
|
|
|
|
/// coordinates) is within the input region of the given `SurfaceAttributes`.
|
2018-01-07 21:30:38 +00:00
|
|
|
fn matching<F>(
|
2018-04-22 09:58:39 +00:00
|
|
|
&self,
|
|
|
|
point: (f64, f64),
|
2019-04-23 20:46:11 +00:00
|
|
|
ctoken: CompositorToken<R>,
|
2020-01-22 04:00:37 +00:00
|
|
|
contains_point: F,
|
2019-02-22 21:50:46 +00:00
|
|
|
) -> Option<(wl_surface::WlSurface, (f64, f64))>
|
2017-09-22 12:56:59 +00:00
|
|
|
where
|
2020-01-22 04:00:37 +00:00
|
|
|
F: Fn(&SurfaceAttributes, (f64, f64)) -> bool,
|
2017-09-22 12:56:59 +00:00
|
|
|
{
|
2020-02-03 09:10:36 +00:00
|
|
|
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
|
2019-04-10 13:22:06 +00:00
|
|
|
let found = RefCell::new(None);
|
2017-09-22 12:56:59 +00:00
|
|
|
if let Some(wl_surface) = self.toplevel.get_surface() {
|
2020-01-01 10:43:16 +00:00
|
|
|
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)| {
|
2020-01-22 04:00:37 +00:00
|
|
|
if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) {
|
|
|
|
x += subdata.location.0;
|
|
|
|
y += subdata.location.1;
|
2017-09-22 12:56:59 +00:00
|
|
|
}
|
2020-01-22 04:00:37 +00:00
|
|
|
|
|
|
|
let surface_local_point = (point.0 - x as f64, point.1 - y as f64);
|
|
|
|
if contains_point(attributes, surface_local_point) {
|
|
|
|
*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
|
|
|
},
|
2019-04-10 13:22:06 +00:00
|
|
|
|_, _, _, _| {},
|
|
|
|
|_, _, _, _| {
|
|
|
|
// only continue if the point is not found
|
|
|
|
found.borrow().is_none()
|
|
|
|
},
|
2017-09-22 12:56:59 +00:00
|
|
|
);
|
|
|
|
}
|
2019-04-10 13:22:06 +00:00
|
|
|
found.into_inner()
|
2017-09-22 12:56:59 +00:00
|
|
|
}
|
|
|
|
|
2019-04-23 20:46:11 +00:00
|
|
|
fn self_update<F>(&mut self, ctoken: CompositorToken<R>, get_size: F)
|
2017-09-22 12:56:59 +00:00
|
|
|
where
|
2019-04-23 20:46:11 +00:00
|
|
|
F: Fn(&SurfaceAttributes) -> Option<(i32, i32)>,
|
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() {
|
2020-01-01 10:43:16 +00:00
|
|
|
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)| {
|
2020-01-22 03:58:41 +00:00
|
|
|
// The input region is intersected with the surface size, so the surface size
|
|
|
|
// can serve as an approximation for the input bounding box.
|
2017-09-22 12:56:59 +00:00
|
|
|
if let Some((w, h)) = get_size(attributes) {
|
|
|
|
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
|
|
|
}
|
2020-02-03 12:16:00 +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 {
|
2020-02-03 12:16:00 +00:00
|
|
|
// 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
|
|
|
|
}
|
|
|
|
},
|
2019-04-10 13:22:06 +00:00
|
|
|
|_, _, _, _| {},
|
|
|
|
|_, _, _, _| true,
|
2017-09-22 12:56:59 +00:00
|
|
|
);
|
|
|
|
}
|
2020-02-03 09:10:36 +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,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-22 04:00:37 +00:00
|
|
|
pub struct WindowMap<R, F, G> {
|
2019-04-23 20:46:11 +00:00
|
|
|
ctoken: CompositorToken<R>,
|
2019-04-23 21:52:14 +00:00
|
|
|
windows: Vec<Window<R>>,
|
2020-01-22 03:58:41 +00:00
|
|
|
/// A function returning the surface size.
|
2017-09-22 12:56:59 +00:00
|
|
|
get_size: F,
|
2020-01-22 04:00:37 +00:00
|
|
|
/// A function that checks if the point is in the surface's input region.
|
|
|
|
contains_point: G,
|
2017-09-22 12:56:59 +00:00
|
|
|
}
|
|
|
|
|
2020-01-22 04:00:37 +00:00
|
|
|
impl<R, F, G> WindowMap<R, F, G>
|
2017-09-22 12:56:59 +00:00
|
|
|
where
|
2019-04-23 20:46:11 +00:00
|
|
|
F: Fn(&SurfaceAttributes) -> Option<(i32, i32)>,
|
2020-01-22 04:00:37 +00:00
|
|
|
G: Fn(&SurfaceAttributes, (f64, f64)) -> bool,
|
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 04:00:37 +00:00
|
|
|
pub fn new(ctoken: CompositorToken<R>, get_size: F, contains_point: G) -> 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(),
|
2018-06-27 12:04:29 +00:00
|
|
|
get_size,
|
2020-01-22 04:00:37 +00:00
|
|
|
contains_point,
|
2017-09-22 12:56:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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,
|
2020-02-03 09:10:36 +00:00
|
|
|
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, &self.get_size);
|
|
|
|
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 {
|
2020-01-22 04:00:37 +00:00
|
|
|
if let Some(surface) = w.matching(point, self.ctoken, &self.contains_point) {
|
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() {
|
2020-01-22 04:00:37 +00:00
|
|
|
if let Some(surface) = w.matching(point, self.ctoken, &self.contains_point) {
|
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 {
|
2017-09-22 12:56:59 +00:00
|
|
|
w.self_update(self.ctoken, &self.get_size);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn clear(&mut self) {
|
|
|
|
self.windows.clear();
|
|
|
|
}
|
2020-02-01 15:30:01 +00:00
|
|
|
|
|
|
|
/// 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)
|
|
|
|
}
|
2020-02-01 15:30:13 +00:00
|
|
|
|
|
|
|
/// 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, &self.get_size);
|
|
|
|
}
|
|
|
|
}
|
2017-09-22 12:56:59 +00:00
|
|
|
}
|