From 5c46ba73e9c8c86266922afbcd9258ca1fa032b8 Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Sun, 3 Sep 2017 19:53:29 +0200 Subject: [PATCH] compositor: rework roles --- examples/helpers/mod.rs | 2 +- examples/helpers/shell.rs | 56 ++++++--- examples/simple.rs | 64 +++++----- src/compositor/global.rs | 23 ++-- src/compositor/handlers.rs | 114 ++++++++++-------- src/compositor/mod.rs | 185 ++++++++++++++++++---------- src/compositor/roles.rs | 239 +++++++++++++++++++++++++++++++++++++ src/compositor/tree.rs | 169 ++++++++++++++++---------- src/lib.rs | 1 - 9 files changed, 626 insertions(+), 227 deletions(-) create mode 100644 src/compositor/roles.rs diff --git a/examples/helpers/mod.rs b/examples/helpers/mod.rs index e913100..6964d2b 100644 --- a/examples/helpers/mod.rs +++ b/examples/helpers/mod.rs @@ -2,4 +2,4 @@ mod shell; mod glium; pub use self::glium::GliumDrawer; -pub use self::shell::WlShellStubHandler; +pub use self::shell::{ShellSurfaceRole, WlShellStubHandler}; diff --git a/examples/helpers/shell.rs b/examples/helpers/shell.rs index 6e56466..e640b0d 100644 --- a/examples/helpers/shell.rs +++ b/examples/helpers/shell.rs @@ -1,6 +1,7 @@ use smithay::compositor::{CompositorToken, Handler as CompositorHandler}; +use smithay::compositor::roles::{Role, RoleType}; use wayland_server::{Client, EventLoopHandle, GlobalHandler, Init, Resource}; use wayland_server::protocol::{wl_shell, wl_shell_surface, wl_surface}; @@ -10,14 +11,17 @@ use wayland_server::protocol::{wl_shell, wl_shell_surface, wl_surface}; /// as well as the roles associated to them. /// /// That's it. -pub struct WlShellStubHandler { +pub struct WlShellStubHandler { my_id: Option, - token: CompositorToken, + token: CompositorToken, surfaces: Vec<(wl_shell_surface::WlShellSurface, wl_surface::WlSurface)>, } -impl WlShellStubHandler { - pub fn new(compositor_token: CompositorToken) -> WlShellStubHandler { +#[derive(Default)] +pub struct ShellSurfaceRole; + +impl WlShellStubHandler { + pub fn new(compositor_token: CompositorToken) -> WlShellStubHandler { WlShellStubHandler { my_id: None, token: compositor_token, @@ -30,28 +34,42 @@ impl WlShellStubHandler { } } -impl Init for WlShellStubHandler { +impl Init for WlShellStubHandler { fn init(&mut self, evqh: &mut EventLoopHandle, index: usize) { self.my_id = Some(index) } } -impl GlobalHandler for WlShellStubHandler - where U: Send + 'static, - H: CompositorHandler + Send + 'static +impl GlobalHandler for WlShellStubHandler +where + U: Send + 'static, + R: RoleType + + Role + + Send + + 'static, + H: CompositorHandler + + Send + + 'static, { fn bind(&mut self, evqh: &mut EventLoopHandle, client: &Client, global: wl_shell::WlShell) { - evqh.register::<_, Self>(&global, - self.my_id - .expect("WlShellStubHandler was not properly initialized.")); + evqh.register::<_, Self>( + &global, + self.my_id.expect( + "WlShellStubHandler was not properly initialized.", + ), + ); } } -impl wl_shell::Handler for WlShellStubHandler +impl wl_shell::Handler for WlShellStubHandler where U: Send + 'static, - H: CompositorHandler + Send + 'static, + R: RoleType + + Role + + Send + + 'static, + H: CompositorHandler + Send + 'static, { fn get_shell_surface(&mut self, evqh: &mut EventLoopHandle, client: &Client, resource: &wl_shell::WlShell, id: wl_shell_surface::WlShellSurface, @@ -59,7 +77,7 @@ where let surface = surface.clone().expect( "WlShellStubHandler can only manage surfaces managed by Smithay's CompositorHandler.", ); - if self.token.give_role(&surface).is_err() { + if self.token.give_role::(&surface).is_err() { // This surface already has a role, and thus cannot be given one! resource.post_error( wl_shell::Error::Role as u32, @@ -72,13 +90,15 @@ where } } -server_declare_handler!(WlShellStubHandler, Send]>, wl_shell::Handler, wl_shell::WlShell); +server_declare_handler!(WlShellStubHandler, Send], H: [CompositorHandler, Send]>, wl_shell::Handler, wl_shell::WlShell); -impl wl_shell_surface::Handler for WlShellStubHandler +impl wl_shell_surface::Handler for WlShellStubHandler where U: Send + 'static, - H: CompositorHandler + Send + 'static, + H: CompositorHandler + + Send + + 'static, { } -server_declare_handler!(WlShellStubHandler, Send]>, wl_shell_surface::Handler, wl_shell_surface::WlShellSurface); +server_declare_handler!(WlShellStubHandler, Send]>, wl_shell_surface::Handler, wl_shell_surface::WlShellSurface); diff --git a/examples/simple.rs b/examples/simple.rs index 906f34b..ea8723a 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -1,5 +1,6 @@ #[macro_use(server_declare_handler)] extern crate wayland_server; +#[macro_use(define_roles)] extern crate smithay; #[macro_use] extern crate glium; @@ -13,18 +14,21 @@ mod helpers; use glium::Surface; -use helpers::{GliumDrawer, WlShellStubHandler}; -use slog::*; +use helpers::{GliumDrawer, ShellSurfaceRole, WlShellStubHandler}; +use slog::{Drain, Logger}; use smithay::backend::graphics::glium::IntoGlium; use smithay::backend::input::InputBackend; use smithay::backend::winit; -use smithay::compositor::{self, CompositorHandler, CompositorToken, TraversalAction}; +use smithay::compositor::{self, CompositorHandler, CompositorToken, SubsurfaceRole, TraversalAction}; +use smithay::compositor::roles::Role; use smithay::shm::{BufferData, ShmGlobal, ShmToken}; use wayland_server::{Client, EventLoopHandle, Liveness, Resource}; use wayland_server::protocol::{wl_compositor, wl_shell, wl_shm, wl_subcompositor, wl_surface}; +define_roles!(Roles => [ ShellSurface, ShellSurfaceRole ] ); + struct SurfaceHandler { shm_token: ShmToken, } @@ -34,9 +38,9 @@ struct SurfaceData { buffer: Option<(Vec, (u32, u32))>, } -impl compositor::Handler for SurfaceHandler { +impl compositor::Handler for SurfaceHandler { fn commit(&mut self, evlh: &mut EventLoopHandle, client: &Client, surface: &wl_surface::WlSurface, - token: CompositorToken) { + token: CompositorToken) { // we retrieve the contents of the associated buffer and copy it token.with_surface_data(surface, |attributes| { match attributes.buffer.take() { @@ -67,6 +71,8 @@ impl compositor::Handler for SurfaceHandler { } } +type MyCompositorHandler = CompositorHandler; + fn main() { // A logger facility, here we use the terminal for this example let log = Logger::root( @@ -97,22 +103,19 @@ fn main() { /* * Initialize the compositor global */ - let compositor_handler_id = event_loop.add_handler_with_init(CompositorHandler::::new( + let compositor_handler_id = event_loop.add_handler_with_init(MyCompositorHandler::new( SurfaceHandler { shm_token: shm_token.clone() }, log.clone(), )); // register it to handle wl_compositor and wl_subcompositor + event_loop.register_global::(compositor_handler_id, 4); event_loop - .register_global::>( - compositor_handler_id, - 4, - ); - event_loop.register_global::>(compositor_handler_id, 1); + .register_global::(compositor_handler_id, 1); // retrieve the tokens let compositor_token = { let state = event_loop.state(); state - .get_handler::>(compositor_handler_id) + .get_handler::(compositor_handler_id) .get_token() }; @@ -121,7 +124,7 @@ fn main() { */ let shell_handler_id = event_loop.add_handler_with_init(WlShellStubHandler::new(compositor_token.clone())); - event_loop.register_global::>( + event_loop.register_global::>( shell_handler_id, 1, ); @@ -150,31 +153,30 @@ fn main() { let state = event_loop.state(); for &(_, ref surface) in state - .get_handler::>(shell_handler_id) + .get_handler::>(shell_handler_id) .surfaces() { if surface.status() != Liveness::Alive { continue; } // this surface is a root of a subsurface tree that needs to be drawn - compositor_token.with_surface_tree( - surface, - (100, 100), - |surface, attributes, &(mut x, mut y)| { - if let Some((ref contents, (w, h))) = attributes.user_data.buffer { - // there is actually something to draw ! - if let Some(ref subdata) = attributes.subsurface_attributes { - x += subdata.x; - y += subdata.y; - } - drawer.draw(&mut frame, contents, (w, h), (x, y), screen_dimensions); - TraversalAction::DoChildren((x, y)) - } else { - // we are not display, so our children are neither - TraversalAction::SkipChildren + compositor_token.with_surface_tree(surface, (100, 100), |surface, + attributes, + role, + &(mut x, mut y)| { + if let Some((ref contents, (w, h))) = attributes.user_data.buffer { + // there is actually something to draw ! + if let Ok(subdata) = Role::::data(role) { + x += subdata.x; + y += subdata.y; } - }, - ); + drawer.draw(&mut frame, contents, (w, h), (x, y), screen_dimensions); + TraversalAction::DoChildren((x, y)) + } else { + // we are not display, so our children are neither + TraversalAction::SkipChildren + } + }); } } frame.finish().unwrap(); diff --git a/src/compositor/global.rs b/src/compositor/global.rs index cf744ca..80609e2 100644 --- a/src/compositor/global.rs +++ b/src/compositor/global.rs @@ -1,25 +1,34 @@ -use super::{CompositorHandler, Handler as UserHandler}; +use super::{CompositorHandler, Handler as UserHandler, Role, RoleType, SubsurfaceRole}; use wayland_server::{Client, EventLoopHandle, GlobalHandler}; use wayland_server::protocol::{wl_compositor, wl_subcompositor}; -impl> GlobalHandler for CompositorHandler - where U: Send + 'static, - H: Send + 'static +impl GlobalHandler for CompositorHandler +where + U: Default + + Send + + 'static, + R: Default + + Send + + 'static, + H: UserHandler + + Send + + 'static, { fn bind(&mut self, evlh: &mut EventLoopHandle, _: &Client, global: wl_compositor::WlCompositor) { debug!(self.log, "New compositor global binded."); - evlh.register::<_, CompositorHandler>(&global, self.my_id); + evlh.register::<_, CompositorHandler>(&global, self.my_id); } } -impl GlobalHandler for CompositorHandler +impl GlobalHandler for CompositorHandler where U: Send + 'static, + R: RoleType + Role + Send + 'static, H: Send + 'static, { fn bind(&mut self, evlh: &mut EventLoopHandle, _: &Client, global: wl_subcompositor::WlSubcompositor) { debug!(self.log, "New subcompositor global binded."); - evlh.register::<_, CompositorHandler>(&global, self.my_id); + evlh.register::<_, CompositorHandler>(&global, self.my_id); } } diff --git a/src/compositor/handlers.rs b/src/compositor/handlers.rs index e418ddc..9a4add9 100644 --- a/src/compositor/handlers.rs +++ b/src/compositor/handlers.rs @@ -1,50 +1,58 @@ -use super::{CompositorHandler, Damage, Handler as UserHandler, Rectangle, RectangleKind, - SubsurfaceAttributes}; +use super::{CompositorHandler, Damage, Handler as UserHandler, Rectangle, RectangleKind, Role, RoleType, + SubsurfaceRole}; use super::region::RegionData; use super::tree::{Location, SurfaceData}; use wayland_server::{Client, Destroy, EventLoopHandle, Liveness, Resource}; use wayland_server::protocol::{wl_buffer, wl_callback, wl_compositor, wl_output, wl_region, wl_subcompositor, wl_subsurface, wl_surface}; -struct CompositorDestructor { +struct CompositorDestructor { _t: ::std::marker::PhantomData, + _r: ::std::marker::PhantomData, } /* * wl_compositor */ -impl wl_compositor::Handler for CompositorHandler +impl wl_compositor::Handler for CompositorHandler where U: Default + Send + 'static, - H: UserHandler + Send + 'static, + R: Default + Send + 'static, + H: UserHandler + Send + 'static, { fn create_surface(&mut self, evqh: &mut EventLoopHandle, _: &Client, _: &wl_compositor::WlCompositor, id: wl_surface::WlSurface) { trace!(self.log, "New surface created."); - unsafe { SurfaceData::::init(&id) }; - evqh.register_with_destructor::<_, CompositorHandler, CompositorDestructor>(&id, self.my_id); + unsafe { SurfaceData::::init(&id) }; + evqh.register_with_destructor::<_, CompositorHandler, CompositorDestructor>( + &id, + self.my_id, + ); } fn create_region(&mut self, evqh: &mut EventLoopHandle, _: &Client, _: &wl_compositor::WlCompositor, id: wl_region::WlRegion) { trace!(self.log, "New region created."); unsafe { RegionData::init(&id) }; - evqh.register_with_destructor::<_, CompositorHandler, CompositorDestructor>(&id, self.my_id); + evqh.register_with_destructor::<_, CompositorHandler, CompositorDestructor>( + &id, + self.my_id, + ); } } -server_declare_handler!(CompositorHandler, Send]>, wl_compositor::Handler, wl_compositor::WlCompositor); +server_declare_handler!(CompositorHandler, Send]>, wl_compositor::Handler, wl_compositor::WlCompositor); /* * wl_surface */ -impl> wl_surface::Handler for CompositorHandler { +impl> wl_surface::Handler for CompositorHandler { fn attach(&mut self, _: &mut EventLoopHandle, _: &Client, surface: &wl_surface::WlSurface, buffer: Option<&wl_buffer::WlBuffer>, x: i32, y: i32) { trace!(self.log, "Attaching buffer to surface."); unsafe { - SurfaceData::::with_data(surface, |d| { + SurfaceData::::with_data(surface, |d| { d.buffer = Some(buffer.map(|b| (b.clone_unchecked(), (x, y)))) }); } @@ -53,7 +61,7 @@ impl> wl_surface::Handler for CompositorHandler { y: i32, width: i32, height: i32) { trace!(self.log, "Registering damage to surface."); unsafe { - SurfaceData::::with_data(surface, |d| { + SurfaceData::::with_data(surface, |d| { d.damage = Damage::Surface(Rectangle { x, y, @@ -74,7 +82,7 @@ impl> wl_surface::Handler for CompositorHandler { trace!(self.log, "Setting surface opaque region."); unsafe { let attributes = region.map(|r| RegionData::get_attributes(r)); - SurfaceData::::with_data(surface, |d| d.opaque_region = attributes); + SurfaceData::::with_data(surface, |d| d.opaque_region = attributes); } } fn set_input_region(&mut self, _: &mut EventLoopHandle, _: &Client, surface: &wl_surface::WlSurface, @@ -82,7 +90,7 @@ impl> wl_surface::Handler for CompositorHandler { trace!(self.log, "Setting surface input region."); unsafe { let attributes = region.map(|r| RegionData::get_attributes(r)); - SurfaceData::::with_data(surface, |d| d.input_region = attributes); + SurfaceData::::with_data(surface, |d| d.input_region = attributes); } } fn commit(&mut self, evlh: &mut EventLoopHandle, client: &Client, surface: &wl_surface::WlSurface) { @@ -94,14 +102,14 @@ impl> wl_surface::Handler for CompositorHandler { surface: &wl_surface::WlSurface, transform: wl_output::Transform) { trace!(self.log, "Setting surface's buffer transform."); unsafe { - SurfaceData::::with_data(surface, |d| d.buffer_transform = transform); + SurfaceData::::with_data(surface, |d| d.buffer_transform = transform); } } fn set_buffer_scale(&mut self, _: &mut EventLoopHandle, _: &Client, surface: &wl_surface::WlSurface, scale: i32) { trace!(self.log, "Setting surface's buffer scale."); unsafe { - SurfaceData::::with_data(surface, |d| d.buffer_scale = scale); + SurfaceData::::with_data(surface, |d| d.buffer_scale = scale); } } fn damage_buffer(&mut self, _: &mut EventLoopHandle, _: &Client, surface: &wl_surface::WlSurface, @@ -111,7 +119,7 @@ impl> wl_surface::Handler for CompositorHandler { "Registering damage to surface (buffer coordinates)." ); unsafe { - SurfaceData::::with_data(surface, |d| { + SurfaceData::::with_data(surface, |d| { d.damage = Damage::Buffer(Rectangle { x, y, @@ -123,11 +131,11 @@ impl> wl_surface::Handler for CompositorHandler { } } -server_declare_handler!(CompositorHandler]>, wl_surface::Handler, wl_surface::WlSurface); +server_declare_handler!(CompositorHandler]>, wl_surface::Handler, wl_surface::WlSurface); -impl Destroy for CompositorDestructor { +impl Destroy for CompositorDestructor { fn destroy(surface: &wl_surface::WlSurface) { - unsafe { SurfaceData::::cleanup(surface) } + unsafe { SurfaceData::::cleanup(surface) } } } @@ -135,7 +143,7 @@ impl Destroy for CompositorDestructor { * wl_region */ -impl wl_region::Handler for CompositorHandler { +impl wl_region::Handler for CompositorHandler { fn add(&mut self, _: &mut EventLoopHandle, _: &Client, region: &wl_region::WlRegion, x: i32, y: i32, width: i32, height: i32) { trace!(self.log, "Adding rectangle to a region."); @@ -170,9 +178,9 @@ impl wl_region::Handler for CompositorHandler { } } -server_declare_handler!(CompositorHandler, wl_region::Handler, wl_region::WlRegion); +server_declare_handler!(CompositorHandler, wl_region::Handler, wl_region::WlRegion); -impl Destroy for CompositorDestructor { +impl Destroy for CompositorDestructor { fn destroy(region: &wl_region::WlRegion) { unsafe { RegionData::cleanup(region) }; } @@ -182,52 +190,63 @@ impl Destroy for CompositorDestructor { * wl_subcompositor */ -impl wl_subcompositor::Handler for CompositorHandler +impl wl_subcompositor::Handler for CompositorHandler where U: Send + 'static, + R: RoleType + + Role + + Send + + 'static, H: Send + 'static, { fn get_subsurface(&mut self, evqh: &mut EventLoopHandle, _: &Client, resource: &wl_subcompositor::WlSubcompositor, id: wl_subsurface::WlSubsurface, surface: &wl_surface::WlSurface, parent: &wl_surface::WlSurface) { trace!(self.log, "Creating new subsurface."); - if let Err(()) = unsafe { SurfaceData::::set_parent(surface, parent) } { - resource.post_error(wl_subcompositor::Error::BadSurface as u32, "Surface already has a role.".into()); - return + if let Err(()) = unsafe { SurfaceData::::set_parent(surface, parent) } { + resource.post_error( + wl_subcompositor::Error::BadSurface as u32, + "Surface already has a role.".into(), + ); + return; } id.set_user_data(Box::into_raw( Box::new(unsafe { surface.clone_unchecked() }), ) as *mut _); - unsafe { - SurfaceData::::with_data(surface, |d| { - d.subsurface_attributes = Some(Default::default()) - }); - } - evqh.register_with_destructor::<_, CompositorHandler, CompositorDestructor>(&id, self.my_id); + evqh.register_with_destructor::<_, CompositorHandler, CompositorDestructor>( + &id, + self.my_id, + ); } } -server_declare_handler!(CompositorHandler, wl_subcompositor::Handler, wl_subcompositor::WlSubcompositor); +server_declare_handler!(CompositorHandler, Send], H: [Send]>, wl_subcompositor::Handler, wl_subcompositor::WlSubcompositor); /* * wl_subsurface */ -unsafe fn with_subsurface_attributes(subsurface: &wl_subsurface::WlSubsurface, f: F) +unsafe fn with_subsurface_attributes(subsurface: &wl_subsurface::WlSubsurface, f: F) where - F: FnOnce(&mut SubsurfaceAttributes), + F: FnOnce(&mut SubsurfaceRole), + R: RoleType + Role, { let ptr = subsurface.get_user_data(); let surface = &*(ptr as *mut wl_surface::WlSurface); - SurfaceData::::with_data(surface, |d| f(d.subsurface_attributes.as_mut().unwrap())); + SurfaceData::::with_role_data::(surface, |d| f(d)).expect( + "The surface does not have a subsurface role while it has a wl_subsurface?!", + ); } -impl wl_subsurface::Handler for CompositorHandler { +impl wl_subsurface::Handler for CompositorHandler +where + R: RoleType + Role, +{ fn set_position(&mut self, _: &mut EventLoopHandle, _: &Client, subsurface: &wl_subsurface::WlSubsurface, x: i32, y: i32) { trace!(self.log, "Setting subsurface position."); unsafe { - with_subsurface_attributes::(subsurface, |attrs| { + with_subsurface_attributes::(subsurface, |attrs| { attrs.x = x; attrs.y = y; }); @@ -239,7 +258,7 @@ impl wl_subsurface::Handler for CompositorHandler { unsafe { let ptr = subsurface.get_user_data(); let surface = &*(ptr as *mut wl_surface::WlSurface); - if let Err(()) = SurfaceData::::reorder(surface, Location::After, sibling) { + if let Err(()) = SurfaceData::::reorder(surface, Location::After, sibling) { subsurface.post_error(wl_subsurface::Error::BadSurface as u32, "Provided surface is not a sibling or parent.".into()); } } @@ -250,7 +269,7 @@ impl wl_subsurface::Handler for CompositorHandler { unsafe { let ptr = subsurface.get_user_data(); let surface = &*(ptr as *mut wl_surface::WlSurface); - if let Err(()) = SurfaceData::::reorder(surface, Location::Before, sibling) { + if let Err(()) = SurfaceData::::reorder(surface, Location::Before, sibling) { subsurface.post_error(wl_subsurface::Error::BadSurface as u32, "Provided surface is not a sibling or parent.".into()); } } @@ -258,28 +277,29 @@ impl wl_subsurface::Handler for CompositorHandler { fn set_sync(&mut self, _: &mut EventLoopHandle, _: &Client, subsurface: &wl_subsurface::WlSubsurface) { trace!(self.log, "Setting subsurface sync."; "sync_status" => true); unsafe { - with_subsurface_attributes::(subsurface, |attrs| { attrs.sync = true; }); + with_subsurface_attributes::(subsurface, |attrs| { attrs.sync = true; }); } } fn set_desync(&mut self, _: &mut EventLoopHandle, _: &Client, subsurface: &wl_subsurface::WlSubsurface) { trace!(self.log, "Setting subsurface sync."; "sync_status" => false); unsafe { - with_subsurface_attributes::(subsurface, |attrs| { attrs.sync = false; }); + with_subsurface_attributes::(subsurface, |attrs| { attrs.sync = false; }); } } } -server_declare_handler!(CompositorHandler, wl_subsurface::Handler, wl_subsurface::WlSubsurface); +server_declare_handler!(CompositorHandler], H: []>, wl_subsurface::Handler, wl_subsurface::WlSubsurface); -impl Destroy for CompositorDestructor { +impl Destroy for CompositorDestructor + where R: RoleType + Role +{ fn destroy(subsurface: &wl_subsurface::WlSubsurface) { let ptr = subsurface.get_user_data(); subsurface.set_user_data(::std::ptr::null_mut()); unsafe { let surface = Box::from_raw(ptr as *mut wl_surface::WlSurface); if surface.status() == Liveness::Alive { - SurfaceData::::with_data(&*surface, |d| d.subsurface_attributes = None); - SurfaceData::::unset_parent(&surface); + SurfaceData::::unset_parent(&surface); } } } diff --git a/src/compositor/mod.rs b/src/compositor/mod.rs index c48d45f..a1529bb 100644 --- a/src/compositor/mod.rs +++ b/src/compositor/mod.rs @@ -28,7 +28,7 @@ //! //! ``` //! # extern crate wayland_server; -//! # extern crate smithay; +//! # #[macro_use] extern crate smithay; //! use wayland_server::protocol::wl_compositor::WlCompositor; //! use wayland_server::protocol::wl_subcompositor::WlSubcompositor; //! use smithay::compositor; @@ -41,19 +41,22 @@ //! // whatever you need here //! } //! +//! // Declare the roles enum +//! define_roles!(MyRoles); +//! //! // Define a sub-handler to take care of the events the CompositorHandler does not rack for you //! struct MyHandler { //! // whatever you need //! } //! //! // Implement the handler trait for this sub-handler -//! impl compositor::Handler for MyHandler { +//! impl compositor::Handler for MyHandler { //! // See the trait documentation for its implementation //! // A default implementation for each method is provided, that does nothing //! } //! //! // A type alias to shorten things: -//! type MyCompositorHandler = compositor::CompositorHandler; +//! type MyCompositorHandler = compositor::CompositorHandler; //! //! # fn main() { //! # let (_display, mut event_loop) = wayland_server::create_display(); @@ -90,15 +93,20 @@ //! state pending from the client as defined by the protocol for wl_surface, as well as your //! user-defined type holding any data you need to have associated with a struct. See its //! documentation for details. +//! +//! This `CompositorToken` also provides access to the metadata associated with the role of the +//! surfaces. See the documentation of the `roles` submodule for a detailed explanation. mod global; mod handlers; mod tree; mod region; +pub mod roles; use self::region::RegionData; -pub use self::tree::{RoleStatus, TraversalAction}; +use self::roles::{Role, RoleType, WrongRole}; use self::tree::SurfaceData; +pub use self::tree::TraversalAction; use wayland_server::{Client, EventLoopHandle, Init, resource_is_registered}; use wayland_server::protocol::{wl_buffer, wl_callback, wl_output, wl_region, wl_surface}; @@ -159,12 +167,6 @@ pub struct SurfaceAttributes { /// Hint provided by the client to suggest that only this part /// of the surface was changed and needs to be redrawn pub damage: Damage, - /// Subsurface-related attribute - /// - /// Is `Some` if this surface is a sub-surface - /// - /// **Warning:** Changing this field by yourself can cause panics. - pub subsurface_attributes: Option, /// User-controlled data /// /// This is your field to host whatever you need. @@ -180,14 +182,14 @@ impl Default for SurfaceAttributes { opaque_region: None, input_region: None, damage: Damage::Full, - subsurface_attributes: None, user_data: Default::default(), } } } /// Attributes defining the behaviour of a sub-surface relative to its parent -pub struct SubsurfaceAttributes { +#[derive(Copy, Clone, Debug)] +pub struct SubsurfaceRole { /// Horizontal location of the top-left corner of this sub-surface relative to /// the top-left corner of its parent pub x: i32, @@ -202,9 +204,9 @@ pub struct SubsurfaceAttributes { pub sync: bool, } -impl Default for SubsurfaceAttributes { - fn default() -> SubsurfaceAttributes { - SubsurfaceAttributes { +impl Default for SubsurfaceRole { + fn default() -> SubsurfaceRole { + SubsurfaceRole { x: 0, y: 0, sync: true, @@ -213,7 +215,7 @@ impl Default for SubsurfaceAttributes { } /// Kind of a rectangle part of a region -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug)] pub enum RectangleKind { /// This rectangle should be added to the region Add, @@ -223,7 +225,7 @@ pub enum RectangleKind { } /// A rectangle defined by its top-left corner and dimensions -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug)] pub struct Rectangle { /// horizontal position of the top-leftcorner of the rectangle, in surface coordinates pub x: i32, @@ -242,7 +244,7 @@ pub struct Rectangle { /// This struct contains an ordered Vec containing the rectangles defining /// a region. They should be added or substracted in this order to compute the /// actual contents of the region. -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct RegionAttributes { /// List of rectangle part of this region pub rects: Vec<(RectangleKind, Rectangle)>, @@ -259,25 +261,26 @@ impl Default for RegionAttributes { /// This token can be cloned at will, and is the entry-point to /// access data associated with the wl_surface and wl_region managed /// by the `CompositorGlobal` that provided it. -pub struct CompositorToken { +pub struct CompositorToken { hid: usize, _data: ::std::marker::PhantomData<*mut U>, + _role: ::std::marker::PhantomData<*mut R>, _handler: ::std::marker::PhantomData<*mut H>, } -unsafe impl Send for CompositorToken {} -unsafe impl Sync for CompositorToken {} +unsafe impl Send for CompositorToken {} +unsafe impl Sync for CompositorToken {} // we implement them manually because #[derive(..)] would require // U: Clone and H: Clone ... -impl Copy for CompositorToken {} -impl Clone for CompositorToken { - fn clone(&self) -> CompositorToken { +impl Copy for CompositorToken {} +impl Clone for CompositorToken { + fn clone(&self) -> CompositorToken { *self } } -impl + Send + 'static> CompositorToken { +impl + Send + 'static> CompositorToken { /// Access the data of a surface /// /// The closure will be called with the contents of the data associated with this surface. @@ -289,32 +292,51 @@ impl + Send + 'static> CompositorToken { F: FnOnce(&mut SurfaceAttributes), { assert!( - resource_is_registered::<_, CompositorHandler>(surface, self.hid), + resource_is_registered::<_, CompositorHandler>(surface, self.hid), "Accessing the data of foreign surfaces is not supported." ); unsafe { - SurfaceData::::with_data(surface, f); + SurfaceData::::with_data(surface, f); } } +} +impl CompositorToken +where + U: Send + 'static, + R: RoleType + Role + Send + 'static, + H: Handler + Send + 'static, +{ /// Access the data of a surface tree /// /// 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. /// + /// The arguments provided to the closure are, in this order: + /// + /// - The surface object itself + /// - a mutable reference to its surface attribute data + /// - a mutable reference to its role data, + /// - a custom value that is passer in a fold-like maneer, but only from the output of a parent + /// to its children. See `TraversalAction` for details. + /// /// If the surface is 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(&self, surface: &wl_surface::WlSurface, initial: T, f: F) -> Result<(), ()> where - F: FnMut(&wl_surface::WlSurface, &mut SurfaceAttributes, &T) -> TraversalAction, + F: FnMut(&wl_surface::WlSurface, + &mut SurfaceAttributes, + &mut R, + &T) + -> TraversalAction, { assert!( - resource_is_registered::<_, CompositorHandler>(surface, self.hid), + resource_is_registered::<_, CompositorHandler>(surface, self.hid), "Accessing the data of foreign surfaces is not supported." ); unsafe { - SurfaceData::::map_tree(surface, initial, f); + SurfaceData::::map_tree(surface, initial, f); } Ok(()) } @@ -327,10 +349,10 @@ impl + Send + 'static> CompositorToken { /// will panic (having more than one compositor is not supported). pub fn get_parent(&self, surface: &wl_surface::WlSurface) -> Option { assert!( - resource_is_registered::<_, CompositorHandler>(surface, self.hid), + resource_is_registered::<_, CompositorHandler>(surface, self.hid), "Accessing the data of foreign surfaces is not supported." ); - unsafe { SurfaceData::::get_parent(surface) } + unsafe { SurfaceData::::get_parent(surface) } } /// Retrieve the children of this surface @@ -339,55 +361,95 @@ impl + Send + 'static> CompositorToken { /// will panic (having more than one compositor is not supported). pub fn get_children(&self, surface: &wl_surface::WlSurface) -> Vec { assert!( - resource_is_registered::<_, CompositorHandler>(surface, self.hid), + resource_is_registered::<_, CompositorHandler>(surface, self.hid), "Accessing the data of foreign surfaces is not supported." ); - unsafe { SurfaceData::::get_children(surface) } + unsafe { SurfaceData::::get_children(surface) } } +} - /// Retrieve the role status this surface +impl + Send + 'static> + CompositorToken { + /// Check wether this surface as a role or not /// /// If the surface is not managed by the CompositorGlobal that provided this token, this /// will panic (having more than one compositor is not supported). - pub fn role_status(&self, surface: &wl_surface::WlSurface) -> RoleStatus { + pub fn has_a_role(&self, surface: &wl_surface::WlSurface) -> bool { assert!( - resource_is_registered::<_, CompositorHandler>(surface, self.hid), + resource_is_registered::<_, CompositorHandler>(surface, self.hid), "Accessing the data of foreign surfaces is not supported." ); - unsafe { SurfaceData::::role_status(surface) } + unsafe { SurfaceData::::has_a_role(surface) } } - /// Register that this surface has a role + /// Check wether this surface as a specific role /// - /// This makes this surface impossible to become a subsurface, as - /// a surface can only have a single role at a time. + /// If the surface is not managed by the CompositorGlobal that provided this token, this + /// will panic (having more than one compositor is not supported). + pub fn has_role(&self, surface: &wl_surface::WlSurface) -> bool + where + R: Role, + { + assert!( + resource_is_registered::<_, CompositorHandler>(surface, self.hid), + "Accessing the data of foreign surfaces is not supported." + ); + unsafe { SurfaceData::::has_role::(surface) } + } + + + /// Register that this surface has given role with default data /// /// Fails if the surface already has a role. /// /// If the surface is not managed by the CompositorGlobal that provided this token, this /// will panic (having more than one compositor is not supported). - pub fn give_role(&self, surface: &wl_surface::WlSurface) -> Result<(), ()> { + pub fn give_role(&self, surface: &wl_surface::WlSurface) -> Result<(), ()> + where + R: Role, + RoleData: Default, + { assert!( - resource_is_registered::<_, CompositorHandler>(surface, self.hid), + resource_is_registered::<_, CompositorHandler>(surface, self.hid), "Accessing the data of foreign surfaces is not supported." ); - unsafe { SurfaceData::::give_role(surface) } + unsafe { SurfaceData::::give_role::(surface) } } - /// Register that this surface has no role + /// Register that this surface has given role with given data /// - /// It is a noop if this surface already didn't have one, but fails if - /// the role was "subsurface". This role is automatically managed and as such - /// cannot be removed manually. + /// Fails if the surface already has a role and returns the data. /// /// If the surface is not managed by the CompositorGlobal that provided this token, this /// will panic (having more than one compositor is not supported). - pub fn remove_role(&self, surface: &wl_surface::WlSurface) -> Result<(), ()> { + pub fn give_role_with(&self, surface: &wl_surface::WlSurface, data: RoleData) + -> Result<(), RoleData> + where + R: Role, + { assert!( - resource_is_registered::<_, CompositorHandler>(surface, self.hid), + resource_is_registered::<_, CompositorHandler>(surface, self.hid), "Accessing the data of foreign surfaces is not supported." ); - unsafe { SurfaceData::::remove_role(surface) } + unsafe { SurfaceData::::give_role_with::(surface, data) } + } + + + /// Register that this surface does not have a role any longer and retrieve the data + /// + /// Fails if the surface didn't already have this role. + /// + /// If the surface is not managed by the CompositorGlobal that provided this token, this + /// will panic (having more than one compositor is not supported). + pub fn remove_role(&self, surface: &wl_surface::WlSurface) -> Result + where + R: Role, + { + assert!( + resource_is_registered::<_, CompositorHandler>(surface, self.hid), + "Accessing the data of foreign surfaces is not supported." + ); + unsafe { SurfaceData::::remove_role::(surface) } } /// Retrieve the metadata associated with a wl_region @@ -396,7 +458,7 @@ impl + Send + 'static> CompositorToken { /// will panic (having more than one compositor is not supported). pub fn get_region_attributes(&self, region: &wl_region::WlRegion) -> RegionAttributes { assert!( - resource_is_registered::<_, CompositorHandler>(region, self.hid), + resource_is_registered::<_, CompositorHandler>(region, self.hid), "Accessing the data of foreign regions is not supported." ); unsafe { RegionData::get_attributes(region) } @@ -411,23 +473,24 @@ impl + Send + 'static> CompositorToken { /// defined in this module. /// /// See the module-level documentation for instructions and examples of use. -pub struct CompositorHandler { +pub struct CompositorHandler { my_id: usize, log: ::slog::Logger, handler: H, + _role: ::std::marker::PhantomData, _data: ::std::marker::PhantomData, } -impl Init for CompositorHandler { +impl Init for CompositorHandler { fn init(&mut self, _evqh: &mut EventLoopHandle, index: usize) { self.my_id = index; debug!(self.log, "Init finished") } } -impl CompositorHandler { +impl CompositorHandler { /// Create a new CompositorHandler - pub fn new(handler: H, logger: L) -> CompositorHandler + pub fn new(handler: H, logger: L) -> CompositorHandler where L: Into>, { @@ -436,12 +499,13 @@ impl CompositorHandler { my_id: ::std::usize::MAX, log: log.new(o!("smithay_module" => "compositor_handler")), handler: handler, + _role: ::std::marker::PhantomData, _data: ::std::marker::PhantomData, } } /// Create a token to access the data associated to the objects managed by this handler. - pub fn get_token(&self) -> CompositorToken { + pub fn get_token(&self) -> CompositorToken { assert!( self.my_id != ::std::usize::MAX, "CompositorHandler is not initialized yet." @@ -450,6 +514,7 @@ impl CompositorHandler { CompositorToken { hid: self.my_id, _data: ::std::marker::PhantomData, + _role: ::std::marker::PhantomData, _handler: ::std::marker::PhantomData, } } @@ -466,7 +531,7 @@ impl CompositorHandler { /// are forwarded directly to a handler implementing this trait that you must provide /// at creation of the `CompositorHandler`. #[allow(unused_variables)] -pub trait Handler: Sized { +pub trait Handler: Sized { /// The double-buffered state has been validated by the client /// /// At this point, the pending state that has been accumulated in the `SurfaceAttributes` associated @@ -475,7 +540,7 @@ pub trait Handler: Sized { /// See [`wayland_server::protocol::wl_surface::Handler::commit`](https://docs.rs/wayland-server/*/wayland_server/protocol/wl_surface/trait.Handler.html#method.commit) /// for more details fn commit(&mut self, evlh: &mut EventLoopHandle, client: &Client, surface: &wl_surface::WlSurface, - token: CompositorToken) { + token: CompositorToken) { } /// The client asks to be notified when would be a good time to update the contents of this surface /// @@ -485,6 +550,6 @@ pub trait Handler: Sized { /// See [`wayland_server::protocol::wl_surface::Handler::frame`](https://docs.rs/wayland-server/*/wayland_server/protocol/wl_surface/trait.Handler.html#method.frame) /// for more details fn frame(&mut self, evlh: &mut EventLoopHandle, client: &Client, surface: &wl_surface::WlSurface, - callback: wl_callback::WlCallback, token: CompositorToken) { + callback: wl_callback::WlCallback, token: CompositorToken) { } } diff --git a/src/compositor/roles.rs b/src/compositor/roles.rs new file mode 100644 index 0000000..ff705c4 --- /dev/null +++ b/src/compositor/roles.rs @@ -0,0 +1,239 @@ +//! Tools for handling surface roles +//! +//! In the wayland protocol, surfaces can have several different roles, which +//! define how they are to be used. The core protocol defines 3 of these roles: +//! +//! - `shell_surface`: This surface is to be considered as what is most often +//! called a "window". +//! - `pointer_surface`: This surface represent the contents of a pointer icon +//! and replaces the default pointer. +//! - `subsurface`: This surface is part of a subsurface tree, and as such has +//! a parent surface. +//! +//! A surface can have only one role at any given time. To change he role of a +//! surface, the client must first remove the previous role before assigning the +//! new one. A surface without a role is not displayed at all. +//! +//! This module provides tools to manage roles of a surface in a composable way +//! allowing all handlers of smithay to manage surface roles while being aware +//! of the possible role conflicts. +//! +//! ## General mechanism +//! +//! First, all roles need to have an unique type, holding its metadata and identifying it +//! to the type-system. Even if your role does not hold any metadata, you still need its +//! unique type, using a unit-like struct rather than `()`. +//! +//! You then need a type for managing the roles of a surface. This type holds information +//! about what is the current role of a surface, and what is the metadata associated with +//! it. +//! +//! For convenience, you can use the `define_roles!` macro provided by Smithay to define this +//! type. You can call it like this: +//! +//! ``` +//! # #[macro_use] +//! # extern crate smithay; +//! # +//! // Metadata for a first role +//! #[derive(Default)] +//! pub struct MyRoleMetadata { +//! } +//! +//! // Metadata for a second role +//! #[derive(Default)] +//! pub struct MyRoleMetadata2 { +//! } +//! +//! define_roles!(Roles => +//! // You can put several roles like this +//! // first identifier is the name of the variant for this +//! // role in the generated enum, second is the token type +//! // for this role +//! [MyRoleName, MyRoleMetadata] +//! [MyRoleName2, MyRoleMetadata2] +//! /* ... */ +//! ); +//! +//! # fn main() {} +//! ``` +//! +//! And this will expand to an enum like this: +//! +//! ```ignore +//! pub enum Roles { +//! NoRole, +//! // The subsurface role is always inserted, as it is required +//! // by the CompositorHandler +//! Subsurface(::smithay::compositor::SubsurfaceAttributes), +//! // all your other roles come here +//! MyRoleName(MyRoleMetadata), +//! MyRoleName2(MyRoleMetadata2), +//! /* ... */ +//! } +//! ``` +//! +//! as well as implement a few trait for it, allowing it to be used by +//! all smithay handlers: +//! +//! - The trait `RoleType`, which defines it as a type handling roles +//! - For each of your roles, the trait `Role` (where `Token` is your +//! token type), marking its hability to handle this given role. +//! +//! All handlers that handle a specific role will require you to provide +//! them with a `CompositorToken` where `R: Role`. +//! +//! See the documentation of these traits for their specific definition and +//! capabilities. + +/// An error type signifying that the surface does not have expected role +/// +/// Generated if you attempt a role operation on a surface that does +/// not have the role you asked for. +#[derive(Debug)] +pub struct WrongRole; + +/// A trait representing a type that can manage surface roles +pub trait RoleType { + /// Check if the associated surface has a role + /// + /// Only reports if the surface has any role or no role. + /// To check for a role in particular, see `Role::has`. + fn has_role(&self) -> bool; +} + +/// A trait representing the capability of a RoleType to handle a given role +/// +/// This trait allows to interact with the different roles a RoleType can +/// handle. +/// +/// This trait is meant to be used generically, for example, to retrieve the +/// data associated with a given role with token `TheRole`: +/// +/// ```ignore +/// let data = >::data(my_roles) +/// .expect("The surface does not have this role."); +/// ``` +/// +/// The methods of this trait are mirrored on `CompositorToken` for easy +/// access to the role data of the surfaces. +/// +/// Note that if a role is automatically handled for you by a Handler provided +/// by smithay, you should not set or unset it manually on a surface. Doing so +/// would likely corrupt the internal state of these handlers, causing spurious +/// protocol errors and unreliable behaviour overall. +pub trait Role: RoleType { + /// Set the role for the associated surface with default associated data + /// + /// Fails if the surface already has a role + fn set(&mut self) -> Result<(), ()> + where + R: Default, + { + self.set_with(Default::default()).map_err(|_| ()) + } + + /// Set the role for the associated surface with given data + /// + /// Fails if the surface already has a role and returns the data + fn set_with(&mut self, data: R) -> Result<(), R>; + + /// Check if the associated surface has this role + fn has(&self) -> bool; + + /// Access the data associated with this role if its the current one + fn data(&self) -> Result<&R, WrongRole>; + + /// Mutably access the data associated with this role if its the current one + fn data_mut(&mut self) -> Result<&mut R, WrongRole>; + + /// Remove this role from the associated surface + /// + /// Fails if the surface does not currently have this role + fn unset(&mut self) -> Result; +} + +#[macro_export] +macro_rules! define_roles( + ($enum_name: ident) => { + define_roles!($enum_name =>); + }; + ($enum_name:ident => $([ $role_name: ident, $role_data: ty])*) => { + define_roles!(__impl $enum_name => + // add in subsurface role + [Subsurface, $crate::compositor::SubsurfaceRole] + $([$role_name, $role_data])* + ); + }; + (__impl $enum_name:ident => $([ $role_name: ident, $role_data: ty])*) => { + pub enum $enum_name { + NoRole, + $($role_name($role_data)),* + } + + impl Default for $enum_name { + fn default() -> $enum_name { + $enum_name::NoRole + } + } + + impl $crate::compositor::roles::RoleType for $enum_name { + fn has_role(&self) -> bool { + if let $enum_name::NoRole = *self { + false + } else { + true + } + } + } + + $( + impl $crate::compositor::roles::Role<$role_data> for $enum_name { + fn set_with(&mut self, data: $role_data) -> ::std::result::Result<(), $role_data> { + if let $enum_name::NoRole = *self { + *self = $enum_name::$role_name(data); + Ok(()) + } else { + Err(data) + } + } + + fn has(&self) -> bool { + if let $enum_name::$role_name(_) = *self { + true + } else { + false + } + } + + fn data(&self) -> ::std::result::Result<&$role_data, $crate::compositor::roles::WrongRole> { + if let $enum_name::$role_name(ref data) = *self { + Ok(data) + } else { + Err($crate::compositor::roles::WrongRole) + } + } + + fn data_mut(&mut self) -> ::std::result::Result<&mut $role_data, $crate::compositor::roles::WrongRole> { + if let $enum_name::$role_name(ref mut data) = *self { + Ok(data) + } else { + Err($crate::compositor::roles::WrongRole) + } + } + + fn unset(&mut self) -> ::std::result::Result<$role_data, $crate::compositor::roles::WrongRole> { + // remove self to make borrow checker happy + let temp = ::std::mem::replace(self, $enum_name::NoRole); + if let $enum_name::$role_name(data) = temp { + Ok(data) + } else { + // put it back in place + ::std::mem::replace(self, temp); + Err($crate::compositor::roles::WrongRole) + } + } + } + )* + }; +); diff --git a/src/compositor/tree.rs b/src/compositor/tree.rs index 914f076..c3c1e9b 100644 --- a/src/compositor/tree.rs +++ b/src/compositor/tree.rs @@ -1,4 +1,5 @@ -use super::SurfaceAttributes; +use super::{SubsurfaceRole, SurfaceAttributes}; +use super::roles::*; use std::sync::Mutex; use wayland_server::{Liveness, Resource}; @@ -23,26 +24,13 @@ use wayland_server::protocol::wl_surface; /// /// All the methods here are unsafe, because they assume the provided wl_surface object /// is correctly initialized regarding its user_data. -pub struct SurfaceData { +pub struct SurfaceData { parent: Option, children: Vec, - has_role: bool, + role: R, attributes: SurfaceAttributes, } -/// Status of a surface regarding its role -pub enum RoleStatus { - /// This surface does not have any role - NoRole, - /// This surface is a subsurface - Subsurface, - /// This surface has a role other than subsurface - /// - /// It is thus the root of a subsurface tree that will - /// have to be displayed - HasRole, -} - pub enum Location { Before, After, @@ -58,12 +46,12 @@ pub enum TraversalAction { Break, } -impl SurfaceData { - fn new() -> SurfaceData { +impl SurfaceData { + fn new() -> SurfaceData { SurfaceData { parent: None, children: Vec::new(), - has_role: false, + role: Default::default(), attributes: Default::default(), } } @@ -71,13 +59,13 @@ impl SurfaceData { /// Initialize the user_data of a surface, must be called right when the surface is created pub unsafe fn init(surface: &wl_surface::WlSurface) { surface.set_user_data(Box::into_raw( - Box::new(Mutex::new(SurfaceData::::new())), + Box::new(Mutex::new(SurfaceData::::new())), ) as *mut _) } } -impl SurfaceData { - unsafe fn get_data(surface: &wl_surface::WlSurface) -> &Mutex> { +impl SurfaceData { + unsafe fn get_data(surface: &wl_surface::WlSurface) -> &Mutex> { let ptr = surface.get_user_data(); &*(ptr as *mut _) } @@ -86,7 +74,7 @@ impl SurfaceData { pub unsafe fn cleanup(surface: &wl_surface::WlSurface) { let ptr = surface.get_user_data(); surface.set_user_data(::std::ptr::null_mut()); - let my_data_mutex: Box>> = Box::from_raw(ptr as *mut _); + let my_data_mutex: Box>> = Box::from_raw(ptr as *mut _); let mut my_data = my_data_mutex.into_inner().unwrap(); if let Some(old_parent) = my_data.parent.take() { if !old_parent.equals(surface) { @@ -107,48 +95,85 @@ impl SurfaceData { child_guard.parent = None; } } +} - /// Retrieve the current role status of this surface - pub unsafe fn role_status(surface: &wl_surface::WlSurface) -> RoleStatus { +impl SurfaceData { + pub unsafe fn has_a_role(surface: &wl_surface::WlSurface) -> bool { debug_assert!(surface.status() == Liveness::Alive); let data_mutex = Self::get_data(surface); let data_guard = data_mutex.lock().unwrap(); - match (data_guard.has_role, data_guard.parent.is_some()) { - (true, true) => RoleStatus::Subsurface, - (true, false) => RoleStatus::HasRole, - (false, false) => RoleStatus::NoRole, - (false, true) => unreachable!(), - } + ::has_role(&data_guard.role) + } + + /// Check wether a surface has a given role + pub unsafe fn has_role(surface: &wl_surface::WlSurface) -> bool + where + R: Role, + { + debug_assert!(surface.status() == Liveness::Alive); + let data_mutex = Self::get_data(surface); + let data_guard = data_mutex.lock().unwrap(); + >::has(&data_guard.role) } /// Register that this surface has a role, fails if it already has one - pub unsafe fn give_role(surface: &wl_surface::WlSurface) -> Result<(), ()> { + pub unsafe fn give_role(surface: &wl_surface::WlSurface) -> Result<(), ()> + where + R: Role, + RoleData: Default, + { debug_assert!(surface.status() == Liveness::Alive); let data_mutex = Self::get_data(surface); let mut data_guard = data_mutex.lock().unwrap(); - if data_guard.has_role { - return Err(()); - } - data_guard.has_role = true; - Ok(()) + >::set(&mut data_guard.role) } - /// Register that this surface has no role + /// Register that this surface has a role with given data + /// + /// Fails if it already has one and returns the data + pub unsafe fn give_role_with(surface: &wl_surface::WlSurface, data: RoleData) + -> Result<(), RoleData> + where + R: Role, + { + debug_assert!(surface.status() == Liveness::Alive); + let data_mutex = Self::get_data(surface); + let mut data_guard = data_mutex.lock().unwrap(); + >::set_with(&mut data_guard.role, data) + } + + /// Register that this surface has no role and returns the data /// /// It is a noop if this surface already didn't have one, but fails if /// the role was "subsurface", it must be removed by the `unset_parent` method. - pub unsafe fn remove_role(surface: &wl_surface::WlSurface) -> Result<(), ()> { + pub unsafe fn remove_role(surface: &wl_surface::WlSurface) -> Result + where + R: Role, + { debug_assert!(surface.status() == Liveness::Alive); let data_mutex = Self::get_data(surface); let mut data_guard = data_mutex.lock().unwrap(); - if data_guard.has_role && data_guard.parent.is_some() { - return Err(()); - } - data_guard.has_role = false; - Ok(()) + >::unset(&mut data_guard.role) } + /// Access to the role data + pub unsafe fn with_role_data(surface: &wl_surface::WlSurface, f: F) + -> Result + where + R: Role, + F: FnOnce(&mut RoleData) -> T, + { + debug_assert!(surface.status() == Liveness::Alive); + let data_mutex = Self::get_data(surface); + let mut data_guard = data_mutex.lock().unwrap(); + let data = >::data_mut(&mut data_guard.role)?; + Ok(f(data)) + } +} + +impl> SurfaceData { /// Sets the parent of a surface + /// /// if this surface already has a role, does nothing and fails, otherwise /// its role is now to be a subsurface pub unsafe fn set_parent(child: &wl_surface::WlSurface, parent: &wl_surface::WlSurface) @@ -160,13 +185,10 @@ impl SurfaceData { { let child_mutex = Self::get_data(child); let mut child_guard = child_mutex.lock().unwrap(); - // if surface already has a role, it cannot be a subsurface - if child_guard.has_role { - return Err(()); - } + // if surface already has a role, it cannot become a subsurface + >::set(&mut child_guard.role)?; debug_assert!(child_guard.parent.is_none()); child_guard.parent = Some(parent.clone_unchecked()); - child_guard.has_role = true; } // register child to new parent // double scoping is to be robust to have a child be its own parent @@ -189,7 +211,8 @@ impl SurfaceData { let old_parent = child_guard.parent.take(); if old_parent.is_some() { // We had a parent, so this does not have a role any more - child_guard.has_role = false; + >::unset(&mut child_guard.role) + .expect("Surface had a parent but not the subsurface role?!"); } old_parent }; @@ -266,14 +289,16 @@ impl SurfaceData { parent_guard.children.insert(new_index, me); Ok(()) } +} +impl SurfaceData { /// Access the attributes associated with a surface /// /// Note that an internal lock is taken during access of this data, /// so the tree cannot be manipulated at the same time - pub unsafe fn with_data(surface: &wl_surface::WlSurface, f: F) + pub unsafe fn with_data(surface: &wl_surface::WlSurface, f: F) -> T where - F: FnOnce(&mut SurfaceAttributes), + F: FnOnce(&mut SurfaceAttributes) -> T, { let data_mutex = Self::get_data(surface); let mut data_guard = data_mutex.lock().unwrap(); @@ -290,28 +315,42 @@ impl SurfaceData { /// false will cause an early-stopping. pub unsafe fn map_tree(root: &wl_surface::WlSurface, initial: T, mut f: F) where - F: FnMut(&wl_surface::WlSurface, &mut SurfaceAttributes, &T) -> TraversalAction, + F: FnMut(&wl_surface::WlSurface, + &mut SurfaceAttributes, + &mut R, + &T) + -> TraversalAction, { // helper function for recursion - unsafe fn map(surface: &wl_surface::WlSurface, root: &wl_surface::WlSurface, initial: &T, - f: &mut F) - -> bool + unsafe fn map(surface: &wl_surface::WlSurface, root: &wl_surface::WlSurface, initial: &T, + f: &mut F) + -> bool where - F: FnMut(&wl_surface::WlSurface, &mut SurfaceAttributes, &T) -> TraversalAction, + F: FnMut(&wl_surface::WlSurface, + &mut SurfaceAttributes, + &mut R, + &T) + -> TraversalAction, { // stop if we met the root, so to not deadlock/inifinte loop if surface.equals(root) { return true; } - let data_mutex = SurfaceData::::get_data(surface); + let data_mutex = SurfaceData::::get_data(surface); 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, initial) { + match f( + surface, + &mut data_guard.attributes, + &mut data_guard.role, + initial, + ) { TraversalAction::DoChildren(t) => { // loop over children for c in &data_guard.children { - if !map(c, root, &t, f) { + if !map::(c, root, &t, f) { return false; } } @@ -324,12 +363,18 @@ impl SurfaceData { let data_mutex = Self::get_data(root); let mut data_guard = data_mutex.lock().unwrap(); + let data_guard = &mut *data_guard; // call the callback on ourselves - match f(root, &mut data_guard.attributes, &initial) { + match f( + root, + &mut data_guard.attributes, + &mut data_guard.role, + &initial, + ) { TraversalAction::DoChildren(t) => { // loop over children for c in &data_guard.children { - if !map::(c, root, &t, &mut f) { + if !map::(c, root, &t, &mut f) { break; } } diff --git a/src/lib.rs b/src/lib.rs index 4422bd9..d706968 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -33,7 +33,6 @@ extern crate slog; extern crate slog_stdlog; pub mod backend; - pub mod compositor; pub mod shm; pub mod keyboard;