wayland.compositor: rework the subsurface tree

Rework the subsurface tree by:

- forbidding subsurface loops
- storing the relative depth of a parent to its children,
  finally respecting the wl_subsurface specification.

closes #23
This commit is contained in:
Victor Berger 2019-04-10 15:22:06 +02:00 committed by Victor Berger
parent 5768e1fd87
commit 9f9e6d4329
6 changed files with 199 additions and 124 deletions

View File

@ -294,9 +294,11 @@ impl<F: GLGraphicsBackend + 'static> GliumDrawer<F> {
compositor_token: MyCompositorToken,
screen_dimensions: (u32, u32),
) {
compositor_token
.with_surface_tree_upward(root, location, |_surface, attributes, role, &(mut x, mut y)| {
// there is actually something to draw !
compositor_token.with_surface_tree_upward(
root,
location,
|_surface, attributes, role, &(mut x, mut y)| {
// Pull a new buffer if available
if attributes.user_data.texture.is_none() {
if let Some(buffer) = attributes.user_data.buffer.take() {
if let Ok(m) = self.texture_from_buffer(buffer.clone()) {
@ -307,7 +309,23 @@ impl<F: GLGraphicsBackend + 'static> GliumDrawer<F> {
buffer.release();
}
}
// Now, should we be drawn ?
if attributes.user_data.texture.is_some() {
// if yes, also process the children
if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) {
x += subdata.location.0;
y += subdata.location.1;
}
TraversalAction::DoChildren((x, y))
} else {
// we are not display, so our children are neither
TraversalAction::SkipChildren
}
},
|_surface, attributes, role, &(mut x, mut y)| {
if let Some(ref metadata) = attributes.user_data.texture {
// we need to re-extract the subsurface offset, as the previous closure
// only passes it to our children
if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) {
x += subdata.location.0;
y += subdata.location.1;
@ -332,13 +350,10 @@ impl<F: GLGraphicsBackend + 'static> GliumDrawer<F> {
..Default::default()
},
);
TraversalAction::DoChildren((x, y))
} else {
// we are not display, so our children are neither
TraversalAction::SkipChildren
}
})
.unwrap();
},
|_, _, _, _| true,
);
}
pub fn draw_windows(

View File

@ -1,3 +1,5 @@
use std::cell::RefCell;
use smithay::{
reexports::wayland_server::protocol::wl_surface,
utils::Rectangle,
@ -63,7 +65,7 @@ where
return None;
}
// need to check more carefully
let mut found = None;
let found = RefCell::new(None);
if let Some(wl_surface) = self.toplevel.get_surface() {
let _ = ctoken.with_surface_tree_downward(
wl_surface,
@ -81,18 +83,22 @@ where
height: h,
};
if my_rect.contains((point.0 as i32, point.1 as i32)) {
found = Some((wl_surface.clone(), (my_rect.x as f64, my_rect.y as f64)));
TraversalAction::Break
} else {
TraversalAction::DoChildren((x, y))
*found.borrow_mut() =
Some((wl_surface.clone(), (my_rect.x as f64, my_rect.y as f64)));
}
TraversalAction::DoChildren((x, y))
} else {
TraversalAction::SkipChildren
}
},
|_, _, _, _| {},
|_, _, _, _| {
// only continue if the point is not found
found.borrow().is_none()
},
);
}
found
found.into_inner()
}
fn self_update<F>(&mut self, ctoken: CompositorToken<U, R>, get_size: F)
@ -129,6 +135,8 @@ where
TraversalAction::SkipChildren
}
},
|_, _, _, _| {},
|_, _, _, _| true,
);
}
self.surface = Rectangle {

View File

@ -300,6 +300,7 @@ impl Drop for EGLImages {
}
}
}
println!("RELEASING EGL BUFFER");
self.buffer.release();
}
}

View File

@ -139,6 +139,7 @@ where
Some(|surface| SurfaceData::<U, R>::cleanup(&surface)),
SurfaceData::<U, R>::new(),
);
SurfaceData::<U, R>::init(&surface);
surface
}

View File

@ -287,11 +287,21 @@ where
{
/// Access the data of a surface tree from bottom to top
///
/// The provided closure is called successively on the surface and all its child subsurfaces,
/// in a depth-first order. This matches the order in which the surfaces are supposed to be
/// drawn: top-most last.
/// You provide three closures, a "filter", a "processor" and a "post filter".
///
/// The arguments provided to the closure are, in this order:
/// The first closure is initially called on a surface to determine if its children
/// should be processed as well. It returns a `TraversalAction<T>` reflecting that.
///
/// The second closure is supposed to do the actual processing. The processing closure for
/// a surface may be called after the processing closure of some of its children, depending
/// on the stack ordering the client requested. Here the surfaces are processed in the same
/// order as they are supposed to be drawn: from the farthest of the screen to the nearest.
///
/// The third closure is called once all the subtree of a node has been processed, and gives
/// an opportunity for early-stopping. If it returns `true` the processing will continue,
/// while if it returns `false` it'll stop.
///
/// The arguments provided to the closures are, in this order:
///
/// - The surface object itself
/// - a mutable reference to its surface attribute data
@ -301,27 +311,40 @@ where
///
/// If the surface not managed by the `CompositorGlobal` that provided this token, this
/// will panic (having more than one compositor is not supported).
pub fn with_surface_tree_upward<F, T>(&self, surface: &WlSurface, initial: T, f: F) -> Result<(), ()>
where
F: FnMut(&WlSurface, &mut SurfaceAttributes<U>, &mut R, &T) -> TraversalAction<T>,
pub fn with_surface_tree_upward<F1, F2, F3, T>(
&self,
surface: &WlSurface,
initial: T,
filter: F1,
processor: F2,
post_filter: F3,
) where
F1: FnMut(&WlSurface, &mut SurfaceAttributes<U>, &mut R, &T) -> TraversalAction<T>,
F2: FnMut(&WlSurface, &mut SurfaceAttributes<U>, &mut R, &T),
F3: FnMut(&WlSurface, &mut SurfaceAttributes<U>, &mut R, &T) -> bool,
{
SurfaceData::<U, R>::map_tree(surface, initial, f, false);
Ok(())
SurfaceData::<U, R>::map_tree(surface, &initial, filter, processor, post_filter, false);
}
/// Access the data of a surface tree from top to bottom
///
/// The provided closure is called successively on the surface and all its child subsurfaces,
/// in a depth-first order. This matches the reverse of the order in which the surfaces are
/// supposed to be drawn: top-most first.
/// Behavior is the same as [`with_surface_tree_upward`](CompositorToken::with_surface_tree_upward), but
/// the processing is done in the reverse order, from the nearest of the screen to the deepest.
///
/// Behavior is the same as [`with_surface_tree_upward`](CompositorToken::with_surface_tree_upward).
pub fn with_surface_tree_downward<F, T>(&self, surface: &WlSurface, initial: T, f: F) -> Result<(), ()>
where
F: FnMut(&WlSurface, &mut SurfaceAttributes<U>, &mut R, &T) -> TraversalAction<T>,
/// This would typically be used to find out which surface of a subsurface tree has been clicked for example.
pub fn with_surface_tree_downward<F1, F2, F3, T>(
&self,
surface: &WlSurface,
initial: T,
filter: F1,
processor: F2,
post_filter: F3,
) where
F1: FnMut(&WlSurface, &mut SurfaceAttributes<U>, &mut R, &T) -> TraversalAction<T>,
F2: FnMut(&WlSurface, &mut SurfaceAttributes<U>, &mut R, &T),
F3: FnMut(&WlSurface, &mut SurfaceAttributes<U>, &mut R, &T) -> bool,
{
SurfaceData::<U, R>::map_tree(surface, initial, f, true);
Ok(())
SurfaceData::<U, R>::map_tree(surface, &initial, filter, processor, post_filter, true);
}
/// Retrieve the parent of this surface

View File

@ -13,11 +13,8 @@ use wayland_server::protocol::wl_surface::WlSurface;
/// fact that lifetime of objects are decided by Wayland-server to ensure
/// the cleanup will be done properly, and we won't leak anything.
///
/// This implementation is not strictly a tree, but rather a directed graph
/// with the constraint that node can have at most one incoming edge. Aka like
/// a tree, but with loops allowed. This is because the Wayland protocol does not
/// have a failure case to forbid this. Note that if any node in such a graph does not
/// have a parent, then the graph is a tree and this node is its root.
/// Each node also appears within its children list, to allow relative placement
/// between them.
pub struct SurfaceData<U, R> {
parent: Option<WlSurface>,
children: Vec<WlSurface>,
@ -44,7 +41,7 @@ impl<U: Default, R: Default> SurfaceData<U, R> {
pub fn new() -> Mutex<SurfaceData<U, R>> {
Mutex::new(SurfaceData {
parent: None,
children: Vec::new(),
children: vec![],
role: Default::default(),
attributes: Default::default(),
})
@ -56,13 +53,20 @@ where
U: 'static,
R: 'static,
{
/// Initializes the surface, must be called at creation for state coherence
pub fn init(surface: &WlSurface) {
let my_data_mutex = surface.as_ref().user_data::<Mutex<SurfaceData<U, R>>>().unwrap();
let mut my_data = my_data_mutex.lock().unwrap();
debug_assert!(my_data.children.len() == 0);
my_data.children.push(surface.clone());
}
/// Cleans the `as_ref().user_data` of that surface, must be called when it is destroyed
pub fn cleanup(surface: &WlSurface) {
let my_data_mutex = surface.as_ref().user_data::<Mutex<SurfaceData<U, R>>>().unwrap();
let mut my_data = my_data_mutex.lock().unwrap();
if let Some(old_parent) = my_data.parent.take() {
if !old_parent.as_ref().equals(surface.as_ref()) {
// We had a parent that is not ourselves, lets unregister ourselves from it
// We had a parent, lets unregister ourselves from it
let old_parent_mutex = old_parent
.as_ref()
.user_data::<Mutex<SurfaceData<U, R>>>()
@ -72,7 +76,6 @@ where
.children
.retain(|c| !c.as_ref().equals(surface.as_ref()));
}
}
// orphan all our children
for child in &my_data.children {
// don't do anything if this child is ourselves
@ -159,6 +162,21 @@ impl<U: 'static, R: RoleType + 'static> SurfaceData<U, R> {
}
impl<U: 'static, R: RoleType + Role<SubsurfaceRole> + 'static> SurfaceData<U, R> {
/// Checks if the first surface is an ancestor of the second
pub fn is_ancestor(a: &WlSurface, b: &WlSurface) -> bool {
let b_mutex = b.as_ref().user_data::<Mutex<SurfaceData<U, R>>>().unwrap();
let b_guard = b_mutex.lock().unwrap();
if let Some(ref parent) = b_guard.parent {
if parent.as_ref().equals(a.as_ref()) {
return true;
} else {
return Self::is_ancestor(a, parent);
}
} else {
return false;
}
}
/// Sets the parent of a surface
///
/// if this surface already has a role, does nothing and fails, otherwise
@ -166,6 +184,10 @@ impl<U: 'static, R: RoleType + Role<SubsurfaceRole> + 'static> SurfaceData<U, R>
pub fn set_parent(child: &WlSurface, parent: &WlSurface) -> Result<(), ()> {
debug_assert!(child.as_ref().is_alive());
debug_assert!(parent.as_ref().is_alive());
// ensure the child is not already a parent of the parent
if Self::is_ancestor(child, parent) {
return Err(());
}
// change child's parent
{
@ -177,7 +199,6 @@ impl<U: 'static, R: RoleType + Role<SubsurfaceRole> + 'static> SurfaceData<U, R>
child_guard.parent = Some(parent.clone());
}
// register child to new parent
// double scoping is to be robust to have a child be its own parent
{
let parent_mutex = parent.as_ref().user_data::<Mutex<SurfaceData<U, R>>>().unwrap();
let mut parent_guard = parent_mutex.lock().unwrap();
@ -222,11 +243,16 @@ impl<U: 'static, R: RoleType + Role<SubsurfaceRole> + 'static> SurfaceData<U, R>
child_guard.parent.as_ref().cloned()
}
/// Retrieve the parent surface (if any) of this surface
pub fn get_children(child: &WlSurface) -> Vec<WlSurface> {
let child_mutex = child.as_ref().user_data::<Mutex<SurfaceData<U, R>>>().unwrap();
let child_guard = child_mutex.lock().unwrap();
child_guard.children.to_vec()
/// Retrieve the children surface (if any) of this surface
pub fn get_children(parent: &WlSurface) -> Vec<WlSurface> {
let parent_mutex = parent.as_ref().user_data::<Mutex<SurfaceData<U, R>>>().unwrap();
let parent_guard = parent_mutex.lock().unwrap();
parent_guard
.children
.iter()
.filter(|s| !s.as_ref().equals(parent.as_ref()))
.cloned()
.collect()
}
/// Reorders a surface relative to one of its sibling
@ -238,10 +264,6 @@ impl<U: 'static, R: RoleType + Role<SubsurfaceRole> + 'static> SurfaceData<U, R>
let data_guard = data_mutex.lock().unwrap();
data_guard.parent.as_ref().cloned().unwrap()
};
if parent.as_ref().equals(relative_to.as_ref()) {
// TODO: handle positioning relative to parent
return Ok(());
}
fn index_of(surface: &WlSurface, slice: &[WlSurface]) -> Option<usize> {
for (i, s) in slice.iter().enumerate() {
@ -255,7 +277,7 @@ impl<U: 'static, R: RoleType + Role<SubsurfaceRole> + 'static> SurfaceData<U, R>
let parent_mutex = parent.as_ref().user_data::<Mutex<SurfaceData<U, R>>>().unwrap();
let mut parent_guard = parent_mutex.lock().unwrap();
let my_index = index_of(surface, &parent_guard.children).unwrap();
let mut other_index = match index_of(surface, &parent_guard.children) {
let mut other_index = match index_of(relative_to, &parent_guard.children) {
Some(idx) => idx,
None => return Err(()),
};
@ -295,76 +317,81 @@ impl<U: 'static, R: 'static> SurfaceData<U, R> {
/// Note that an internal lock is taken during access of this data,
/// so the tree cannot be manipulated at the same time.
///
/// The callback returns whether the traversal should continue or not. Returning
/// false will cause an early-stopping.
pub fn map_tree<F, T>(root: &WlSurface, initial: T, mut f: F, reverse: bool)
where
F: FnMut(&WlSurface, &mut SurfaceAttributes<U>, &mut R, &T) -> TraversalAction<T>,
{
// helper function for recursion
fn map<U: 'static, R: 'static, F, T>(
/// The first callback determines if this node children should be processed or not.
///
/// The second actually does the processing, being called on children in display depth
/// order.
///
/// The third is called once all the children of a node has been processed (including itself), only if the first
/// returned `DoChildren`, and gives an opportunity to early stop
pub fn map_tree<F1, F2, F3, T>(
surface: &WlSurface,
root: &WlSurface,
initial: &T,
f: &mut F,
mut filter: F1,
mut processor: F2,
mut post_filter: F3,
reverse: bool,
) where
F1: FnMut(&WlSurface, &mut SurfaceAttributes<U>, &mut R, &T) -> TraversalAction<T>,
F2: FnMut(&WlSurface, &mut SurfaceAttributes<U>, &mut R, &T),
F3: FnMut(&WlSurface, &mut SurfaceAttributes<U>, &mut R, &T) -> bool,
{
Self::map(
surface,
initial,
&mut filter,
&mut processor,
&mut post_filter,
reverse,
);
}
// helper function for map_tree
fn map<F1, F2, F3, T>(
surface: &WlSurface,
initial: &T,
filter: &mut F1,
processor: &mut F2,
post_filter: &mut F3,
reverse: bool,
) -> bool
where
F: FnMut(&WlSurface, &mut SurfaceAttributes<U>, &mut R, &T) -> TraversalAction<T>,
F1: FnMut(&WlSurface, &mut SurfaceAttributes<U>, &mut R, &T) -> TraversalAction<T>,
F2: FnMut(&WlSurface, &mut SurfaceAttributes<U>, &mut R, &T),
F3: FnMut(&WlSurface, &mut SurfaceAttributes<U>, &mut R, &T) -> bool,
{
// stop if we met the root, so to not deadlock/inifinte loop
if surface.as_ref().equals(root.as_ref()) {
return true;
}
let data_mutex = surface.as_ref().user_data::<Mutex<SurfaceData<U, R>>>().unwrap();
let mut data_guard = data_mutex.lock().unwrap();
let data_guard = &mut *data_guard;
// call the callback on ourselves
match f(surface, &mut data_guard.attributes, &mut data_guard.role, initial) {
// call the filter on ourselves
match filter(surface, &mut data_guard.attributes, &mut data_guard.role, initial) {
TraversalAction::DoChildren(t) => {
// loop over children
if reverse {
for c in data_guard.children.iter().rev() {
if !map::<U, R, _, _>(c, root, &t, f, true) {
if c.as_ref().equals(surface.as_ref()) {
processor(surface, &mut data_guard.attributes, &mut data_guard.role, initial);
} else if !Self::map(c, &t, filter, processor, post_filter, true) {
return false;
}
}
} else {
for c in &data_guard.children {
if !map::<U, R, _, _>(c, root, &t, f, false) {
if c.as_ref().equals(surface.as_ref()) {
processor(surface, &mut data_guard.attributes, &mut data_guard.role, initial);
} else if !Self::map(c, &t, filter, processor, post_filter, false) {
return false;
}
}
}
post_filter(surface, &mut data_guard.attributes, &mut data_guard.role, initial)
}
TraversalAction::SkipChildren => {
// still process ourselves
processor(surface, &mut data_guard.attributes, &mut data_guard.role, initial);
true
}
TraversalAction::SkipChildren => true,
TraversalAction::Break => false,
}
}
let data_mutex = root.as_ref().user_data::<Mutex<SurfaceData<U, R>>>().unwrap();
let mut data_guard = data_mutex.lock().unwrap();
let data_guard = &mut *data_guard;
// call the callback on ourselves
if let TraversalAction::DoChildren(t) =
f(root, &mut data_guard.attributes, &mut data_guard.role, &initial)
{
// loop over children
if reverse {
for c in data_guard.children.iter().rev() {
if !map::<U, R, _, _>(c, root, &t, &mut f, true) {
break;
}
}
} else {
for c in &data_guard.children {
if !map::<U, R, _, _>(c, root, &t, &mut f, false) {
break;
}
}
}
}
}
}