diff --git a/src/lib.rs b/src/lib.rs index 09fb217..98ef926 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -35,7 +35,7 @@ pub mod backend; pub mod compositor; pub mod shm; pub mod keyboard; -//pub mod shell; +pub mod shell; fn slog_or_stdlog(logger: L) -> ::slog::Logger where 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 -);