From 13d047926499487f969ea25e27d0d497901cdd31 Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Sun, 4 Jun 2017 17:47:37 +0200 Subject: [PATCH] First draft of compositor global. --- .rustfmt.toml | 1 - src/compositor/global.rs | 58 ++++++++ src/compositor/handlers.rs | 278 +++++++++++++++++++++++++++++++++++++ src/compositor/mod.rs | 276 ++++++++++++++++++++++++++++++++++++ src/compositor/region.rs | 42 ++++++ src/compositor/tree.rs | 252 +++++++++++++++++++++++++++++++++ src/lib.rs | 4 +- 7 files changed, 909 insertions(+), 2 deletions(-) create mode 100644 src/compositor/global.rs create mode 100644 src/compositor/handlers.rs create mode 100644 src/compositor/mod.rs create mode 100644 src/compositor/region.rs create mode 100644 src/compositor/tree.rs diff --git a/.rustfmt.toml b/.rustfmt.toml index 82fb65d..0409b3d 100644 --- a/.rustfmt.toml +++ b/.rustfmt.toml @@ -6,6 +6,5 @@ reorder_imports = true reorder_imported_names = true report_todo = "Never" report_fixme = "Never" -normalize_comments = true use_try_shorthand = true max_width = 110 diff --git a/src/compositor/global.rs b/src/compositor/global.rs new file mode 100644 index 0000000..96864bf --- /dev/null +++ b/src/compositor/global.rs @@ -0,0 +1,58 @@ +use super::CompositorToken; +use super::handlers::CompositorHandler; + +use wayland_server::{Client, EventLoopHandle, GlobalHandler, Init}; +use wayland_server::protocol::{wl_compositor, wl_subcompositor}; + +pub struct CompositorGlobal { + handler_id: Option, + log: ::slog::Logger, + _data: ::std::marker::PhantomData<*mut U>, +} + +impl CompositorGlobal { + pub fn new(logger: L) -> CompositorGlobal + where L: Into> + { + let log = ::slog_or_stdlog(logger); + CompositorGlobal { + handler_id: None, + log: log.new(o!("smithay_module" => "wompositor_handler")), + _data: ::std::marker::PhantomData, + } + } + + pub fn get_token(&self) -> CompositorToken { + super::make_token(self.handler_id + .expect("CompositorGlobal was not initialized.")) + } +} + +impl Init for CompositorGlobal + where U: Send + Sync + 'static +{ + fn init(&mut self, evlh: &mut EventLoopHandle, _index: usize) { + let id = evlh.add_handler_with_init(CompositorHandler::::new(self.log.clone())); + self.handler_id = Some(id); + } +} + +impl GlobalHandler for CompositorGlobal + where U: Send + Sync + 'static +{ + fn bind(&mut self, evlh: &mut EventLoopHandle, _: &Client, global: wl_compositor::WlCompositor) { + let hid = self.handler_id + .expect("CompositorGlobal was not initialized."); + evlh.register::<_, CompositorHandler>(&global, hid); + } +} + +impl GlobalHandler for CompositorGlobal + where U: Send + Sync + 'static +{ + fn bind(&mut self, evlh: &mut EventLoopHandle, _: &Client, global: wl_subcompositor::WlSubcompositor) { + let hid = self.handler_id + .expect("CompositorGlobal was not initialized."); + evlh.register::<_, CompositorHandler>(&global, hid); + } +} diff --git a/src/compositor/handlers.rs b/src/compositor/handlers.rs new file mode 100644 index 0000000..e8b017b --- /dev/null +++ b/src/compositor/handlers.rs @@ -0,0 +1,278 @@ +use super::{Rectangle, RectangleKind, SubsurfaceAttributes, Damage}; +use super::region::RegionData; +use super::tree::SurfaceData; +use wayland_server::{Client, Destroy, EventLoopHandle, Init, Resource}; +use wayland_server::protocol::{wl_buffer, wl_callback, wl_compositor, wl_output, wl_region, + wl_subcompositor, wl_subsurface, wl_surface}; + +pub struct CompositorHandler { + my_id: usize, + log: ::slog::Logger, + _data: ::std::marker::PhantomData, +} + +struct CompositorDestructor { + _t: ::std::marker::PhantomData, +} + +impl Init for CompositorHandler { + fn init(&mut self, _evqh: &mut EventLoopHandle, index: usize) { + self.my_id = index; + debug!(self.log, "Init finished") + } +} + +impl CompositorHandler { + pub fn new(log: ::slog::Logger) -> CompositorHandler { + CompositorHandler { + my_id: ::std::usize::MAX, + log: log, + _data: ::std::marker::PhantomData::, + } + } +} + +/* + * wl_compositor + */ + +impl wl_compositor::Handler for CompositorHandler { + fn create_surface(&mut self, evqh: &mut EventLoopHandle, _: &Client, + _: &wl_compositor::WlCompositor, id: wl_surface::WlSurface) { + 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) { + unsafe { RegionData::init(&id) }; + evqh.register_with_destructor::<_, CompositorHandler, CompositorDestructor>(&id, self.my_id); + } +} + +unsafe impl ::wayland_server::Handler + for CompositorHandler { + unsafe fn message(&mut self, evq: &mut EventLoopHandle, client: &Client, + resource: &wl_compositor::WlCompositor, opcode: u32, + args: *const ::wayland_server::sys::wl_argument) + -> Result<(), ()> { + as ::wayland_server::protocol::wl_compositor::Handler>::__message(self, evq, client, resource, opcode, args) + } +} + +/* + * wl_surface + */ + +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) { + unsafe { + SurfaceData::::with_data(surface, + |d| d.buffer = Some(buffer.map(|b| (b.clone_unchecked(), (x, y))))); + } + } + fn damage(&mut self, _: &mut EventLoopHandle, _: &Client, surface: &wl_surface::WlSurface, x: i32, + y: i32, width: i32, height: i32) { + unsafe { + SurfaceData::::with_data(surface, + |d| d.damage = Damage::Surface(Rectangle { x, y, width, height })); + } + } + fn frame(&mut self, _: &mut EventLoopHandle, _: &Client, surface: &wl_surface::WlSurface, + callback: wl_callback::WlCallback) { + unimplemented!() + } + fn set_opaque_region(&mut self, _: &mut EventLoopHandle, _: &Client, surface: &wl_surface::WlSurface, + region: Option<&wl_region::WlRegion>) { + unsafe { + let attributes = region.map(|r| RegionData::get_attributes(r)); + SurfaceData::::with_data(surface, |d| d.opaque_region = attributes); + } + } + fn set_input_region(&mut self, _: &mut EventLoopHandle, _: &Client, surface: &wl_surface::WlSurface, + region: Option<&wl_region::WlRegion>) { + unsafe { + let attributes = region.map(|r| RegionData::get_attributes(r)); + SurfaceData::::with_data(surface, |d| d.input_region = attributes); + } + } + fn commit(&mut self, _: &mut EventLoopHandle, _: &Client, surface: &wl_surface::WlSurface) { + unimplemented!() + } + fn set_buffer_transform(&mut self, _: &mut EventLoopHandle, _: &Client, + surface: &wl_surface::WlSurface, transform: wl_output::Transform) { + unsafe { + SurfaceData::::with_data(surface, |d| d.buffer_transform = transform); + } + } + fn set_buffer_scale(&mut self, _: &mut EventLoopHandle, _: &Client, surface: &wl_surface::WlSurface, + scale: i32) { + unsafe { + SurfaceData::::with_data(surface, |d| d.buffer_scale = scale); + } + } + fn damage_buffer(&mut self, _: &mut EventLoopHandle, _: &Client, surface: &wl_surface::WlSurface, + x: i32, y: i32, width: i32, height: i32) { + unsafe { + SurfaceData::::with_data(surface, + |d| d.damage = Damage::Buffer(Rectangle { x, y, width, height })); + } + } +} + +unsafe impl ::wayland_server::Handler for CompositorHandler { + unsafe fn message(&mut self, evq: &mut EventLoopHandle, client: &Client, + resource: &wl_surface::WlSurface, opcode: u32, + args: *const ::wayland_server::sys::wl_argument) + -> Result<(), ()> { + as ::wayland_server::protocol::wl_surface::Handler>::__message(self, evq, client, resource, opcode, args) + } +} + +impl Destroy for CompositorDestructor { + fn destroy(surface: &wl_surface::WlSurface) { + unsafe { SurfaceData::::cleanup(surface) } + } +} + +/* + * wl_region + */ + +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) { + unsafe { + RegionData::add_rectangle(region, RectangleKind::Add, + Rectangle { + x, + y, + width, + height, + }) + }; + } + fn subtract(&mut self, _: &mut EventLoopHandle, _: &Client, region: &wl_region::WlRegion, x: i32, + y: i32, width: i32, height: i32) { + unsafe { + RegionData::add_rectangle(region, RectangleKind::Subtract, + Rectangle { + x, + y, + width, + height, + }) + }; + } +} + +unsafe impl ::wayland_server::Handler for CompositorHandler { + unsafe fn message(&mut self, evq: &mut EventLoopHandle, client: &Client, + resource: &wl_region::WlRegion, opcode: u32, + args: *const ::wayland_server::sys::wl_argument) + -> Result<(), ()> { + as ::wayland_server::protocol::wl_region::Handler>::__message(self, evq, client, resource, opcode, args) + } +} + +impl Destroy for CompositorDestructor { + fn destroy(region: &wl_region::WlRegion) { + unsafe { RegionData::cleanup(region) }; + } +} + +/* + * wl_subcompositor + */ + +impl wl_subcompositor::Handler for CompositorHandler { + 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) { + 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); + } +} + +unsafe impl ::wayland_server::Handler + for CompositorHandler { + unsafe fn message(&mut self, evq: &mut EventLoopHandle, client: &Client, + resource: &wl_subcompositor::WlSubcompositor, opcode: u32, + args: *const ::wayland_server::sys::wl_argument) + -> Result<(), ()> { + as ::wayland_server::protocol::wl_subcompositor::Handler>::__message(self, evq, client, resource, opcode, args) + } +} + +/* + * wl_subsurface + */ + +unsafe fn with_subsurface_attributes(subsurface: &wl_subsurface::WlSubsurface, f: F) + where F: FnOnce(&mut SubsurfaceAttributes) +{ + 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())); +} + +impl wl_subsurface::Handler for CompositorHandler { + fn set_position(&mut self, _: &mut EventLoopHandle, _: &Client, + resource: &wl_subsurface::WlSubsurface, x: i32, y: i32) { + unsafe { + with_subsurface_attributes::(resource, |attrs| { + attrs.x = x; + attrs.y = y; + }); + } + } + fn place_above(&mut self, _: &mut EventLoopHandle, _: &Client, + resource: &wl_subsurface::WlSubsurface, sibling: &wl_surface::WlSurface) { + unimplemented!() + } + fn place_below(&mut self, _: &mut EventLoopHandle, _: &Client, + resource: &wl_subsurface::WlSubsurface, sibling: &wl_surface::WlSurface) { + unimplemented!() + } + fn set_sync(&mut self, _: &mut EventLoopHandle, _: &Client, + resource: &wl_subsurface::WlSubsurface) { + unsafe { + with_subsurface_attributes::(resource, |attrs| { attrs.sync = true; }); + } + } + fn set_desync(&mut self, _: &mut EventLoopHandle, _: &Client, + resource: &wl_subsurface::WlSubsurface) { + unsafe { + with_subsurface_attributes::(resource, |attrs| { attrs.sync = false; }); + } + } +} + +unsafe impl ::wayland_server::Handler for CompositorHandler { + unsafe fn message(&mut self, evq: &mut EventLoopHandle, client: &Client, + resource: &wl_subsurface::WlSubsurface, opcode: u32, + args: *const ::wayland_server::sys::wl_argument) + -> Result<(), ()> { + as ::wayland_server::protocol::wl_subsurface::Handler>::__message(self, evq, client, resource, opcode, args) + } +} + +impl Destroy for CompositorDestructor { + 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); + SurfaceData::::with_data(&*surface, |d| d.subsurface_attributes = None); + SurfaceData::::unset_parent(&surface); + } + } +} diff --git a/src/compositor/mod.rs b/src/compositor/mod.rs new file mode 100644 index 0000000..064afbd --- /dev/null +++ b/src/compositor/mod.rs @@ -0,0 +1,276 @@ +mod global; +mod handlers; +mod tree; +mod region; + +pub use self::global::CompositorGlobal; +use self::handlers::CompositorHandler; +pub use self::tree::RoleStatus; +use self::tree::SurfaceData; + +use wayland_server::protocol::{wl_buffer, wl_output, wl_surface}; +use wayland_server::resource_is_registered; + +/// Description of which part of a surface +/// should be considered damaged and needs to be redrawn +pub enum Damage { + /// The whole surface must be considered damaged (this is the default) + Full, + /// A rectangle containing the damaged zone, in surface coordinates + Surface(Rectangle), + /// A rectangle containing the smaazed zone, in buffer coordinates + /// + /// Note: Buffer scaling must be taken into consideration + Buffer(Rectangle) +} + +/// Data associated with a surface, aggreged by the handlers +/// +/// Most of the fields of this struct represent a double-buffered state, which +/// should only be applied once a `commit` request is received from the surface. +/// +/// You are responsible for setting those values as you see fit to avoid +/// processing them two times. +pub struct SurfaceAttributes { + /// Buffer defining the contents of the surface + /// + /// The tuple represent the coordinates of this buffer + /// relative to the location of the current buffer. + /// + /// If set to `Some(None)`, it means the user specifically asked for the + /// surface to be unmapped. + /// + /// You are free to set this field to `None` to avoid processing it several + /// times. It'll be set to `Some(...)` if the user attaches a buffer (or NULL) to + /// the surface. + pub buffer: Option>, + /// Scale of the contents of the buffer, for higher-resolution contents. + /// + /// If it matches the one of the output displaying this surface, no change + /// is necessary. + pub buffer_scale: i32, + /// Transform under which interpret the contents of the buffer + /// + /// If it matches the one of the output displaying this surface, no change + /// is necessary. + pub buffer_transform: wl_output::Transform, + /// Region of the surface that is guaranteed to be opaque + /// + /// By default the whole surface is potentially transparent + pub opaque_region: Option, + /// Region of the surface that is sensitive to user input + /// + /// By default the whole surface should be sensitive + pub input_region: Option, + /// Damage rectangle + /// + /// 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. + pub user_data: U, +} + +impl Default for SurfaceAttributes { + fn default() -> SurfaceAttributes { + SurfaceAttributes { + buffer: None, + buffer_scale: 1, + buffer_transform: wl_output::Transform::Normal, + 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 { + /// Horizontal location of the top-left corner of this sub-surface relative to + /// the top-left corner of its parent + pub x: i32, + /// Vertical location of the top-left corner of this sub-surface relative to + /// the top-left corner of its parent + pub y: i32, + /// Sync status of this sub-surface + /// + /// If `true`, this surface should be repainted synchronously with its parent + /// if `false`, it should be considered independant of its parent regarding + /// repaint timings. + pub sync: bool, +} + +impl Default for SubsurfaceAttributes { + fn default() -> SubsurfaceAttributes { + SubsurfaceAttributes { + x: 0, + y: 0, + sync: true, + } + } +} + +/// Kind of a rectangle part of a region +#[derive(Copy,Clone)] +pub enum RectangleKind { + /// This rectangle should be added to the region + Add, + /// The intersection of this rectangle with the region should + /// be removed from the region + Subtract, +} + +/// A rectangle defined by its top-left corner and dimensions +#[derive(Copy,Clone)] +pub struct Rectangle { + /// horizontal position of the top-leftcorner of the rectangle, in surface coordinates + pub x: i32, + /// vertical position of the top-leftcorner of the rectangle, in surface coordinates + pub y: i32, + /// width of the rectangle + pub width: i32, + /// height of the rectangle + pub height: i32, +} + +/// Description of the contents of a region +/// +/// A region is defined as an union and difference of 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)] +pub struct RegionAttributes { + /// List of rectangle part of this region + pub rects: Vec<(RectangleKind, Rectangle)>, +} + +impl Default for RegionAttributes { + fn default() -> RegionAttributes { + RegionAttributes { rects: Vec::new() } + } +} + +/// A Compositor global token +/// +/// 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. +#[derive(Copy,Clone)] +pub struct CompositorToken { + hid: usize, + _data: ::std::marker::PhantomData<*mut U>, +} + +fn make_token(hid: usize) -> CompositorToken { + CompositorToken { + hid: hid, + _data: ::std::marker::PhantomData, + } +} + +impl CompositorToken { + /// Access the data of a surface + /// + /// The closure will be called with the contents of the data associated with this surface. + /// + /// 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_data(&self, surface: &wl_surface::WlSurface, f: F) + where F: FnOnce(&mut SurfaceAttributes) + { + assert!(resource_is_registered::<_, CompositorHandler>(surface, self.hid), + "Accessing the data of foreign surfaces is not supported."); + unsafe { + SurfaceData::::with_data(surface, f); + } + } + + /// 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. + /// + /// 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, f: F) -> Result<(), ()> + where F: FnMut(&wl_surface::WlSurface, &mut SurfaceAttributes) -> bool + { + assert!(resource_is_registered::<_, CompositorHandler>(surface, self.hid), + "Accessing the data of foreign surfaces is not supported."); + unsafe { + SurfaceData::::map_tree(surface, f); + } + Ok(()) + } + + /// Retrieve the parent of this surface + /// + /// Returns `None` is this surface is a root surface + /// + /// 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 get_parent(&self, surface: &wl_surface::WlSurface) -> Option { + assert!(resource_is_registered::<_, CompositorHandler>(surface, self.hid), + "Accessing the data of foreign surfaces is not supported."); + unsafe { + SurfaceData::::get_parent(surface) + } + } + + /// Retrieve the role status this surface + /// + /// 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 { + assert!(resource_is_registered::<_, CompositorHandler>(surface, self.hid), + "Accessing the data of foreign surfaces is not supported."); + unsafe { + SurfaceData::::role_status(surface) + } + } + + /// Register that this surface has a role + /// + /// This makes this surface impossible to become a subsurface, as + /// a surface can only have a single role at a time. + /// + /// 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<(),()> { + assert!(resource_is_registered::<_, CompositorHandler>(surface, self.hid), + "Accessing the data of foreign surfaces is not supported."); + unsafe { + SurfaceData::::give_role(surface) + } + } + + /// Register that this surface has no role + /// + /// 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. + /// + /// 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<(),()> { + assert!(resource_is_registered::<_, CompositorHandler>(surface, self.hid), + "Accessing the data of foreign surfaces is not supported."); + unsafe { + SurfaceData::::remove_role(surface) + } + } +} diff --git a/src/compositor/region.rs b/src/compositor/region.rs new file mode 100644 index 0000000..ff9ebd7 --- /dev/null +++ b/src/compositor/region.rs @@ -0,0 +1,42 @@ +use super::{Rectangle, RegionAttributes, RectangleKind}; + +use std::sync::Mutex; +use wayland_server::Resource; + +use wayland_server::protocol::wl_region; + +#[derive(Default)] +pub struct RegionData { + attributes: RegionAttributes, +} + +impl RegionData { + /// Initialize the user_data of a region, must be called right when the surface is created + pub unsafe fn init(region: &wl_region::WlRegion) { + region.set_user_data(Box::into_raw(Box::new(Mutex::new(RegionData::default()))) as *mut _) + } + + /// Cleans the user_data of that surface, must be called when it is destroyed + pub unsafe fn cleanup(region: &wl_region::WlRegion) { + let ptr = region.get_user_data(); + region.set_user_data(::std::ptr::null_mut()); + let _my_data_mutex: Box> = Box::from_raw(ptr as *mut _); + } + + unsafe fn get_data(region: &wl_region::WlRegion) -> &Mutex { + let ptr = region.get_user_data(); + &*(ptr as *mut _) + } + + pub unsafe fn get_attributes(region: &wl_region::WlRegion) -> RegionAttributes { + let data_mutex = Self::get_data(region); + let data_guard = data_mutex.lock().unwrap(); + data_guard.attributes.clone() + } + + pub unsafe fn add_rectangle(region: &wl_region::WlRegion, kind: RectangleKind, rect: Rectangle) { + let data_mutex = Self::get_data(region); + let mut data_guard = data_mutex.lock().unwrap(); + data_guard.attributes.rects.push((kind, rect)); + } +} diff --git a/src/compositor/tree.rs b/src/compositor/tree.rs new file mode 100644 index 0000000..24f2896 --- /dev/null +++ b/src/compositor/tree.rs @@ -0,0 +1,252 @@ +use super::SurfaceAttributes; +use std::sync::Mutex; + +use wayland_server::{Liveness, Resource}; +use wayland_server::protocol::wl_surface; + +/// Node of a subsurface tree, holding some user specified data type U +/// at each node +/// +/// This type is internal to Smithay, and should not appear in the +/// public API +/// +/// It is a bidirectionnal tree, meaning we can move along it in both +/// direction (top-bottom or bottom-up). We are taking advantage of the +/// 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. +/// +/// All the methods here are unsafe, because they assume the provided wl_surface object +/// is correctly initialized regarding its user_data. +pub struct SurfaceData { + parent: Option, + children: Vec, + has_role: bool, + 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 + Sursurface, + /// This surface has a role other than subsurface + /// + /// It is thus the root of a subsurface tree that will + /// have to be displayed + HasRole, +} + +impl SurfaceData { + fn new() -> SurfaceData { + SurfaceData { + parent: None, + children: Vec::new(), + has_role: false, + attributes: Default::default(), + } + } + + /// 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()))) as *mut _) + } +} + +impl SurfaceData { + unsafe fn get_data(surface: &wl_surface::WlSurface) -> &Mutex> { + let ptr = surface.get_user_data(); + &*(ptr as *mut _) + } + + /// Cleans the user_data of that surface, must be called when it is destroyed + 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 mut my_data = my_data_mutex.into_inner().unwrap(); + if let Some(old_parent) = my_data.parent.take() { + if !old_parent.equals(surface) { + // We had a parent that is not ourselves, lets unregister ourselves from it + let old_parent_mutex = Self::get_data(&old_parent); + let mut old_parent_guard = old_parent_mutex.lock().unwrap(); + old_parent_guard.children.retain(|c| !c.equals(surface)); + } + } + // orphan all our children + for child in &my_data.children { + // don't do anything if this child is ourselves + if child.equals(surface) { + continue; + } + let child_mutex = Self::get_data(child); + let mut child_guard = child_mutex.lock().unwrap(); + child_guard.parent = None; + } + } + + /// Retrieve the current role status of this surface + pub unsafe fn role_status(surface: &wl_surface::WlSurface) -> RoleStatus { + 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::Sursurface, + (true, false) => RoleStatus::HasRole, + (false, false) => RoleStatus::NoRole, + (false, true) => unreachable!(), + } + } + + /// Register that this surface has a role, fails if it already has one + pub unsafe fn give_role(surface: &wl_surface::WlSurface) -> Result<(), ()> { + 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(()) + } + + /// Register that this surface has no role + /// + /// 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<(), ()> { + 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(()) + } + + /// 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) + -> Result<(), ()> { + debug_assert!(child.status() == Liveness::Alive); + debug_assert!(parent.status() == Liveness::Alive); + + // change child's parent + { + 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(()); + } + 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 + { + let parent_mutex = Self::get_data(parent); + let mut parent_guard = parent_mutex.lock().unwrap(); + parent_guard.children.push(child.clone_unchecked()) + } + Ok(()) + } + + /// Remove a pre-existing parent of this child + /// + /// Does nothing if it has no parent + pub unsafe fn unset_parent(child: &wl_surface::WlSurface) { + debug_assert!(child.status() == Liveness::Alive); + let old_parent = { + let child_mutex = Self::get_data(child); + let mut child_guard = child_mutex.lock().unwrap(); + 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; + } + old_parent + }; + // unregister from our parent + if let Some(old_parent) = old_parent { + let parent_mutex = Self::get_data(&old_parent); + let mut parent_guard = parent_mutex.lock().unwrap(); + parent_guard.children.retain(|c| !c.equals(child)); + } + } + + /// Retrieve the parent surface (if any) of this surface + pub unsafe fn get_parent(child: &wl_surface::WlSurface) -> Option { + let child_mutex = Self::get_data(child); + let child_guard = child_mutex.lock().unwrap(); + child_guard.parent.as_ref().map(|p| p.clone_unchecked()) + } + + /// 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) + where F: FnOnce(&mut SurfaceAttributes) + { + let data_mutex = Self::get_data(surface); + let mut data_guard = data_mutex.lock().unwrap(); + f(&mut data_guard.attributes) + } + + /// Access sequentially the attributes associated with a surface tree, + /// in a depth-first order + /// + /// 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 wether the traversal should continue or not. Returning + /// false will cause an early-stopping. + pub unsafe fn map_tree(root: &wl_surface::WlSurface, mut f: F) + where F: FnMut(&wl_surface::WlSurface, &mut SurfaceAttributes) -> bool + { + // helper function for recursion + unsafe fn map(surface: &wl_surface::WlSurface, root: &wl_surface::WlSurface, f: &mut F) -> bool + where F: FnMut(&wl_surface::WlSurface, &mut SurfaceAttributes) -> bool + { + // 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 mut data_guard = data_mutex.lock().unwrap(); + // call the callback on ourselves + if f(surface, &mut data_guard.attributes) { + // loop over children + for c in &data_guard.children { + if !map(c, root, f) { + return false; + } + } + } + true + } + + let data_mutex = Self::get_data(root); + let mut data_guard = data_mutex.lock().unwrap(); + // call the callback on ourselves + if f(root, &mut data_guard.attributes) { + // loop over children + for c in &data_guard.children { + if !map::(c, root, &mut f) { + break; + } + } + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 3bfd660..4422bd9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -32,8 +32,10 @@ extern crate glium; extern crate slog; extern crate slog_stdlog; -pub mod shm; pub mod backend; + +pub mod compositor; +pub mod shm; pub mod keyboard; fn slog_or_stdlog(logger: L) -> ::slog::Logger