diff --git a/.gitignore b/.gitignore index afc9901..e63a49c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ target Cargo.lock *.bk +.vscode diff --git a/Cargo.toml b/Cargo.toml index 35127d5..67db4f2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Victor Berger "] license = "MIT" [dependencies] -wayland-server = "0.9.9" +wayland-server = "0.10.1" nix = "0.7.0" xkbcommon = "0.2.1" tempfile = "2.1.5" @@ -18,7 +18,7 @@ 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" -wayland-protocols = { version = "0.9.9", features = ["unstable_protocols", "server"] } +wayland-protocols = { version = "0.10.1", features = ["unstable_protocols", "server"] } [build-dependencies] gl_generator = "0.5" diff --git a/examples/helpers/implementations.rs b/examples/helpers/implementations.rs new file mode 100644 index 0000000..231c3f8 --- /dev/null +++ b/examples/helpers/implementations.rs @@ -0,0 +1,95 @@ +use rand; +use smithay::compositor::{CompositorToken, SurfaceUserImplementation}; +use smithay::shell::{PopupConfigure, ShellSurfaceRole, ShellSurfaceUserImplementation, ToplevelConfigure}; +use smithay::shm::with_buffer_contents; + +define_roles!(Roles => [ ShellSurface, ShellSurfaceRole ] ); + +#[derive(Default)] +pub struct SurfaceData { + pub buffer: Option<(Vec, (u32, u32))>, + pub location: Option<(i32, i32)>, +} + +pub fn surface_implementation() -> SurfaceUserImplementation { + SurfaceUserImplementation { + commit: |_, _, surface, token| { + // 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)))) => { + // we ignore hotspot coordinates in this simple example + 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))); + }).unwrap(); + buffer.release(); + } + Some(None) => { + // erase the contents + attributes.user_data.buffer = None; + } + None => {} + } + }); + }, + frame: |_, _, _, callback, _| { + callback.done(0); + }, + } +} + +pub fn shell_implementation( + ) + -> ShellSurfaceUserImplementation, ()> +{ + ShellSurfaceUserImplementation { + new_client: |_, _, _| {}, + client_pong: |_, _, _| {}, + new_toplevel: |_, token, toplevel| { + let wl_surface = toplevel.get_surface().unwrap(); + token.with_surface_data(wl_surface, |data| { + // place the window at a random location in the [0;300]x[0;300] square + use rand::distributions::{IndependentSample, Range}; + let range = Range::new(0, 300); + let mut rng = rand::thread_rng(); + let x = range.ind_sample(&mut rng); + let y = range.ind_sample(&mut rng); + data.user_data.location = Some((x, y)) + }); + ToplevelConfigure { + size: None, + states: vec![], + serial: 42, + } + }, + new_popup: |_, _, _| { + PopupConfigure { + size: (10, 10), + position: (10, 10), + serial: 42, + } + }, + move_: |_, _, _, _, _| {}, + resize: |_, _, _, _, _, _| {}, + grab: |_, _, _, _, _| {}, + change_display_state: |_, _, _, _, _, _, _| { + ToplevelConfigure { + size: None, + states: vec![], + serial: 42, + } + }, + show_window_menu: |_, _, _, _, _, _, _| {}, + } +} diff --git a/examples/helpers/mod.rs b/examples/helpers/mod.rs index 407fec4..36e6945 100644 --- a/examples/helpers/mod.rs +++ b/examples/helpers/mod.rs @@ -1,3 +1,5 @@ mod glium; +mod implementations; pub use self::glium::GliumDrawer; +pub use self::implementations::*; diff --git a/examples/simple.rs b/examples/simple.rs index 2a48d6b..38ae92a 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -13,151 +13,15 @@ extern crate wayland_server; mod helpers; use glium::Surface; - -use helpers::GliumDrawer; +use helpers::{shell_implementation, surface_implementation, GliumDrawer}; 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, SubsurfaceRole, TraversalAction}; +use smithay::compositor::{compositor_init, SubsurfaceRole, TraversalAction}; use smithay::compositor::roles::Role; -use smithay::shell::{self, PopupConfigure, PopupSurface, ShellClient, ShellHandler, ShellSurfaceRole, - ToplevelConfigure, ToplevelSurface}; -use smithay::shm::{ShmGlobal, ShmToken}; - -use wayland_protocols::unstable::xdg_shell::server::{zxdg_shell_v6, zxdg_toplevel_v6}; - -use wayland_server::{Client, EventLoopHandle}; -use wayland_server::protocol::{wl_callback, wl_compositor, wl_output, wl_seat, wl_shell, wl_shm, - wl_subcompositor, wl_surface}; - -define_roles!(Roles => [ ShellSurface, ShellSurfaceRole ] ); - -struct SurfaceHandler { - shm_token: ShmToken, -} - -#[derive(Default)] -struct SurfaceData { - buffer: Option<(Vec, (u32, u32))>, - location: Option<(i32, i32)>, -} - -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)))) => { - // we ignore hotspot coordinates in this simple example - 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))); - }) - .unwrap(); - buffer.release(); - } - Some(None) => { - // erase the contents - attributes.user_data.buffer = None; - } - None => {} - } - }); - } - - fn frame(&mut self, _evlh: &mut EventLoopHandle, _client: &Client, surface: &wl_surface::WlSurface, - callback: wl_callback::WlCallback, _token: CompositorToken) { - callback.done(0); - } -} - -struct ShellSurfaceHandler { - token: CompositorToken, -} - -impl ShellSurfaceHandler { - fn new(token: CompositorToken) -> ShellSurfaceHandler { - ShellSurfaceHandler { token } - } -} - -impl shell::Handler for ShellSurfaceHandler { - fn new_client(&mut self, _evlh: &mut EventLoopHandle, _client: ShellClient<()>) {} - fn client_pong(&mut self, _evlh: &mut EventLoopHandle, _client: ShellClient<()>) {} - fn new_toplevel(&mut self, _evlh: &mut EventLoopHandle, - surface: ToplevelSurface) - -> ToplevelConfigure { - let wl_surface = surface.get_surface().unwrap(); - self.token.with_surface_data(wl_surface, |data| { - // place the window at a random location in the [0;300]x[0;300] square - use rand::distributions::{IndependentSample, Range}; - let range = Range::new(0, 300); - let mut rng = rand::thread_rng(); - let x = range.ind_sample(&mut rng); - let y = range.ind_sample(&mut rng); - data.user_data.location = Some((x, y)) - }); - ToplevelConfigure { - size: None, - states: vec![], - serial: 42, - } - } - fn new_popup(&mut self, _evlh: &mut EventLoopHandle, - _surface: PopupSurface) - -> PopupConfigure { - PopupConfigure { - size: (10, 10), - position: (10, 10), - serial: 42, - } - } - fn move_(&mut self, _evlh: &mut EventLoopHandle, - _surface: ToplevelSurface, _seat: &wl_seat::WlSeat, - _serial: u32) { - } - fn resize(&mut self, _evlh: &mut EventLoopHandle, - _surface: ToplevelSurface, _seat: &wl_seat::WlSeat, - _serial: u32, _edges: zxdg_toplevel_v6::ResizeEdge) { - } - fn grab(&mut self, _evlh: &mut EventLoopHandle, - _surface: PopupSurface, _seat: &wl_seat::WlSeat, - _serial: u32) { - } - fn change_display_state(&mut self, _evlh: &mut EventLoopHandle, - _surface: ToplevelSurface, - _maximized: Option, _minimized: Option, _fullscreen: Option, - _output: Option<&wl_output::WlOutput>) - -> ToplevelConfigure { - ToplevelConfigure { - size: None, - states: vec![], - serial: 42, - } - } - fn show_window_menu(&mut self, _evlh: &mut EventLoopHandle, - _surface: ToplevelSurface, - _seat: &wl_seat::WlSeat, _serial: u32, _x: i32, _y: i32) { - } -} - - -type MyCompositorHandler = CompositorHandler; -type MyShellHandler = ShellHandler; +use smithay::shell::shell_init; +use smithay::shm::init_shm_global; fn main() { // A logger facility, here we use the terminal for this example @@ -172,51 +36,21 @@ fn main() { let (mut display, mut event_loop) = wayland_server::create_display(); /* - * Initialize wl_shm global + * Initialize the globals */ - // Insert the ShmGlobal as a handler to your event loop - // Here, we specify tha the standard Argb8888 and Xrgb8888 is the only supported. - 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 - event_loop.register_global::(shm_handler_id, 1); - // retreive the token - let shm_token = { - let state = event_loop.state(); - state.get_handler::(shm_handler_id).get_token() - }; + init_shm_global(&mut event_loop, vec![], log.clone()); - /* - * Initialize the compositor global - */ - 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, 1); - // retrieve the tokens - let compositor_token = { - let state = event_loop.state(); - state - .get_handler::(compositor_handler_id) - .get_token() - }; + let (compositor_token, _, _) = + compositor_init(&mut event_loop, surface_implementation(), (), log.clone()); - /* - * Initialize the shell global - */ - let shell_handler_id = event_loop.add_handler_with_init(MyShellHandler::new( - ShellSurfaceHandler::new(compositor_token), + let (shell_state_token, _, _) = shell_init( + &mut event_loop, + compositor_token, + shell_implementation(), compositor_token, log.clone(), - )); - event_loop.register_global::(shell_handler_id, 1); - event_loop.register_global::(shell_handler_id, 1); + ); /* * Initialize glium @@ -240,10 +74,7 @@ fn main() { { let screen_dimensions = context.get_framebuffer_dimensions(); let state = event_loop.state(); - for toplevel_surface in state - .get_handler::(shell_handler_id) - .toplevel_surfaces() - { + for toplevel_surface in state.get(&shell_state_token).toplevel_surfaces() { if let Some(wl_surface) = toplevel_surface.get_surface() { // this surface is a root of a subsurface tree that needs to be drawn let initial_place = compositor_token diff --git a/src/compositor/global.rs b/src/compositor/global.rs deleted file mode 100644 index db03be3..0000000 --- a/src/compositor/global.rs +++ /dev/null @@ -1,28 +0,0 @@ -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: 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); - } -} - -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); - } -} diff --git a/src/compositor/handlers.rs b/src/compositor/handlers.rs index b8acb67..63d20d7 100644 --- a/src/compositor/handlers.rs +++ b/src/compositor/handlers.rs @@ -1,66 +1,101 @@ -use super::{CompositorHandler, Damage, Handler as UserHandler, Rectangle, RectangleKind, Role, RoleType, - SubsurfaceRole}; +use super::{CompositorToken, Damage, Rectangle, RectangleKind, Role, RoleType, SubsurfaceRole, + SurfaceUserImplementation}; 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, - _r: ::std::marker::PhantomData, -} +use std::cell::RefCell; +use std::rc::Rc; +use wayland_server::{Client, EventLoopHandle, Liveness, Resource}; +use wayland_server::protocol::{wl_compositor, wl_region, wl_subcompositor, wl_subsurface, wl_surface}; /* * wl_compositor */ -impl wl_compositor::Handler for CompositorHandler +pub(crate) fn compositor_bind(evlh: &mut EventLoopHandle, idata: &mut SurfaceIData, + _: &Client, compositor: wl_compositor::WlCompositor) where - U: Default + Send + 'static, - R: Default + Send + 'static, - H: UserHandler + Send + 'static, + U: Default + 'static, + R: Default + 'static, + ID: '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, - ); - } + trace!(idata.log, "Binding a new wl_compositor."); + evlh.register( + &compositor, + compositor_implementation::(), + idata.clone(), + None, + ); } -server_declare_handler!(CompositorHandler, Send]>, wl_compositor::Handler, wl_compositor::WlCompositor); +fn compositor_implementation() -> wl_compositor::Implementation> +where + U: Default + 'static, + R: Default + 'static, + ID: 'static, +{ + wl_compositor::Implementation { + create_surface: |evlh, idata, _, _, surface| { + unsafe { SurfaceData::::init(&surface) }; + evlh.register( + &surface, + surface_implementation::(), + idata.clone(), + Some(destroy_surface::), + ); + }, + create_region: |evlh, _, _, _, region| { + unsafe { RegionData::init(®ion) }; + evlh.register(®ion, region_implementation(), (), Some(destroy_region)); + }, + } +} /* * 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 { +/// Internal implementation data of surfaces +/// +/// This type is only visible as type parameter of +/// the `Global` handle you are provided. +pub struct SurfaceIData { + log: ::slog::Logger, + implem: SurfaceUserImplementation, + idata: Rc>, +} + +impl SurfaceIData { + pub(crate) fn make(log: ::slog::Logger, implem: SurfaceUserImplementation, idata: ID) + -> SurfaceIData { + SurfaceIData { + log: log, + implem: implem, + idata: Rc::new(RefCell::new(idata)), + } + } +} + +impl Clone for SurfaceIData { + fn clone(&self) -> SurfaceIData { + SurfaceIData { + log: self.log.clone(), + implem: self.implem.clone(), + idata: self.idata.clone(), + } + } +} + +pub(crate) fn surface_implementation( + ) + -> wl_surface::Implementation> +{ + wl_surface::Implementation { + attach: |_, _, _, surface, buffer, x, y| 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 { + }, + damage: |_, _, _, surface, x, y, width, height| unsafe { SurfaceData::::with_data(surface, |d| { d.damage = Damage::Surface(Rectangle { x, @@ -69,56 +104,38 @@ impl> wl_surface::Handler for CompositorHandler) { - trace!(self.log, "Setting surface opaque region."); - unsafe { + }, + frame: |evlh, idata, _, surface, callback| { + let mut user_idata = idata.idata.borrow_mut(); + trace!(idata.log, "Calling user callback for wl_surface.frame"); + (idata.implem.frame)( + evlh, + &mut *user_idata, + surface, + callback, + CompositorToken::make(), + ) + }, + set_opaque_region: |_, _, _, surface, 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 { + }, + set_input_region: |_, _, _, surface, 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 { + }, + commit: |evlh, idata, _, surface| { + let mut user_idata = idata.idata.borrow_mut(); + trace!(idata.log, "Calling user callback for wl_surface.commit"); + (idata.implem.commit)(evlh, &mut *user_idata, surface, CompositorToken::make()) + }, + set_buffer_transform: |_, _, _, surface, 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 { + }, + set_buffer_scale: |_, _, _, surface, 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 { + }, + damage_buffer: |_, _, _, surface, x, y, width, height| unsafe { SurfaceData::::with_data(surface, |d| { d.damage = Damage::Buffer(Rectangle { x, @@ -127,97 +144,102 @@ impl> wl_surface::Handler for CompositorHandler]>, wl_surface::Handler, wl_surface::WlSurface); - -impl Destroy for CompositorDestructor { - fn destroy(surface: &wl_surface::WlSurface) { - unsafe { SurfaceData::::cleanup(surface) } - } +fn destroy_surface(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, - }, - ) - }; +pub(crate) fn region_implementation() -> wl_region::Implementation<()> { + wl_region::Implementation { + add: |_, _, _, region, x, y, width, height| { + unsafe { + RegionData::add_rectangle( + region, + RectangleKind::Add, + Rectangle { + x, + y, + width, + height, + }, + ) + }; + }, + subtract: |_, _, _, region, x, y, width, height| { + unsafe { + RegionData::add_rectangle( + region, + RectangleKind::Subtract, + Rectangle { + x, + y, + width, + height, + }, + ) + }; + }, + destroy: |_, _, _, _| {}, } } -server_declare_handler!(CompositorHandler, wl_region::Handler, wl_region::WlRegion); - -impl Destroy for CompositorDestructor { - fn destroy(region: &wl_region::WlRegion) { - unsafe { RegionData::cleanup(region) }; - } +fn destroy_region(region: &wl_region::WlRegion) { + unsafe { RegionData::cleanup(region) }; } /* * wl_subcompositor */ -impl wl_subcompositor::Handler for CompositorHandler +pub(crate) fn subcompositor_bind(evlh: &mut EventLoopHandle, _: &mut (), _: &Client, + subcompositor: wl_subcompositor::WlSubcompositor) where - U: Send + 'static, - R: RoleType + Role + Send + 'static, - H: Send + 'static, + R: RoleType + Role + 'static, + U: '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 _, - ); - evqh.register_with_destructor::<_, CompositorHandler, CompositorDestructor>( - &id, - self.my_id, - ); - } + evlh.register( + &subcompositor, + subcompositor_implementation::(), + (), + None, + ); } -server_declare_handler!(CompositorHandler, Send], H: [Send]>, wl_subcompositor::Handler, wl_subcompositor::WlSubcompositor); +fn subcompositor_implementation() -> wl_subcompositor::Implementation<()> +where + R: RoleType + Role + 'static, + U: 'static, +{ + wl_subcompositor::Implementation { + get_subsurface: |evlh, _, _, subcompositor, subsurface, surface, parent| { + if let Err(()) = unsafe { SurfaceData::::set_parent(surface, parent) } { + subcompositor.post_error( + wl_subcompositor::Error::BadSurface as u32, + "Surface already has a role.".into(), + ); + return; + } + subsurface.set_user_data( + Box::into_raw(Box::new(unsafe { surface.clone_unchecked() })) as *mut _, + ); + evlh.register( + &subsurface, + subsurface_implementation::(), + (), + Some(destroy_subsurface::), + ); + }, + destroy: |_, _, _, _| {}, + } +} /* * wl_subsurface @@ -226,7 +248,8 @@ server_declare_handler!(CompositorHandler(subsurface: &wl_subsurface::WlSubsurface, f: F) where F: FnOnce(&mut SubsurfaceRole), - R: RoleType + Role, + U: 'static, + R: RoleType + Role + 'static, { let ptr = subsurface.get_user_data(); let surface = &*(ptr as *mut wl_surface::WlSurface); @@ -235,24 +258,19 @@ where ); } -impl wl_subsurface::Handler for CompositorHandler +fn subsurface_implementation() -> wl_subsurface::Implementation<()> where - R: RoleType + Role, + R: RoleType + Role + 'static, + U: 'static, { - fn set_position(&mut self, _: &mut EventLoopHandle, _: &Client, - subsurface: &wl_subsurface::WlSubsurface, x: i32, y: i32) { - trace!(self.log, "Setting subsurface position."); - unsafe { + wl_subsurface::Implementation { + set_position: |_, _, _, subsurface, x, y| 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 { + }, + place_above: |_, _, _, subsurface, sibling| unsafe { let ptr = subsurface.get_user_data(); let surface = &*(ptr as *mut wl_surface::WlSurface); if let Err(()) = SurfaceData::::reorder(surface, Location::After, sibling) { @@ -261,12 +279,8 @@ where "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 { + }, + place_below: |_, _, _, subsurface, sibling| unsafe { let ptr = subsurface.get_user_data(); let surface = &*(ptr as *mut wl_surface::WlSurface); if let Err(()) = SurfaceData::::reorder(surface, Location::Before, sibling) { @@ -275,36 +289,32 @@ where "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; }); - } + }, + set_sync: |_, _, _, subsurface| unsafe { + with_subsurface_attributes::(subsurface, |attrs| { + attrs.sync = true; + }); + }, + set_desync: |_, _, _, subsurface| unsafe { + with_subsurface_attributes::(subsurface, |attrs| { + attrs.sync = false; + }); + }, + destroy: |_, _, _, _| {}, } } -server_declare_handler!(CompositorHandler], H: []>, wl_subsurface::Handler, wl_subsurface::WlSubsurface); - -impl Destroy for CompositorDestructor +fn destroy_subsurface(subsurface: &wl_subsurface::WlSubsurface) where - R: RoleType + Role, + U: 'static, + R: RoleType + Role + 'static, { - 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::::unset_parent(&surface); - } + 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::::unset_parent(&surface); } } } diff --git a/src/compositor/mod.rs b/src/compositor/mod.rs index 77d11b0..68336c2 100644 --- a/src/compositor/mod.rs +++ b/src/compositor/mod.rs @@ -1,13 +1,12 @@ //! 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`. +//! This module provides automatic handling of sufaces, subsurfaces +//! and region wayland objects, by registering an implementation for +//! for the `wl_compositor` and `wl_subcompositor` globals. //! -//! ## Why use this handler +//! ## Why use this implementation //! -//! This handler does a simple job: it stores in a coherent way the state of +//! This implementation 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. //! @@ -15,7 +14,7 @@ //! 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 +//! This implementation 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. //! @@ -23,15 +22,16 @@ //! //! ### 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: +//! To initialize this implementation, use the `compositor_init` method provided +//! by this module. It'll require you to first define as few things, as shown in +//! this example: //! //! ``` //! # extern crate wayland_server; //! # #[macro_use] extern crate smithay; //! use wayland_server::protocol::wl_compositor::WlCompositor; //! use wayland_server::protocol::wl_subcompositor::WlSubcompositor; -//! use smithay::compositor; +//! use smithay::compositor::{compositor_init, SurfaceUserImplementation}; //! //! // 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 @@ -44,39 +44,25 @@ //! // 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 { -//! // 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(); +//! // define your implementation for surface +//! let my_implementation = SurfaceUserImplementation { +//! commit: |evlh, idata, surface, token| { /* ... */ }, +//! frame: |evlh, idata, surface, callback, token| { /* ... */ } +//! }; +//! // define your implementation data +//! let my_implementation_data = (); //! -//! // 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 */) +//! // Call the init function: +//! let (token, _, _) = compositor_init::( +//! &mut event_loop, +//! my_implementation, // instance of compositor::SurfaceUserImplementation +//! my_implementation_data, // whatever implementation data you need +//! 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() -//! }; +//! // this `token` is what you'll use to access the surface associated data //! //! // You're now ready to go! //! # } @@ -85,8 +71,9 @@ //! ### 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 init function. This token is necessary to retrieve the metadata associated with +//! a surface. It can be cloned, and is sendable accross threads (as long as your +//! `Mydata` and `MyRoles` are `Send` too). See `CompositorToken` for //! the details of what it enables you. //! //! The surface metadata is held in the `SurfaceAttributes` struct. In contains double-buffered @@ -97,19 +84,19 @@ //! 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; +pub use self::handlers::SurfaceIData; use self::region::RegionData; use self::roles::{Role, RoleType, WrongRole}; use self::tree::SurfaceData; pub use self::tree::TraversalAction; -use wayland_server::{resource_is_registered, Client, EventLoopHandle, Init}; - -use wayland_server::protocol::{wl_buffer, wl_callback, wl_output, wl_region, wl_surface}; +use wayland_server::{resource_is_registered, EventLoop, EventLoopHandle, Global}; +use wayland_server::protocol::{wl_buffer, wl_callback, wl_compositor, wl_output, wl_region, + wl_subcompositor, wl_surface}; /// Description of which part of a surface /// should be considered damaged and needs to be redrawn @@ -124,6 +111,12 @@ pub enum Damage { Buffer(Rectangle), } +#[derive(Copy, Clone, Default)] +struct Marker { + _u: ::std::marker::PhantomData, + _r: ::std::marker::PhantomData, +} + /// Data associated with a surface, aggreged by the handlers /// /// Most of the fields of this struct represent a double-buffered state, which @@ -261,26 +254,35 @@ 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 { - hid: usize, +pub struct CompositorToken { _data: ::std::marker::PhantomData<*mut U>, _role: ::std::marker::PhantomData<*mut R>, - _handler: ::std::marker::PhantomData<*mut H>, + _idata: ::std::marker::PhantomData<*mut ID>, } -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 { +// U: Clone and R: Clone +impl Copy for CompositorToken {} +impl Clone for CompositorToken { + fn clone(&self) -> CompositorToken { *self } } -impl + Send + 'static> CompositorToken { +impl CompositorToken { + pub(crate) fn make() -> CompositorToken { + CompositorToken { + _data: ::std::marker::PhantomData, + _role: ::std::marker::PhantomData, + _idata: ::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. @@ -292,18 +294,21 @@ impl + Send + 'static> Co F: FnOnce(&mut SurfaceAttributes) -> T, { assert!( - resource_is_registered::<_, CompositorHandler>(surface, self.hid), + resource_is_registered( + surface, + &self::handlers::surface_implementation::() + ), "Accessing the data of foreign surfaces is not supported." ); unsafe { SurfaceData::::with_data(surface, f) } } } -impl CompositorToken +impl CompositorToken where - U: Send + 'static, - R: RoleType + Role + Send + 'static, - H: Handler + Send + 'static, + U: 'static, + R: RoleType + Role + 'static, + ID: 'static, { /// Access the data of a surface tree /// @@ -327,7 +332,10 @@ where -> TraversalAction, { assert!( - resource_is_registered::<_, CompositorHandler>(surface, self.hid), + resource_is_registered( + surface, + &self::handlers::surface_implementation::() + ), "Accessing the data of foreign surfaces is not supported." ); unsafe { @@ -344,7 +352,10 @@ where /// 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( + surface, + &self::handlers::surface_implementation::() + ), "Accessing the data of foreign surfaces is not supported." ); unsafe { SurfaceData::::get_parent(surface) } @@ -356,22 +367,27 @@ where /// 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( + surface, + &self::handlers::surface_implementation::() + ), "Accessing the data of foreign surfaces is not supported." ); unsafe { SurfaceData::::get_children(surface) } } } -impl + Send + 'static> - CompositorToken { +impl 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 has_a_role(&self, surface: &wl_surface::WlSurface) -> bool { assert!( - resource_is_registered::<_, CompositorHandler>(surface, self.hid), + resource_is_registered( + surface, + &self::handlers::surface_implementation::() + ), "Accessing the data of foreign surfaces is not supported." ); unsafe { SurfaceData::::has_a_role(surface) } @@ -386,7 +402,10 @@ impl + Send + R: Role, { assert!( - resource_is_registered::<_, CompositorHandler>(surface, self.hid), + resource_is_registered( + surface, + &self::handlers::surface_implementation::() + ), "Accessing the data of foreign surfaces is not supported." ); unsafe { SurfaceData::::has_role::(surface) } @@ -405,7 +424,10 @@ impl + Send + RoleData: Default, { assert!( - resource_is_registered::<_, CompositorHandler>(surface, self.hid), + resource_is_registered( + surface, + &self::handlers::surface_implementation::() + ), "Accessing the data of foreign surfaces is not supported." ); unsafe { SurfaceData::::give_role::(surface) } @@ -423,7 +445,10 @@ impl + Send + R: Role, { assert!( - resource_is_registered::<_, CompositorHandler>(surface, self.hid), + resource_is_registered( + surface, + &self::handlers::surface_implementation::() + ), "Accessing the data of foreign surfaces is not supported." ); unsafe { SurfaceData::::give_role_with::(surface, data) } @@ -442,7 +467,10 @@ impl + Send + F: FnOnce(&mut RoleData) -> T, { assert!( - resource_is_registered::<_, CompositorHandler>(surface, self.hid), + resource_is_registered( + surface, + &self::handlers::surface_implementation::() + ), "Accessing the data of foreign surfaces is not supported." ); unsafe { SurfaceData::::with_role_data::(surface, f) } @@ -459,7 +487,10 @@ impl + Send + R: Role, { assert!( - resource_is_registered::<_, CompositorHandler>(surface, self.hid), + resource_is_registered( + surface, + &self::handlers::surface_implementation::() + ), "Accessing the data of foreign surfaces is not supported." ); unsafe { SurfaceData::::remove_role::(surface) } @@ -471,98 +502,93 @@ impl + Send + /// 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." + resource_is_registered(region, &self::handlers::region_implementation()), + "Accessing the data of foreign surfaces is not supported." ); unsafe { RegionData::get_attributes(region) } } } -/// A struct handling the `wl_compositor` and `wl_subcompositor` globals +/// Create new 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. +/// The globals are directly registered into the eventloop, and this function +/// returns a `CompositorToken` which you'll need access the data associated to +/// the `wl_surface`s. /// -/// See the module-level documentation for instructions and examples of use. -pub struct CompositorHandler { - my_id: usize, - log: ::slog::Logger, - handler: H, - _role: ::std::marker::PhantomData, - _data: ::std::marker::PhantomData, +/// It also returns the two global handles, in case you whish to remove these +/// globals from the event loop in the future. +pub fn compositor_init( + evl: &mut EventLoop, implem: SurfaceUserImplementation, idata: ID, logger: L) + -> ( + CompositorToken, + Global>, + Global, + ) +where + L: Into>, + U: Default + 'static, + R: Default + RoleType + Role + 'static, + ID: 'static, +{ + let log = ::slog_or_stdlog(logger); + let idata = self::handlers::SurfaceIData::make( + log.new(o!("smithay_module" => "compositor_handler")), + implem, + idata, + ); + let compositor_global_token = + evl.register_global::(4, self::handlers::compositor_bind, idata); + let subcompositor_global_token = evl.register_global::( + 1, + self::handlers::subcompositor_bind::, + (), + ); + + ( + CompositorToken::make(), + compositor_global_token, + subcompositor_global_token, + ) } -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, - _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 { - 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, - _role: ::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 +/// Sub-implementation 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 { +/// are forwarded directly to this implementation that you must provide +/// at creation of the compositor global. +pub struct SurfaceUserImplementation { /// 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) + /// See [`wayland_server::protocol::wl_surface::Implementation::commit`](https://docs.rs/wayland-server/0.10.1/wayland_server/protocol/wl_surface/struct.Implementation.html#structfield.commit) /// for more details - fn commit(&mut self, evlh: &mut EventLoopHandle, client: &Client, surface: &wl_surface::WlSurface, - token: CompositorToken) { - } + pub commit: fn( + evlh: &mut EventLoopHandle, + idata: &mut ID, + 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) + /// See [`wayland_server::protocol::wl_surface::Implementation::frame`](https://docs.rs/wayland-server/0.10.1/wayland_server/protocol/wl_surface/struct.Implementation.html#structfield.frame) /// for more details - fn frame(&mut self, evlh: &mut EventLoopHandle, client: &Client, surface: &wl_surface::WlSurface, - callback: wl_callback::WlCallback, token: CompositorToken) { + pub frame: fn( + evlh: &mut EventLoopHandle, + idata: &mut ID, + surface: &wl_surface::WlSurface, + callback: wl_callback::WlCallback, + token: CompositorToken, + ), +} + +impl Copy for SurfaceUserImplementation {} +impl Clone for SurfaceUserImplementation { + fn clone(&self) -> SurfaceUserImplementation { + *self } } diff --git a/src/compositor/region.rs b/src/compositor/region.rs index d6a7a22..b4f0b48 100644 --- a/src/compositor/region.rs +++ b/src/compositor/region.rs @@ -1,8 +1,6 @@ use super::{Rectangle, RectangleKind, RegionAttributes}; - use std::sync::Mutex; use wayland_server::Resource; - use wayland_server::protocol::wl_region; #[derive(Default)] @@ -13,9 +11,8 @@ pub struct RegionData { 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 _, - ) + 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 diff --git a/src/compositor/tree.rs b/src/compositor/tree.rs index ccd0de4..134f7c9 100644 --- a/src/compositor/tree.rs +++ b/src/compositor/tree.rs @@ -1,7 +1,6 @@ use super::{SubsurfaceRole, SurfaceAttributes}; use super::roles::*; use std::sync::Mutex; - use wayland_server::{Liveness, Resource}; use wayland_server::protocol::wl_surface; @@ -64,7 +63,11 @@ impl SurfaceData { } } -impl SurfaceData { +impl SurfaceData +where + U: 'static, + R: 'static, +{ unsafe fn get_data(surface: &wl_surface::WlSurface) -> &Mutex> { let ptr = surface.get_user_data(); &*(ptr as *mut _) @@ -97,7 +100,7 @@ impl SurfaceData { } } -impl SurfaceData { +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); @@ -171,7 +174,7 @@ impl SurfaceData { } } -impl> SurfaceData { +impl + 'static> SurfaceData { /// Sets the parent of a surface /// /// if this surface already has a role, does nothing and fails, otherwise @@ -291,7 +294,7 @@ impl> SurfaceData { } } -impl SurfaceData { +impl SurfaceData { /// Access the attributes associated with a surface /// /// Note that an internal lock is taken during access of this data, @@ -319,9 +322,9 @@ impl SurfaceData { -> 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, &mut R, &T) -> TraversalAction, diff --git a/src/lib.rs b/src/lib.rs index d6c17a3..98ef926 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,7 +12,6 @@ extern crate nix; extern crate rental; extern crate tempfile; extern crate wayland_protocols; -#[macro_use] extern crate wayland_server; extern crate xkbcommon; diff --git a/src/shell/global.rs b/src/shell/global.rs deleted file mode 100644 index 07e13a3..0000000 --- a/src/shell/global.rs +++ /dev/null @@ -1,54 +0,0 @@ -use super::{Handler as UserHandler, ShellClientData, ShellHandler, ShellSurfaceRole}; -use super::wl_handlers::WlShellDestructor; -use super::xdg_handlers::XdgShellDestructor; - -use compositor::Handler as CompositorHandler; -use compositor::roles::*; - -use std::sync::Mutex; - -use wayland_protocols::unstable::xdg_shell::server::zxdg_shell_v6; -use wayland_server::{Client, EventLoopHandle, GlobalHandler, Resource}; -use wayland_server::protocol::{wl_shell, wl_shell_surface}; - -fn shell_client_data() -> ShellClientData { - ShellClientData { - pending_ping: 0, - data: Default::default(), - } -} - -impl GlobalHandler for ShellHandler -where - U: Send + 'static, - R: Role + Send + 'static, - H: CompositorHandler + Send + 'static, - SH: UserHandler + Send + 'static, - SD: Default + Send + 'static, -{ - fn bind(&mut self, evlh: &mut EventLoopHandle, _: &Client, global: wl_shell::WlShell) { - debug!(self.log, "New wl_shell global binded."); - global.set_user_data(Box::into_raw(Box::new(Mutex::new(( - shell_client_data::(), - Vec::::new(), - )))) as *mut _); - evlh.register_with_destructor::<_, Self, WlShellDestructor>(&global, self.my_id); - } -} - -impl GlobalHandler for ShellHandler -where - U: Send + 'static, - R: Role + Send + 'static, - H: CompositorHandler + Send + 'static, - SH: UserHandler + Send + 'static, - SD: Default + Send + 'static, -{ - fn bind(&mut self, evlh: &mut EventLoopHandle, _: &Client, global: zxdg_shell_v6::ZxdgShellV6) { - debug!(self.log, "New xdg_shell global binded."); - global.set_user_data( - Box::into_raw(Box::new(Mutex::new(shell_client_data::()))) as *mut _, - ); - evlh.register_with_destructor::<_, Self, XdgShellDestructor>(&global, self.my_id); - } -} diff --git a/src/shell/mod.rs b/src/shell/mod.rs index 7d58480..895a4b7 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -1,14 +1,13 @@ //! Utilities for handling shell surfaces, toplevel and popups //! -//! This module provides the `ShellHandler` type, which implements automatic handling of -//! shell surfaces objects, by being registered as a global handler for `wl_shell` and -//! `xdg_shell`. +//! This module provides automatic handling of shell surfaces objects, by being registered +//! as a global handler for `wl_shell` and `xdg_shell`. //! -//! ## Why use this handler +//! ## Why use this implementation //! -//! This handler can track for you the various shell surfaces defined by the clients by -//! handling the `xdg_shell` protocol. It also includes a compatibility layer for the -//! deprecated `wl_shell` global. +//! This implementation can track for you the various shell surfaces defined by the +//! clients by handling the `xdg_shell` protocol. It also includes a compatibility +//! layer for the deprecated `wl_shell` global. //! //! It allows you to easily access a list of all shell surfaces defined by your clients //! access their associated metadata and underlying `wl_surface`s. @@ -21,28 +20,24 @@ //! //! ### Initialization //! -//! To initialize this handler, simply instanciate it and register it to the event loop -//! as a global handler for xdg_shell and wl_shell. You will need to provide it the -//! `CompositorToken` you retrieved from an instanciation of the `CompositorHandler` -//! provided by smithay. +//! To initialize this handler, simple use the `shell_init` function provided in this +//! module. You will need to provide it the `CompositorToken` you retrieved from an +//! instanciation of the `CompositorHandler` provided by smithay. //! -//! ``` +//! ```no_run //! # extern crate wayland_server; //! # #[macro_use] extern crate smithay; //! # extern crate wayland_protocols; //! # //! use smithay::compositor::roles::*; //! use smithay::compositor::CompositorToken; -//! use smithay::shell::{ShellHandler, Handler as ShellHandlerTrait, ShellSurfaceRole}; +//! use smithay::shell::{shell_init, ShellSurfaceRole, ShellSurfaceUserImplementation}; //! use wayland_server::protocol::wl_shell::WlShell; //! use wayland_protocols::unstable::xdg_shell::server::zxdg_shell_v6::ZxdgShellV6; //! use wayland_server::{EventLoop, EventLoopHandle}; -//! # use smithay::shell::*; //! # use wayland_server::protocol::{wl_seat, wl_output}; //! # use wayland_protocols::unstable::xdg_shell::server::zxdg_toplevel_v6; //! # #[derive(Default)] struct MySurfaceData; -//! # struct MyHandlerForCompositor; -//! # impl ::smithay::compositor::Handler for MyHandlerForCompositor {} //! //! // define the roles type. You need to integrate the ShellSurface role: //! define_roles!(MyRoles => @@ -55,100 +50,70 @@ //! /* ... */ //! } //! -//! // define a sub-handler for the shell::Handler trait -//! struct MyHandlerForShell { -//! /* ... */ -//! } -//! -//! # type MyToplevelSurface = ToplevelSurface; -//! # type MyPopupSurface = PopupSurface; -//! -//! impl ShellHandlerTrait for MyHandlerForShell { -//! /* ... a few methods to implement, see shell::Handler -//! documentation for details ... */ -//! # fn new_client(&mut self, evlh: &mut EventLoopHandle, client: ShellClient) { unimplemented!() } -//! # fn client_pong(&mut self, evlh: &mut EventLoopHandle, client: ShellClient) { unimplemented!() } -//! # fn new_toplevel(&mut self, evlh: &mut EventLoopHandle, surface: MyToplevelSurface) -//! # -> ToplevelConfigure { unimplemented!() } -//! # fn new_popup(&mut self, evlh: &mut EventLoopHandle, surface: MyPopupSurface) -//! # -> PopupConfigure { unimplemented!() } -//! # fn move_(&mut self, evlh: &mut EventLoopHandle, surface: MyToplevelSurface, -//! # seat: &wl_seat::WlSeat, serial: u32) { unimplemented!() } -//! # fn resize(&mut self, evlh: &mut EventLoopHandle, surface: MyToplevelSurface, -//! # seat: &wl_seat::WlSeat, serial: u32, edges: zxdg_toplevel_v6::ResizeEdge) { unimplemented!() } -//! # fn grab(&mut self, evlh: &mut EventLoopHandle, surface: MyPopupSurface, -//! # seat: &wl_seat::WlSeat, serial: u32) { unimplemented!() } -//! # fn change_display_state(&mut self, evlh: &mut EventLoopHandle, surface: MyToplevelSurface, -//! # maximized: Option, minimized: Option, fullscreen: Option, -//! # output: Option<&wl_output::WlOutput>) -//! # -> ToplevelConfigure { unimplemented!() } -//! # fn show_window_menu(&mut self, evlh: &mut EventLoopHandle, surface: MyToplevelSurface, -//! # seat: &wl_seat::WlSeat, serial: u32, x: i32, y: i32) { unimplemented!() } -//! } -//! -//! # type MyCompositorHandler = smithay::compositor::CompositorHandler; -//! // A type alias for brevety. ShellHandler has many type parameters: -//! type MyShellHandler = ShellHandler< -//! MySurfaceData, // the surface data you defined for the CompositorHandler -//! MyRoles, // the roles type -//! MyHandlerForCompositor, // the sub-handler type you defined for the CompositorHandler -//! MyHandlerForShell, // the sub-handler type you defined for this ShellHandler -//! MyShellData // the client data you defined for this ShellHandler -//! >; //! # fn main() { //! # let (_display, mut event_loop) = wayland_server::create_display(); -//! # let compositor_hid = event_loop.add_handler_with_init( -//! # MyCompositorHandler::new(MyHandlerForCompositor{ /* ... */ }, None /* put a logger here */) +//! # let (compositor_token, _, _) = smithay::compositor::compositor_init::<(), MyRoles, _, _>( +//! # &mut event_loop, +//! # unimplemented!(), +//! # (), +//! # None //! # ); -//! # let compositor_token = { -//! # let state = event_loop.state(); -//! # state.get_handler::(compositor_hid).get_token() -//! # }; +//! // define your implementation for shell +//! let my_shell_implementation = ShellSurfaceUserImplementation { +//! new_client: |evlh, idata, client| { unimplemented!() }, +//! client_pong: |evlh, idata, client| { unimplemented!() }, +//! new_toplevel: |evlh, idata, toplevel| { unimplemented!() }, +//! new_popup: |evlh, idata, popup| { unimplemented!() }, +//! move_: |evlh, idata, toplevel, seat, serial| { unimplemented!() }, +//! resize: |evlh, idata, toplevel, seat, serial, edges| { unimplemented!() }, +//! grab: |evlh, idata, popup, seat, serial| { unimplemented!() }, +//! change_display_state: |evlh, idata, toplevel, maximized, minimized, fullscreen, output| { +//! unimplemented!() +//! }, +//! show_window_menu: |evlh, idata, toplevel, seat, serial, x, y| { unimplemented!() }, +//! }; //! -//! let shell_hid = event_loop.add_handler_with_init( -//! MyShellHandler::new( -//! MyHandlerForShell{ /* ... */ }, -//! compositor_token, // the composior token you retrieved from the CompositorHandler -//! None /* put a logger here */ -//! ) +//! // define your implementation data +//! let my_shell_implementation_data = (); +//! +//! let (shell_state_token, _, _) = shell_init::<_, _, _, _, MyShellData, _>( +//! &mut event_loop, +//! compositor_token, // token from the compositor implementation +//! my_shell_implementation, // instance of shell::ShellSurfaceUserImplementation +//! my_shell_implementation_data, // whatever data you need here +//! None // put a logger if you want //! ); //! -//! event_loop.register_global::(shell_hid, 1); -//! event_loop.register_global::(shell_hid, 1); -//! //! // You're now ready to go! //! # } //! ``` //! //! ### Access to shell surface and clients data //! -//! There are mainly 3 kind of objects that you'll manipulate from this handler: +//! There are mainly 3 kind of objects that you'll manipulate from this implementation: //! //! - `ShellClient`: This is a handle representing an isntanciation of a shell global //! you can associate client-wise metadata to it (this is the `MyShellData` type in //! the example above). //! - `ToplevelSurface`: This is a handle representing a toplevel surface, you can -//! retrive a list of all currently alive toplevel surface from the `Shellhandler`. +//! retrive a list of all currently alive toplevel surface from the `ShellState`. //! - `PopupSurface`: This is a handle representing a popup/tooltip surface. Similarly, -//! you can get a list of all currently alive popup surface from the `ShellHandler`. +//! you can get a list of all currently alive popup surface from the `ShellState`. //! //! You'll obtain these objects though two means: either via the callback methods of -//! the subhandler you provided, or via methods on the `ShellHandler` that you can -//! access from the `state()` of the event loop. +//! the subhandler you provided, or via methods on the `ShellState` that you can +//! access from the `state()` of the event loop and the token returned by the init +//! function. -use compositor::{CompositorToken, Handler as CompositorHandler, Rectangle}; +use compositor::{CompositorToken, Rectangle}; use compositor::roles::Role; - +use std::cell::RefCell; +use std::rc::Rc; use wayland_protocols::unstable::xdg_shell::server::{zxdg_popup_v6, zxdg_positioner_v6 as xdg_positioner, zxdg_shell_v6, zxdg_surface_v6, zxdg_toplevel_v6}; - -use wayland_server::{EventLoopHandle, EventResult, Init, Liveness, Resource}; +use wayland_server::{EventLoop, EventLoopHandle, EventResult, Global, Liveness, Resource, StateToken}; use wayland_server::protocol::{wl_output, wl_seat, wl_shell, wl_shell_surface, wl_surface}; -mod global; mod wl_handlers; mod xdg_handlers; @@ -301,54 +266,100 @@ impl Default for ShellSurfacePendingState { } } -/// The handler for the shell globals +/// Internal implementation data of shell surfaces /// -/// See module-level documentation for its use. -pub struct ShellHandler { - my_id: usize, +/// This type is only visible as type parameter of +/// the `Global` handle you are provided. +pub struct ShellSurfaceIData { log: ::slog::Logger, - token: CompositorToken, - handler: SH, - known_toplevels: Vec>, - known_popups: Vec>, - _shell_data: ::std::marker::PhantomData, + compositor_token: CompositorToken, + implementation: ShellSurfaceUserImplementation, + idata: Rc>, + state_token: StateToken>, } -impl Init for ShellHandler { - fn init(&mut self, _evqh: &mut EventLoopHandle, index: usize) { - self.my_id = index; - debug!(self.log, "Init finished") - } -} - -impl ShellHandler -where - U: Send + 'static, - R: Role + Send + 'static, - H: CompositorHandler + Send + 'static, -{ - /// Create a new CompositorHandler - pub fn new(handler: SH, token: CompositorToken, logger: L) -> ShellHandler - where - L: Into>, - { - let log = ::slog_or_stdlog(logger); - ShellHandler { - my_id: ::std::usize::MAX, - log: log.new(o!("smithay_module" => "shell_handler")), - token: token, - handler: handler, - known_toplevels: Vec::new(), - known_popups: Vec::new(), - _shell_data: ::std::marker::PhantomData, +impl Clone for ShellSurfaceIData { + fn clone(&self) -> ShellSurfaceIData { + ShellSurfaceIData { + log: self.log.clone(), + compositor_token: self.compositor_token.clone(), + implementation: self.implementation.clone(), + idata: self.idata.clone(), + state_token: self.state_token.clone(), } } +} - /// Access the inner handler of this CompositorHandler - pub fn get_handler(&mut self) -> &mut SH { - &mut self.handler - } +/// Create new xdg_shell and wl_shell globals. +/// +/// The globals are directly registered into the eventloop, and this function +/// returns a `StateToken<_>` which you'll need access the list of shell +/// surfaces created by your clients. +/// +/// It also returns the two global handles, in case you whish to remove these +/// globals from the event loop in the future. +pub fn shell_init( + evl: &mut EventLoop, token: CompositorToken, + implementation: ShellSurfaceUserImplementation, idata: SID, logger: L) + -> ( + StateToken>, + Global>, + Global>, + ) +where + U: 'static, + R: Role + 'static, + CID: 'static, + SID: 'static, + SD: Default + 'static, + L: Into>, +{ + let log = ::slog_or_stdlog(logger); + let shell_state = ShellState { + known_toplevels: Vec::new(), + known_popups: Vec::new(), + }; + let shell_state_token = evl.state().insert(shell_state); + let shell_surface_idata = ShellSurfaceIData { + log: log.new(o!("smithay_module" => "shell_handler")), + compositor_token: token, + implementation: implementation, + idata: Rc::new(RefCell::new(idata)), + state_token: shell_state_token.clone(), + }; + + // TODO: init globals + let wl_shell_global = evl.register_global( + 1, + self::wl_handlers::wl_shell_bind::, + shell_surface_idata.clone(), + ); + let xdg_shell_global = evl.register_global( + 1, + self::xdg_handlers::xdg_shell_bind::, + shell_surface_idata.clone(), + ); + + (shell_state_token, wl_shell_global, xdg_shell_global) +} + +/// Shell global state +/// +/// This state allows you to retrieve a list of surfaces +/// currently known to the shell global. +pub struct ShellState { + known_toplevels: Vec>, + known_popups: Vec>, +} + +impl ShellState +where + U: 'static, + R: Role + 'static, + CID: 'static, + SD: 'static, +{ /// Cleans the internal surface storage by removing all dead surfaces pub fn cleanup_surfaces(&mut self) { self.known_toplevels.retain(|s| s.alive()); @@ -356,12 +367,12 @@ where } /// Access all the shell surfaces known by this handler - pub fn toplevel_surfaces(&self) -> &[ToplevelSurface] { + pub fn toplevel_surfaces(&self) -> &[ToplevelSurface] { &self.known_toplevels[..] } /// Access all the popup surfaces known by this handler - pub fn popup_surfaces(&self) -> &[PopupSurface] { + pub fn popup_surfaces(&self) -> &[PopupSurface] { &self.known_popups[..] } } @@ -375,11 +386,18 @@ enum ShellClientKind { Xdg(zxdg_shell_v6::ZxdgShellV6), } -struct ShellClientData { +pub(crate) struct ShellClientData { pending_ping: u32, data: SD, } +fn make_shell_client_data() -> ShellClientData { + ShellClientData { + pending_ping: 0, + data: Default::default(), + } +} + /// A shell client /// /// This represents an instanciation of a shell @@ -490,18 +508,19 @@ enum SurfaceKind { /// /// This is an unified abstraction over the toplevel surfaces /// of both `wl_shell` and `xdg_shell`. -pub struct ToplevelSurface { +pub struct ToplevelSurface { wl_surface: wl_surface::WlSurface, shell_surface: SurfaceKind, - token: CompositorToken, + token: CompositorToken, _shell_data: ::std::marker::PhantomData, } -impl ToplevelSurface +impl ToplevelSurface where - U: Send + 'static, - R: Role + Send + 'static, - H: CompositorHandler + Send + 'static, + U: 'static, + R: Role + 'static, + CID: 'static, + SD: 'static, { /// Is the toplevel surface refered by this handle still alive? pub fn alive(&self) -> bool { @@ -628,18 +647,19 @@ where /// /// This is an unified abstraction over the popup surfaces /// of both `wl_shell` and `xdg_shell`. -pub struct PopupSurface { +pub struct PopupSurface { wl_surface: wl_surface::WlSurface, shell_surface: SurfaceKind, - token: CompositorToken, + token: CompositorToken, _shell_data: ::std::marker::PhantomData, } -impl PopupSurface +impl PopupSurface where - U: Send + 'static, - R: Role + Send + 'static, - H: CompositorHandler + Send + 'static, + U: 'static, + R: Role + 'static, + CID: 'static, + SD: 'static, { /// Is the popup surface refered by this handle still alive? pub fn alive(&self) -> bool { @@ -807,47 +827,66 @@ pub struct PopupConfigure { pub serial: u32, } -/// A trait for the sub-handler provided to the ShellHandler +/// A sub-implementation for the shell /// -/// You need to implement this trait to handle events that the ShellHandler +/// You need to provide this to handle events that the provided implementation /// cannot process for you directly. /// -/// Depending on what you want to do, you might implement some of these methods +/// Depending on what you want to do, you might implement some of these functions /// as doing nothing. -pub trait Handler { +pub struct ShellSurfaceUserImplementation { /// A new shell client was instanciated - fn new_client(&mut self, evlh: &mut EventLoopHandle, client: ShellClient); + pub new_client: fn(evlh: &mut EventLoopHandle, idata: &mut SID, client: ShellClient), /// The pong for a pending ping of this shell client was received /// /// The ShellHandler already checked for you that the serial matches the one /// from the pending ping. - fn client_pong(&mut self, evlh: &mut EventLoopHandle, client: ShellClient); + pub client_pong: fn(evlh: &mut EventLoopHandle, idata: &mut SID, client: ShellClient), /// A new toplevel surface was created /// - /// You need to return a `ToplevelConfigure` from this method, which will be sent + /// You need to return a `ToplevelConfigure` from this function, which will be sent /// to the client to configure this surface - fn new_toplevel(&mut self, evlh: &mut EventLoopHandle, surface: ToplevelSurface) - -> ToplevelConfigure; + pub new_toplevel: fn( + evlh: &mut EventLoopHandle, + idata: &mut SID, + surface: ToplevelSurface, + ) -> ToplevelConfigure, /// A new popup surface was created /// - /// You need to return a `PopupConfigure` from this method, which will be sent + /// You need to return a `PopupConfigure` from this function, which will be sent /// to the client to configure this surface - fn new_popup(&mut self, evlh: &mut EventLoopHandle, surface: PopupSurface) - -> PopupConfigure; + pub new_popup: fn(evlh: &mut EventLoopHandle, idata: &mut SID, surface: PopupSurface) + -> PopupConfigure, /// The client requested the start of an interactive move for this surface - fn move_(&mut self, evlh: &mut EventLoopHandle, surface: ToplevelSurface, - seat: &wl_seat::WlSeat, serial: u32); + pub move_: fn( + evlh: &mut EventLoopHandle, + idata: &mut SID, + surface: ToplevelSurface, + seat: &wl_seat::WlSeat, + serial: u32, + ), /// The client requested the start of an interactive resize for this surface /// /// The `edges` argument specifies which part of the window's border is being dragged. - fn resize(&mut self, evlh: &mut EventLoopHandle, surface: ToplevelSurface, - seat: &wl_seat::WlSeat, serial: u32, edges: zxdg_toplevel_v6::ResizeEdge); + pub resize: fn( + evlh: &mut EventLoopHandle, + idata: &mut SID, + surface: ToplevelSurface, + seat: &wl_seat::WlSeat, + serial: u32, + edges: zxdg_toplevel_v6::ResizeEdge, + ), /// This popup requests a grab of the pointer /// /// This means it requests to be sent a `popup_done` event when the pointer leaves /// the grab area. - fn grab(&mut self, evlh: &mut EventLoopHandle, surface: PopupSurface, - seat: &wl_seat::WlSeat, serial: u32); + pub grab: fn( + evlh: &mut EventLoopHandle, + idata: &mut SID, + surface: PopupSurface, + seat: &wl_seat::WlSeat, + serial: u32, + ), /// A toplevel surface requested its display state to be changed /// /// Each field represents the request of the client for a specific property: @@ -861,14 +900,33 @@ pub trait Handler { /// /// You are to answer with a `ToplevelConfigure` that will be sent to the client in /// response. - fn change_display_state(&mut self, evlh: &mut EventLoopHandle, surface: ToplevelSurface, - maximized: Option, minimized: Option, fullscreen: Option, - output: Option<&wl_output::WlOutput>) - -> ToplevelConfigure; + pub change_display_state: fn( + evlh: &mut EventLoopHandle, + idata: &mut SID, + surface: ToplevelSurface, + maximized: Option, + minimized: Option, + fullscreen: Option, + output: Option<&wl_output::WlOutput>, + ) -> ToplevelConfigure, /// The client requests the window menu to be displayed on this surface at this location /// /// This menu belongs to the compositor. It is typically expected to contain options for /// control of the window (maximize/minimize/close/move/etc...). - fn show_window_menu(&mut self, evlh: &mut EventLoopHandle, surface: ToplevelSurface, - seat: &wl_seat::WlSeat, serial: u32, x: i32, y: i32); + pub show_window_menu: fn( + evlh: &mut EventLoopHandle, + idata: &mut SID, + surface: ToplevelSurface, + seat: &wl_seat::WlSeat, + serial: u32, + x: i32, + y: i32, + ), +} + +impl Copy for ShellSurfaceUserImplementation {} +impl Clone for ShellSurfaceUserImplementation { + fn clone(&self) -> ShellSurfaceUserImplementation { + *self + } } diff --git a/src/shell/wl_handlers.rs b/src/shell/wl_handlers.rs index e536a51..26946c8 100644 --- a/src/shell/wl_handlers.rs +++ b/src/shell/wl_handlers.rs @@ -1,35 +1,49 @@ -use super::{Handler as UserHandler, PopupConfigure, PopupState, PositionerState, ShellClient, - ShellClientData, ShellHandler, ShellSurfacePendingState, ShellSurfaceRole, ToplevelConfigure, - ToplevelState}; - -use compositor::{CompositorToken, Handler as CompositorHandler, Rectangle}; +use super::{make_shell_client_data, PopupConfigure, PopupState, PositionerState, ShellClient, + ShellClientData, ShellSurfaceIData, ShellSurfacePendingState, ShellSurfaceRole, + ToplevelConfigure, ToplevelState}; +use compositor::{CompositorToken, Rectangle}; use compositor::roles::*; - use std::sync::Mutex; - use wayland_protocols::unstable::xdg_shell::server::{zxdg_positioner_v6 as xdg_positioner, zxdg_toplevel_v6}; +use wayland_server::{Client, EventLoopHandle, Resource}; +use wayland_server::protocol::{wl_output, wl_shell, wl_shell_surface, wl_surface}; -use wayland_server::{Client, Destroy, EventLoopHandle, Resource}; -use wayland_server::protocol::{wl_output, wl_seat, wl_shell, wl_shell_surface, wl_surface}; - -pub struct WlShellDestructor { - _data: ::std::marker::PhantomData, +pub(crate) fn wl_shell_bind(evlh: &mut EventLoopHandle, + idata: &mut ShellSurfaceIData, + _: &Client, shell: wl_shell::WlShell) +where + U: 'static, + R: Role + 'static, + CID: 'static, + SID: 'static, + SD: Default + 'static, +{ + shell.set_user_data(Box::into_raw(Box::new(Mutex::new(( + make_shell_client_data::(), + Vec::::new(), + )))) as *mut _); + evlh.register( + &shell, + shell_implementation(), + idata.clone(), + Some(destroy_shell::), + ); + let mut user_idata = idata.idata.borrow_mut(); + (idata.implementation.new_client)(evlh, &mut *user_idata, make_shell_client(&shell)); } /* * wl_shell */ -pub type ShellUserData = Mutex<(ShellClientData, Vec)>; +pub(crate) type ShellUserData = Mutex<(ShellClientData, Vec)>; -impl Destroy for WlShellDestructor { - fn destroy(shell: &wl_shell::WlShell) { - let ptr = shell.get_user_data(); - shell.set_user_data(::std::ptr::null_mut()); - let data = unsafe { Box::from_raw(ptr as *mut ShellUserData) }; - // explicitly call drop to not forget what we're doing here - ::std::mem::drop(data); - } +fn destroy_shell(shell: &wl_shell::WlShell) { + let ptr = shell.get_user_data(); + shell.set_user_data(::std::ptr::null_mut()); + let data = unsafe { Box::from_raw(ptr as *mut ShellUserData) }; + // explicitly call drop to not forget what we're doing here + ::std::mem::drop(data); } pub fn make_shell_client(resource: &wl_shell::WlShell) -> ShellClient { @@ -39,69 +53,68 @@ pub fn make_shell_client(resource: &wl_shell::WlShell) -> ShellClient { } } -impl wl_shell::Handler for ShellHandler +fn shell_implementation( + ) + -> wl_shell::Implementation> where - U: Send + 'static, - R: Role + Send + 'static, - H: CompositorHandler + Send + 'static, - SH: UserHandler + Send + 'static, - SD: Send + 'static, + U: 'static, + R: Role + 'static, + CID: 'static, + SID: 'static, + SD: 'static, { - fn get_shell_surface(&mut self, evlh: &mut EventLoopHandle, _: &Client, resource: &wl_shell::WlShell, - id: wl_shell_surface::WlShellSurface, surface: &wl_surface::WlSurface) { - trace!(self.log, "Creating new wl_shell_surface."); - let role_data = ShellSurfaceRole { - pending_state: ShellSurfacePendingState::None, - window_geometry: None, - pending_configures: Vec::new(), - configured: true, - }; - if let Err(_) = self.token.give_role_with(surface, role_data) { - resource.post_error( - wl_shell::Error::Role as u32, - "Surface already has a role.".into(), + wl_shell::Implementation { + get_shell_surface: |evlh, idata, _, shell, shell_surface, surface| { + let role_data = ShellSurfaceRole { + pending_state: ShellSurfacePendingState::None, + window_geometry: None, + pending_configures: Vec::new(), + configured: true, + }; + if let Err(_) = idata.compositor_token.give_role_with(surface, role_data) { + shell.post_error( + wl_shell::Error::Role as u32, + "Surface already has a role.".into(), + ); + return; + } + shell_surface.set_user_data( + Box::into_raw(Box::new(unsafe { surface.clone_unchecked() })) as *mut _, + ); + evlh.register( + &shell_surface, + shell_surface_implementation(), + idata.clone(), + Some(destroy_shell_surface), ); - return; - } - id.set_user_data( - Box::into_raw(Box::new(unsafe { surface.clone_unchecked() })) as *mut _, - ); - evlh.register_with_destructor::<_, Self, WlShellDestructor>(&id, self.my_id); - // register ourselves to the wl_shell for ping handling - let mutex = unsafe { &*(resource.get_user_data() as *mut ShellUserData) }; - let mut guard = mutex.lock().unwrap(); - if guard.1.len() == 0 && guard.0.pending_ping != 0 { - // there is a pending ping that no surface could receive yet, send it - // note this is not possible that it was received and then a wl_shell_surface was - // destroyed, because wl_shell_surface has no destructor! - id.ping(guard.0.pending_ping); - } - guard.1.push(id); + // register ourselves to the wl_shell for ping handling + let mutex = unsafe { &*(shell.get_user_data() as *mut ShellUserData) }; + let mut guard = mutex.lock().unwrap(); + if guard.1.len() == 0 && guard.0.pending_ping != 0 { + // there is a pending ping that no surface could receive yet, send it + // note this is not possible that it was received and then a wl_shell_surface was + // destroyed, because wl_shell_surface has no destructor! + shell_surface.ping(guard.0.pending_ping); + } + guard.1.push(shell_surface); + }, } } -server_declare_handler!( - ShellHandler, Send], H:[CompositorHandler, Send], SH:[UserHandler, Send], SD: [Send]>, - wl_shell::Handler, - wl_shell::WlShell -); - /* * wl_shell_surface */ pub type ShellSurfaceUserData = (wl_surface::WlSurface, wl_shell::WlShell); -impl Destroy for WlShellDestructor { - fn destroy(shell_surface: &wl_shell_surface::WlShellSurface) { - let ptr = shell_surface.get_user_data(); - shell_surface.set_user_data(::std::ptr::null_mut()); - // drop the WlSurface object - let surface = unsafe { Box::from_raw(ptr as *mut ShellSurfaceUserData) }; - // explicitly call drop to not forget what we're doing here - ::std::mem::drop(surface); - } +fn destroy_shell_surface(shell_surface: &wl_shell_surface::WlShellSurface) { + let ptr = shell_surface.get_user_data(); + shell_surface.set_user_data(::std::ptr::null_mut()); + // drop the WlSurface object + let surface = unsafe { Box::from_raw(ptr as *mut ShellSurfaceUserData) }; + // explicitly call drop to not forget what we're doing here + ::std::mem::drop(surface); } fn make_toplevel_handle(token: CompositorToken, @@ -140,231 +153,294 @@ pub fn send_popup_configure(resource: &wl_shell_surface::WlShellSurface, configu resource.configure(wl_shell_surface::Resize::empty(), w, h); } -impl ShellHandler -where - U: Send + 'static, - R: Role + Send + 'static, - H: CompositorHandler + Send + 'static, - SH: UserHandler + Send + 'static, - SD: Send + 'static, -{ - fn wl_handle_display_state_change(&mut self, evlh: &mut EventLoopHandle, - resource: &wl_shell_surface::WlShellSurface, - maximized: Option, minimized: Option, - fullscreen: Option, output: Option<&wl_output::WlOutput>) { - let handle = make_toplevel_handle(self.token, resource); - // handler callback - let configure = - self.handler - .change_display_state(evlh, handle, maximized, minimized, fullscreen, output); - // send the configure response to client - let (w, h) = configure.size.unwrap_or((0, 0)); - resource.configure(wl_shell_surface::None, w, h); - } +fn wl_handle_display_state_change(evlh: &mut EventLoopHandle, + idata: &ShellSurfaceIData, + shell_surface: &wl_shell_surface::WlShellSurface, + maximized: Option, minimized: Option, + fullscreen: Option, + output: Option<&wl_output::WlOutput>) { + let handle = make_toplevel_handle(idata.compositor_token, shell_surface); + // handler callback + let mut user_idata = idata.idata.borrow_mut(); + let configure = (idata.implementation.change_display_state)( + evlh, + &mut *user_idata, + handle, + maximized, + minimized, + fullscreen, + output, + ); + // send the configure response to client + let (w, h) = configure.size.unwrap_or((0, 0)); + shell_surface.configure(wl_shell_surface::None, w, h); +} - fn wl_ensure_toplevel(&mut self, evlh: &mut EventLoopHandle, - resource: &wl_shell_surface::WlShellSurface) { - let ptr = resource.get_user_data(); - let &(ref wl_surface, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) }; - // copy token to make borrow checker happy - let token = self.token; - let need_send = token - .with_role_data::(wl_surface, |data| { - match data.pending_state { - ShellSurfacePendingState::Toplevel(_) => { - return false; - } - ShellSurfacePendingState::Popup(_) => { - // this is no longer a popup, deregister it - self.known_popups.retain(|other| { +fn wl_set_parent(idata: &ShellSurfaceIData, + shell_surface: &wl_shell_surface::WlShellSurface, + parent: Option) +where + U: 'static, + R: Role + 'static, + CID: 'static, + SID: 'static, + SD: 'static, +{ + let ptr = shell_surface.get_user_data(); + let &(ref wl_surface, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) }; + idata + .compositor_token + .with_role_data::(wl_surface, |data| match data.pending_state { + ShellSurfacePendingState::Toplevel(ref mut state) => { + state.parent = parent; + } + _ => unreachable!(), + }) + .unwrap(); +} + +fn wl_ensure_toplevel(evlh: &mut EventLoopHandle, + idata: &ShellSurfaceIData, + shell_surface: &wl_shell_surface::WlShellSurface) +where + U: 'static, + R: Role + 'static, + CID: 'static, + SID: 'static, + SD: 'static, +{ + let ptr = shell_surface.get_user_data(); + let &(ref wl_surface, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) }; + // copy token to make borrow checker happy + let token = idata.compositor_token; + let need_send = token + .with_role_data::(wl_surface, |data| { + match data.pending_state { + ShellSurfacePendingState::Toplevel(_) => { + return false; + } + ShellSurfacePendingState::Popup(_) => { + // this is no longer a popup, deregister it + evlh.state() + .get_mut(&idata.state_token) + .known_popups + .retain(|other| { other .get_surface() .map(|s| !s.equals(wl_surface)) .unwrap_or(false) }); - } - ShellSurfacePendingState::None => {} } - // This was not previously toplevel, need to make it toplevel - data.pending_state = ShellSurfacePendingState::Toplevel(ToplevelState { - parent: None, - title: String::new(), - app_id: String::new(), - min_size: (0, 0), - max_size: (0, 0), - }); - return true; - }) - .expect( - "xdg_surface exists but surface has not shell_surface role?!", - ); - // we need to notify about this new toplevel surface - if need_send { - let handle = make_toplevel_handle(self.token, resource); - let configure = self.handler.new_toplevel(evlh, handle); - send_toplevel_configure(resource, configure); - } - } -} - -impl wl_shell_surface::Handler for ShellHandler -where - U: Send + 'static, - R: Role + Send + 'static, - H: CompositorHandler + Send + 'static, - SH: UserHandler + Send + 'static, - SD: Send + 'static, -{ - fn pong(&mut self, evlh: &mut EventLoopHandle, _: &Client, - resource: &wl_shell_surface::WlShellSurface, serial: u32) { - let &(_, ref shell) = unsafe { &*(resource.get_user_data() as *mut ShellSurfaceUserData) }; - let valid = { - let mutex = unsafe { &*(shell.get_user_data() as *mut ShellUserData) }; - let mut guard = mutex.lock().unwrap(); - if guard.0.pending_ping == serial { - guard.0.pending_ping = 0; - true - } else { - false + ShellSurfacePendingState::None => {} } - }; - if valid { - self.handler.client_pong(evlh, make_shell_client(shell)); - } - } - - fn move_(&mut self, evlh: &mut EventLoopHandle, _: &Client, - resource: &wl_shell_surface::WlShellSurface, seat: &wl_seat::WlSeat, serial: u32) { - let handle = make_toplevel_handle(self.token, resource); - self.handler.move_(evlh, handle, seat, serial); - } - - fn resize(&mut self, evlh: &mut EventLoopHandle, _: &Client, - resource: &wl_shell_surface::WlShellSurface, seat: &wl_seat::WlSeat, serial: u32, - edges: wl_shell_surface::Resize) { - let edges = zxdg_toplevel_v6::ResizeEdge::from_raw(edges.bits()) - .unwrap_or(zxdg_toplevel_v6::ResizeEdge::None); - let handle = make_toplevel_handle(self.token, resource); - self.handler.resize(evlh, handle, seat, serial, edges); - } - - fn set_toplevel(&mut self, evlh: &mut EventLoopHandle, _: &Client, - resource: &wl_shell_surface::WlShellSurface) { - self.wl_ensure_toplevel(evlh, resource); - self.wl_handle_display_state_change(evlh, resource, Some(false), Some(false), Some(false), None) - } - - fn set_transient(&mut self, evlh: &mut EventLoopHandle, _: &Client, - resource: &wl_shell_surface::WlShellSurface, parent: &wl_surface::WlSurface, _x: i32, - _y: i32, _flags: wl_shell_surface::Transient) { - self.wl_ensure_toplevel(evlh, resource); - // set the parent - let ptr = resource.get_user_data(); - let &(ref wl_surface, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) }; - self.token - .with_role_data::(wl_surface, |data| match data.pending_state { - ShellSurfacePendingState::Toplevel(ref mut state) => { - state.parent = Some(unsafe { parent.clone_unchecked() }); - } - _ => unreachable!(), - }) - .unwrap(); - // set as regular surface - self.wl_handle_display_state_change(evlh, resource, Some(false), Some(false), Some(false), None) - } - - fn set_fullscreen(&mut self, evlh: &mut EventLoopHandle, _: &Client, - resource: &wl_shell_surface::WlShellSurface, - _method: wl_shell_surface::FullscreenMethod, _framerate: u32, - output: Option<&wl_output::WlOutput>) { - self.wl_ensure_toplevel(evlh, resource); - self.wl_handle_display_state_change(evlh, resource, Some(false), Some(false), Some(true), output) - } - - fn set_popup(&mut self, evlh: &mut EventLoopHandle, _: &Client, - resource: &wl_shell_surface::WlShellSurface, seat: &wl_seat::WlSeat, serial: u32, - parent: &wl_surface::WlSurface, x: i32, y: i32, _: wl_shell_surface::Transient) { - let ptr = resource.get_user_data(); - let &(ref wl_surface, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) }; - // we are reseting the popup state, so remove this surface from everywhere - self.known_toplevels.retain(|other| { - other - .get_surface() - .map(|s| !s.equals(wl_surface)) - .unwrap_or(false) - }); - self.known_popups.retain(|other| { - other - .get_surface() - .map(|s| !s.equals(wl_surface)) - .unwrap_or(false) - }); - self.token - .with_role_data(wl_surface, |data| { - data.pending_state = ShellSurfacePendingState::Popup(PopupState { - parent: unsafe { parent.clone_unchecked() }, - positioner: PositionerState { - rect_size: (1, 1), - anchor_rect: Rectangle { - x, - y, - width: 1, - height: 1, - }, - anchor_edges: xdg_positioner::Anchor::empty(), - gravity: xdg_positioner::Gravity::empty(), - constraint_adjustment: xdg_positioner::ConstraintAdjustment::empty(), - offset: (0, 0), - }, - }); - }) - .expect("wl_shell_surface exists but wl_surface has wrong role?!"); - - // notify the handler about this new popup - let handle = make_popup_handle(self.token, resource); - let configure = self.handler.new_popup(evlh, handle); - send_popup_configure(resource, configure); - self.handler - .grab(evlh, make_popup_handle(self.token, resource), seat, serial); - } - - fn set_maximized(&mut self, evlh: &mut EventLoopHandle, _: &Client, - resource: &wl_shell_surface::WlShellSurface, output: Option<&wl_output::WlOutput>) { - self.wl_ensure_toplevel(evlh, resource); - self.wl_handle_display_state_change(evlh, resource, Some(true), Some(false), Some(false), output) - } - - fn set_title(&mut self, _: &mut EventLoopHandle, _: &Client, - resource: &wl_shell_surface::WlShellSurface, title: String) { - let ptr = resource.get_user_data(); - let &(ref surface, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) }; - self.token - .with_role_data(surface, |data| match data.pending_state { - ShellSurfacePendingState::Toplevel(ref mut state) => { - state.title = title; - } - _ => {} - }) - .expect("wl_shell_surface exists but wl_surface has wrong role?!"); - } - - fn set_class(&mut self, _: &mut EventLoopHandle, _: &Client, - resource: &wl_shell_surface::WlShellSurface, class_: String) { - let ptr = resource.get_user_data(); - let &(ref surface, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) }; - self.token - .with_role_data(surface, |data| match data.pending_state { - ShellSurfacePendingState::Toplevel(ref mut state) => { - state.app_id = class_; - } - _ => {} - }) - .expect("wl_shell_surface exists but wl_surface has wrong role?!"); + // This was not previously toplevel, need to make it toplevel + data.pending_state = ShellSurfacePendingState::Toplevel(ToplevelState { + parent: None, + title: String::new(), + app_id: String::new(), + min_size: (0, 0), + max_size: (0, 0), + }); + return true; + }) + .expect( + "xdg_surface exists but surface has not shell_surface role?!", + ); + // we need to notify about this new toplevel surface + if need_send { + evlh.state() + .get_mut(&idata.state_token) + .known_toplevels + .push(make_toplevel_handle(idata.compositor_token, shell_surface)); + let handle = make_toplevel_handle(idata.compositor_token, shell_surface); + let mut user_idata = idata.idata.borrow_mut(); + let configure = (idata.implementation.new_toplevel)(evlh, &mut *user_idata, handle); + send_toplevel_configure(shell_surface, configure); } } -server_declare_handler!( - ShellHandler, Send], H:[CompositorHandler, Send], SH: [UserHandler, Send], SD: [Send]>, - wl_shell_surface::Handler, - wl_shell_surface::WlShellSurface -); +fn shell_surface_implementation( + ) + -> wl_shell_surface::Implementation> +where + U: 'static, + R: Role + 'static, + CID: 'static, + SID: 'static, + SD: 'static, +{ + wl_shell_surface::Implementation { + pong: |evlh, idata, _, shell_surface, serial| { + let &(_, ref shell) = unsafe { &*(shell_surface.get_user_data() as *mut ShellSurfaceUserData) }; + let valid = { + let mutex = unsafe { &*(shell.get_user_data() as *mut ShellUserData) }; + let mut guard = mutex.lock().unwrap(); + if guard.0.pending_ping == serial { + guard.0.pending_ping = 0; + true + } else { + false + } + }; + if valid { + let mut user_idata = idata.idata.borrow_mut(); + (idata.implementation.client_pong)(evlh, &mut *user_idata, make_shell_client(shell)); + } + }, + move_: |evlh, idata, _, shell_surface, seat, serial| { + let handle = make_toplevel_handle(idata.compositor_token, shell_surface); + let mut user_idata = idata.idata.borrow_mut(); + (idata.implementation.move_)(evlh, &mut *user_idata, handle, seat, serial); + }, + resize: |evlh, idata, _, shell_surface, seat, serial, edges| { + let edges = zxdg_toplevel_v6::ResizeEdge::from_raw(edges.bits()) + .unwrap_or(zxdg_toplevel_v6::ResizeEdge::None); + let handle = make_toplevel_handle(idata.compositor_token, shell_surface); + let mut user_idata = idata.idata.borrow_mut(); + (idata.implementation.resize)(evlh, &mut *user_idata, handle, seat, serial, edges); + }, + set_toplevel: |evlh, idata, _, shell_surface| { + wl_ensure_toplevel(evlh, idata, shell_surface); + wl_set_parent(idata, shell_surface, None); + wl_handle_display_state_change( + evlh, + idata, + shell_surface, + Some(false), + Some(false), + Some(false), + None, + ) + }, + set_transient: |evlh, idata, _, shell_surface, parent, _, _, _| { + wl_ensure_toplevel(evlh, idata, shell_surface); + wl_set_parent( + idata, + shell_surface, + Some(unsafe { parent.clone_unchecked() }), + ); + wl_handle_display_state_change( + evlh, + idata, + shell_surface, + Some(false), + Some(false), + Some(false), + None, + ) + }, + set_fullscreen: |evlh, idata, _, shell_surface, _, _, output| { + wl_ensure_toplevel(evlh, idata, shell_surface); + wl_set_parent(idata, shell_surface, None); + wl_handle_display_state_change( + evlh, + idata, + shell_surface, + Some(false), + Some(false), + Some(true), + output, + ) + }, + set_popup: |evlh, idata, _, shell_surface, seat, serial, parent, x, y, _| { + let ptr = shell_surface.get_user_data(); + let &(ref wl_surface, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) }; + // we are reseting the popup state, so remove this surface from everywhere + evlh.state() + .get_mut(&idata.state_token) + .known_toplevels + .retain(|other| { + other + .get_surface() + .map(|s| !s.equals(wl_surface)) + .unwrap_or(false) + }); + evlh.state() + .get_mut(&idata.state_token) + .known_popups + .retain(|other| { + other + .get_surface() + .map(|s| !s.equals(wl_surface)) + .unwrap_or(false) + }); + idata + .compositor_token + .with_role_data(wl_surface, |data| { + data.pending_state = ShellSurfacePendingState::Popup(PopupState { + parent: unsafe { parent.clone_unchecked() }, + positioner: PositionerState { + rect_size: (1, 1), + anchor_rect: Rectangle { + x, + y, + width: 1, + height: 1, + }, + anchor_edges: xdg_positioner::Anchor::empty(), + gravity: xdg_positioner::Gravity::empty(), + constraint_adjustment: xdg_positioner::ConstraintAdjustment::empty(), + offset: (0, 0), + }, + }); + }) + .expect("wl_shell_surface exists but wl_surface has wrong role?!"); + + // notify the handler about this new popup + evlh.state() + .get_mut(&idata.state_token) + .known_popups + .push(make_popup_handle(idata.compositor_token, shell_surface)); + let handle = make_popup_handle(idata.compositor_token, shell_surface); + let mut user_idata = idata.idata.borrow_mut(); + let configure = (idata.implementation.new_popup)(evlh, &mut *user_idata, handle); + send_popup_configure(shell_surface, configure); + (idata.implementation.grab)( + evlh, + &mut *user_idata, + make_popup_handle(idata.compositor_token, shell_surface), + seat, + serial, + ); + }, + set_maximized: |evlh, idata, _, shell_surface, output| { + wl_ensure_toplevel(evlh, idata, shell_surface); + wl_set_parent(idata, shell_surface, None); + wl_handle_display_state_change( + evlh, + idata, + shell_surface, + Some(true), + Some(false), + Some(false), + output, + ) + }, + set_title: |_, idata, _, shell_surface, title| { + let ptr = shell_surface.get_user_data(); + let &(ref surface, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) }; + idata + .compositor_token + .with_role_data(surface, |data| match data.pending_state { + ShellSurfacePendingState::Toplevel(ref mut state) => { + state.title = title; + } + _ => {} + }) + .expect("wl_shell_surface exists but wl_surface has wrong role?!"); + }, + set_class: |_, idata, _, shell_surface, class| { + let ptr = shell_surface.get_user_data(); + let &(ref surface, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) }; + idata + .compositor_token + .with_role_data(surface, |data| match data.pending_state { + ShellSurfacePendingState::Toplevel(ref mut state) => { + state.app_id = class; + } + _ => {} + }) + .expect("wl_shell_surface exists but wl_surface has wrong role?!"); + }, + } +} diff --git a/src/shell/xdg_handlers.rs b/src/shell/xdg_handlers.rs index 5b7fe53..d1f27cc 100644 --- a/src/shell/xdg_handlers.rs +++ b/src/shell/xdg_handlers.rs @@ -1,169 +1,170 @@ -use super::{Handler as UserHandler, PopupConfigure, PopupState, PositionerState, ShellClient, - ShellClientData, ShellHandler, ShellSurfacePendingState, ShellSurfaceRole, ToplevelConfigure, - ToplevelState}; - -use compositor::{CompositorToken, Handler as CompositorHandler, Rectangle}; +use super::{make_shell_client_data, PopupConfigure, PopupState, PositionerState, ShellClient, + ShellClientData, ShellSurfaceIData, ShellSurfacePendingState, ShellSurfaceRole, + ToplevelConfigure, ToplevelState}; +use compositor::{CompositorToken, Rectangle}; use compositor::roles::*; - use std::sync::Mutex; - use wayland_protocols::unstable::xdg_shell::server::{zxdg_popup_v6, zxdg_positioner_v6, zxdg_shell_v6, zxdg_surface_v6, zxdg_toplevel_v6}; -use wayland_server::{Client, Destroy, EventLoopHandle, Resource}; -use wayland_server::protocol::{wl_output, wl_seat, wl_surface}; +use wayland_server::{Client, EventLoopHandle, Resource}; +use wayland_server::protocol::{wl_output, wl_surface}; -pub struct XdgShellDestructor { - _data: ::std::marker::PhantomData, +pub(crate) fn xdg_shell_bind(evlh: &mut EventLoopHandle, + idata: &mut ShellSurfaceIData, + _: &Client, shell: zxdg_shell_v6::ZxdgShellV6) +where + U: 'static, + R: Role + 'static, + CID: 'static, + SID: 'static, + SD: Default + 'static, +{ + shell.set_user_data( + Box::into_raw(Box::new(Mutex::new(make_shell_client_data::()))) as *mut _, + ); + evlh.register( + &shell, + shell_implementation(), + idata.clone(), + Some(destroy_shell::), + ); + let mut user_idata = idata.idata.borrow_mut(); + (idata.implementation.new_client)(evlh, &mut *user_idata, make_shell_client(&shell)); } /* * xdg_shell */ -pub type ShellUserData = Mutex>; +pub(crate) type ShellUserData = Mutex>; -impl Destroy for XdgShellDestructor { - fn destroy(shell: &zxdg_shell_v6::ZxdgShellV6) { - let ptr = shell.get_user_data(); - shell.set_user_data(::std::ptr::null_mut()); - let data = unsafe { Box::from_raw(ptr as *mut ShellUserData) }; - // explicit call to drop to not forget what we're doing here - ::std::mem::drop(data); - } +fn destroy_shell(shell: &zxdg_shell_v6::ZxdgShellV6) { + let ptr = shell.get_user_data(); + shell.set_user_data(::std::ptr::null_mut()); + let data = unsafe { Box::from_raw(ptr as *mut ShellUserData) }; + // explicit call to drop to not forget what we're doing here + ::std::mem::drop(data); } -pub fn make_shell_client(resource: &zxdg_shell_v6::ZxdgShellV6) -> ShellClient { +pub(crate) fn make_shell_client(resource: &zxdg_shell_v6::ZxdgShellV6) -> ShellClient { ShellClient { kind: super::ShellClientKind::Xdg(unsafe { resource.clone_unchecked() }), _data: ::std::marker::PhantomData, } } -impl zxdg_shell_v6::Handler for ShellHandler +fn shell_implementation( + ) + -> zxdg_shell_v6::Implementation> where - U: Send + 'static, - R: Role + Send + 'static, - H: CompositorHandler + Send + 'static, - SH: UserHandler + Send + 'static, - SD: Send + 'static, + U: 'static, + R: Role + 'static, + CID: 'static, + SID: 'static, + SD: 'static, { - fn destroy(&mut self, _: &mut EventLoopHandle, _: &Client, _: &zxdg_shell_v6::ZxdgShellV6) {} - fn create_positioner(&mut self, evlh: &mut EventLoopHandle, _: &Client, - _: &zxdg_shell_v6::ZxdgShellV6, id: zxdg_positioner_v6::ZxdgPositionerV6) { - trace!(self.log, "Creating new xdg_positioner."); - id.set_user_data(Box::into_raw(Box::new(PositionerState { - rect_size: (0, 0), - anchor_rect: Rectangle { - x: 0, - y: 0, - width: 0, - height: 0, - }, - anchor_edges: zxdg_positioner_v6::Anchor::empty(), - gravity: zxdg_positioner_v6::Gravity::empty(), - constraint_adjustment: zxdg_positioner_v6::ConstraintAdjustment::empty(), - offset: (0, 0), - })) as *mut _); - evlh.register_with_destructor::<_, Self, XdgShellDestructor>(&id, self.my_id); - } - fn get_xdg_surface(&mut self, evlh: &mut EventLoopHandle, _: &Client, - resource: &zxdg_shell_v6::ZxdgShellV6, id: zxdg_surface_v6::ZxdgSurfaceV6, - surface: &wl_surface::WlSurface) { - trace!(self.log, "Creating new wl_shell_surface."); - let role_data = ShellSurfaceRole { - pending_state: ShellSurfacePendingState::None, - window_geometry: None, - pending_configures: Vec::new(), - configured: false, - }; - if let Err(_) = self.token.give_role_with(surface, role_data) { - resource.post_error( - zxdg_shell_v6::Error::Role as u32, - "Surface already has a role.".into(), + zxdg_shell_v6::Implementation { + destroy: |_, _, _, _| {}, + create_positioner: |evlh, _, _, _, positioner| { + let data = PositionerState { + rect_size: (0, 0), + anchor_rect: Rectangle { + x: 0, + y: 0, + width: 0, + height: 0, + }, + anchor_edges: zxdg_positioner_v6::Anchor::empty(), + gravity: zxdg_positioner_v6::Gravity::empty(), + constraint_adjustment: zxdg_positioner_v6::ConstraintAdjustment::empty(), + offset: (0, 0), + }; + positioner.set_user_data(Box::into_raw(Box::new(data)) as *mut _); + evlh.register( + &positioner, + positioner_implementation(), + (), + Some(destroy_positioner), ); - return; - } - id.set_user_data( - Box::into_raw(Box::new((unsafe { surface.clone_unchecked() }, unsafe { - resource.clone_unchecked() - }))) as *mut _, - ); - evlh.register_with_destructor::<_, Self, XdgShellDestructor>(&id, self.my_id); - } - - fn pong(&mut self, evlh: &mut EventLoopHandle, _: &Client, resource: &zxdg_shell_v6::ZxdgShellV6, - serial: u32) { - let valid = { - let mutex = unsafe { &*(resource.get_user_data() as *mut ShellUserData) }; - let mut guard = mutex.lock().unwrap(); - if guard.pending_ping == serial { - guard.pending_ping = 0; - true - } else { - false + }, + get_xdg_surface: |evlh, idata, _, shell, xdg_surface, wl_surface| { + let role_data = ShellSurfaceRole { + pending_state: ShellSurfacePendingState::None, + window_geometry: None, + pending_configures: Vec::new(), + configured: false, + }; + if let Err(_) = idata.compositor_token.give_role_with(wl_surface, role_data) { + shell.post_error( + zxdg_shell_v6::Error::Role as u32, + "Surface already has a role.".into(), + ); + return; } - }; - if valid { - self.handler.client_pong(evlh, make_shell_client(resource)); - } + xdg_surface.set_user_data( + Box::into_raw(Box::new((unsafe { wl_surface.clone_unchecked() }, unsafe { + shell.clone_unchecked() + }))) as *mut _, + ); + evlh.register( + &xdg_surface, + surface_implementation(), + idata.clone(), + Some(destroy_surface), + ); + }, + pong: |evlh, idata, _, shell, serial| { + let valid = { + let mutex = unsafe { &*(shell.get_user_data() as *mut ShellUserData) }; + let mut guard = mutex.lock().unwrap(); + if guard.pending_ping == serial { + guard.pending_ping = 0; + true + } else { + false + } + }; + if valid { + let mut user_idata = idata.idata.borrow_mut(); + (idata.implementation.client_pong)(evlh, &mut *user_idata, make_shell_client(shell)); + } + }, } } -server_declare_handler!( - ShellHandler, Send], H:[CompositorHandler, Send], SH: [UserHandler, Send], SD: [Send]>, - zxdg_shell_v6::Handler, - zxdg_shell_v6::ZxdgShellV6 -); - /* * xdg_positioner */ -impl Destroy for XdgShellDestructor { - fn destroy(positioner: &zxdg_positioner_v6::ZxdgPositionerV6) { - let ptr = positioner.get_user_data(); - positioner.set_user_data(::std::ptr::null_mut()); - // drop the PositionerState - let surface = unsafe { Box::from_raw(ptr as *mut PositionerState) }; - // explicit call to drop to not forget what we're doing here - ::std::mem::drop(surface); - } +fn destroy_positioner(positioner: &zxdg_positioner_v6::ZxdgPositionerV6) { + let ptr = positioner.get_user_data(); + positioner.set_user_data(::std::ptr::null_mut()); + // drop the PositionerState + let surface = unsafe { Box::from_raw(ptr as *mut PositionerState) }; + // explicit call to drop to not forget what we're doing here + ::std::mem::drop(surface); } -impl zxdg_positioner_v6::Handler for ShellHandler -where - U: Send + 'static, - R: Role + Send + 'static, - H: CompositorHandler + Send + 'static, - SH: UserHandler + Send + 'static, - SD: Send + 'static, -{ - fn destroy(&mut self, _: &mut EventLoopHandle, _: &Client, _: &zxdg_positioner_v6::ZxdgPositionerV6) {} - - fn set_size(&mut self, _: &mut EventLoopHandle, _: &Client, - resource: &zxdg_positioner_v6::ZxdgPositionerV6, width: i32, height: i32) { - if width < 1 || height < 1 { - resource.post_error( +fn positioner_implementation() -> zxdg_positioner_v6::Implementation<()> { + zxdg_positioner_v6::Implementation { + destroy: |_, _, _, _| {}, + set_size: |_, _, _, positioner, width, height| if width < 1 || height < 1 { + positioner.post_error( zxdg_positioner_v6::Error::InvalidInput as u32, "Invalid size for positioner.".into(), ); } else { - let ptr = resource.get_user_data(); + let ptr = positioner.get_user_data(); let state = unsafe { &mut *(ptr as *mut PositionerState) }; state.rect_size = (width, height); - } - } - - fn set_anchor_rect(&mut self, _: &mut EventLoopHandle, _: &Client, - resource: &zxdg_positioner_v6::ZxdgPositionerV6, x: i32, y: i32, width: i32, - height: i32) { - if width < 1 || height < 1 { - resource.post_error( + }, + set_anchor_rect: |_, _, _, positioner, x, y, width, height| if width < 1 || height < 1 { + positioner.post_error( zxdg_positioner_v6::Error::InvalidInput as u32, "Invalid size for positioner's anchor rectangle.".into(), ); } else { - let ptr = resource.get_user_data(); + let ptr = positioner.get_user_data(); let state = unsafe { &mut *(ptr as *mut PositionerState) }; state.anchor_rect = Rectangle { x, @@ -171,246 +172,238 @@ where width, height, }; - } - } - - fn set_anchor(&mut self, _: &mut EventLoopHandle, _: &Client, - resource: &zxdg_positioner_v6::ZxdgPositionerV6, anchor: zxdg_positioner_v6::Anchor) { - use self::zxdg_positioner_v6::{AnchorBottom, AnchorLeft, AnchorRight, AnchorTop}; - if anchor.contains(AnchorLeft | AnchorRight) || anchor.contains(AnchorTop | AnchorBottom) { - resource.post_error( - zxdg_positioner_v6::Error::InvalidInput as u32, - "Invalid anchor for positioner.".into(), - ); - } else { - let ptr = resource.get_user_data(); + }, + set_anchor: |_, _, _, positioner, anchor| { + use self::zxdg_positioner_v6::{AnchorBottom, AnchorLeft, AnchorRight, AnchorTop}; + if anchor.contains(AnchorLeft | AnchorRight) || anchor.contains(AnchorTop | AnchorBottom) { + positioner.post_error( + zxdg_positioner_v6::Error::InvalidInput as u32, + "Invalid anchor for positioner.".into(), + ); + } else { + let ptr = positioner.get_user_data(); + let state = unsafe { &mut *(ptr as *mut PositionerState) }; + state.anchor_edges = anchor; + } + }, + set_gravity: |_, _, _, positioner, gravity| { + use self::zxdg_positioner_v6::{GravityBottom, GravityLeft, GravityRight, GravityTop}; + if gravity.contains(GravityLeft | GravityRight) || gravity.contains(GravityTop | GravityBottom) { + positioner.post_error( + zxdg_positioner_v6::Error::InvalidInput as u32, + "Invalid gravity for positioner.".into(), + ); + } else { + let ptr = positioner.get_user_data(); + let state = unsafe { &mut *(ptr as *mut PositionerState) }; + state.gravity = gravity; + } + }, + set_constraint_adjustment: |_, _, _, positioner, constraint_adjustment| { + let constraint_adjustment = + zxdg_positioner_v6::ConstraintAdjustment::from_bits_truncate(constraint_adjustment); + let ptr = positioner.get_user_data(); let state = unsafe { &mut *(ptr as *mut PositionerState) }; - state.anchor_edges = anchor; - } - } - - fn set_gravity(&mut self, _: &mut EventLoopHandle, _: &Client, - resource: &zxdg_positioner_v6::ZxdgPositionerV6, gravity: zxdg_positioner_v6::Gravity) { - use self::zxdg_positioner_v6::{GravityBottom, GravityLeft, GravityRight, GravityTop}; - if gravity.contains(GravityLeft | GravityRight) || gravity.contains(GravityTop | GravityBottom) { - resource.post_error( - zxdg_positioner_v6::Error::InvalidInput as u32, - "Invalid gravity for positioner.".into(), - ); - } else { - let ptr = resource.get_user_data(); + state.constraint_adjustment = constraint_adjustment; + }, + set_offset: |_, _, _, positioner, x, y| { + let ptr = positioner.get_user_data(); let state = unsafe { &mut *(ptr as *mut PositionerState) }; - state.gravity = gravity; - } - } - - fn set_constraint_adjustment(&mut self, _: &mut EventLoopHandle, _: &Client, - resource: &zxdg_positioner_v6::ZxdgPositionerV6, - constraint_adjustment: u32) { - let constraint_adjustment = - zxdg_positioner_v6::ConstraintAdjustment::from_bits_truncate(constraint_adjustment); - let ptr = resource.get_user_data(); - let state = unsafe { &mut *(ptr as *mut PositionerState) }; - state.constraint_adjustment = constraint_adjustment; - } - - fn set_offset(&mut self, _: &mut EventLoopHandle, _: &Client, - resource: &zxdg_positioner_v6::ZxdgPositionerV6, x: i32, y: i32) { - let ptr = resource.get_user_data(); - let state = unsafe { &mut *(ptr as *mut PositionerState) }; - state.offset = (x, y); + state.offset = (x, y); + }, } } -server_declare_handler!( - ShellHandler, Send], H:[CompositorHandler, Send], SH: [UserHandler, Send], SD: [Send]>, - zxdg_positioner_v6::Handler, - zxdg_positioner_v6::ZxdgPositionerV6 -); - /* * xdg_surface */ -impl Destroy for XdgShellDestructor { - fn destroy(surface: &zxdg_surface_v6::ZxdgSurfaceV6) { - let ptr = surface.get_user_data(); - surface.set_user_data(::std::ptr::null_mut()); - // drop the PositionerState - let data = unsafe { - Box::from_raw( - ptr as *mut (zxdg_surface_v6::ZxdgSurfaceV6, zxdg_shell_v6::ZxdgShellV6), - ) - }; - // explicit call to drop to not forget what we're doing here - ::std::mem::drop(data); - } +fn destroy_surface(surface: &zxdg_surface_v6::ZxdgSurfaceV6) { + let ptr = surface.get_user_data(); + surface.set_user_data(::std::ptr::null_mut()); + // drop the state + let data = unsafe { + Box::from_raw( + ptr as *mut (zxdg_surface_v6::ZxdgSurfaceV6, zxdg_shell_v6::ZxdgShellV6), + ) + }; + // explicit call to drop to not forget what we're doing here + ::std::mem::drop(data); } -impl zxdg_surface_v6::Handler for ShellHandler +fn surface_implementation( + ) + -> zxdg_surface_v6::Implementation> where - U: Send + 'static, - R: Role + Send + 'static, - H: CompositorHandler + Send + 'static, - SH: UserHandler + Send + 'static, - SD: Send + 'static, + U: 'static, + R: Role + 'static, + CID: 'static, + SID: 'static, + SD: 'static, { - fn destroy(&mut self, _: &mut EventLoopHandle, _: &Client, resource: &zxdg_surface_v6::ZxdgSurfaceV6) { - let ptr = resource.get_user_data(); - let &(ref surface, ref shell) = - unsafe { &*(ptr as *mut (wl_surface::WlSurface, zxdg_shell_v6::ZxdgShellV6)) }; - self.token - .with_role_data::(surface, |data| { - if let ShellSurfacePendingState::None = data.pending_state { - // all is good - } else { - shell.post_error( - zxdg_shell_v6::Error::Role as u32, - "xdg_surface was destroyed before its role object".into(), - ); - } - }) - .expect( - "xdg_surface exists but surface has not shell_surface role?!", - ); - } - - fn get_toplevel(&mut self, evlh: &mut EventLoopHandle, _: &Client, - resource: &zxdg_surface_v6::ZxdgSurfaceV6, id: zxdg_toplevel_v6::ZxdgToplevelV6) { - let ptr = resource.get_user_data(); - let &(ref surface, ref shell) = - unsafe { &*(ptr as *mut (wl_surface::WlSurface, zxdg_shell_v6::ZxdgShellV6)) }; - self.token - .with_role_data::(surface, |data| { - data.pending_state = ShellSurfacePendingState::Toplevel(ToplevelState { - parent: None, - title: String::new(), - app_id: String::new(), - min_size: (0, 0), - max_size: (0, 0), - }); - }) - .expect( - "xdg_surface exists but surface has not shell_surface role?!", - ); - - id.set_user_data(Box::into_raw(Box::new(unsafe { - ( - surface.clone_unchecked(), - shell.clone_unchecked(), - resource.clone_unchecked(), - ) - })) as *mut _); - evlh.register_with_destructor::<_, Self, XdgShellDestructor>(&id, self.my_id); - - // register to self - self.known_toplevels - .push(make_toplevel_handle(self.token, &id)); - - // intial configure event - let handle = make_toplevel_handle(self.token, &id); - let configure = self.handler.new_toplevel(evlh, handle); - send_toplevel_configure(self.token, &id, configure); - } - - fn get_popup(&mut self, evlh: &mut EventLoopHandle, _: &Client, - resource: &zxdg_surface_v6::ZxdgSurfaceV6, id: zxdg_popup_v6::ZxdgPopupV6, - parent: &zxdg_surface_v6::ZxdgSurfaceV6, - positioner: &zxdg_positioner_v6::ZxdgPositionerV6) { - let ptr = resource.get_user_data(); - let &(ref surface, ref shell) = - unsafe { &*(ptr as *mut (wl_surface::WlSurface, zxdg_shell_v6::ZxdgShellV6)) }; - - let positioner_data = unsafe { &*(positioner.get_user_data() as *const PositionerState) }; - - let parent_ptr = parent.get_user_data(); - let &(ref parent_surface, _) = - unsafe { &*(parent_ptr as *mut (wl_surface::WlSurface, zxdg_shell_v6::ZxdgShellV6)) }; - - self.token - .with_role_data::(surface, |data| { - data.pending_state = ShellSurfacePendingState::Popup(PopupState { - parent: unsafe { parent_surface.clone_unchecked() }, - positioner: positioner_data.clone(), - }); - }) - .expect( - "xdg_surface exists but surface has not shell_surface role?!", - ); - - id.set_user_data(Box::into_raw(Box::new(unsafe { - ( - surface.clone_unchecked(), - shell.clone_unchecked(), - resource.clone_unchecked(), - ) - })) as *mut _); - evlh.register_with_destructor::<_, Self, XdgShellDestructor>(&id, self.my_id); - - // register to self - self.known_popups.push(make_popup_handle(self.token, &id)); - - // intial configure event - let handle = make_popup_handle(self.token, &id); - let configure = self.handler.new_popup(evlh, handle); - send_popup_configure(self.token, &id, configure); - } - - fn set_window_geometry(&mut self, _: &mut EventLoopHandle, _: &Client, - resource: &zxdg_surface_v6::ZxdgSurfaceV6, x: i32, y: i32, width: i32, - height: i32) { - let ptr = resource.get_user_data(); - let &(ref surface, _) = - unsafe { &*(ptr as *mut (wl_surface::WlSurface, zxdg_shell_v6::ZxdgShellV6)) }; - self.token - .with_role_data::(surface, |data| { - data.window_geometry = Some(Rectangle { - x, - y, - width, - height, - }); - }) - .expect( - "xdg_surface exists but surface has not shell_surface role?!", - ); - } - - fn ack_configure(&mut self, _: &mut EventLoopHandle, _: &Client, - resource: &zxdg_surface_v6::ZxdgSurfaceV6, serial: u32) { - let ptr = resource.get_user_data(); - let &(ref surface, ref shell) = - unsafe { &*(ptr as *mut (wl_surface::WlSurface, zxdg_shell_v6::ZxdgShellV6)) }; - self.token - .with_role_data::(surface, |data| { - let mut found = false; - data.pending_configures.retain(|&s| { - if s == serial { - found = true; + zxdg_surface_v6::Implementation { + destroy: |_, idata, _, xdg_surface| { + let ptr = xdg_surface.get_user_data(); + let &(ref surface, ref shell) = + unsafe { &*(ptr as *mut (wl_surface::WlSurface, zxdg_shell_v6::ZxdgShellV6)) }; + idata + .compositor_token + .with_role_data::(surface, |data| { + if let ShellSurfacePendingState::None = data.pending_state { + // all is good + } else { + shell.post_error( + zxdg_shell_v6::Error::Role as u32, + "xdg_surface was destroyed before its role object".into(), + ); } - s > serial - }); - if !found { - // client responded to a non-existing configure - shell.post_error( - zxdg_shell_v6::Error::InvalidSurfaceState as u32, - format!("Wrong configure serial: {}", serial), - ); - } - data.configured = true; - }) - .expect( - "xdg_surface exists but surface has not shell_surface role?!", + }) + .expect( + "xdg_surface exists but surface has not shell_surface role?!", + ); + }, + get_toplevel: |evlh, idata, _, xdg_surface, toplevel| { + let ptr = xdg_surface.get_user_data(); + let &(ref surface, ref shell) = + unsafe { &*(ptr as *mut (wl_surface::WlSurface, zxdg_shell_v6::ZxdgShellV6)) }; + idata + .compositor_token + .with_role_data::(surface, |data| { + data.pending_state = ShellSurfacePendingState::Toplevel(ToplevelState { + parent: None, + title: String::new(), + app_id: String::new(), + min_size: (0, 0), + max_size: (0, 0), + }); + }) + .expect( + "xdg_surface exists but surface has not shell_surface role?!", + ); + + toplevel.set_user_data(Box::into_raw(Box::new(unsafe { + ( + surface.clone_unchecked(), + shell.clone_unchecked(), + xdg_surface.clone_unchecked(), + ) + })) as *mut _); + evlh.register( + &toplevel, + toplevel_implementation(), + idata.clone(), + Some(destroy_toplevel), ); + + // register to self + evlh.state() + .get_mut(&idata.state_token) + .known_toplevels + .push(make_toplevel_handle(idata.compositor_token, &toplevel)); + + // intial configure event + let handle = make_toplevel_handle(idata.compositor_token, &toplevel); + let mut user_idata = idata.idata.borrow_mut(); + let configure = (idata.implementation.new_toplevel)(evlh, &mut *user_idata, handle); + send_toplevel_configure(idata.compositor_token, &toplevel, configure); + }, + get_popup: |evlh, idata, _, xdg_surface, popup, parent, positioner| { + let ptr = xdg_surface.get_user_data(); + let &(ref surface, ref shell) = + unsafe { &*(ptr as *mut (wl_surface::WlSurface, zxdg_shell_v6::ZxdgShellV6)) }; + + let positioner_data = unsafe { &*(positioner.get_user_data() as *const PositionerState) }; + + let parent_ptr = parent.get_user_data(); + let &(ref parent_surface, _) = + unsafe { &*(parent_ptr as *mut (wl_surface::WlSurface, zxdg_shell_v6::ZxdgShellV6)) }; + + idata + .compositor_token + .with_role_data::(surface, |data| { + data.pending_state = ShellSurfacePendingState::Popup(PopupState { + parent: unsafe { parent_surface.clone_unchecked() }, + positioner: positioner_data.clone(), + }); + }) + .expect( + "xdg_surface exists but surface has not shell_surface role?!", + ); + + popup.set_user_data(Box::into_raw(Box::new(unsafe { + ( + surface.clone_unchecked(), + shell.clone_unchecked(), + xdg_surface.clone_unchecked(), + ) + })) as *mut _); + evlh.register( + &popup, + popup_implementation(), + idata.clone(), + Some(destroy_popup), + ); + + // register to self + evlh.state() + .get_mut(&idata.state_token) + .known_popups + .push(make_popup_handle(idata.compositor_token, &popup)); + + // intial configure event + let handle = make_popup_handle(idata.compositor_token, &popup); + let mut user_idata = idata.idata.borrow_mut(); + let configure = (idata.implementation.new_popup)(evlh, &mut *user_idata, handle); + send_popup_configure(idata.compositor_token, &popup, configure); + }, + set_window_geometry: |_, idata, _, surface, x, y, width, height| { + let ptr = surface.get_user_data(); + let &(ref surface, _) = + unsafe { &*(ptr as *mut (wl_surface::WlSurface, zxdg_shell_v6::ZxdgShellV6)) }; + idata + .compositor_token + .with_role_data::(surface, |data| { + data.window_geometry = Some(Rectangle { + x, + y, + width, + height, + }); + }) + .expect( + "xdg_surface exists but surface has not shell_surface role?!", + ); + }, + ack_configure: |_, idata, _, surface, serial| { + let ptr = surface.get_user_data(); + let &(ref surface, ref shell) = + unsafe { &*(ptr as *mut (wl_surface::WlSurface, zxdg_shell_v6::ZxdgShellV6)) }; + idata + .compositor_token + .with_role_data::(surface, |data| { + let mut found = false; + data.pending_configures.retain(|&s| { + if s == serial { + found = true; + } + s > serial + }); + if !found { + // client responded to a non-existing configure + shell.post_error( + zxdg_shell_v6::Error::InvalidSurfaceState as u32, + format!("Wrong configure serial: {}", serial), + ); + } + data.configured = true; + }) + .expect( + "xdg_surface exists but surface has not shell_surface role?!", + ); + }, } } -server_declare_handler!( - ShellHandler, Send], H:[CompositorHandler, Send], SH: [UserHandler, Send], SD: [Send]>, - zxdg_surface_v6::Handler, - zxdg_surface_v6::ZxdgSurfaceV6 -); - /* * xdg_toplevel */ @@ -421,63 +414,76 @@ pub type ShellSurfaceUserData = ( zxdg_surface_v6::ZxdgSurfaceV6, ); -impl Destroy for XdgShellDestructor { - fn destroy(surface: &zxdg_toplevel_v6::ZxdgToplevelV6) { - let ptr = surface.get_user_data(); - surface.set_user_data(::std::ptr::null_mut()); - // drop the PositionerState - let data = unsafe { Box::from_raw(ptr as *mut ShellSurfaceUserData) }; - // explicit call to drop to not forget what we're doing there - ::std::mem::drop(data); - } +fn destroy_toplevel(surface: &zxdg_toplevel_v6::ZxdgToplevelV6) { + let ptr = surface.get_user_data(); + surface.set_user_data(::std::ptr::null_mut()); + // drop the PositionerState + let data = unsafe { Box::from_raw(ptr as *mut ShellSurfaceUserData) }; + // explicit call to drop to not forget what we're doing there + ::std::mem::drop(data); } -impl ShellHandler +// Utility functions allowing to factor out a lot of the upcoming logic +fn with_surface_toplevel_data(idata: &ShellSurfaceIData, + toplevel: &zxdg_toplevel_v6::ZxdgToplevelV6, f: F) where - U: Send + 'static, - R: Role + Send + 'static, - H: CompositorHandler + Send + 'static, - SH: UserHandler + Send + 'static, - SD: Send + 'static, + U: 'static, + R: Role + 'static, + CID: 'static, + SID: 'static, + SD: 'static, + F: FnOnce(&mut ToplevelState), { - // Utility function allowing to factor out a lot of the upcoming logic - fn with_surface_toplevel_data(&self, resource: &zxdg_toplevel_v6::ZxdgToplevelV6, f: F) - where - F: FnOnce(&mut ToplevelState), - { - let ptr = resource.get_user_data(); - let &(ref surface, _, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) }; - self.token - .with_role_data::(surface, |data| match data.pending_state { - ShellSurfacePendingState::Toplevel(ref mut toplevel_data) => f(toplevel_data), - _ => unreachable!(), - }) - .expect( - "xdg_toplevel exists but surface has not shell_surface role?!", - ); - } - - fn xdg_handle_display_state_change(&mut self, evlh: &mut EventLoopHandle, - resource: &zxdg_toplevel_v6::ZxdgToplevelV6, - maximized: Option, minimized: Option, - fullscreen: Option, output: Option<&wl_output::WlOutput>) { - let handle = make_toplevel_handle(self.token, resource); - // handler callback - let configure = - self.handler - .change_display_state(evlh, handle, maximized, minimized, fullscreen, output); - // send the configure response to client - send_toplevel_configure(self.token, resource, configure); - } + let ptr = toplevel.get_user_data(); + let &(ref surface, _, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) }; + idata + .compositor_token + .with_role_data::(surface, |data| match data.pending_state { + ShellSurfacePendingState::Toplevel(ref mut toplevel_data) => f(toplevel_data), + _ => unreachable!(), + }) + .expect( + "xdg_toplevel exists but surface has not shell_surface role?!", + ); } -pub fn send_toplevel_configure(token: CompositorToken, - resource: &zxdg_toplevel_v6::ZxdgToplevelV6, - configure: ToplevelConfigure) +fn xdg_handle_display_state_change(evlh: &mut EventLoopHandle, + idata: &ShellSurfaceIData, + toplevel: &zxdg_toplevel_v6::ZxdgToplevelV6, + maximized: Option, minimized: Option, + fullscreen: Option, + output: Option<&wl_output::WlOutput>) where - U: Send + 'static, - R: Role + Send + 'static, - H: CompositorHandler + Send + 'static, + U: 'static, + R: Role + 'static, + CID: 'static, + SID: 'static, + SD: 'static, +{ + let handle = make_toplevel_handle(idata.compositor_token, toplevel); + // handler callback + let mut user_idata = idata.idata.borrow_mut(); + let configure = (idata.implementation.change_display_state)( + evlh, + &mut *user_idata, + handle, + maximized, + minimized, + fullscreen, + output, + ); + // send the configure response to client + send_toplevel_configure(idata.compositor_token, toplevel, configure); +} + + +pub fn send_toplevel_configure(token: CompositorToken, + resource: &zxdg_toplevel_v6::ZxdgToplevelV6, + configure: ToplevelConfigure) +where + U: 'static, + R: Role + 'static, + ID: 'static, { let &(ref surface, _, ref shell_surface) = unsafe { &*(resource.get_user_data() as *mut ShellSurfaceUserData) }; @@ -515,148 +521,125 @@ fn make_toplevel_handle(token: CompositorToken, } } -impl zxdg_toplevel_v6::Handler for ShellHandler +fn toplevel_implementation( + ) + -> zxdg_toplevel_v6::Implementation> where - U: Send + 'static, - R: Role + Send + 'static, - H: CompositorHandler + Send + 'static, - SH: UserHandler + Send + 'static, - SD: Send + 'static, + U: 'static, + R: Role + 'static, + CID: 'static, + SID: 'static, + SD: 'static, { - fn destroy(&mut self, _: &mut EventLoopHandle, _: &Client, resource: &zxdg_toplevel_v6::ZxdgToplevelV6) { - let ptr = resource.get_user_data(); - let &(ref surface, _, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) }; - self.token - .with_role_data::(surface, |data| { - data.pending_state = ShellSurfacePendingState::None; - data.configured = false; + zxdg_toplevel_v6::Implementation { + destroy: |evlh, idata, _, toplevel| { + let ptr = toplevel.get_user_data(); + let &(ref surface, _, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) }; + idata + .compositor_token + .with_role_data::(surface, |data| { + data.pending_state = ShellSurfacePendingState::None; + data.configured = false; + }) + .expect( + "xdg_toplevel exists but surface has not shell_surface role?!", + ); + // remove this surface from the known ones (as well as any leftover dead surface) + evlh.state() + .get_mut(&idata.state_token) + .known_toplevels + .retain(|other| { + other + .get_surface() + .map(|s| !s.equals(surface)) + .unwrap_or(false) + }); + }, + set_parent: |_, idata, _, toplevel, parent| { + with_surface_toplevel_data(idata, toplevel, |toplevel_data| { + toplevel_data.parent = parent.map(|toplevel_surface_parent| { + let parent_ptr = toplevel_surface_parent.get_user_data(); + let &(ref parent_surface, _) = + unsafe { &*(parent_ptr as *mut (wl_surface::WlSurface, zxdg_shell_v6::ZxdgShellV6)) }; + unsafe { parent_surface.clone_unchecked() } + }) + }); + }, + set_title: |_, idata, _, toplevel, title| { + with_surface_toplevel_data(idata, toplevel, |toplevel_data| { + toplevel_data.title = title; + }); + }, + set_app_id: |_, idata, _, toplevel, app_id| { + with_surface_toplevel_data(idata, toplevel, |toplevel_data| { + toplevel_data.app_id = app_id; + }); + }, + show_window_menu: |evlh, idata, _, toplevel, seat, serial, x, y| { + let handle = make_toplevel_handle(idata.compositor_token, toplevel); + let mut user_idata = idata.idata.borrow_mut(); + (idata.implementation.show_window_menu)(evlh, &mut *user_idata, handle, seat, serial, x, y) + }, + move_: |evlh, idata, _, toplevel, seat, serial| { + let handle = make_toplevel_handle(idata.compositor_token, toplevel); + let mut user_idata = idata.idata.borrow_mut(); + (idata.implementation.move_)(evlh, &mut *user_idata, handle, seat, serial) + }, + resize: |evlh, idata, _, toplevel, seat, serial, edges| { + let edges = + zxdg_toplevel_v6::ResizeEdge::from_raw(edges).unwrap_or(zxdg_toplevel_v6::ResizeEdge::None); + let handle = make_toplevel_handle(idata.compositor_token, toplevel); + let mut user_idata = idata.idata.borrow_mut(); + (idata.implementation.resize)(evlh, &mut *user_idata, handle, seat, serial, edges) + }, + set_max_size: |_, idata, _, toplevel, width, height| { + with_surface_toplevel_data(idata, toplevel, |toplevel_data| { + toplevel_data.max_size = (width, height); }) - .expect( - "xdg_toplevel exists but surface has not shell_surface role?!", - ); - // remove this surface from the known ones (as well as any leftover dead surface) - self.known_toplevels.retain(|other| { - other - .get_surface() - .map(|s| !s.equals(surface)) - .unwrap_or(false) - }); - } - - fn set_parent(&mut self, _: &mut EventLoopHandle, _: &Client, - resource: &zxdg_toplevel_v6::ZxdgToplevelV6, - parent: Option<&zxdg_toplevel_v6::ZxdgToplevelV6>) { - self.with_surface_toplevel_data(resource, |toplevel_data| { - toplevel_data.parent = parent.map(|toplevel_surface_parent| { - let parent_ptr = toplevel_surface_parent.get_user_data(); - let &(ref parent_surface, _) = - unsafe { &*(parent_ptr as *mut (wl_surface::WlSurface, zxdg_shell_v6::ZxdgShellV6)) }; - unsafe { parent_surface.clone_unchecked() } + }, + set_min_size: |_, idata, _, toplevel, width, height| { + with_surface_toplevel_data(idata, toplevel, |toplevel_data| { + toplevel_data.min_size = (width, height); }) - }); - } - - fn set_title(&mut self, _: &mut EventLoopHandle, _: &Client, - resource: &zxdg_toplevel_v6::ZxdgToplevelV6, title: String) { - self.with_surface_toplevel_data(resource, |toplevel_data| { toplevel_data.title = title; }); - } - - fn set_app_id(&mut self, _: &mut EventLoopHandle, _: &Client, - resource: &zxdg_toplevel_v6::ZxdgToplevelV6, app_id: String) { - self.with_surface_toplevel_data(resource, |toplevel_data| { toplevel_data.app_id = app_id; }); - } - - fn show_window_menu(&mut self, evlh: &mut EventLoopHandle, _: &Client, - resource: &zxdg_toplevel_v6::ZxdgToplevelV6, seat: &wl_seat::WlSeat, serial: u32, - x: i32, y: i32) { - let handle = make_toplevel_handle(self.token, resource); - self.handler - .show_window_menu(evlh, handle, seat, serial, x, y); - } - - fn move_(&mut self, evlh: &mut EventLoopHandle, _: &Client, - resource: &zxdg_toplevel_v6::ZxdgToplevelV6, seat: &wl_seat::WlSeat, serial: u32) { - let handle = make_toplevel_handle(self.token, resource); - self.handler.move_(evlh, handle, seat, serial); - } - - fn resize(&mut self, evlh: &mut EventLoopHandle, _: &Client, - resource: &zxdg_toplevel_v6::ZxdgToplevelV6, seat: &wl_seat::WlSeat, serial: u32, edges: u32) { - let edges = - zxdg_toplevel_v6::ResizeEdge::from_raw(edges).unwrap_or(zxdg_toplevel_v6::ResizeEdge::None); - let handle = make_toplevel_handle(self.token, resource); - self.handler.resize(evlh, handle, seat, serial, edges); - } - - fn set_max_size(&mut self, _: &mut EventLoopHandle, _: &Client, - resource: &zxdg_toplevel_v6::ZxdgToplevelV6, width: i32, height: i32) { - self.with_surface_toplevel_data(resource, |toplevel_data| { - toplevel_data.max_size = (width, height); - }); - } - - fn set_min_size(&mut self, _: &mut EventLoopHandle, _: &Client, - resource: &zxdg_toplevel_v6::ZxdgToplevelV6, width: i32, height: i32) { - self.with_surface_toplevel_data(resource, |toplevel_data| { - toplevel_data.min_size = (width, height); - }); - } - - fn set_maximized(&mut self, evlh: &mut EventLoopHandle, _: &Client, - resource: &zxdg_toplevel_v6::ZxdgToplevelV6) { - self.xdg_handle_display_state_change(evlh, resource, Some(true), None, None, None); - } - - fn unset_maximized(&mut self, evlh: &mut EventLoopHandle, _: &Client, - resource: &zxdg_toplevel_v6::ZxdgToplevelV6) { - self.xdg_handle_display_state_change(evlh, resource, Some(false), None, None, None); - } - - fn set_fullscreen(&mut self, evlh: &mut EventLoopHandle, _: &Client, - resource: &zxdg_toplevel_v6::ZxdgToplevelV6, output: Option<&wl_output::WlOutput>) { - self.xdg_handle_display_state_change(evlh, resource, None, None, Some(true), output); - } - - fn unset_fullscreen(&mut self, evlh: &mut EventLoopHandle, _: &Client, - resource: &zxdg_toplevel_v6::ZxdgToplevelV6) { - self.xdg_handle_display_state_change(evlh, resource, None, None, Some(false), None); - } - - fn set_minimized(&mut self, evlh: &mut EventLoopHandle, _: &Client, - resource: &zxdg_toplevel_v6::ZxdgToplevelV6) { - self.xdg_handle_display_state_change(evlh, resource, None, Some(true), None, None); + }, + set_maximized: |evlh, idata, _, toplevel| { + xdg_handle_display_state_change(evlh, idata, toplevel, Some(true), None, None, None); + }, + unset_maximized: |evlh, idata, _, toplevel| { + xdg_handle_display_state_change(evlh, idata, toplevel, Some(false), None, None, None); + }, + set_fullscreen: |evlh, idata, _, toplevel, seat| { + xdg_handle_display_state_change(evlh, idata, toplevel, None, None, Some(true), seat); + }, + unset_fullscreen: |evlh, idata, _, toplevel| { + xdg_handle_display_state_change(evlh, idata, toplevel, None, None, Some(false), None); + }, + set_minimized: |evlh, idata, _, toplevel| { + xdg_handle_display_state_change(evlh, idata, toplevel, None, Some(true), None, None); + }, } } -server_declare_handler!( - ShellHandler, Send], H:[CompositorHandler, Send], SH: [UserHandler, Send], SD: [Send]>, - zxdg_toplevel_v6::Handler, - zxdg_toplevel_v6::ZxdgToplevelV6 -); - /* * xdg_popup */ - - -impl Destroy for XdgShellDestructor { - fn destroy(surface: &zxdg_popup_v6::ZxdgPopupV6) { - let ptr = surface.get_user_data(); - surface.set_user_data(::std::ptr::null_mut()); - // drop the PositionerState - let data = unsafe { Box::from_raw(ptr as *mut ShellSurfaceUserData) }; - // explicit call to drop to not forget what we're doing - ::std::mem::drop(data); - } +fn destroy_popup(surface: &zxdg_popup_v6::ZxdgPopupV6) { + let ptr = surface.get_user_data(); + surface.set_user_data(::std::ptr::null_mut()); + // drop the PositionerState + let data = unsafe { Box::from_raw(ptr as *mut ShellSurfaceUserData) }; + // explicit call to drop to not forget what we're doing + ::std::mem::drop(data); } -pub fn send_popup_configure(token: CompositorToken, resource: &zxdg_popup_v6::ZxdgPopupV6, - configure: PopupConfigure) +pub(crate) fn send_popup_configure(token: CompositorToken, + resource: &zxdg_popup_v6::ZxdgPopupV6, + configure: PopupConfigure) where - U: Send + 'static, - R: Role + Send + 'static, - H: CompositorHandler + Send + 'static, + U: 'static, + R: Role + 'static, + ID: 'static, { let &(ref surface, _, ref shell_surface) = unsafe { &*(resource.get_user_data() as *mut ShellSurfaceUserData) }; @@ -685,50 +668,51 @@ fn make_popup_handle(token: CompositorToken, resource: &zx } } -impl zxdg_popup_v6::Handler for ShellHandler +fn popup_implementation( + ) + -> zxdg_popup_v6::Implementation> where - U: Send + 'static, - R: Role + Send + 'static, - H: CompositorHandler + Send + 'static, - SH: UserHandler + Send + 'static, - SD: Send + 'static, + U: 'static, + R: Role + 'static, + CID: 'static, + SID: 'static, + SD: 'static, { - fn destroy(&mut self, _: &mut EventLoopHandle, _: &Client, resource: &zxdg_popup_v6::ZxdgPopupV6) { - let ptr = resource.get_user_data(); - let &(ref surface, _, _) = unsafe { - &*(ptr as - *mut ( - wl_surface::WlSurface, - zxdg_shell_v6::ZxdgShellV6, - zxdg_surface_v6::ZxdgSurfaceV6, - )) - }; - self.token - .with_role_data::(surface, |data| { - data.pending_state = ShellSurfacePendingState::None; - data.configured = false; - }) - .expect( - "xdg_toplevel exists but surface has not shell_surface role?!", - ); - // remove this surface from the known ones (as well as any leftover dead surface) - self.known_popups.retain(|other| { - other - .get_surface() - .map(|s| !s.equals(surface)) - .unwrap_or(false) - }); - } - - fn grab(&mut self, evlh: &mut EventLoopHandle, _: &Client, resource: &zxdg_popup_v6::ZxdgPopupV6, - seat: &wl_seat::WlSeat, serial: u32) { - let handle = make_popup_handle(self.token, resource); - self.handler.grab(evlh, handle, seat, serial); + zxdg_popup_v6::Implementation { + destroy: |evlh, idata, _, popup| { + let ptr = popup.get_user_data(); + let &(ref surface, _, _) = unsafe { + &*(ptr + as *mut ( + wl_surface::WlSurface, + zxdg_shell_v6::ZxdgShellV6, + zxdg_surface_v6::ZxdgSurfaceV6, + )) + }; + idata + .compositor_token + .with_role_data::(surface, |data| { + data.pending_state = ShellSurfacePendingState::None; + data.configured = false; + }) + .expect( + "xdg_toplevel exists but surface has not shell_surface role?!", + ); + // remove this surface from the known ones (as well as any leftover dead surface) + evlh.state() + .get_mut(&idata.state_token) + .known_popups + .retain(|other| { + other + .get_surface() + .map(|s| !s.equals(surface)) + .unwrap_or(false) + }); + }, + grab: |evlh, idata, _, popup, seat, serial| { + let handle = make_popup_handle(idata.compositor_token, popup); + let mut user_idata = idata.idata.borrow_mut(); + (idata.implementation.grab)(evlh, &mut *user_idata, handle, seat, serial) + }, } } - -server_declare_handler!( - ShellHandler, Send], H:[CompositorHandler, Send], SH: [UserHandler, Send], SD: [Send]>, - zxdg_popup_v6::Handler, - zxdg_popup_v6::ZxdgPopupV6 -); diff --git a/src/shm/mod.rs b/src/shm/mod.rs index c0e05d5..88ff5e1 100644 --- a/src/shm/mod.rs +++ b/src/shm/mod.rs @@ -19,39 +19,39 @@ //! extern crate wayland_server; //! extern crate smithay; //! -//! use smithay::shm::ShmGlobal; -//! use wayland_server::protocol::wl_shm; +//! use smithay::shm::init_shm_global; +//! use wayland_server::protocol::wl_shm::Format; //! //! # fn main() { //! # let (_, mut event_loop) = wayland_server::create_display(); -//! -//! // Insert the ShmGlobal as a handler to your event loop +//! // Insert the ShmGlobal into your event loop //! // Here, we specify that Yuyv and C8 format are supported //! // additionnaly to the standart Argb8888 and Xrgb8888. -//! let handler_id = event_loop.add_handler_with_init(ShmGlobal::new( -//! vec![wl_shm::Format::Yuyv, wl_shm::Format::C8], +//! let shm_global = init_shm_global( +//! &mut event_loop, +//! vec![Format::Yuyv, Format::C8], //! None // we don't provide a logger here -//! )); -//! // 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 -//! let shm_token = { -//! let state = event_loop.state(); -//! state.get_handler::(handler_id).get_token() -//! }; -//! +//! ); //! # } //! ``` //! //! Then, when you have a `WlBuffer` and need to retrieve its contents, use the token method to //! do it: //! -//! ```ignore -//! shm_token.with_buffer_contents(&buffer, -//! |slice: &[u8], buffer_metadata: &BufferData| { +//! ``` +//! # extern crate wayland_server; +//! # extern crate smithay; +//! # use wayland_server::protocol::wl_buffer::WlBuffer; +//! # fn wrap(buffer: &WlBuffer) { +//! use smithay::shm::{with_buffer_contents, BufferData}; +//! +//! with_buffer_contents(&buffer, +//! |slice: &[u8], buffer_metadata: BufferData| { //! // do something to draw it on the screen //! } //! ); +//! # } +//! # fn main() {} //! ``` //! //! **Note** @@ -63,67 +63,50 @@ use self::pool::{Pool, ResizeError}; - -use std::os::unix::io::RawFd; +use std::rc::Rc; use std::sync::Arc; - -use wayland_server::{resource_is_registered, Client, Destroy, EventLoopHandle, GlobalHandler, Init, Resource}; +use wayland_server::{resource_is_registered, Client, EventLoop, EventLoopHandle, Global, Resource}; use wayland_server::protocol::{wl_buffer, wl_shm, wl_shm_pool}; mod pool; -/// A global for handling SHM pool and buffers +#[derive(Clone)] +/// Internal data storage of ShmGlobal /// -/// You must register it to an event loop using `register_with_init`, or it will -/// quickly panic. -pub struct ShmGlobal { - formats: Vec, - handler_id: Option, +/// This type is only visible as type parameter of +/// the `Global` handle you are provided. +pub struct ShmGlobalData { + formats: Rc>, log: ::slog::Logger, } -impl ShmGlobal { - /// Create a new SHM global advertizing given supported formats. - /// - /// This global will always advertize `ARGB8888` and `XRGB8888` format - /// as they are required by the protocol. Formats given as argument - /// as additionnaly advertized. - pub fn new(mut formats: Vec, logger: L) -> ShmGlobal - where - L: Into>, - { - let log = ::slog_or_stdlog(logger); - - // always add the mandatory formats - formats.push(wl_shm::Format::Argb8888); - formats.push(wl_shm::Format::Xrgb8888); - ShmGlobal { - formats: formats, - handler_id: None, - log: log.new(o!("smithay_module" => "shm_handler")), - } - } - - /// Retreive a token from the SHM global. - /// - /// This can only be called once the `ShmGlobal` has been added to and event loop - /// 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) -> ShmToken { - ShmToken { - hid: self.handler_id.expect("ShmGlobal was not initialized."), - } - } -} - -/// An SHM global token +/// Create a new SHM global advertizing given supported formats. /// -/// It is needed to access the contents of the buffers & pools managed by the -/// associated `ShmGlobal`. -#[derive(Clone)] -pub struct ShmToken { - hid: usize, +/// This global will always advertize `ARGB8888` and `XRGB8888` format +/// as they are required by the protocol. Formats given as argument +/// as additionnaly advertized. +/// +/// The global is directly registered into the eventloop, and this function +/// returns the global handle, in case you whish to remove this global in +/// the future. +pub fn init_shm_global(evl: &mut EventLoop, mut formats: Vec, logger: L) + -> Global +where + L: Into>, +{ + let log = ::slog_or_stdlog(logger); + + // always add the mandatory formats + formats.push(wl_shm::Format::Argb8888); + formats.push(wl_shm::Format::Xrgb8888); + let data = ShmGlobalData { + formats: Rc::new(formats), + log: log.new(o!("smithay_module" => "shm_handler")), + }; + + let global = evl.register_global::(1, shm_global_bind, data); + + global } /// Error that can occur when accessing an SHM buffer @@ -140,109 +123,82 @@ pub enum BufferAccessError { BadMap, } -impl ShmToken { - /// Call given closure with the contents of the given buffer - /// - /// If the buffer is managed by the associated ShmGlobal, its contents are - /// extracted and the closure is extracted with them: - /// - /// - The first argument is a data slice of the contents of the pool - /// - The second argument is the specification of this buffer is this pool - /// - /// If the buffer is not managed by the associated ShmGlobal, the closure is not called - /// and this method will return `Err(())` (this will be the case for an EGL buffer for example). - pub fn with_buffer_contents(&self, buffer: &wl_buffer::WlBuffer, f: F) -> Result<(), BufferAccessError> - where - F: FnOnce(&[u8], BufferData), +/// Call given closure with the contents of the given buffer +/// +/// If the buffer is managed by the provided ShmGlobal, its contents are +/// extracted and the closure is extracted with them: +/// +/// - The first argument is a data slice of the contents of the pool +/// - The second argument is the specification of this buffer is this pool +/// +/// If the buffer is not managed by the provided ShmGlobal, the closure is not called +/// and this method will return `Err(())` (this will be the case for an EGL buffer for example). +pub fn with_buffer_contents(buffer: &wl_buffer::WlBuffer, f: F) -> Result<(), BufferAccessError> +where + F: FnOnce(&[u8], BufferData), +{ + if !resource_is_registered(buffer, &buffer_implementation()) { + return Err(BufferAccessError::NotManaged); + } + let data = unsafe { &*(buffer.get_user_data() as *mut InternalBufferData) }; + + if data.pool + .with_data_slice(|slice| f(slice, data.data)) + .is_err() { - if !resource_is_registered::<_, ShmHandler>(buffer, self.hid) { - return Err(BufferAccessError::NotManaged); - } - let data = unsafe { &*(buffer.get_user_data() as *mut InternalBufferData) }; + // SIGBUS error occured + buffer.post_error(wl_shm::Error::InvalidFd as u32, "Bad pool size.".into()); + return Err(BufferAccessError::BadMap); + } + Ok(()) +} - if data.pool - .with_data_slice(|slice| f(slice, data.data)) - .is_err() - { - // SIGBUS error occured - buffer.post_error(wl_shm::Error::InvalidFd as u32, "Bad pool size.".into()); - return Err(BufferAccessError::BadMap); - } - Ok(()) +fn shm_global_bind(evlh: &mut EventLoopHandle, data: &mut ShmGlobalData, _: &Client, global: wl_shm::WlShm) { + // register an handler for this shm + evlh.register(&global, shm_implementation(), data.clone(), None); + // and then the custom formats + for f in &data.formats[..] { + global.format(*f); } } -impl Init for ShmGlobal { - fn init(&mut self, evqh: &mut EventLoopHandle, _index: usize) { - let id = evqh.add_handler_with_init(ShmHandler { - my_id: ::std::usize::MAX, - valid_formats: self.formats.clone(), - log: self.log.clone(), - }); - self.handler_id = Some(id); - } -} - -impl GlobalHandler for ShmGlobal { - fn bind(&mut self, evqh: &mut EventLoopHandle, _: &Client, global: wl_shm::WlShm) { - let hid = self.handler_id.expect("ShmGlobal was not initialized."); - // register an handler for this shm - evqh.register::<_, ShmHandler>(&global, hid); - // and then the custom formats - for f in &self.formats { - global.format(*f); - } - } -} - -struct ShmHandler { - my_id: usize, - valid_formats: Vec, - log: ::slog::Logger, -} - -impl Init for ShmHandler { - fn init(&mut self, _evqh: &mut EventLoopHandle, index: usize) { - self.my_id = index; - debug!(self.log, "Init finished") - } -} - -impl wl_shm::Handler for ShmHandler { - fn create_pool(&mut self, evqh: &mut EventLoopHandle, _client: &Client, shm: &wl_shm::WlShm, - pool: wl_shm_pool::WlShmPool, fd: RawFd, size: i32) { - if size <= 0 { - shm.post_error( - wl_shm::Error::InvalidFd as u32, - "Invalid size for a new wl_shm_pool.".into(), - ); - return; - } - let mmap_pool = match Pool::new(fd, size as usize, self.log.clone()) { - Ok(p) => p, - Err(()) => { +fn shm_implementation() -> wl_shm::Implementation { + wl_shm::Implementation { + create_pool: |evlh, data, _, shm, pool, fd, size| { + if size <= 0 { shm.post_error( wl_shm::Error::InvalidFd as u32, - format!("Failed mmap of fd {}.", fd), + "Invalid size for a new wl_shm_pool.".into(), ); return; } - }; - let arc_pool = Box::new(Arc::new(mmap_pool)); - evqh.register_with_destructor::<_, ShmHandler, ShmHandler>(&pool, self.my_id); - pool.set_user_data(Box::into_raw(arc_pool) as *mut ()); + let mmap_pool = match Pool::new(fd, size as usize, data.log.clone()) { + Ok(p) => p, + Err(()) => { + shm.post_error( + wl_shm::Error::InvalidFd as u32, + format!("Failed mmap of fd {}.", fd), + ); + return; + } + }; + let arc_pool = Box::new(Arc::new(mmap_pool)); + evlh.register( + &pool, + shm_pool_implementation(), + data.clone(), + Some(destroy_shm_pool), + ); + pool.set_user_data(Box::into_raw(arc_pool) as *mut ()); + }, } } -impl Destroy for ShmHandler { - fn destroy(pool: &wl_shm_pool::WlShmPool) { - let arc_pool = unsafe { Box::from_raw(pool.get_user_data() as *mut Arc) }; - drop(arc_pool) - } +fn destroy_shm_pool(pool: &wl_shm_pool::WlShmPool) { + let arc_pool = unsafe { Box::from_raw(pool.get_user_data() as *mut Arc) }; + drop(arc_pool) } -declare_handler!(ShmHandler, wl_shm::Handler, wl_shm::WlShm); - /// Details of the contents of a buffer relative to its pool #[derive(Copy, Clone, Debug)] pub struct BufferData { @@ -263,56 +219,53 @@ struct InternalBufferData { data: BufferData, } -impl wl_shm_pool::Handler for ShmHandler { - fn create_buffer(&mut self, evqh: &mut EventLoopHandle, _client: &Client, - pool: &wl_shm_pool::WlShmPool, buffer: wl_buffer::WlBuffer, offset: i32, width: i32, - height: i32, stride: i32, format: wl_shm::Format) { - if !self.valid_formats.contains(&format) { - buffer.post_error(wl_shm::Error::InvalidFormat as u32, String::new()); - return; - } - let arc_pool = unsafe { &*(pool.get_user_data() as *mut Arc) }; - let data = Box::into_raw(Box::new(InternalBufferData { - pool: arc_pool.clone(), - data: BufferData { - offset: offset, - width: width, - height: height, - stride: stride, - format: format, - }, - })); - evqh.register_with_destructor::<_, ShmHandler, ShmHandler>(&buffer, self.my_id); - buffer.set_user_data(data as *mut ()); - } - - fn resize(&mut self, _evqh: &mut EventLoopHandle, _client: &Client, pool: &wl_shm_pool::WlShmPool, - size: i32) { - let arc_pool = unsafe { &*(pool.get_user_data() as *mut Arc) }; - match arc_pool.resize(size) { - Ok(()) => {} - Err(ResizeError::InvalidSize) => { - pool.post_error( - wl_shm::Error::InvalidFd as u32, - "Invalid new size for a wl_shm_pool.".into(), - ); +fn shm_pool_implementation() -> wl_shm_pool::Implementation { + wl_shm_pool::Implementation { + create_buffer: |evlh, data, _, pool, buffer, offset, width, height, stride, format| { + if !data.formats.contains(&format) { + buffer.post_error(wl_shm::Error::InvalidFormat as u32, String::new()); + return; } - Err(ResizeError::MremapFailed) => { - pool.post_error(wl_shm::Error::InvalidFd as u32, "mremap failed.".into()); + let arc_pool = unsafe { &*(pool.get_user_data() as *mut Arc) }; + let data = Box::into_raw(Box::new(InternalBufferData { + pool: arc_pool.clone(), + data: BufferData { + offset: offset, + width: width, + height: height, + stride: stride, + format: format, + }, + })); + evlh.register(&buffer, buffer_implementation(), (), Some(destroy_buffer)); + buffer.set_user_data(data as *mut ()); + }, + resize: |_, _, _, pool, size| { + let arc_pool = unsafe { &*(pool.get_user_data() as *mut Arc) }; + match arc_pool.resize(size) { + Ok(()) => {} + Err(ResizeError::InvalidSize) => { + pool.post_error( + wl_shm::Error::InvalidFd as u32, + "Invalid new size for a wl_shm_pool.".into(), + ); + } + Err(ResizeError::MremapFailed) => { + pool.post_error(wl_shm::Error::InvalidFd as u32, "mremap failed.".into()); + } } - } + }, + destroy: |_, _, _, _| {}, } } -impl Destroy for ShmHandler { - fn destroy(buffer: &wl_buffer::WlBuffer) { - let buffer_data = unsafe { Box::from_raw(buffer.get_user_data() as *mut InternalBufferData) }; - drop(buffer_data) - } +fn destroy_buffer(buffer: &wl_buffer::WlBuffer) { + let buffer_data = unsafe { Box::from_raw(buffer.get_user_data() as *mut InternalBufferData) }; + drop(buffer_data) } -declare_handler!(ShmHandler, wl_shm_pool::Handler, wl_shm_pool::WlShmPool); - -impl wl_buffer::Handler for ShmHandler {} - -declare_handler!(ShmHandler, wl_buffer::Handler, wl_buffer::WlBuffer); +fn buffer_implementation() -> wl_buffer::Implementation<()> { + wl_buffer::Implementation { + destroy: |_, _, _, _| {}, + } +}