diff --git a/src/compositor.rs b/src/compositor.rs index 70ec31a4..ba77f312 100644 --- a/src/compositor.rs +++ b/src/compositor.rs @@ -9,15 +9,15 @@ use errors::{HandleErr, HandleResult}; use types::surface::{InternalSurface, InternalSurfaceState}; use extensions::server_decoration::ServerDecorationManager; use manager::{InputManager, InputManagerHandler, OutputManager, OutputManagerHandler, - XdgShellManager, - XdgShellManagerHandler, XdgV6ShellManager, XdgV6ShellManagerHandler}; + XdgShellManager, XdgShellManagerHandler, XdgV6ShellManager, + XdgV6ShellManagerHandler, LayerShellManagerHandler, LayerShellManager}; use render::GenericRenderer; use wayland_sys::server::{wl_display, wl_event_loop, signal::wl_signal_add, WAYLAND_SERVER_HANDLE}; use wlroots_sys::{wlr_backend, wlr_backend_autocreate, wlr_backend_destroy, wlr_backend_start, wlr_compositor, wlr_compositor_create, wlr_compositor_destroy, wlr_xdg_shell_v6, wlr_xdg_shell_v6_create, - wlr_xdg_shell, wlr_xdg_shell_create}; + wlr_xdg_shell, wlr_xdg_shell_create, wlr_layer_shell_create, wlr_layer_shell}; use wlroots_sys::wayland_server::sys::wl_display_init_shm; /// Global compositor pointer, used to refer to the compositor state unsafely. @@ -80,12 +80,17 @@ pub struct Compositor { xdg_shell_manager: Option>, /// Manager for XDG shells v6. xdg_v6_shell_manager: Option>, + /// Manager for layer shells. + layer_shell_manager: Option>, /// Pointer to the xdg_shell global. /// If xdg_shell_manager is `None`, this value will be `NULL`. xdg_shell_global: *mut wlr_xdg_shell, /// Pointer to the xdg_shell_v6 global. /// If xdg_v6_shell_manager is `None`, this value will be `NULL`. xdg_v6_shell_global: *mut wlr_xdg_shell_v6, + /// Pointer to the layer_shell global. + /// If layer_shell_manager is `None`, this value will be `NULL`. + layer_shell_global: *mut wlr_layer_shell, /// Pointer to the wlr_compositor. compositor: *mut wlr_compositor, /// Pointer to the wlroots backend in use. @@ -122,6 +127,7 @@ pub struct CompositorBuilder { output_manager_handler: Option>, xdg_shell_manager_handler: Option>, xdg_v6_shell_manager_handler: Option>, + layer_shell_manager_handler: Option>, gles2: bool, server_decoration_manager: bool, data_device_manager: bool, @@ -142,6 +148,7 @@ impl CompositorBuilder { output_manager_handler: None, xdg_shell_manager_handler: None, xdg_v6_shell_manager_handler: None, + layer_shell_manager_handler: None, xwayland: None, user_terminate: None } } @@ -179,6 +186,14 @@ impl CompositorBuilder { self } + /// Set the handler for the layer shells. + pub fn layer_shell_manager(mut self, + layer_shell_manager_handler: Box) + -> Self { + self.layer_shell_manager_handler = Some(layer_shell_manager_handler); + self + } + /// Decide whether or not to enable the data device manager. /// /// This is used to do DnD, or "drag 'n drop" copy paste. @@ -311,6 +326,17 @@ impl CompositorBuilder { xdg_v6_shell_manager }); + // Set up the layer shell handler and associated Wayland global, + // if user provided a manager for it. + let mut layer_shell_global = ptr::null_mut(); + let layer_shell_manager = self.layer_shell_manager_handler.map(|handler| { + layer_shell_global = wlr_layer_shell_create(display as *mut _); + let mut layer_shell_manager = LayerShellManager::new(handler); + wl_signal_add(&mut (*layer_shell_global).events.new_surface as *mut _ as _, + layer_shell_manager.add_listener() as *mut _ as _); + layer_shell_manager + }); + // Set up the XWayland server, if the user wants it. let xwayland = self.xwayland.and_then(|manager| { Some(XWaylandServer::new(display as _, @@ -344,6 +370,8 @@ impl CompositorBuilder { xdg_shell_global, xdg_v6_shell_manager, xdg_v6_shell_global, + layer_shell_manager, + layer_shell_global, data_device_manager, compositor, backend, diff --git a/src/lib.rs b/src/lib.rs index a70bbe74..ecbce4ac 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -36,8 +36,10 @@ pub use self::events::{key_events, seat_events, tablet_pad_events, tablet_tool_e xdg_shell_v6_events, xdg_shell_events}; pub use self::manager::{InputManagerHandler, KeyboardHandler, OutputBuilder, OutputBuilderResult, OutputHandler, OutputManagerHandler, PointerHandler, TabletPadHandler, - TabletToolHandler, TouchHandler, XdgV6ShellHandler, XdgV6ShellManagerHandler, - XdgShellHandler, XdgShellManagerHandler}; + TabletToolHandler, TouchHandler, + XdgV6ShellHandler, XdgV6ShellManagerHandler, + XdgShellHandler, XdgShellManagerHandler, + LayerShellManagerHandler, LayerShellHandler}; pub use self::types::area::*; pub use self::types::cursor::*; pub use self::types::data_device::*; diff --git a/src/manager/layer_shell_handler.rs b/src/manager/layer_shell_handler.rs new file mode 100644 index 00000000..23559ce9 --- /dev/null +++ b/src/manager/layer_shell_handler.rs @@ -0,0 +1,111 @@ +//! Handler for layer shell client. + +use libc; + +use wayland_sys::server::WAYLAND_SERVER_HANDLE; +use wlroots_sys::{wlr_layer_surface, wlr_xdg_popup}; + +use compositor::{compositor_handle, CompositorHandle}; +use manager::construct_xdg_shell; +use {SurfaceHandle, LayerSurface, LayerSurfaceHandle, XdgShellSurface, XdgShellSurfaceHandle, + XdgPopup, XdgShellState, XdgShellHandler, SurfaceHandler}; + + +/// Handles events from the client layer shells. +pub trait LayerShellHandler { + /// Called when the surface is ready to be mapped. It should be added to the list of views + /// at this time. + fn on_map(&mut self, CompositorHandle, SurfaceHandle, LayerSurfaceHandle) {} + + /// Called when the surface should be unmapped. + /// + /// It should be removed from the list of views at this time, + /// but may be remapped at a later time. + fn on_unmap(&mut self, CompositorHandle, SurfaceHandle, LayerSurfaceHandle) {} + + /// Called when there is a new popup. + fn new_popup(&mut self, CompositorHandle, SurfaceHandle, LayerSurfaceHandle, XdgShellSurfaceHandle) + -> (Option>, Option>) + { (None, None) } + + /// Called when the Layer Shell is destroyed. + fn destroyed(&mut self, CompositorHandle, SurfaceHandle, LayerSurfaceHandle) {} +} + +wayland_listener!(LayerShell, (LayerSurface, Box), [ + destroy_listener => destroy_notify: |this: &mut LayerShell, data: *mut libc::c_void,| unsafe { + let layer_surface_ptr = data as *mut wlr_layer_surface; + { + let (ref shell_surface, ref mut manager) = this.data; + let surface = shell_surface.surface(); + let compositor = match compositor_handle() { + Some(handle) => handle, + None => return + }; + manager.destroyed(compositor, + surface, + shell_surface.weak_reference()); + } + ffi_dispatch!(WAYLAND_SERVER_HANDLE, + wl_list_remove, + &mut (*this.destroy_listener()).link as *mut _ as _); + ffi_dispatch!(WAYLAND_SERVER_HANDLE, + wl_list_remove, + &mut (*this.on_map_listener()).link as *mut _ as _); + ffi_dispatch!(WAYLAND_SERVER_HANDLE, + wl_list_remove, + &mut (*this.on_unmap_listener()).link as *mut _ as _); + ffi_dispatch!(WAYLAND_SERVER_HANDLE, + wl_list_remove, + &mut (*this.new_popup_listener()).link as *mut _ as _); + Box::from_raw((*layer_surface_ptr).data as *mut LayerShell); + }; + on_map_listener => on_map_notify: |this: &mut LayerShell, _data: *mut libc::c_void,| unsafe { + let (ref shell_surface, ref mut manager) = this.data; + let surface = shell_surface.surface(); + let compositor = match compositor_handle() { + Some(handle) => handle, + None => return + }; + manager.on_map(compositor, + surface, + shell_surface.weak_reference()); + }; + on_unmap_listener => on_unmap_notify: |this: &mut LayerShell, _data: *mut libc::c_void,| + unsafe { + let (ref shell_surface, ref mut manager) = this.data; + let surface = shell_surface.surface(); + let compositor = match compositor_handle() { + Some(handle) => handle, + None => return + }; + + manager.on_unmap(compositor, + surface, + shell_surface.weak_reference()); + }; + new_popup_listener => new_popup_notify: |this: &mut LayerShell, data: *mut libc::c_void,| + unsafe { + let (ref shell_surface, ref mut manager) = this.data; + let surface = shell_surface.surface(); + let compositor = match compositor_handle() { + Some(handle) => handle, + None => return + }; + let popup_ptr = data as *mut wlr_xdg_popup; + let xdg_surface_ptr = (*popup_ptr).base; + let popup = XdgPopup::from_shell(xdg_surface_ptr, popup_ptr); + let xdg_surface = XdgShellSurface::new(xdg_surface_ptr, XdgShellState::Popup(popup)); + + let res = manager.new_popup(compositor, + surface, + shell_surface.weak_reference(), + xdg_surface.weak_reference()); + if let (Some(shell_surface_handler), surface_handler) = res { + construct_xdg_shell(xdg_surface, + shell_surface_handler, + surface_handler, + xdg_surface_ptr); + } + }; +]); diff --git a/src/manager/layer_shell_manager.rs b/src/manager/layer_shell_manager.rs new file mode 100644 index 00000000..ef06d7ca --- /dev/null +++ b/src/manager/layer_shell_manager.rs @@ -0,0 +1,59 @@ +//! Manager for layer shell clients. + +use libc; +use wayland_sys::server::signal::wl_signal_add; +use wlroots_sys::wlr_layer_surface; + +use {LayerSurface, LayerSurfaceHandle, LayerShellHandler, OutputHandle}; +use super::layer_shell_handler::LayerShell; +use compositor::{compositor_handle, CompositorHandle}; + +pub trait LayerShellManagerHandler { + /// Callback that is triggered when a new layer shell surface appears. + /// + /// The output is the output that was specified by the client. + /// If it was `None` then none was specified and you *must* specify it. + /// Do so by setting the value in the `Option`. + fn new_surface(&mut self, + CompositorHandle, + LayerSurfaceHandle, + &mut Option) + -> Option>; +} + +wayland_listener!(LayerShellManager, Box, [ + add_listener => add_notify: |this: &mut LayerShellManager, data: *mut libc::c_void,| unsafe { + let ref mut manager = this.data; + let layer_surface_ptr = data as *mut wlr_layer_surface; + let compositor = match compositor_handle() { + Some(handle) => handle, + None => return + }; + wlr_log!(L_DEBUG, "New layer shell surface request {:p}", layer_surface_ptr); + let mut layer_surface = LayerSurface::new(layer_surface_ptr); + let mut output = if (*layer_surface_ptr).output.is_null() { + None + } else { + Some(OutputHandle::from_ptr((*layer_surface_ptr).output)) + }; + let new_surface_res = manager.new_surface(compositor, layer_surface.weak_reference(), &mut output); + if let Some(output) = output { + (*layer_surface_ptr).output = output.as_ptr(); + } else { + layer_surface.close(); + return + } + if let Some(layer_surface_handler) = new_surface_res { + let mut layer_surface = LayerShell::new((layer_surface, layer_surface_handler)); + wl_signal_add(&mut (*layer_surface_ptr).events.destroy as *mut _ as _, + layer_surface.destroy_listener() as _); + wl_signal_add(&mut (*layer_surface_ptr).events.map as *mut _ as _, + layer_surface.on_map_listener() as _); + wl_signal_add(&mut (*layer_surface_ptr).events.unmap as *mut _ as _, + layer_surface.on_unmap_listener() as _); + wl_signal_add(&mut (*layer_surface_ptr).events.new_popup as *mut _ as _, + layer_surface.new_popup_listener() as _); + (*layer_surface_ptr).data = Box::into_raw(layer_surface) as *mut _; + } + }; +]); diff --git a/src/manager/mod.rs b/src/manager/mod.rs index d2566cd1..0b27aaf9 100644 --- a/src/manager/mod.rs +++ b/src/manager/mod.rs @@ -8,6 +8,8 @@ mod xdg_shell_v6_manager; mod xdg_shell_v6_handler; mod xdg_shell_manager; mod xdg_shell_handler; +mod layer_shell_manager; +mod layer_shell_handler; mod tablet_pad_handler; mod tablet_tool_handler; @@ -24,3 +26,5 @@ pub use self::xdg_shell_v6_handler::*; pub use self::xdg_shell_v6_manager::*; pub use self::xdg_shell_handler::*; pub use self::xdg_shell_manager::*; +pub use self::layer_shell_handler::*; +pub use self::layer_shell_manager::*; diff --git a/src/manager/xdg_shell_manager.rs b/src/manager/xdg_shell_manager.rs index 51cfb255..0ade0498 100644 --- a/src/manager/xdg_shell_manager.rs +++ b/src/manager/xdg_shell_manager.rs @@ -47,43 +47,49 @@ wayland_listener!(XdgShellManager, Box, [ shell_surface.weak_reference()); if let (Some(shell_surface_handler), surface_handler) = new_surface_res { + construct_xdg_shell(shell_surface, shell_surface_handler, surface_handler, data); - let mut shell_surface = XdgShell::new((shell_surface, shell_surface_handler)); - let surface_state = (*(*data).surface).data as *mut InternalSurfaceState; - if let Some(surface_handler) = surface_handler { - (*(*surface_state).surface).data().1 = surface_handler; - } - - wl_signal_add(&mut (*data).events.destroy as *mut _ as _, - shell_surface.destroy_listener() as _); - wl_signal_add(&mut (*(*data).surface).events.commit as *mut _ as _, - shell_surface.commit_listener() as _); - wl_signal_add(&mut (*data).events.ping_timeout as *mut _ as _, - shell_surface.ping_timeout_listener() as _); - wl_signal_add(&mut (*data).events.new_popup as *mut _ as _, - shell_surface.new_popup_listener() as _); - let events = with_handles!([(shell_surface: {shell_surface.surface_mut()})] => { - match shell_surface.state() { - None | Some(&mut Popup(_)) => None, - Some(&mut TopLevel(ref mut toplevel)) => Some((*toplevel.as_ptr()).events) - } - }).expect("Cannot borrow xdg shell surface"); - if let Some(mut events) = events { - wl_signal_add(&mut events.request_maximize as *mut _ as _, - shell_surface.maximize_listener() as _); - wl_signal_add(&mut events.request_fullscreen as *mut _ as _, - shell_surface.fullscreen_listener() as _); - wl_signal_add(&mut events.request_minimize as *mut _ as _, - shell_surface.minimize_listener() as _); - wl_signal_add(&mut events.request_move as *mut _ as _, - shell_surface.move_listener() as _); - wl_signal_add(&mut events.request_resize as *mut _ as _, - shell_surface.resize_listener() as _); - wl_signal_add(&mut events.request_show_window_menu as *mut _ as _, - shell_surface.show_window_menu_listener() as _); - } - let shell_data = (*data).data as *mut XdgShellSurfaceState; - (*shell_data).shell = Box::into_raw(shell_surface); } }; ]); +pub(crate) unsafe fn construct_xdg_shell(shell_surface: XdgShellSurface, + shell_surface_handler: Box, + surface_handler: Option>, + data: *mut wlr_xdg_surface) { + let mut shell_surface = XdgShell::new((shell_surface, shell_surface_handler)); + let surface_state = (*(*data).surface).data as *mut InternalSurfaceState; + if let Some(surface_handler) = surface_handler { + (*(*surface_state).surface).data().1 = surface_handler; + } + + wl_signal_add(&mut (*data).events.destroy as *mut _ as _, + shell_surface.destroy_listener() as _); + wl_signal_add(&mut (*(*data).surface).events.commit as *mut _ as _, + shell_surface.commit_listener() as _); + wl_signal_add(&mut (*data).events.ping_timeout as *mut _ as _, + shell_surface.ping_timeout_listener() as _); + wl_signal_add(&mut (*data).events.new_popup as *mut _ as _, + shell_surface.new_popup_listener() as _); + let events = with_handles!([(shell_surface: {shell_surface.surface_mut()})] => { + match shell_surface.state() { + None | Some(&mut Popup(_)) => None, + Some(&mut TopLevel(ref mut toplevel)) => Some((*toplevel.as_ptr()).events) + } + }).expect("Cannot borrow xdg shell surface"); + if let Some(mut events) = events { + wl_signal_add(&mut events.request_maximize as *mut _ as _, + shell_surface.maximize_listener() as _); + wl_signal_add(&mut events.request_fullscreen as *mut _ as _, + shell_surface.fullscreen_listener() as _); + wl_signal_add(&mut events.request_minimize as *mut _ as _, + shell_surface.minimize_listener() as _); + wl_signal_add(&mut events.request_move as *mut _ as _, + shell_surface.move_listener() as _); + wl_signal_add(&mut events.request_resize as *mut _ as _, + shell_surface.resize_listener() as _); + wl_signal_add(&mut events.request_show_window_menu as *mut _ as _, + shell_surface.show_window_menu_listener() as _); + } + let shell_data = (*data).data as *mut XdgShellSurfaceState; + (*shell_data).shell = Box::into_raw(shell_surface); +} diff --git a/src/types/shell/layer_shell.rs b/src/types/shell/layer_shell.rs new file mode 100644 index 00000000..a0654294 --- /dev/null +++ b/src/types/shell/layer_shell.rs @@ -0,0 +1,380 @@ +//! wlr_layer_shell allows clients to arrange themselves in "layers" on the +//! desktop in accordance with the wlr-layer-shell protocol. +//! +//! When a client is added, the new_surface signal will be raised and passed +//! a reference to our wlr_layer_surface. At this time, the client will have +//! configured the surface as it desires, including information like +//! desired anchors and margins. +//! +//! The compositor should use this information to decide how to arrange the layer +//! on-screen, then determine the dimensions of the layer and call +//! wlr_layer_surface_configure. +//! +//! The client will then attach a buffer and commit +//! the surface, at which point the wlr_layer_surface map signal is raised and +//! the compositor should begin rendering the surface. + +use std::{panic, ptr, cell::Cell, rc::{Rc, Weak}, marker::PhantomData}; + +use libc::{c_double, c_void}; +use wlroots_sys::{wlr_layer_surface_state, wlr_layer_surface, + wlr_layer_surface_configure, wlr_layer_surface_close, + wlr_layer_surface_for_each_surface, wlr_surface, + wlr_layer_surface_surface_at, zwlr_layer_shell_v1_layer, wlr_xdg_popup}; + +use utils::c_to_rust_string; +use errors::{HandleErr, HandleResult}; + +use {SurfaceHandle, OutputHandle, XdgShellSurfaceHandle}; + +#[derive(Debug)] +pub struct LayerSurface { + liveliness: Rc>, + layer_surface: *mut wlr_layer_surface +} + +#[derive(Debug, Clone)] +/// A handle to a layer surface that can be upgraded when there are no +/// other references active to it. +pub struct LayerSurfaceHandle { + handle: Weak>, + layer_surface: *mut wlr_layer_surface +} + +/// The meta information about a layer surface. +pub struct LayerSurfaceState<'surface> { + state: *const wlr_layer_surface_state, + phantom: PhantomData<&'surface LayerSurface> +} + +/// The configuration sent with a change in state. +pub struct LayerSurfaceConfigure<'surface> { + configure: *const wlr_layer_surface_configure, + phantom: PhantomData<&'surface LayerSurface> +} + +impl <'surface> LayerSurfaceState<'surface> { + unsafe fn new<'unbound>(state: *const wlr_layer_surface_state) -> Self { + LayerSurfaceState { state, phantom: PhantomData } + } + + pub fn anchor(&self) -> u32 { + unsafe { (*self.state).anchor } + } + + pub fn exclusive_zone(&self) -> i32 { + unsafe { (*self.state).exclusive_zone } + } + + /// Get the margin in this format: (top, right, bottom, left). + pub fn margin(&self) -> (u32, u32, u32, u32) { + unsafe { + ((*self.state).margin.top, + (*self.state).margin.right, + (*self.state).margin.bottom, + (*self.state).margin.left) + } + } + + pub fn keyboard_interactive(&self) -> bool { + unsafe { (*self.state).keyboard_interactive } + } + + /// Get the desired size of the surface in (width, height) format. + pub fn desired_size(&self) -> (u32, u32) { + unsafe { ((*self.state).desired_width, (*self.state).desired_height) } + } + + /// Get the desired size of the surface in (width, height) format. + pub fn actual_size(&self) -> (u32, u32) { + unsafe { ((*self.state).actual_width, (*self.state).actual_height) } + } +} + +impl <'surface> LayerSurfaceConfigure<'surface> { + unsafe fn new<'unbound>(configure: *const wlr_layer_surface_configure) -> Self { + LayerSurfaceConfigure { configure, phantom: PhantomData } + } + + pub fn serial(&self) -> u32 { + unsafe { (*self.configure).serial } + } + + pub fn state(&'surface self) -> LayerSurfaceState<'surface> { + unsafe { LayerSurfaceState::new(&(*self.configure).state) } + } +} + +impl LayerSurface { + pub(crate) unsafe fn new(layer_surface: *mut wlr_layer_surface) -> Self { + let liveliness = Rc::new(Cell::new(false)); + LayerSurface { liveliness, + layer_surface } + } + + unsafe fn from_handle(handle: &LayerSurfaceHandle) -> HandleResult { + let liveliness = handle.handle + .upgrade() + .ok_or_else(|| HandleErr::AlreadyDropped)?; + Ok(LayerSurface { liveliness, layer_surface: handle.as_ptr() }) + } + + /// Creates a weak reference to a `LayerSurface`. + pub fn weak_reference(&self) -> LayerSurfaceHandle { + LayerSurfaceHandle { handle: Rc::downgrade(&self.liveliness), + layer_surface: self.layer_surface } + } + + /// Gets the surface used by this Layer shell. + pub fn surface(&self) -> SurfaceHandle { + unsafe { + let surface = (*self.layer_surface).surface; + if surface.is_null() { + panic!("Layer surface had a null surface!") + } + SurfaceHandle::from_ptr(surface) + } + } + + pub fn output(&self) -> OutputHandle { + unsafe { + let output = (*self.layer_surface).output; + if output.is_null() { + panic!("Layer surface had a null output!") + } + OutputHandle::from_ptr(output) + } + } + + /// Get a list of all the popups for this layer surface. + pub fn popups(&self) -> Vec { + unsafe { + let mut result = vec![]; + wl_list_for_each!((*self.layer_surface).popups, + link, + (popup: wlr_xdg_popup) => { + result.push(XdgShellSurfaceHandle::from_ptr((*popup).base)) + }); + result + } + } + + /// Get the namespace this surface resides in. + pub fn namespace(&self) -> Option { + unsafe { c_to_rust_string((*self.layer_surface).namespace) } + } + + pub fn layer(&self) -> zwlr_layer_shell_v1_layer { + unsafe { (*self.layer_surface).layer } + } + + pub fn added(&self) -> bool { + unsafe { (*self.layer_surface).added } + } + + pub fn configured(&self) -> bool { + unsafe { (*self.layer_surface).configured } + } + + pub fn mapped(&self) -> bool { + unsafe { (*self.layer_surface).mapped } + } + + pub fn closed(&self) -> bool { + unsafe { (*self.layer_surface).closed} + } + + pub fn configure_serial(&self) -> u32 { + unsafe { (*self.layer_surface).configure_serial } + } + + pub fn configure_next_serial(&self) -> u32 { + unsafe { (*self.layer_surface).configure_next_serial } + } + + pub fn configure_list<'surface>(&'surface self) -> Vec> { + let mut result = Vec::new(); + unsafe { + wl_list_for_each!((*self.layer_surface).configure_list, + link, + (configure: wlr_layer_surface_configure) => { + result.push(LayerSurfaceConfigure::new(configure)) + }); + } + result + } + + pub fn acked_configure<'surface>(&'surface self) -> Option> { + unsafe { + let acked_configure = (*self.layer_surface).acked_configure; + if acked_configure.is_null() { + None + } else { + Some(LayerSurfaceConfigure::new(acked_configure)) + } + } + } + + pub fn client_pending<'surface>(&'surface self) -> LayerSurfaceState<'surface> { + unsafe { + LayerSurfaceState::new(&(*self.layer_surface).client_pending) + } + } + + pub fn server_pending<'surface>(&'surface self) -> LayerSurfaceState<'surface> { + unsafe { + LayerSurfaceState::new(&(*self.layer_surface).server_pending) + } + } + + pub fn current<'surface>(&'surface self) -> LayerSurfaceState<'surface> { + unsafe { + LayerSurfaceState::new(&(*self.layer_surface).current) + } + } + + /// Unmaps this layer surface and notifies the client that it has been closed. + pub fn close(&mut self) { + unsafe { + wlr_layer_surface_close(self.layer_surface) + } + } + + /// Find a surface within this layer-surface tree at the given surface-local + /// coordinates. + /// + //// Returns the surface and coordinates in the leaf surface + /// coordinate system or None if no surface is found at that location. + /// + /// Return coordinates are in (x, y) format + pub fn surface_at(&self, sx: c_double, sy: c_double) -> Option<(SurfaceHandle, c_double, c_double)> { + unsafe { + let (mut sub_x, mut sub_y) = (0.0, 0.0); + let surface_ptr = wlr_layer_surface_surface_at(self.layer_surface, sx, sy, &mut sub_x, &mut sub_y); + if surface_ptr.is_null() { + None + } else { + Some((SurfaceHandle::from_ptr(surface_ptr), sub_x, sub_y)) + } + } + } + + /// Calls the iterator function for each sub-surface and popup of this surface + pub fn for_each_surface(&self, mut iterator: &mut FnMut(SurfaceHandle, i32, i32)) { + unsafe extern "C" fn c_iterator(wlr_surface: *mut wlr_surface, sx: i32, sy: i32, data: *mut c_void) { + let iterator = &mut *(data as *mut &mut FnMut(SurfaceHandle, i32, i32)); + let surface = SurfaceHandle::from_ptr(wlr_surface); + iterator(surface, sx, sy); + } + unsafe { + let iterator_ptr: *mut c_void = &mut iterator as *mut _ as *mut c_void; + wlr_layer_surface_for_each_surface(self.layer_surface, Some(c_iterator), iterator_ptr); + } + } +} + +impl Drop for LayerSurface { + fn drop(&mut self) { + if Rc::strong_count(&self.liveliness) == 1 { + wlr_log!(L_DEBUG, "Dropped Layer Shell Surface {:p}", self.layer_surface); + let weak_count = Rc::weak_count(&self.liveliness); + if weak_count > 0 { + wlr_log!(L_DEBUG, + "Still {} weak pointers to Layer Shell Surface {:p}", + weak_count, self.layer_surface); + } + } + } +} + +impl LayerSurfaceHandle { + /// Constructs a new LayerSurfaceHandle that is always invalid. Calling `run` on this + /// will always fail. + /// + /// This is useful for pre-filling a value before it's provided by the server, or + /// for mocking/testing. + pub fn new() -> Self { + unsafe { + LayerSurfaceHandle { handle: Weak::new(), + layer_surface: ptr::null_mut() } + } + } + + /// Upgrades the wayland shell handle to a reference to the backing `LayerSurface`. + /// + /// # Unsafety + /// This function is unsafe, because it creates an unbound `LayerSurface` + /// which may live forever.. + /// But no surface lives forever and might be disconnected at any time. + pub(crate) unsafe fn upgrade(&self) -> HandleResult { + self.handle.upgrade() + .ok_or(HandleErr::AlreadyDropped) + // NOTE + // We drop the Rc here because having two would allow a dangling + // pointer to exist! + .and_then(|check| { + let shell_surface = LayerSurface::from_handle(self)?; + if check.get() { + return Err(HandleErr::AlreadyBorrowed) + } + check.set(true); + Ok(shell_surface) + }) + } + + /// Run a function on the referenced LayerSurface, if it still exists + /// + /// Returns the result of the function, if successful + /// + /// # Safety + /// By enforcing a rather harsh limit on the lifetime of the output + /// to a short lived scope of an anonymous function, + /// this function ensures the LayerSurface does not live longer + /// than it exists. + /// + /// # Panics + /// This function will panic if multiple mutable borrows are detected. + /// This will happen if you call `upgrade` directly within this callback, + /// or if you run this function within the another run to the same `LayerSurface`. + /// + /// So don't nest `run` calls and everything will be ok :). + pub fn run(&self, runner: F) -> HandleResult + where F: FnOnce(&mut LayerSurface) -> R + { + let mut layer_surface = unsafe { self.upgrade()? }; + let res = panic::catch_unwind(panic::AssertUnwindSafe(|| runner(&mut layer_surface))); + self.handle.upgrade().map(|check| { + // Sanity check that it hasn't been tampered with. + if !check.get() { + wlr_log!(L_ERROR, + "After running LayerSurface callback, \ + mutable lock was false for: {:?}", + layer_surface); + panic!("Lock in incorrect state!"); + } + check.set(false); + }); + match res { + Ok(res) => Ok(res), + Err(err) => panic::resume_unwind(err) + } + } + + unsafe fn as_ptr(&self) -> *mut wlr_layer_surface { + self.layer_surface + } +} + +impl Default for LayerSurfaceHandle { + fn default() -> Self { + LayerSurfaceHandle::new() + } +} + +impl PartialEq for LayerSurfaceHandle { + fn eq(&self, other: &LayerSurfaceHandle) -> bool { + self.layer_surface == other.layer_surface + } +} + +impl Eq for LayerSurfaceHandle {} diff --git a/src/types/shell/mod.rs b/src/types/shell/mod.rs index 77e342f8..3af7274d 100644 --- a/src/types/shell/mod.rs +++ b/src/types/shell/mod.rs @@ -1,5 +1,7 @@ mod xdg_shell_v6; mod xdg_shell; +mod layer_shell; pub use self::xdg_shell_v6::*; pub use self::xdg_shell::*; +pub use self::layer_shell::*; diff --git a/src/types/shell/xdg_shell_v6.rs b/src/types/shell/xdg_shell_v6.rs index 0dd11b4c..cfe8f876 100644 --- a/src/types/shell/xdg_shell_v6.rs +++ b/src/types/shell/xdg_shell_v6.rs @@ -182,6 +182,10 @@ impl XdgV6ShellSurface { } } + /// Call `iterator` on each surface in the xdg-surface tree, with the surface's + /// position relative to the root xdg-surface. + /// + /// The function is called from root to leaves (in rendering order). pub fn for_each_surface(&self, mut iterator: &mut FnMut(SurfaceHandle, i32, i32)) { unsafe { unsafe extern "C" fn c_iterator(wlr_surface: *mut wlr_surface, sx: i32, sy: i32, data: *mut c_void) { @@ -195,10 +199,6 @@ impl XdgV6ShellSurface { } /// Creates a weak reference to an `XdgV6ShellSurface`. - /// - /// # Panics - /// If this `XdgV6ShellSurface` is a previously upgraded `XdgV6ShellSurfaceHandle`, - /// then this function will panic. pub fn weak_reference(&self) -> XdgV6ShellSurfaceHandle { XdgV6ShellSurfaceHandle { handle: Rc::downgrade(&self.liveliness), state: match self.state { diff --git a/src/types/surface/surface.rs b/src/types/surface/surface.rs index 0d53bd2e..eb150f25 100644 --- a/src/types/surface/surface.rs +++ b/src/types/surface/surface.rs @@ -8,7 +8,7 @@ use wayland_sys::server::signal::wl_signal_add; use wlroots_sys::{timespec, wlr_subsurface, wlr_surface, wlr_surface_get_root_surface, wlr_surface_has_buffer, wlr_surface_point_accepts_input, wlr_surface_send_enter, wlr_surface_send_frame_done, wlr_surface_send_leave, wlr_surface_surface_at, - wlr_surface_is_xdg_surface}; + wlr_surface_is_layer_surface, wlr_surface_is_xdg_surface}; use super::{Subsurface, SubsurfaceHandle, SubsurfaceHandler, SubsurfaceManager, SurfaceState, InternalSubsurface}; @@ -300,6 +300,13 @@ impl Surface { unsafe { (*self.surface).surface_to_buffer_matrix } } + /// Determines if this surface is a layer surface. + pub fn is_layer_surface(&self) -> bool { + unsafe { + wlr_surface_is_layer_surface(self.surface) + } + } + /// Creates a weak reference to a `Surface`. /// /// # Panics diff --git a/wlroots-sys/build.rs b/wlroots-sys/build.rs index 7cbe2131..4f328519 100644 --- a/wlroots-sys/build.rs +++ b/wlroots-sys/build.rs @@ -127,7 +127,7 @@ fn meson() { } /// Gets the unstable and stable protocols in /usr/share-wayland-protocols and -/// generates server headers for them. +/// in wlroots/protocol. /// /// The path to the folder with the generated headers is returned. It will /// have two directories, `stable`, and `unstable`. @@ -157,6 +157,26 @@ fn generate_protocol_headers() -> io::Result { .unwrap(); } } + for entry in fs::read_dir("./wlroots/protocol")? { + let entry = entry?; + let path = entry.path(); + let mut filename = entry.file_name().into_string().unwrap(); + if filename.ends_with(".xml") { + let new_length = filename.len() - 4; + filename.truncate(new_length); + } else { + continue + } + filename.push_str("-protocol"); + Command::new("wayland-scanner").arg("server-header") + .arg(path.clone()) + .arg(format!("{}/{}.h", + out_path.to_str().unwrap(), + filename)) + .status() + .unwrap(); + } + Ok(out_path) } diff --git a/wlroots-sys/src/wlroots.h b/wlroots-sys/src/wlroots.h index 01f08b16..3ae033f8 100644 --- a/wlroots-sys/src/wlroots.h +++ b/wlroots-sys/src/wlroots.h @@ -41,6 +41,7 @@ #include #include #include +#include #include /// Util includes