diff --git a/.gitignore b/.gitignore index a9d37c5..afc9901 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ target Cargo.lock +*.bk 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/Cargo.toml b/Cargo.toml index 8793c6a..772bdd9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ slog-stdlog = "2.0.0-0.2" libloading = "0.4.0" wayland-client = { version = "~0.8.6", optional = true } winit = { git = "https://github.com/tomaka/winit.git", optional = true } -glium = { version = "~0.16.0", optional = true } +glium = { version = "~0.16.0", optional = true, default-features = false } input = { version = "~0.2.0", optional = true } clippy = { version = "*", optional = true } rental = "0.4.11" @@ -23,7 +23,8 @@ rental = "0.4.11" gl_generator = "0.5" [dev-dependencies] -slog-term = "~1.5" +slog-term = "2.0" +slog-async = "2.0" [features] default = ["backend_winit", "backend_libinput", "renderer_glium"] diff --git a/examples/helpers/glium.rs b/examples/helpers/glium.rs new file mode 100644 index 0000000..23a8278 --- /dev/null +++ b/examples/helpers/glium.rs @@ -0,0 +1,127 @@ +use glium; +use glium::Surface; +use glium::index::PrimitiveType; + +#[derive(Copy, Clone)] +struct Vertex { + position: [f32; 2], + tex_coords: [f32; 2], +} + +implement_vertex!(Vertex, position, tex_coords); + +pub struct GliumDrawer<'a, F: 'a> { + display: &'a F, + vertex_buffer: glium::VertexBuffer, + index_buffer: glium::IndexBuffer, + program: glium::Program, +} + +impl<'a, F: glium::backend::Facade + 'a> GliumDrawer<'a, F> { + pub fn new(display: &'a F) -> GliumDrawer<'a, F> { + + // building the vertex buffer, which contains all the vertices that we will draw + let vertex_buffer = glium::VertexBuffer::new( + display, + &[ + Vertex { + position: [0.0, 0.0], + tex_coords: [0.0, 0.0], + }, + Vertex { + position: [0.0, 1.0], + tex_coords: [0.0, 1.0], + }, + Vertex { + position: [1.0, 1.0], + tex_coords: [1.0, 1.0], + }, + Vertex { + position: [1.0, 0.0], + tex_coords: [1.0, 0.0], + }, + ], + ).unwrap(); + + // building the index buffer + let index_buffer = + glium::IndexBuffer::new(display, PrimitiveType::TriangleStrip, &[1 as u16, 2, 0, 3]).unwrap(); + + // compiling shaders and linking them together + let program = program!(display, + 100 => { + vertex: " + #version 100 + uniform lowp mat4 matrix; + attribute lowp vec2 position; + attribute lowp vec2 tex_coords; + varying lowp vec2 v_tex_coords; + void main() { + gl_Position = matrix * vec4(position, 0.0, 1.0); + v_tex_coords = tex_coords; + } + ", + + fragment: " + #version 100 + uniform lowp sampler2D tex; + varying lowp vec2 v_tex_coords; + void main() { + lowp vec4 color = texture2D(tex, v_tex_coords); + gl_FragColor.r = color.z; + gl_FragColor.g = color.y; + gl_FragColor.b = color.x; + gl_FragColor.a = color.w; + } + ", + }, + ).unwrap(); + + GliumDrawer { + display, + vertex_buffer, + index_buffer, + program, + } + } + + pub fn draw(&self, target: &mut glium::Frame, contents: &[u8], surface_dimensions: (u32, u32), + surface_location: (i32, i32), screen_size: (u32, u32)) { + + let image = glium::texture::RawImage2d { + data: contents.into(), + width: surface_dimensions.0, + height: surface_dimensions.1, + format: glium::texture::ClientFormat::U8U8U8U8, + }; + let opengl_texture = glium::texture::Texture2d::new(self.display, image).unwrap(); + + let xscale = 2.0 * (surface_dimensions.0 as f32) / (screen_size.0 as f32); + let yscale = -2.0 * (surface_dimensions.1 as f32) / (screen_size.1 as f32); + + let x = 2.0 * (surface_location.0 as f32) / (screen_size.0 as f32) - 1.0; + let y = 1.0 - 2.0 * (surface_location.1 as f32) / (screen_size.1 as f32); + + let uniforms = + uniform! { + matrix: [ + [xscale, 0.0 , 0.0, 0.0], + [ 0.0 , yscale , 0.0, 0.0], + [ 0.0 , 0.0 , 1.0, 0.0], + [ x , y , 0.0, 1.0] + ], + tex: &opengl_texture + }; + + target + .draw( + &self.vertex_buffer, + &self.index_buffer, + &self.program, + &uniforms, + &Default::default(), + ) + .unwrap(); + + } +} diff --git a/examples/helpers/mod.rs b/examples/helpers/mod.rs new file mode 100644 index 0000000..e913100 --- /dev/null +++ b/examples/helpers/mod.rs @@ -0,0 +1,5 @@ +mod shell; +mod glium; + +pub use self::glium::GliumDrawer; +pub use self::shell::WlShellStubHandler; diff --git a/examples/helpers/shell.rs b/examples/helpers/shell.rs new file mode 100644 index 0000000..6e56466 --- /dev/null +++ b/examples/helpers/shell.rs @@ -0,0 +1,84 @@ + + +use smithay::compositor::{CompositorToken, Handler as CompositorHandler}; +use wayland_server::{Client, EventLoopHandle, GlobalHandler, Init, Resource}; +use wayland_server::protocol::{wl_shell, wl_shell_surface, wl_surface}; + +/// A very basic handler for wl_shell +/// +/// All it does is track which wl_shell_surface exist and which do not, +/// as well as the roles associated to them. +/// +/// That's it. +pub struct WlShellStubHandler { + my_id: Option, + token: CompositorToken, + surfaces: Vec<(wl_shell_surface::WlShellSurface, wl_surface::WlSurface)>, +} + +impl WlShellStubHandler { + pub fn new(compositor_token: CompositorToken) -> WlShellStubHandler { + WlShellStubHandler { + my_id: None, + token: compositor_token, + surfaces: Vec::new(), + } + } + + pub fn surfaces(&self) -> &[(wl_shell_surface::WlShellSurface, wl_surface::WlSurface)] { + &self.surfaces + } +} + +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 +{ + 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.")); + } +} + +impl wl_shell::Handler for WlShellStubHandler +where + U: 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, + surface: &wl_surface::WlSurface) { + let surface = surface.clone().expect( + "WlShellStubHandler can only manage surfaces managed by Smithay's CompositorHandler.", + ); + 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, + "Surface already has a role.".into(), + ); + return; + } + evqh.register::<_, Self>(&id, self.my_id.unwrap()); + self.surfaces.push((id, surface)) + } +} + +server_declare_handler!(WlShellStubHandler, Send]>, wl_shell::Handler, wl_shell::WlShell); + +impl wl_shell_surface::Handler for WlShellStubHandler +where + U: Send + 'static, + H: CompositorHandler + Send + 'static, +{ +} + +server_declare_handler!(WlShellStubHandler, Send]>, wl_shell_surface::Handler, wl_shell_surface::WlShellSurface); diff --git a/examples/simple.rs b/examples/simple.rs index ef264ef..906f34b 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -1,47 +1,185 @@ +#[macro_use(server_declare_handler)] extern crate wayland_server; extern crate smithay; +#[macro_use] extern crate glium; +#[macro_use] +extern crate slog; +extern crate slog_async; +extern crate slog_term; + +mod helpers; + use glium::Surface; + +use helpers::{GliumDrawer, WlShellStubHandler}; +use slog::*; + use smithay::backend::graphics::glium::IntoGlium; use smithay::backend::input::InputBackend; use smithay::backend::winit; -use smithay::shm::ShmGlobal; -use wayland_server::protocol::wl_shm; +use smithay::compositor::{self, CompositorHandler, CompositorToken, TraversalAction}; +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}; + +struct SurfaceHandler { + shm_token: ShmToken, +} + +#[derive(Default)] +struct SurfaceData { + buffer: Option<(Vec, (u32, u32))>, +} + +impl compositor::Handler for SurfaceHandler { + fn commit(&mut self, evlh: &mut EventLoopHandle, client: &Client, surface: &wl_surface::WlSurface, + token: CompositorToken) { + // we retrieve the contents of the associated buffer and copy it + token.with_surface_data(surface, |attributes| { + match attributes.buffer.take() { + Some(Some((buffer, (x, y)))) => { + self.shm_token.with_buffer_contents(&buffer, |slice, data| { + let offset = data.offset as usize; + let stride = data.stride as usize; + let width = data.width as usize; + let height = data.height as usize; + let mut new_vec = Vec::with_capacity(width * height * 4); + for i in 0..height { + new_vec.extend( + &slice[(offset + i * stride)..(offset + i * stride + width * 4)], + ); + } + attributes.user_data.buffer = + Some((new_vec, (data.width as u32, data.height as u32))); + }); + + } + Some(None) => { + // erase the contents + attributes.user_data.buffer = None; + } + None => {} + } + }); + } +} fn main() { + // A logger facility, here we use the terminal for this example + let log = Logger::root( + slog_async::Async::default(slog_term::term_full().fuse()).fuse(), + o!(), + ); + // Initialize a simple backend for testing - let (renderer, mut input) = winit::init().unwrap(); + let (renderer, mut input) = winit::init(log.clone()).unwrap(); - let (_display, mut event_loop) = wayland_server::create_display(); + let (mut display, mut event_loop) = wayland_server::create_display(); + /* + * Initialize wl_shm global + */ // Insert the ShmGlobal as a handler to your event loop // Here, we specify tha the standard Argb8888 and Xrgb8888 is the only supported. - let handler_id = event_loop.add_handler_with_init(ShmGlobal::new( - vec![], - None, /* we don't provide a logger here */ - )); - + let shm_handler_id = event_loop.add_handler_with_init(ShmGlobal::new(vec![], log.clone())); // Register this handler to advertise a wl_shm global of version 1 - let shm_global = event_loop.register_global::(handler_id, 1); - - // Retrieve the shm token for later use to access the buffers + event_loop.register_global::(shm_handler_id, 1); + // retreive the token let shm_token = { let state = event_loop.state(); - state.get_handler::(handler_id).get_token() + state.get_handler::(shm_handler_id).get_token() }; - // Init glium + + /* + * Initialize the compositor global + */ + let compositor_handler_id = event_loop.add_handler_with_init(CompositorHandler::::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, 1); + // retrieve the tokens + let compositor_token = { + let state = event_loop.state(); + state + .get_handler::>(compositor_handler_id) + .get_token() + }; + + /* + * Initialize the shell stub global + */ + let shell_handler_id = + event_loop.add_handler_with_init(WlShellStubHandler::new(compositor_token.clone())); + event_loop.register_global::>( + shell_handler_id, + 1, + ); + + /* + * Initialize glium + */ let context = renderer.into_glium(); + let drawer = GliumDrawer::new(&context); + + /* + * Add a listening socket: + */ + let name = display.add_socket_auto().unwrap().into_string().unwrap(); + println!("Listening on socket: {}", name); loop { input.dispatch_new_events().unwrap(); let mut frame = context.draw(); - frame.clear(None, Some((0.0, 0.0, 0.0, 1.0)), false, None, None); + frame.clear(None, Some((0.8, 0.8, 0.9, 1.0)), false, None, None); + // redraw the frame, in a simple but inneficient way + { + let screen_dimensions = context.get_framebuffer_dimensions(); + let state = event_loop.state(); + for &(_, ref surface) in + state + .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 + } + }, + ); + } + } frame.finish().unwrap(); event_loop.dispatch(Some(16)).unwrap(); + display.flush_clients(); } } diff --git a/src/backend/graphics/glium.rs b/src/backend/graphics/glium.rs index a3629de..dacfb51 100644 --- a/src/backend/graphics/glium.rs +++ b/src/backend/graphics/glium.rs @@ -3,7 +3,7 @@ use backend::graphics::egl::{EGLGraphicsBackend, SwapBuffersError}; use glium::Frame; use glium::SwapBuffersError as GliumSwapBuffersError; -use glium::backend::{Backend, Context}; +use glium::backend::{Backend, Context, Facade}; use glium::debug::DebugCallbackBehavior; use std::ops::Deref; use std::os::raw::c_void; @@ -63,6 +63,12 @@ impl Deref for GliumGraphicsBackend { } } +impl Facade for GliumGraphicsBackend { + fn get_context(&self) -> &Rc { + &self.context + } +} + /// Converter trait to expose `glium` compatibility for all `EGLGraphicsBackend`s pub trait IntoGlium: EGLGraphicsBackend + Sized { /// Wrap the given `EGLGraphicsBackend` to a `GliumGraphicBackend` diff --git a/src/backend/winit.rs b/src/backend/winit.rs index e54ceb8..9f72f11 100644 --- a/src/backend/winit.rs +++ b/src/backend/winit.rs @@ -763,6 +763,7 @@ impl InputBackend for WinitInputBackend { *time_counter += 1; } Event::DeviceEvent { .. } => {} + _ => {} }); } diff --git a/src/compositor/global.rs b/src/compositor/global.rs new file mode 100644 index 0000000..cf744ca --- /dev/null +++ b/src/compositor/global.rs @@ -0,0 +1,25 @@ +use super::{CompositorHandler, Handler as UserHandler}; + +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 +{ + 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); + } +} + +impl GlobalHandler for CompositorHandler +where + U: 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); + } +} diff --git a/src/compositor/handlers.rs b/src/compositor/handlers.rs new file mode 100644 index 0000000..e418ddc --- /dev/null +++ b/src/compositor/handlers.rs @@ -0,0 +1,286 @@ +use super::{CompositorHandler, Damage, Handler as UserHandler, Rectangle, RectangleKind, + SubsurfaceAttributes}; +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 { + _t: ::std::marker::PhantomData, +} + +/* + * wl_compositor + */ + +impl wl_compositor::Handler for CompositorHandler +where + U: 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); + } + 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); + } +} + +server_declare_handler!(CompositorHandler, Send]>, wl_compositor::Handler, wl_compositor::WlCompositor); + +/* + * 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) { + trace!(self.log, "Attaching buffer to surface."); + 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) { + trace!(self.log, "Registering damage to surface."); + unsafe { + SurfaceData::::with_data(surface, |d| { + d.damage = Damage::Surface(Rectangle { + x, + y, + width, + height, + }) + }); + } + } + fn frame(&mut self, evlh: &mut EventLoopHandle, client: &Client, surface: &wl_surface::WlSurface, + callback: wl_callback::WlCallback) { + trace!(self.log, "Frame surface callback."); + let token = self.get_token(); + UserHandler::frame(&mut self.handler, evlh, client, surface, callback, token); + } + fn set_opaque_region(&mut self, _: &mut EventLoopHandle, _: &Client, surface: &wl_surface::WlSurface, + region: Option<&wl_region::WlRegion>) { + 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); + } + } + fn set_input_region(&mut self, _: &mut EventLoopHandle, _: &Client, surface: &wl_surface::WlSurface, + region: Option<&wl_region::WlRegion>) { + 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); + } + } + fn commit(&mut self, evlh: &mut EventLoopHandle, client: &Client, surface: &wl_surface::WlSurface) { + trace!(self.log, "Commit surface callback."); + let token = self.get_token(); + UserHandler::commit(&mut self.handler, evlh, client, surface, token); + } + fn set_buffer_transform(&mut self, _: &mut EventLoopHandle, _: &Client, + 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); + } + } + 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); + } + } + fn damage_buffer(&mut self, _: &mut EventLoopHandle, _: &Client, surface: &wl_surface::WlSurface, + x: i32, y: i32, width: i32, height: i32) { + trace!( + self.log, + "Registering damage to surface (buffer coordinates)." + ); + unsafe { + SurfaceData::::with_data(surface, |d| { + d.damage = Damage::Buffer(Rectangle { + x, + y, + width, + height, + }) + }); + } + } +} + +server_declare_handler!(CompositorHandler]>, wl_surface::Handler, wl_surface::WlSurface); + +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) { + trace!(self.log, "Adding rectangle to a region."); + 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) { + trace!(self.log, "Subtracting rectangle to a region."); + unsafe { + RegionData::add_rectangle( + region, + RectangleKind::Subtract, + Rectangle { + x, + y, + width, + height, + }, + ) + }; + } +} + +server_declare_handler!(CompositorHandler, wl_region::Handler, wl_region::WlRegion); + +impl Destroy for CompositorDestructor { + fn destroy(region: &wl_region::WlRegion) { + unsafe { RegionData::cleanup(region) }; + } +} + +/* + * wl_subcompositor + */ + +impl wl_subcompositor::Handler for CompositorHandler +where + U: 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 + } + 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); + } +} + +server_declare_handler!(CompositorHandler, wl_subcompositor::Handler, wl_subcompositor::WlSubcompositor); + +/* + * 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, + subsurface: &wl_subsurface::WlSubsurface, x: i32, y: i32) { + trace!(self.log, "Setting subsurface position."); + unsafe { + with_subsurface_attributes::(subsurface, |attrs| { + attrs.x = x; + attrs.y = y; + }); + } + } + fn place_above(&mut self, _: &mut EventLoopHandle, _: &Client, + subsurface: &wl_subsurface::WlSubsurface, sibling: &wl_surface::WlSurface) { + trace!(self.log, "Setting subsurface above an other."); + unsafe { + let ptr = subsurface.get_user_data(); + let surface = &*(ptr as *mut wl_surface::WlSurface); + 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()); + } + } + } + fn place_below(&mut self, _: &mut EventLoopHandle, _: &Client, + subsurface: &wl_subsurface::WlSubsurface, sibling: &wl_surface::WlSurface) { + trace!(self.log, "Setting subsurface below an other."); + unsafe { + let ptr = subsurface.get_user_data(); + let surface = &*(ptr as *mut wl_surface::WlSurface); + 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()); + } + } + } + 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; }); + } + } + 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; }); + } + } +} + +server_declare_handler!(CompositorHandler, wl_subsurface::Handler, wl_subsurface::WlSubsurface); + +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); + if surface.status() == Liveness::Alive { + 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..c48d45f --- /dev/null +++ b/src/compositor/mod.rs @@ -0,0 +1,490 @@ +//! Utilities for handling surfaces, subsurfaces and regions +//! +//! This module provides the `CompositorHandler` type, with implements +//! automatic handling of sufaces, subsurfaces and region wayland objects, +//! by being registered as a global handler for `wl_compositor` and +//! `wl_subcompositor`. +//! +//! ## Why use this handler +//! +//! This handler does a simple job: it stores in a coherent way the state of +//! surface trees with subsurfaces, to provide you a direct access to the tree +//! structure and all surface metadata. +//! +//! As such, you can, given a root surface with a role requiring it to be displayed, +//! you can iterate over the whole tree of subsurfaces to recover all the metadata you +//! need to display the subsurface tree. +//! +//! This handler will not do anything more than present you the metadata specified by the +//! client in a coherent and practical way. All the logic regarding to drawing itself, and +//! the positionning of windows (surface trees) one relative to another is out of its scope. +//! +//! ## How to use it +//! +//! ### Initialization +//! +//! To initialize this handler, simply instanciate it and register it to the event loop +//! as a global handler for wl_compositor and wl_subcompositor: +//! +//! ``` +//! # extern crate wayland_server; +//! # extern crate smithay; +//! use wayland_server::protocol::wl_compositor::WlCompositor; +//! use wayland_server::protocol::wl_subcompositor::WlSubcompositor; +//! use smithay::compositor; +//! +//! // Define some user data to be associated with the surfaces. +//! // It must implement the Default trait, which will represent the state of a surface which +//! // has just been created. +//! #[derive(Default)] +//! struct MyData { +//! // whatever you need here +//! } +//! +//! // 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 { +//! // 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; +//! +//! # fn main() { +//! # let (_display, mut event_loop) = wayland_server::create_display(); +//! +//! // Instanciate the CompositorHandler and give it to the event loop +//! let compositor_hid = event_loop.add_handler_with_init( +//! MyCompositorHandler::new(MyHandler{ /* ... */ }, None /* put a logger here */) +//! ); +//! +//! // Register it as a handler for wl_compositor +//! event_loop.register_global::(compositor_hid, 4); +//! +//! // Register it as a handler for wl_subcompositor +//! event_loop.register_global::(compositor_hid, 1); +//! +//! // retrieve the token needed to access the surfaces' metadata +//! let compositor_token = { +//! let state = event_loop.state(); +//! state.get_handler::(compositor_hid).get_token() +//! }; +//! +//! // You're now ready to go! +//! # } +//! ``` +//! +//! ### Use the surface metadata +//! +//! As you can see in the previous example, in the end we are retrieving a token from +//! the `CompositorHandler`. This token is necessary to retrieve the metadata associated with +//! a surface. It can be cloned, and is sendable accross threads. See `CompositorToken` for +//! the details of what it enables you. +//! +//! The surface metadata is held in the `SurfaceAttributes` struct. In contains double-buffered +//! 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. + +mod global; +mod handlers; +mod tree; +mod region; + +use self::region::RegionData; +pub use self::tree::{RoleStatus, TraversalAction}; +use self::tree::SurfaceData; +use wayland_server::{Client, EventLoopHandle, Init, resource_is_registered}; + +use wayland_server::protocol::{wl_buffer, wl_callback, wl_output, wl_region, wl_surface}; + +/// 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 damaged 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. +pub struct CompositorToken { + hid: usize, + _data: ::std::marker::PhantomData<*mut U>, + _handler: ::std::marker::PhantomData<*mut H>, +} + +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 { + *self + } +} + +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. + /// + /// 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. This matches the order in which the surfaces are supposed to be + /// drawn: top-most last. + /// + /// 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, + { + assert!( + resource_is_registered::<_, CompositorHandler>(surface, self.hid), + "Accessing the data of foreign surfaces is not supported." + ); + unsafe { + SurfaceData::::map_tree(surface, initial, 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 children of 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 get_children(&self, surface: &wl_surface::WlSurface) -> Vec { + assert!( + resource_is_registered::<_, CompositorHandler>(surface, self.hid), + "Accessing the data of foreign surfaces is not supported." + ); + unsafe { SurfaceData::::get_children(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) } + } + + /// Retrieve the metadata associated with a wl_region + /// + /// If the region is not managed by the CompositorGlobal that provided this token, this + /// 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), + "Accessing the data of foreign regions is not supported." + ); + unsafe { RegionData::get_attributes(region) } + } +} + +/// A struct handling the `wl_compositor` and `wl_subcompositor` globals +/// +/// It allows you to choose a custom `U` type to store data you want +/// associated with the surfaces in their metadata, as well a providing +/// a sub-handler to handle the events defined by the `Handler` trait +/// defined in this module. +/// +/// See the module-level documentation for instructions and examples of use. +pub struct CompositorHandler { + my_id: usize, + log: ::slog::Logger, + handler: H, + _data: ::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 { + /// Create a new CompositorHandler + pub fn new(handler: H, logger: L) -> CompositorHandler + where + L: Into>, + { + let log = ::slog_or_stdlog(logger); + CompositorHandler { + my_id: ::std::usize::MAX, + log: log.new(o!("smithay_module" => "compositor_handler")), + handler: handler, + _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 { + assert!( + self.my_id != ::std::usize::MAX, + "CompositorHandler is not initialized yet." + ); + trace!(self.log, "Creating a compositor token."); + CompositorToken { + hid: self.my_id, + _data: ::std::marker::PhantomData, + _handler: ::std::marker::PhantomData, + } + } + + /// Access the underlying sub-handler + pub fn get_handler(&mut self) -> &mut H { + &mut self.handler + } +} + +/// Sub-handler trait for surface event handling +/// +/// The global provided by Smithay cannot process these events for you, so they +/// 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 { + /// The double-buffered state has been validated by the client + /// + /// At this point, the pending state that has been accumulated in the `SurfaceAttributes` associated + /// to this surface should be integrated into the current state of the surface. + /// + /// 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) { + } + /// The client asks to be notified when would be a good time to update the contents of this surface + /// + /// You must keep the provided `WlCallback` and trigger it at the appropriate time by calling + /// its `done()` method. + /// + /// 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) { + } +} diff --git a/src/compositor/region.rs b/src/compositor/region.rs new file mode 100644 index 0000000..7f56d1c --- /dev/null +++ b/src/compositor/region.rs @@ -0,0 +1,44 @@ +use super::{Rectangle, RectangleKind, RegionAttributes}; + +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..914f076 --- /dev/null +++ b/src/compositor/tree.rs @@ -0,0 +1,340 @@ +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 + 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, +} + +/// Possible actions to do after handling a node diring tree traversal +pub enum TraversalAction { + /// Traverse its children as well, providing them the data T + DoChildren(T), + /// Skip its children + SkipChildren, + /// Stop traversal completely + Break, +} + +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::Subsurface, + (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()) + } + + /// Retrieve the parent surface (if any) of this surface + pub unsafe fn get_children(child: &wl_surface::WlSurface) -> Vec { + let child_mutex = Self::get_data(child); + let child_guard = child_mutex.lock().unwrap(); + child_guard + .children + .iter() + .map(|p| p.clone_unchecked()) + .collect() + } + + /// Reorders a surface relative to one of its sibling + /// + /// Fails if `relative_to` is not a sibling or parent of `surface`. + pub unsafe fn reorder(surface: &wl_surface::WlSurface, to: Location, + relative_to: &wl_surface::WlSurface) + -> Result<(), ()> { + let parent = { + let data_mutex = Self::get_data(surface); + let data_guard = data_mutex.lock().unwrap(); + data_guard + .parent + .as_ref() + .map(|p| p.clone_unchecked()) + .unwrap() + }; + if parent.equals(relative_to) { + // TODO: handle positioning relative to parent + return Ok(()); + } + + fn index_of(surface: &wl_surface::WlSurface, slice: &[wl_surface::WlSurface]) -> Option { + for (i, s) in slice.iter().enumerate() { + if s.equals(surface) { + return Some(i); + } + } + None + } + + let parent_mutex = Self::get_data(&parent); + 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) { + Some(idx) => idx, + None => return Err(()), + }; + let me = parent_guard.children.remove(my_index); + if my_index < other_index { + other_index -= 1; + } + let new_index = match to { + Location::Before => other_index, + Location::After => other_index + 1, + }; + parent_guard.children.insert(new_index, me); + Ok(()) + } + + /// 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, initial: T, mut f: F) + where + F: FnMut(&wl_surface::WlSurface, &mut SurfaceAttributes, &T) -> TraversalAction, + { + // helper function for recursion + 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, + { + // 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 + match f(surface, &mut data_guard.attributes, initial) { + TraversalAction::DoChildren(t) => { + // loop over children + for c in &data_guard.children { + if !map(c, root, &t, f) { + return false; + } + } + true + } + TraversalAction::SkipChildren => true, + TraversalAction::Break => false, + } + } + + let data_mutex = Self::get_data(root); + let mut data_guard = data_mutex.lock().unwrap(); + // call the callback on ourselves + match f(root, &mut data_guard.attributes, &initial) { + TraversalAction::DoChildren(t) => { + // loop over children + for c in &data_guard.children { + if !map::(c, root, &t, &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 diff --git a/src/shm/mod.rs b/src/shm/mod.rs index bf9b873..9dba511 100644 --- a/src/shm/mod.rs +++ b/src/shm/mod.rs @@ -13,7 +13,7 @@ //! //! To use it, first add a `ShmGlobal` to your event loop, specifying the formats //! you want to support (ARGB8888 and XRGB8888 are always considered as supported, -//! as specified by the wayland protocol) and obtain its `ShmGlobalToken`. +//! as specified by the wayland protocol) and obtain its `ShmToken`. //! //! ``` //! extern crate wayland_server; @@ -110,8 +110,8 @@ impl ShmGlobal { /// and has been initialized. If it is not the case, this method will panic. /// /// This is needed to retrieve the contents of the shm pools and buffers. - pub fn get_token(&self) -> ShmGlobalToken { - ShmGlobalToken { hid: self.handler_id.expect("ShmGlobal was not initialized.") } + pub fn get_token(&self) -> ShmToken { + ShmToken { hid: self.handler_id.expect("ShmGlobal was not initialized.") } } } @@ -119,7 +119,8 @@ impl ShmGlobal { /// /// It is needed to access the contents of the buffers & pools managed by the /// associated `ShmGlobal`. -pub struct ShmGlobalToken { +#[derive(Clone)] +pub struct ShmToken { hid: usize, } @@ -136,7 +137,7 @@ pub enum BufferAccessError { BadMap, } -impl ShmGlobalToken { +impl ShmToken { /// Call given closure with the contents of the given buffer /// /// If the buffer is managed by the associated ShmGlobal, its contents are